/* * Copyright (C) 2007-2019 Xagasoft, All rights reserved. * * This file is part of the libbu++ library and is released under the * terms of the license contained in the file LICENSE. */ #include "bu/blobbuilder.h" #include "bu/blob.h" #define PAGE_SIZE 1024 ///// // BlobBuilderCore::Chunk // Bu::BlobBuilderCore::Chunk::Chunk( const char *pSrc, int32_t iLength ) : iLength( iLength ), pData( 0 ), pNext( 0 ) { if( iLength < PAGE_SIZE ) { pData = new char[PAGE_SIZE]; } else { pData = new char[iLength]; } memcpy( pData, pSrc, iLength ); } Bu::BlobBuilderCore::Chunk::~Chunk() { delete[] pData; pData = 0; pNext = 0; // Why not delete pNext here? A BlobBuilder could easily wind up having // quite a few chunks, and if it does, deleting the linked list from here // could actually exhaust the program stack. Instead these are all deleted // in the core down below. } void Bu::BlobBuilderCore::Chunk::append( const char *&pSrc, int32_t &iLength ) { if( this->iLength >= PAGE_SIZE ) { // This chunk is full, just return. return; } int32_t iCopy = PAGE_SIZE-this->iLength; if( iCopy > iLength ) { iCopy = iLength; } memcpy( pData+this->iLength, pSrc, iCopy ); this->iLength += iCopy; pSrc += iCopy; iLength -= iCopy; } Bu::BlobBuilderCore::Chunk *Bu::BlobBuilderCore::Chunk::split( int32_t iIndex ) { if( iIndex == 0 ) return NULL; if( iIndex >= iLength ) return NULL; Chunk *pNew = new Chunk( pData+iIndex, iLength-iIndex ); iLength -= iIndex; pNew->pNext = pNext; pNext = pNew; return pNew; } ///// // BlobBuilderCore // Bu::BlobBuilderCore::BlobBuilderCore() : pFirst( 0 ), pLast( 0 ), iLength( 0 ) { } Bu::BlobBuilderCore::BlobBuilderCore( const Bu::BlobBuilderCore &rSrc ) : pFirst( 0 ), pLast( 0 ), iLength( rSrc.iLength ) { } Bu::BlobBuilderCore::~BlobBuilderCore() { clear(); } void Bu::BlobBuilderCore::clear() { Chunk *pCur = pFirst; while( pCur ) { Chunk *pNext = pCur->pNext; delete pCur; pCur = pNext; } delete pFirst; pFirst = pLast = 0; iLength = 0; } void Bu::BlobBuilderCore::append( const char *pSrc, int32_t iLength ) { if( pFirst == 0 ) { // Nothing in the list, just add a chunk. pFirst = pLast = new Chunk( pSrc, iLength ); return; } else if( pLast->iLength < 1024 ) { // Append to the last chunk first, this will modify pSrc & iLength. pLast->append( pSrc, iLength ); } // If there's unused data at the end, append it now. if( iLength > 0 ) { pLast->pNext = new Chunk( pSrc, iLength ); } } void Bu::BlobBuilderCore::prepend( const char *pSrc, int32_t iLength ) { if( pFirst == 0 ) { pFirst = pLast = new Chunk( pSrc, iLength ); } else { Chunk *pNew = new Chunk( pSrc, iLength ); pNew->pNext = pFirst; pFirst = pNew; } } void Bu::BlobBuilderCore::insert( int32_t iBefore, const char *pSrc, int32_t iLength ) { if( iBefore <= 0 ) { prepend( pSrc, iLength ); return; } if( iBefore >= this->iLength ) { append( pSrc, iLength ); return; } Chunk *pCur = pFirst; while( pCur ) { if( iBefore == 0 ) { // Insert between chunks, no splitting required. Chunk *pNew = new Chunk( pSrc, iLength ); pNew->pNext = pCur->pNext; pCur->pNext = pNew; if( pLast == pCur ) pLast = pNew; } if( iBefore < pCur->iLength ) { // This is the chunk we need to split. Chunk *pNew = pCur->split( iBefore ); if( pLast == pCur ) pLast = pNew; continue; } pCur = pCur->pNext; } } void Bu::BlobBuilderCore::set( const char *pSrc, int32_t iLength ) { clear(); append( pSrc, iLength ); } void Bu::BlobBuilderCore::copyTo( void *pDestRaw, int32_t iLength ) const { char *pDest = reinterpret_cast(pDestRaw); Chunk *pCur = pFirst; while( pCur && iLength ) { int32_t iChunkLen = pCur->iLength; if( iChunkLen > iLength ) iChunkLen = iLength; memcpy( pDest, pCur->pData, iChunkLen ); pDest += iChunkLen; iLength -= iChunkLen; pCur = pCur->pNext; } } ////// // BlobBuilder // Bu::BlobBuilder::BlobBuilder() { } Bu::BlobBuilder::BlobBuilder( const Bu::BlobBuilder &rSrc ) : Bu::SharedCore( rSrc ) { } Bu::BlobBuilder::~BlobBuilder() { } void Bu::BlobBuilder::set( const Blob &rSrc ) { _hardCopy(); core->set( rSrc.getData(), rSrc.getSize() ); } void Bu::BlobBuilder::set( const char *pSrc, int32_t iLength ) { _hardCopy(); core->set( pSrc, iLength ); } void Bu::BlobBuilder::set( const char *pSrc ) { set( pSrc, strlen( pSrc ) ); } void Bu::BlobBuilder::append( const Blob &rSrc ) { _hardCopy(); core->append( rSrc.getData(), rSrc.getSize() ); } void Bu::BlobBuilder::append( const char *pSrc, int32_t iLength ) { _hardCopy(); core->append( pSrc, iLength ); } void Bu::BlobBuilder::append( const char *pSrc ) { append( pSrc, strlen( pSrc ) ); } void Bu::BlobBuilder::prepend( const Blob &rSrc ) { _hardCopy(); core->prepend( rSrc.getData(), rSrc.getSize() ); } void Bu::BlobBuilder::prepend( const char *pSrc, int32_t iLength ) { _hardCopy(); core->prepend( pSrc, iLength ); } void Bu::BlobBuilder::prepend( const char *pSrc ) { prepend( pSrc, strlen( pSrc ) ); } void Bu::BlobBuilder::insert( int32_t iBefore, const Blob &rSrc ) { _hardCopy(); core->insert( iBefore, rSrc.getData(), rSrc.getSize() ); } void Bu::BlobBuilder::insert( int32_t iBefore, const char *pSrc, int32_t iLength ) { _hardCopy(); core->insert( iBefore, pSrc, iLength ); } void Bu::BlobBuilder::insert( int32_t iBefore, const char *pSrc ) { insert( iBefore, pSrc, strlen( pSrc ) ); } void Bu::BlobBuilder::clear() { _hardCopy(); core->clear(); } int32_t Bu::BlobBuilder::getSize() const { return core->iLength; } Bu::Blob Bu::BlobBuilder::getBlob() const { return Blob( *this ); } void Bu::BlobBuilder::copyTo( void *pDestRaw, int32_t iDestSize ) const { core->copyTo( pDestRaw, iDestSize ); } Bu::BlobBuilder &Bu::BlobBuilder::operator=( const Blob &rSrc ) { set( rSrc ); return *this; } Bu::BlobBuilder &Bu::BlobBuilder::operator=( const char *pSrc ) { set( pSrc ); return *this; } Bu::BlobBuilder &Bu::BlobBuilder::operator+=( const Blob &rSrc ) { append( rSrc ); return *this; } Bu::BlobBuilder &Bu::BlobBuilder::operator+=( const char *pSrc ) { append( pSrc ); return *this; }