PageRenderTime 270ms CodeModel.GetById 245ms RepoModel.GetById 0ms app.codeStats 0ms

/mordor/streams/buffer.cpp

http://github.com/mozy/mordor
C++ | 991 lines | 889 code | 80 blank | 22 comment | 253 complexity | 384e932818cfd8234d63602ec8886cdb MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include "buffer.h"
  3. #include <string.h>
  4. #include <algorithm>
  5. #include "mordor/assert.h"
  6. #include "mordor/util.h"
  7. #ifdef WINDOWS
  8. static u_long iovLength(size_t length)
  9. {
  10. return (u_long)std::min<size_t>(length, 0xffffffff);
  11. }
  12. #else
  13. static size_t iovLength(size_t length)
  14. {
  15. return length;
  16. }
  17. #endif
  18. namespace Mordor {
  19. Buffer::SegmentData::SegmentData()
  20. {
  21. start(NULL);
  22. length(0);
  23. }
  24. Buffer::SegmentData::SegmentData(size_t length)
  25. {
  26. m_array.reset(new unsigned char[length]);
  27. start(m_array.get());
  28. this->length(length);
  29. }
  30. Buffer::SegmentData::SegmentData(void *buffer, size_t length)
  31. {
  32. m_array.reset((unsigned char *)buffer, &nop<unsigned char *>);
  33. start(m_array.get());
  34. this->length(length);
  35. }
  36. Buffer::SegmentData
  37. Buffer::SegmentData::slice(size_t start, size_t length)
  38. {
  39. if (length == (size_t)~0)
  40. length = this->length() - start;
  41. MORDOR_ASSERT(start <= this->length());
  42. MORDOR_ASSERT(length + start <= this->length());
  43. SegmentData result;
  44. result.m_array = m_array;
  45. result.start((unsigned char*)this->start() + start);
  46. result.length(length);
  47. return result;
  48. }
  49. const Buffer::SegmentData
  50. Buffer::SegmentData::slice(size_t start, size_t length) const
  51. {
  52. if (length == (size_t)~0)
  53. length = this->length() - start;
  54. MORDOR_ASSERT(start <= this->length());
  55. MORDOR_ASSERT(length + start <= this->length());
  56. SegmentData result;
  57. result.m_array = m_array;
  58. result.start((unsigned char*)this->start() + start);
  59. result.length(length);
  60. return result;
  61. }
  62. void
  63. Buffer::SegmentData::extend(size_t length)
  64. {
  65. // NO CHECKS FOR BUFFER OVERRUN!!
  66. m_length += length;
  67. }
  68. Buffer::Segment::Segment(size_t length)
  69. : m_writeIndex(0), m_data(length)
  70. {
  71. invariant();
  72. }
  73. Buffer::Segment::Segment(Buffer::SegmentData data)
  74. : m_writeIndex(data.length()), m_data(data)
  75. {
  76. invariant();
  77. }
  78. Buffer::Segment::Segment(void *buffer, size_t length)
  79. : m_writeIndex(0), m_data(buffer, length)
  80. {
  81. invariant();
  82. }
  83. size_t
  84. Buffer::Segment::readAvailable() const
  85. {
  86. invariant();
  87. return m_writeIndex;
  88. }
  89. size_t
  90. Buffer::Segment::writeAvailable() const
  91. {
  92. invariant();
  93. return m_data.length() - m_writeIndex;
  94. }
  95. size_t
  96. Buffer::Segment::length() const
  97. {
  98. invariant();
  99. return m_data.length();
  100. }
  101. void
  102. Buffer::Segment::produce(size_t length)
  103. {
  104. MORDOR_ASSERT(length <= writeAvailable());
  105. m_writeIndex += length;
  106. invariant();
  107. }
  108. void
  109. Buffer::Segment::consume(size_t length)
  110. {
  111. MORDOR_ASSERT(length <= readAvailable());
  112. m_writeIndex -= length;
  113. m_data = m_data.slice(length);
  114. invariant();
  115. }
  116. void
  117. Buffer::Segment::truncate(size_t length)
  118. {
  119. MORDOR_ASSERT(length <= readAvailable());
  120. MORDOR_ASSERT(m_writeIndex = readAvailable());
  121. m_writeIndex = length;
  122. m_data = m_data.slice(0, length);
  123. invariant();
  124. }
  125. void
  126. Buffer::Segment::extend(size_t length)
  127. {
  128. m_data.extend(length);
  129. m_writeIndex += length;
  130. }
  131. const Buffer::SegmentData
  132. Buffer::Segment::readBuffer() const
  133. {
  134. invariant();
  135. return m_data.slice(0, m_writeIndex);
  136. }
  137. Buffer::SegmentData
  138. Buffer::Segment::writeBuffer()
  139. {
  140. invariant();
  141. return m_data.slice(m_writeIndex);
  142. }
  143. const Buffer::SegmentData
  144. Buffer::Segment::writeBuffer() const
  145. {
  146. invariant();
  147. return m_data.slice(m_writeIndex);
  148. }
  149. void
  150. Buffer::Segment::invariant() const
  151. {
  152. MORDOR_ASSERT(m_writeIndex <= m_data.length());
  153. }
  154. Buffer::Buffer()
  155. {
  156. m_readAvailable = m_writeAvailable = 0;
  157. m_writeIt = m_segments.end();
  158. invariant();
  159. }
  160. Buffer::Buffer(const Buffer &copy)
  161. {
  162. m_readAvailable = m_writeAvailable = 0;
  163. m_writeIt = m_segments.end();
  164. copyIn(copy);
  165. }
  166. Buffer::Buffer(const char *string)
  167. {
  168. m_readAvailable = m_writeAvailable = 0;
  169. m_writeIt = m_segments.end();
  170. copyIn(string, strlen(string));
  171. }
  172. Buffer::Buffer(const std::string &string)
  173. {
  174. m_readAvailable = m_writeAvailable = 0;
  175. m_writeIt = m_segments.end();
  176. copyIn(string);
  177. }
  178. Buffer::Buffer(const void *data, size_t length)
  179. {
  180. m_readAvailable = m_writeAvailable = 0;
  181. m_writeIt = m_segments.end();
  182. copyIn(data, length);
  183. }
  184. Buffer &
  185. Buffer::operator =(const Buffer &copy)
  186. {
  187. clear();
  188. copyIn(copy);
  189. return *this;
  190. }
  191. size_t
  192. Buffer::readAvailable() const
  193. {
  194. return m_readAvailable;
  195. }
  196. size_t
  197. Buffer::writeAvailable() const
  198. {
  199. return m_writeAvailable;
  200. }
  201. size_t
  202. Buffer::segments() const
  203. {
  204. return m_segments.size();
  205. }
  206. void
  207. Buffer::adopt(void *buffer, size_t length)
  208. {
  209. invariant();
  210. Segment newSegment(buffer, length);
  211. if (readAvailable() == 0) {
  212. // put the new buffer at the front if possible to avoid
  213. // fragmentation
  214. m_segments.push_front(newSegment);
  215. m_writeIt = m_segments.begin();
  216. } else {
  217. m_segments.push_back(newSegment);
  218. if (m_writeAvailable == 0) {
  219. m_writeIt = m_segments.end();
  220. --m_writeIt;
  221. }
  222. }
  223. m_writeAvailable += length;
  224. invariant();
  225. }
  226. void
  227. Buffer::reserve(size_t length)
  228. {
  229. if (writeAvailable() < length) {
  230. // over-reserve to avoid fragmentation
  231. Segment newSegment(length * 2 - writeAvailable());
  232. if (readAvailable() == 0) {
  233. // put the new buffer at the front if possible to avoid
  234. // fragmentation
  235. m_segments.push_front(newSegment);
  236. m_writeIt = m_segments.begin();
  237. } else {
  238. m_segments.push_back(newSegment);
  239. if (m_writeAvailable == 0) {
  240. m_writeIt = m_segments.end();
  241. --m_writeIt;
  242. }
  243. }
  244. m_writeAvailable += newSegment.length();
  245. invariant();
  246. }
  247. }
  248. void
  249. Buffer::compact()
  250. {
  251. invariant();
  252. if (m_writeIt != m_segments.end()) {
  253. if (m_writeIt->readAvailable() > 0) {
  254. Segment newSegment = Segment(m_writeIt->readBuffer());
  255. m_segments.insert(m_writeIt, newSegment);
  256. }
  257. m_writeIt = m_segments.erase(m_writeIt, m_segments.end());
  258. m_writeAvailable = 0;
  259. }
  260. MORDOR_ASSERT(writeAvailable() == 0);
  261. }
  262. void
  263. Buffer::clear(bool clearWriteAvailableAsWell)
  264. {
  265. invariant();
  266. if (clearWriteAvailableAsWell) {
  267. m_readAvailable = m_writeAvailable = 0;
  268. m_segments.clear();
  269. m_writeIt = m_segments.end();
  270. } else {
  271. m_readAvailable = 0;
  272. if (m_writeIt != m_segments.end() && m_writeIt->readAvailable())
  273. m_writeIt->consume(m_writeIt->readAvailable());
  274. m_segments.erase(m_segments.begin(), m_writeIt);
  275. }
  276. invariant();
  277. MORDOR_ASSERT(m_readAvailable == 0);
  278. }
  279. void
  280. Buffer::produce(size_t length)
  281. {
  282. MORDOR_ASSERT(length <= writeAvailable());
  283. m_readAvailable += length;
  284. m_writeAvailable -= length;
  285. while (length > 0) {
  286. Segment &segment = *m_writeIt;
  287. size_t toProduce = (std::min)(segment.writeAvailable(), length);
  288. segment.produce(toProduce);
  289. length -= toProduce;
  290. if (segment.writeAvailable() == 0)
  291. ++m_writeIt;
  292. }
  293. MORDOR_ASSERT(length == 0);
  294. invariant();
  295. }
  296. void
  297. Buffer::consume(size_t length)
  298. {
  299. MORDOR_ASSERT(length <= readAvailable());
  300. m_readAvailable -= length;
  301. while (length > 0) {
  302. Segment &segment = *m_segments.begin();
  303. size_t toConsume = (std::min)(segment.readAvailable(), length);
  304. segment.consume(toConsume);
  305. length -= toConsume;
  306. if (segment.length() == 0)
  307. m_segments.pop_front();
  308. }
  309. MORDOR_ASSERT(length == 0);
  310. invariant();
  311. }
  312. void
  313. Buffer::truncate(size_t length)
  314. {
  315. MORDOR_ASSERT(length <= readAvailable());
  316. if (length == m_readAvailable)
  317. return;
  318. // Split any mixed read/write bufs
  319. if (m_writeIt != m_segments.end() && m_writeIt->readAvailable() != 0) {
  320. m_segments.insert(m_writeIt, Segment(m_writeIt->readBuffer()));
  321. m_writeIt->consume(m_writeIt->readAvailable());
  322. }
  323. m_readAvailable = length;
  324. std::list<Segment>::iterator it;
  325. for (it = m_segments.begin(); it != m_segments.end() && length > 0; ++it) {
  326. Segment &segment = *it;
  327. if (length <= segment.readAvailable()) {
  328. segment.truncate(length);
  329. length = 0;
  330. ++it;
  331. break;
  332. } else {
  333. length -= segment.readAvailable();
  334. }
  335. }
  336. MORDOR_ASSERT(length == 0);
  337. while (it != m_segments.end() && it->readAvailable() > 0) {
  338. MORDOR_ASSERT(it->writeAvailable() == 0);
  339. it = m_segments.erase(it);
  340. }
  341. invariant();
  342. }
  343. const std::vector<iovec>
  344. Buffer::readBuffers(size_t length) const
  345. {
  346. if (length == (size_t)~0)
  347. length = readAvailable();
  348. MORDOR_ASSERT(length <= readAvailable());
  349. std::vector<iovec> result;
  350. result.reserve(m_segments.size());
  351. size_t remaining = length;
  352. std::list<Segment>::const_iterator it;
  353. for (it = m_segments.begin(); it != m_segments.end(); ++it) {
  354. size_t toConsume = (std::min)(it->readAvailable(), remaining);
  355. SegmentData data = it->readBuffer().slice(0, toConsume);
  356. #ifdef WINDOWS
  357. while (data.length() > 0) {
  358. iovec wsabuf;
  359. wsabuf.iov_base = (void *)data.start();
  360. wsabuf.iov_len = iovLength(data.length());
  361. result.push_back(wsabuf);
  362. data = data.slice(wsabuf.iov_len);
  363. }
  364. #else
  365. iovec iov;
  366. iov.iov_base = (void *)data.start();
  367. iov.iov_len = data.length();
  368. result.push_back(iov);
  369. #endif
  370. remaining -= toConsume;
  371. if (remaining == 0)
  372. break;
  373. }
  374. MORDOR_ASSERT(remaining == 0);
  375. invariant();
  376. return result;
  377. }
  378. const iovec
  379. Buffer::readBuffer(size_t length, bool coalesce) const
  380. {
  381. iovec result;
  382. result.iov_base = NULL;
  383. result.iov_len = 0;
  384. if (length == (size_t)~0)
  385. length = readAvailable();
  386. MORDOR_ASSERT(length <= readAvailable());
  387. if (readAvailable() == 0)
  388. return result;
  389. // Optimize case where all that is requested is contained in the first
  390. // buffer
  391. if (m_segments.front().readAvailable() >= length) {
  392. SegmentData data = m_segments.front().readBuffer().slice(0, length);
  393. result.iov_base = data.start();
  394. result.iov_len = iovLength(data.length());
  395. return result;
  396. }
  397. // If they don't want us to coalesce, just return as much as we can from
  398. // the first segment
  399. if (!coalesce) {
  400. SegmentData data = m_segments.front().readBuffer();
  401. result.iov_base = data.start();
  402. result.iov_len = iovLength(data.length());
  403. return result;
  404. }
  405. // Breaking constness!
  406. Buffer* _this = const_cast<Buffer*>(this);
  407. // try to avoid allocation
  408. if (m_writeIt != m_segments.end() && m_writeIt->writeAvailable()
  409. >= readAvailable()) {
  410. copyOut(m_writeIt->writeBuffer().start(), readAvailable());
  411. Segment newSegment = Segment(m_writeIt->writeBuffer().slice(0,
  412. readAvailable()));
  413. _this->m_segments.clear();
  414. _this->m_segments.push_back(newSegment);
  415. _this->m_writeAvailable = 0;
  416. _this->m_writeIt = _this->m_segments.end();
  417. invariant();
  418. SegmentData data = newSegment.readBuffer().slice(0, length);
  419. result.iov_base = data.start();
  420. result.iov_len = iovLength(data.length());
  421. return result;
  422. }
  423. Segment newSegment = Segment(readAvailable());
  424. copyOut(newSegment.writeBuffer().start(), readAvailable());
  425. newSegment.produce(readAvailable());
  426. _this->m_segments.clear();
  427. _this->m_segments.push_back(newSegment);
  428. _this->m_writeAvailable = 0;
  429. _this->m_writeIt = _this->m_segments.end();
  430. invariant();
  431. SegmentData data = newSegment.readBuffer().slice(0, length);
  432. result.iov_base = data.start();
  433. result.iov_len = iovLength(data.length());
  434. return result;
  435. }
  436. std::vector<iovec>
  437. Buffer::writeBuffers(size_t length)
  438. {
  439. if (length == (size_t)~0)
  440. length = writeAvailable();
  441. reserve(length);
  442. std::vector<iovec> result;
  443. result.reserve(m_segments.size());
  444. size_t remaining = length;
  445. std::list<Segment>::iterator it = m_writeIt;
  446. while (remaining > 0) {
  447. Segment& segment = *it;
  448. size_t toProduce = (std::min)(segment.writeAvailable(), remaining);
  449. SegmentData data = segment.writeBuffer().slice(0, toProduce);
  450. #ifdef WINDOWS
  451. while (data.length() > 0) {
  452. iovec wsabuf;
  453. wsabuf.iov_base = (void *)data.start();
  454. wsabuf.iov_len = iovLength(data.length());
  455. result.push_back(wsabuf);
  456. data = data.slice(wsabuf.iov_len);
  457. }
  458. #else
  459. iovec iov;
  460. iov.iov_base = (void *)data.start();
  461. iov.iov_len = data.length();
  462. result.push_back(iov);
  463. #endif
  464. remaining -= toProduce;
  465. ++it;
  466. }
  467. MORDOR_ASSERT(remaining == 0);
  468. invariant();
  469. return result;
  470. }
  471. iovec
  472. Buffer::writeBuffer(size_t length, bool coalesce)
  473. {
  474. iovec result;
  475. result.iov_base = NULL;
  476. result.iov_len = 0;
  477. if (length == 0u)
  478. return result;
  479. // Must allocate just the write segment
  480. if (writeAvailable() == 0) {
  481. reserve(length);
  482. MORDOR_ASSERT(m_writeIt != m_segments.end());
  483. MORDOR_ASSERT(m_writeIt->writeAvailable() >= length);
  484. SegmentData data = m_writeIt->writeBuffer().slice(0, length);
  485. result.iov_base = data.start();
  486. result.iov_len = iovLength(data.length());
  487. return result;
  488. }
  489. // Can use an existing write segment
  490. if (writeAvailable() > 0 && m_writeIt->writeAvailable() >= length) {
  491. SegmentData data = m_writeIt->writeBuffer().slice(0, length);
  492. result.iov_base = data.start();
  493. result.iov_len = iovLength(data.length());
  494. return result;
  495. }
  496. // If they don't want us to coalesce, just return as much as we can from
  497. // the first segment
  498. if (!coalesce) {
  499. SegmentData data = m_writeIt->writeBuffer();
  500. result.iov_base = data.start();
  501. result.iov_len = iovLength(data.length());
  502. return result;
  503. }
  504. // Existing bufs are insufficient... remove them and reserve anew
  505. compact();
  506. reserve(length);
  507. MORDOR_ASSERT(m_writeIt != m_segments.end());
  508. MORDOR_ASSERT(m_writeIt->writeAvailable() >= length);
  509. SegmentData data = m_writeIt->writeBuffer().slice(0, length);
  510. result.iov_base = data.start();
  511. result.iov_len = iovLength(data.length());
  512. return result;
  513. }
  514. void
  515. Buffer::copyIn(const Buffer &buffer, size_t length, size_t pos)
  516. {
  517. if (pos > buffer.readAvailable())
  518. MORDOR_THROW_EXCEPTION(std::out_of_range("position out of range"));
  519. if (length == (size_t)~0)
  520. length = buffer.readAvailable() - pos;
  521. MORDOR_ASSERT(buffer.readAvailable() >= length + pos);
  522. invariant();
  523. if (length == 0)
  524. return;
  525. // Split any mixed read/write bufs
  526. if (m_writeIt != m_segments.end() && m_writeIt->readAvailable() != 0) {
  527. m_segments.insert(m_writeIt, Segment(m_writeIt->readBuffer()));
  528. m_writeIt->consume(m_writeIt->readAvailable());
  529. invariant();
  530. }
  531. std::list<Segment>::const_iterator it = buffer.m_segments.begin();
  532. while (pos != 0 && it != buffer.m_segments.end()) {
  533. if (pos < it->readAvailable())
  534. break;
  535. pos -= it->readAvailable();
  536. ++it;
  537. }
  538. MORDOR_ASSERT(it != buffer.m_segments.end());
  539. for (; it != buffer.m_segments.end(); ++it) {
  540. size_t toConsume = (std::min)(it->readAvailable() - pos, length);
  541. if (m_readAvailable != 0 && it == buffer.m_segments.begin()) {
  542. std::list<Segment>::iterator previousIt = m_writeIt;
  543. --previousIt;
  544. if ((char *)previousIt->readBuffer().start() +
  545. previousIt->readBuffer().length() == (char *)it->readBuffer().start() + pos &&
  546. previousIt->m_data.m_array.get() == it->m_data.m_array.get()) {
  547. MORDOR_ASSERT(previousIt->writeAvailable() == 0);
  548. previousIt->extend(toConsume);
  549. m_readAvailable += toConsume;
  550. length -= toConsume;
  551. pos = 0;
  552. if (length == 0)
  553. break;
  554. continue;
  555. }
  556. }
  557. Segment newSegment = Segment(it->readBuffer().slice(pos, toConsume));
  558. m_segments.insert(m_writeIt, newSegment);
  559. m_readAvailable += toConsume;
  560. length -= toConsume;
  561. pos = 0;
  562. if (length == 0)
  563. break;
  564. }
  565. MORDOR_ASSERT(length == 0);
  566. MORDOR_ASSERT(readAvailable() >= length);
  567. }
  568. void
  569. Buffer::copyIn(const void *data, size_t length)
  570. {
  571. invariant();
  572. while (m_writeIt != m_segments.end() && length > 0) {
  573. size_t todo = (std::min)(length, m_writeIt->writeAvailable());
  574. memcpy(m_writeIt->writeBuffer().start(), data, todo);
  575. m_writeIt->produce(todo);
  576. m_writeAvailable -= todo;
  577. m_readAvailable += todo;
  578. data = (unsigned char*)data + todo;
  579. length -= todo;
  580. if (m_writeIt->writeAvailable() == 0)
  581. ++m_writeIt;
  582. invariant();
  583. }
  584. if (length > 0) {
  585. Segment newSegment(length);
  586. memcpy(newSegment.writeBuffer().start(), data, length);
  587. newSegment.produce(length);
  588. m_segments.push_back(newSegment);
  589. m_readAvailable += length;
  590. }
  591. MORDOR_ASSERT(readAvailable() >= length);
  592. }
  593. void
  594. Buffer::copyIn(const std::string &string)
  595. {
  596. return copyIn(string.c_str(), string.size());
  597. }
  598. void
  599. Buffer::copyIn(const char *string)
  600. {
  601. copyIn(string, strlen(string));
  602. }
  603. void
  604. Buffer::copyOut(void *buffer, size_t length, size_t pos) const
  605. {
  606. if (length == 0)
  607. return;
  608. MORDOR_ASSERT(length + pos <= readAvailable());
  609. unsigned char *next = (unsigned char*)buffer;
  610. std::list<Segment>::const_iterator it = m_segments.begin();
  611. while (pos != 0 && it != m_segments.end()) {
  612. if (pos < it->readAvailable())
  613. break;
  614. pos -= it->readAvailable();
  615. ++it;
  616. }
  617. MORDOR_ASSERT(it != m_segments.end());
  618. for (; it != m_segments.end(); ++it) {
  619. size_t todo = (std::min)(length, it->readAvailable() - pos);
  620. memcpy(next, (char *)it->readBuffer().start() + pos, todo);
  621. next += todo;
  622. length -= todo;
  623. pos = 0;
  624. if (length == 0)
  625. break;
  626. }
  627. MORDOR_ASSERT(length == 0);
  628. }
  629. ptrdiff_t
  630. Buffer::find(char delimiter, size_t length) const
  631. {
  632. if (length == (size_t)~0)
  633. length = readAvailable();
  634. MORDOR_ASSERT(length <= readAvailable());
  635. size_t totalLength = 0;
  636. bool success = false;
  637. std::list<Segment>::const_iterator it;
  638. for (it = m_segments.begin(); it != m_segments.end(); ++it) {
  639. const void *start = it->readBuffer().start();
  640. size_t toscan = (std::min)(length, it->readAvailable());
  641. const void *point = memchr(start, delimiter, toscan);
  642. if (point != NULL) {
  643. success = true;
  644. totalLength += (unsigned char*)point - (unsigned char*)start;
  645. break;
  646. }
  647. totalLength += toscan;
  648. length -= toscan;
  649. if (length == 0)
  650. break;
  651. }
  652. if (success)
  653. return totalLength;
  654. return -1;
  655. }
  656. ptrdiff_t
  657. Buffer::find(const std::string &string, size_t length) const
  658. {
  659. if (length == (size_t)~0)
  660. length = readAvailable();
  661. MORDOR_ASSERT(length <= readAvailable());
  662. MORDOR_ASSERT(!string.empty());
  663. size_t totalLength = 0;
  664. size_t foundSoFar = 0;
  665. std::list<Segment>::const_iterator it;
  666. for (it = m_segments.begin(); it != m_segments.end(); ++it) {
  667. const void *start = it->readBuffer().start();
  668. size_t toscan = (std::min)(length, it->readAvailable());
  669. while (toscan > 0) {
  670. if (foundSoFar == 0) {
  671. const void *point = memchr(start, string[0], toscan);
  672. if (point != NULL) {
  673. foundSoFar = 1;
  674. size_t found = (unsigned char*)point -
  675. (unsigned char*)start;
  676. toscan -= found + 1;
  677. length -= found + 1;
  678. totalLength += found;
  679. start = (unsigned char*)point + 1;
  680. } else {
  681. totalLength += toscan;
  682. length -= toscan;
  683. toscan = 0;
  684. continue;
  685. }
  686. }
  687. MORDOR_ASSERT(foundSoFar != 0);
  688. size_t tocompare = (std::min)(toscan, string.size() - foundSoFar);
  689. if (memcmp(start, string.c_str() + foundSoFar, tocompare) == 0) {
  690. foundSoFar += tocompare;
  691. toscan -= tocompare;
  692. length -= tocompare;
  693. if (foundSoFar == string.size())
  694. break;
  695. } else {
  696. totalLength += foundSoFar;
  697. foundSoFar = 0;
  698. }
  699. }
  700. if (foundSoFar == string.size())
  701. break;
  702. if (length == 0)
  703. break;
  704. }
  705. if (foundSoFar == string.size())
  706. return totalLength;
  707. return -1;
  708. }
  709. std::string
  710. Buffer::toString() const
  711. {
  712. if (m_readAvailable == 0)
  713. return std::string();
  714. std::string result;
  715. result.resize(m_readAvailable);
  716. copyOut(&result[0], result.size());
  717. return result;
  718. }
  719. std::string
  720. Buffer::getDelimited(char delimiter, bool eofIsDelimiter, bool includeDelimiter)
  721. {
  722. ptrdiff_t offset = find(delimiter, ~0);
  723. MORDOR_ASSERT(offset >= -1);
  724. if (offset == -1 && !eofIsDelimiter)
  725. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  726. eofIsDelimiter = offset == -1;
  727. if (offset == -1)
  728. offset = readAvailable();;
  729. std::string result;
  730. result.resize(offset + (eofIsDelimiter ? 0 : (includeDelimiter ? 1 : 0)));
  731. copyOut(&result[0], result.size());
  732. consume(result.size());
  733. if (!eofIsDelimiter && !includeDelimiter)
  734. consume(1u);
  735. return result;
  736. }
  737. std::string
  738. Buffer::getDelimited(const std::string &delimiter, bool eofIsDelimiter,
  739. bool includeDelimiter)
  740. {
  741. ptrdiff_t offset = find(delimiter, ~0);
  742. MORDOR_ASSERT(offset >= -1);
  743. if (offset == -1 && !eofIsDelimiter)
  744. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  745. eofIsDelimiter = offset == -1;
  746. if (offset == -1)
  747. offset = readAvailable();;
  748. std::string result;
  749. result.resize(offset + (eofIsDelimiter ? 0 :
  750. (includeDelimiter ? delimiter.size() : 0)));
  751. copyOut(&result[0], result.size());
  752. consume(result.size());
  753. if (!eofIsDelimiter && !includeDelimiter)
  754. consume(delimiter.size());
  755. return result;
  756. }
  757. void
  758. Buffer::visit(boost::function<void (const void *, size_t)> dg, size_t length) const
  759. {
  760. if (length == (size_t)~0)
  761. length = readAvailable();
  762. MORDOR_ASSERT(length <= readAvailable());
  763. std::list<Segment>::const_iterator it;
  764. for (it = m_segments.begin(); it != m_segments.end() && length > 0; ++it) {
  765. size_t todo = (std::min)(length, it->readAvailable());
  766. MORDOR_ASSERT(todo != 0);
  767. dg(it->readBuffer().start(), todo);
  768. length -= todo;
  769. }
  770. MORDOR_ASSERT(length == 0);
  771. }
  772. bool
  773. Buffer::operator == (const Buffer &rhs) const
  774. {
  775. if (rhs.readAvailable() != readAvailable())
  776. return false;
  777. return opCmp(rhs) == 0;
  778. }
  779. bool
  780. Buffer::operator != (const Buffer &rhs) const
  781. {
  782. if (rhs.readAvailable() != readAvailable())
  783. return true;
  784. return opCmp(rhs) != 0;
  785. }
  786. bool
  787. Buffer::operator== (const std::string &string) const
  788. {
  789. if (string.size() != readAvailable())
  790. return false;
  791. return opCmp(string.c_str(), string.size()) == 0;
  792. }
  793. bool
  794. Buffer::operator!= (const std::string &string) const
  795. {
  796. if (string.size() != readAvailable())
  797. return true;
  798. return opCmp(string.c_str(), string.size()) != 0;
  799. }
  800. bool
  801. Buffer::operator== (const char *string) const
  802. {
  803. size_t length = strlen(string);
  804. if (length != readAvailable())
  805. return false;
  806. return opCmp(string, length) == 0;
  807. }
  808. bool
  809. Buffer::operator!= (const char *string) const
  810. {
  811. size_t length = strlen(string);
  812. if (length != readAvailable())
  813. return true;
  814. return opCmp(string, length) != 0;
  815. }
  816. int
  817. Buffer::opCmp(const Buffer &rhs) const
  818. {
  819. std::list<Segment>::const_iterator leftIt, rightIt;
  820. int lengthResult = (int)((ptrdiff_t)readAvailable() - (ptrdiff_t)rhs.readAvailable());
  821. leftIt = m_segments.begin(); rightIt = rhs.m_segments.begin();
  822. size_t leftOffset = 0, rightOffset = 0;
  823. while (leftIt != m_segments.end() && rightIt != rhs.m_segments.end())
  824. {
  825. MORDOR_ASSERT(leftOffset <= leftIt->readAvailable());
  826. MORDOR_ASSERT(rightOffset <= rightIt->readAvailable());
  827. size_t tocompare = (std::min)(leftIt->readAvailable() - leftOffset,
  828. rightIt->readAvailable() - rightOffset);
  829. if (tocompare == 0)
  830. break;
  831. int result = memcmp(
  832. (const unsigned char *)leftIt->readBuffer().start() + leftOffset,
  833. (const unsigned char *)rightIt->readBuffer().start() + rightOffset,
  834. tocompare);
  835. if (result != 0)
  836. return result;
  837. leftOffset += tocompare;
  838. rightOffset += tocompare;
  839. if (leftOffset == leftIt->readAvailable()) {
  840. leftOffset = 0;
  841. ++leftIt;
  842. }
  843. if (rightOffset == rightIt->readAvailable()) {
  844. rightOffset = 0;
  845. ++rightIt;
  846. }
  847. }
  848. return lengthResult;
  849. }
  850. int
  851. Buffer::opCmp(const char *string, size_t length) const
  852. {
  853. size_t offset = 0;
  854. std::list<Segment>::const_iterator it;
  855. int lengthResult = (int)((ptrdiff_t)readAvailable() - (ptrdiff_t)length);
  856. if (lengthResult > 0)
  857. length = readAvailable();
  858. for (it = m_segments.begin(); it != m_segments.end(); ++it) {
  859. size_t tocompare = (std::min)(it->readAvailable(), length);
  860. int result = memcmp(it->readBuffer().start(), string + offset, tocompare);
  861. if (result != 0)
  862. return result;
  863. length -= tocompare;
  864. offset += tocompare;
  865. if (length == 0)
  866. return lengthResult;
  867. }
  868. return lengthResult;
  869. }
  870. void
  871. Buffer::invariant() const
  872. {
  873. #ifndef NDEBUG_PERF
  874. size_t read = 0;
  875. size_t write = 0;
  876. bool seenWrite = false;
  877. std::list<Segment>::const_iterator it;
  878. for (it = m_segments.begin(); it != m_segments.end(); ++it) {
  879. const Segment &segment = *it;
  880. // Strict ordering
  881. MORDOR_ASSERT(!seenWrite || (seenWrite && segment.readAvailable() == 0));
  882. read += segment.readAvailable();
  883. write += segment.writeAvailable();
  884. if (!seenWrite && segment.writeAvailable() != 0) {
  885. seenWrite = true;
  886. MORDOR_ASSERT(m_writeIt == it);
  887. }
  888. // We should keep segments optimally merged together
  889. std::list<Segment>::const_iterator nextIt = it;
  890. ++nextIt;
  891. if (nextIt != m_segments.end()) {
  892. const Segment& next = *nextIt;
  893. if (segment.writeAvailable() == 0 &&
  894. next.readAvailable() != 0) {
  895. MORDOR_ASSERT((const unsigned char*)segment.readBuffer().start() +
  896. segment.readAvailable() != next.readBuffer().start() ||
  897. segment.m_data.m_array.get() != next.m_data.m_array.get());
  898. } else if (segment.writeAvailable() != 0 &&
  899. next.readAvailable() == 0) {
  900. MORDOR_ASSERT((const unsigned char*)segment.writeBuffer().start() +
  901. segment.writeAvailable() != next.writeBuffer().start() ||
  902. segment.m_data.m_array.get() != next.m_data.m_array.get());
  903. }
  904. }
  905. }
  906. MORDOR_ASSERT(read == m_readAvailable);
  907. MORDOR_ASSERT(write == m_writeAvailable);
  908. MORDOR_ASSERT(write != 0 || (write == 0 && m_writeIt == m_segments.end()));
  909. #endif
  910. }
  911. }