/* * Copyright 2016 Adrian Thurston * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _AAPL_ROPE_H #define _AAPL_ROPE_H #include #include #include struct RopeBlock { static const int BLOCK_SZ = 8192; RopeBlock() : size(BLOCK_SZ), hoff(0), toff(0) { } int size; int hoff; int toff; RopeBlock *next; }; struct Rope { Rope() : hblk(0), tblk(0), ropeLen(0) { } /* Write to the tail block, at tail offset. */ RopeBlock *hblk; RopeBlock *tblk; /* Number of bytes in rope. */ int ropeLen; RopeBlock *allocateBlock( int supporting ) { int size = ( supporting > RopeBlock::BLOCK_SZ ) ? supporting : RopeBlock::BLOCK_SZ; char *bd = new char[sizeof(RopeBlock) + size]; RopeBlock *block = (RopeBlock*) bd; block->size = size; block->hoff = 0; block->toff = 0; block->next = 0; return block; } char *data( RopeBlock *rb ) { return (char*)rb + sizeof( RopeBlock ) + rb->hoff; } char *writeTo( RopeBlock *rb ) { return (char*)rb + sizeof( RopeBlock ) + rb->toff; } int length( RopeBlock *rb ) { return rb->toff - rb->hoff; } int length() { return ropeLen; } int available( RopeBlock *rb ) { return rb->size - rb->toff; } char *append( const char *src, int len ) { if ( tblk == 0 ) { /* There are no blocks. */ hblk = tblk = allocateBlock( len ); } else { int avail = available( tblk ); /* Move to the next block? */ if ( len > avail ) { RopeBlock *block = allocateBlock( len ); tblk->next = block; tblk = block; } } char *ret = writeTo(tblk); tblk->toff += len; ropeLen += len; if ( src != 0 ) memcpy( ret, src, len ); return ret; } char *appendBlock( int len ) { if ( tblk == 0 ) { /* There are no blocks. */ hblk = tblk = allocateBlock( len ); } else { RopeBlock *block = allocateBlock( len ); tblk->next = block; tblk = block; } char *ret = writeTo(tblk); tblk->toff += len; ropeLen += len; return ret; } /* Transfer data from other. Leaves it empty. */ void append( Rope &other ) { if ( hblk == 0 ) { transfer( other ); } else if ( other.hblk == 0 ) { /* nothing to do, other list empty. */ } else { tblk->next = other.hblk; tblk = other.tblk; ropeLen += other.ropeLen; } other.abandon(); } void empty() { RopeBlock *blk = hblk; while ( blk != 0 ) { RopeBlock *next = blk->next; delete[] (char*)blk; blk = next; } hblk = 0; tblk = 0; ropeLen = 0; } void abandon() { hblk = 0; tblk = 0; ropeLen = 0; } void transfer( Rope &from ) { empty(); this->hblk = from.hblk; this->tblk = from.tblk; this->ropeLen = from.ropeLen; from.hblk = from.tblk = 0; from.ropeLen = 0; } }; /* * StringStream for appending to streams with an ostream. */ struct RopeOutBuf : public std::streambuf { RopeOutBuf( Rope &r ) : r(r) { } int_type overflow( int_type c ) { if ( c != EOF ) { char z = c; r.append( &z, 1 ); } return c; } std::streamsize xsputn( const char *data, std::streamsize num ) { r.append( data, num ); return num; } Rope &r; }; struct RopeStream : public std::ostream { RopeStream( Rope &r ) : std::ostream( 0 ), buf( r ) { rdbuf( &buf ); } RopeOutBuf buf; }; #endif