/* * 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/textbuilder.h" #include "bu/exceptionbase.h" #include "bu/text.h" #define PAGE_SIZE 16 Bu::TextBuilderCore::Chunk::Chunk( const CodePoint *pSrc, int32_t iLength ) : iLength( iLength ), pData( 0 ), pNext( 0 ) { if( iLength < PAGE_SIZE ) { pData = new CodePoint[PAGE_SIZE]; } else { pData = new CodePoint[iLength]; } memcpy( pData, pSrc, iLength*sizeof(CodePoint) ); } Bu::TextBuilderCore::Chunk::~Chunk() { delete[] pData; pData = 0; pNext = 0; } void Bu::TextBuilderCore::Chunk::append( const Bu::CodePoint *&pSrc, int32_t &iLength ) { if( this->iLength >= PAGE_SIZE ) { // This chink is full, just return. return; } int32_t iCopy = PAGE_SIZE-this->iLength; if( iCopy > iLength ) { iCopy = iLength; } memcpy( pData+this->iLength, pSrc, iCopy*sizeof(Bu::CodePoint) ); this->iLength += iCopy; pSrc += iCopy; iLength -= iCopy; } Bu::TextBuilderCore::Chunk *Bu::TextBuilderCore::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; } ////// // TextBuilderCore // Bu::TextBuilderCore::TextBuilderCore() : pFirst( 0 ), pLast( 0 ), iLength( 0 ) { } Bu::TextBuilderCore::TextBuilderCore( const TextBuilderCore &rSrc ) : pFirst( 0 ), pLast( 0 ), iLength( rSrc.iLength ) { throw Bu::ExceptionBase("Not yet implemented."); } Bu::TextBuilderCore::~TextBuilderCore() { clear(); } void Bu::TextBuilderCore::clear() { Chunk *pCur = pFirst; while( pCur ) { Chunk *pNext = pCur->pNext; delete pCur; pCur = pNext; } pFirst = pLast = 0; iLength = 0; } void Bu::TextBuilderCore::append( const CodePoint *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::TextBuilderCore::prepend( const CodePoint *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::TextBuilderCore::insert( int32_t iBefore, const CodePoint *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::TextBuilderCore::set( const CodePoint *pSrc, int32_t iLength ) { clear(); append( pSrc, iLength ); } void Bu::TextBuilderCore::copyTo( void *pDestRaw, int32_t iLength ) { CodePoint *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*sizeof(CodePoint) ); pDest += iChunkLen; iLength -= iChunkLen; pCur = pCur->pNext; } } Bu::CodePoint Bu::TextBuilderCore::getAt( int32_t iIndex ) const { if( iIndex < 0 || iIndex >= iLength ) throw Bu::ExceptionBase("Requested index is out of range."); Chunk *pCur = pFirst; while( iIndex >= pCur->iLength ) { iIndex -= pCur->iLength; pCur = pCur->pNext; } return pCur->pData[iIndex]; } ///// // TextBuilder // Bu::TextBuilder::TextBuilder() { } Bu::TextBuilder::TextBuilder( const Text &rSrc ) { core->append( rSrc.getData(), rSrc.getSize() ); } Bu::TextBuilder::TextBuilder( const TextBuilder &rSrc ) : Bu::SharedCore( rSrc ) { } Bu::TextBuilder::~TextBuilder() { } void Bu::TextBuilder::set( const Text &rSrc ) { _hardCopy(); core->set( rSrc.getData(), rSrc.getSize() ); } void Bu::TextBuilder::append( const Text &rSrc ) { _hardCopy(); core->append( rSrc.getData(), rSrc.getSize() ); } void Bu::TextBuilder::append( const CodePoint *pSrc, int32_t iLength ) { _hardCopy(); core->append( pSrc, iLength ); } void Bu::TextBuilder::prepend( const Text &rSrc ) { _hardCopy(); core->prepend( rSrc.getData(), rSrc.getSize() ); } void Bu::TextBuilder::insert( int32_t iBefore, const Text &rSrc ) { _hardCopy(); core->insert( iBefore, rSrc.getData(), rSrc.getSize() ); } void Bu::TextBuilder::clear() { _hardCopy(); core->clear(); } int32_t Bu::TextBuilder::getSize() const { return core->iLength; } Bu::Text Bu::TextBuilder::getText() const { return Text( *this ); } void Bu::TextBuilder::copyTo( void *pDestRaw, int32_t iDestSize ) const { core->copyTo( pDestRaw, iDestSize ); } Bu::CodePoint Bu::TextBuilder::operator[]( int32_t iIndex ) const { return core->getAt( iIndex ); } Bu::TextBuilder &Bu::TextBuilder::operator=( const Text &rSrc ) { set( rSrc ); return *this; } Bu::TextBuilder &Bu::TextBuilder::operator==( const Text &rSrc ) { return *this; }