/* * Copyright (C) 2007-2023 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" #include "bu/exceptionbase.h" #define PAGE_SIZE 8 ///// // 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::Blob &rSrc ) : pFirst( 0 ), pLast( 0 ), iLength( 0 ) { append( rSrc.getData(), rSrc.getSize() ); } Bu::BlobBuilderCore::BlobBuilderCore( const Bu::BlobBuilderCore &rSrc ) : pFirst( 0 ), pLast( 0 ), iLength( rSrc.iLength ) { throw Bu::ExceptionBase("Not yet implemented."); } Bu::BlobBuilderCore::~BlobBuilderCore() { clear(); } void Bu::BlobBuilderCore::clear() { Chunk *pCur = pFirst; while( pCur ) { Chunk *pNext = pCur->pNext; delete pCur; pCur = pNext; } pFirst = pLast = 0; iLength = 0; } void Bu::BlobBuilderCore::append( const char *pSrc, int32_t iLength ) { this->iLength += iLength; if( pFirst == 0 ) { // Nothing in the list, just add a chunk. pFirst = pLast = new Chunk( pSrc, iLength ); return; } else if( pLast->iLength < PAGE_SIZE ) { // 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 ); pLast = pLast->pNext; } } 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; } this->iLength += iLength; } 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; } this->iLength += iLength; } 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; } } char Bu::BlobBuilderCore::getAt( int32_t iIndex ) const { if( iIndex < 0 || iIndex >= iLength ) throw Bu::ExceptionBase("Requseted index is out of range."); Chunk *pCur = pFirst; while( iIndex >= pCur->iLength ) { iIndex -= pCur->iLength; pCur = pCur->pNext; } return pCur->pData[iIndex]; } ////// // BlobBuilder // Bu::BlobBuilder::BlobBuilder() { } Bu::BlobBuilder::BlobBuilder( const Bu::Blob &rSrc ) { append( rSrc ); } 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 ) { _hardCopy(); 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 ) { _hardCopy(); 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 ) { _hardCopy(); 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 ) { _hardCopy(); 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 ); } char Bu::BlobBuilder::operator[]( int32_t iIndex ) const { return core->getAt( iIndex ); } 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 char chr ) { set( &chr, 1 ); 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; } Bu::BlobBuilder &Bu::BlobBuilder::operator+=( const char chr ) { append( &chr, 1 ); 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; } Bu::BlobBuilder &Bu::BlobBuilder::operator<<( const char chr ) { append( chr ); return *this; }