PageRenderTime 552ms CodeModel.GetById 141ms app.highlight 319ms RepoModel.GetById 83ms app.codeStats 1ms

/mordor/streams/buffer.cpp

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