/* * 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. */ #define BU_TRACE #include "bu/trace.h" #include "bu/string.h" #include "bu/hash.h" #include "bu/membuf.h" #include "bu/formatter.h" #include "bu/blob.h" #include #define nMinSize (256) Bu::StringCore::StringCore() : nLength( 0 ), pFirst( NULL ), pLast( NULL ) { } Bu::StringCore::StringCore( const StringCore &rSrc ) : nLength( rSrc.nLength ), pFirst( NULL ), pLast( NULL ) { if( rSrc.pFirst == NULL || rSrc.nLength == 0 ) { pFirst = pLast = NULL; } else { pFirst = pLast = newChunk( nLength ); Chunk *pLink = rSrc.pFirst; int iPos = 0; while( pLink != NULL ) { memcpy( pFirst->pData+iPos, pLink->pData, pLink->nLength ); iPos += pLink->nLength; pLink = pLink->pNext; } } } Bu::StringCore::~StringCore() { clear(); } void Bu::StringCore::clear() const { if( pFirst == NULL ) return; Chunk *i = pFirst; for(;;) { Chunk *n = i->pNext; delete[] i->pData; delete i; if( n == NULL ) break; i = n; } pFirst = pLast = NULL; nLength = 0; } Bu::StringCore::Chunk *Bu::StringCore::newChunk() const { Chunk *pNew = new Chunk; pNew->pNext = NULL; return pNew; } Bu::StringCore::Chunk *Bu::StringCore::newChunk( long nLen ) const { Chunk *pNew = new Chunk; pNew->pNext = NULL; pNew->nLength = nLen; pNew->pData = new char[(nLenpData[nLen] = (char)0; return pNew; } Bu::StringCore::Chunk *Bu::StringCore::copyChunk( Bu::StringCore::Chunk *pSrc ) const { Chunk *pNew = new Chunk; pNew->pNext = pSrc->pNext; pNew->nLength = pSrc->nLength; pNew->pData = new char[ (pNew->nLengthnLength)+1 ]; memcpy( pNew->pData, pSrc->pData, pSrc->nLength ); pNew->pData[pNew->nLength] = (char)0; return pNew; } void Bu::StringCore::appendChunk( Bu::StringCore::Chunk *pNewChunk ) { if( pFirst == NULL ) pLast = pFirst = pNewChunk; else { pLast->pNext = pNewChunk; pLast = pNewChunk; } nLength += pNewChunk->nLength; } void Bu::StringCore::prependChunk( Bu::StringCore::Chunk *pNewChunk ) { if( pFirst == NULL ) pLast = pFirst = pNewChunk; else { pNewChunk->pNext = pFirst; pFirst = pNewChunk; } nLength += pNewChunk->nLength; } Bu::String::String() : core( new StringCore() ) { } Bu::String::String( const char *pData ) : core( new StringCore() ) { append( pData ); } Bu::String::String( const char *pData, long nLength ) : core( new StringCore() ) { append( pData, nLength ); } Bu::String::String( const Bu::String &rSrc ) : core( new StringCore( *rSrc.core ) ) { } Bu::String::String( const Bu::String &rSrc, long nLength ) : core( new StringCore() ) { append( rSrc, nLength ); } Bu::String::String( const Bu::String &rSrc, long nStart, long nLength ) : core( new StringCore() ) { append( rSrc, nStart, nLength ); } Bu::String::String( long nSize ) : core( new StringCore() ) { core->pFirst = core->pLast = core->newChunk( nSize ); core->nLength = nSize; } Bu::String::String( const class Bu::Blob &rSrc ) : core( new StringCore() ) { append( rSrc.getData(), rSrc.getSize() ); } Bu::String::String( const Bu::String::const_iterator &s ) : core( new StringCore() ) { append( s ); } Bu::String::String( const Bu::String::const_iterator &s, const Bu::String::const_iterator &e ) : core( new StringCore() ) { append( s, e ); } Bu::String::~String() { delete core; } void Bu::String::append( const char *pData ) { if( !pData ) return; long nLen; for( nLen = 0; pData[nLen] != (char)0; nLen++ ) { } append( pData, 0, nLen ); } void Bu::String::append( const char *pData, long nLen ) { append( pData, 0, nLen ); } void Bu::String::append( const char *pData, long nStart, long nLen ) { if( !pData ) return; if( nLen <= 0 ) return; pData += nStart; if( core->pLast && core->pLast->nLength+1 < nMinSize ) { int nAmnt = nMinSize - core->pLast->nLength; if( nAmnt > nLen ) nAmnt = nLen; memcpy( core->pLast->pData+core->pLast->nLength, pData, nAmnt ); pData += nAmnt; core->pLast->nLength += nAmnt; nLen -= nAmnt; core->nLength += nAmnt; } if( nLen > 0 ) { Chunk *pNew = core->newChunk( nLen ); memcpy( pNew->pData, pData, nLen ); core->appendChunk( pNew ); // core->nLength += nLen; } } void Bu::String::append( const char &cData ) { if( core->pLast && core->pLast->nLength+1 < nMinSize ) { core->pLast->pData[core->pLast->nLength] = cData; ++core->pLast->nLength; ++core->nLength; // pLast->pData[pLast->nLength] = (char)0; } else { append( &cData, 1 ); } } void Bu::String::append( const String & sData ) { append( sData.getStr(), 0, sData.getSize() ); } void Bu::String::append( const String & sData, long nLen ) { append( sData.getStr(), 0, nLen ); } void Bu::String::append( const String & sData, long nStart, long nLen ) { if( nLen < 0 ) nLen = sData.getSize() - nStart; append( sData.getStr(), nStart, nLen ); } void Bu::String::append( const const_iterator &s ) { if( !s.isValid() ) return; Chunk *pSrc = s.pChunk; Chunk *pNew = core->newChunk( pSrc->nLength-s.iPos ); memcpy( pNew->pData, pSrc->pData+s.iPos, pSrc->nLength-s.iPos ); core->appendChunk( pNew ); while( (pSrc = pSrc->pNext) ) { core->appendChunk( core->copyChunk( pSrc ) ); } } void Bu::String::append( const iterator &s ) { append( const_iterator( s ) ); } void Bu::String::append( const const_iterator &s, const const_iterator &e ) { if( !s.isValid() ) return; if( !e.isValid() ) { append( s ); return; } if( s.pChunk == e.pChunk ) { // Simple case, they're the same chunk Chunk *pNew = core->newChunk( e.iPos-s.iPos ); memcpy( pNew->pData, s.pChunk->pData+s.iPos, e.iPos-s.iPos ); core->appendChunk( pNew ); } else { // A little trickier, scan the blocks... Chunk *pSrc = s.pChunk; Chunk *pNew = core->newChunk( pSrc->nLength-s.iPos ); memcpy( pNew->pData, pSrc->pData+s.iPos, pSrc->nLength-s.iPos ); core->appendChunk( pNew ); while( (pSrc = pSrc->pNext) != e.pChunk ) { core->appendChunk( core->copyChunk( pSrc ) ); } pNew = core->newChunk( e.iPos ); memcpy( pNew->pData, pSrc->pData, e.iPos ); core->appendChunk( pNew ); } } void Bu::String::prepend( const String & sData ) { prepend( sData.getStr(), sData.getSize() ); } void Bu::String::prepend( const char *pData ) { if( pData == NULL ) return; long nLen; for( nLen = 0; pData[nLen] != (char)0; nLen++ ) { } Chunk *pNew = core->newChunk( nLen ); memcpy( pNew->pData, pData, nLen ); core->prependChunk( pNew ); } void Bu::String::prepend( const char *pData, long nLen ) { Chunk *pNew = core->newChunk( nLen ); memcpy( pNew->pData, pData, nLen ); core->prependChunk( pNew ); } void Bu::String::prepend( const char c ) { prepend( &c, 1 ); } void Bu::String::insert( long nPos, const char *pData, long nLen ) { if( nLen <= 0 ) return; if( nPos <= 0 ) { prepend( pData, nLen ); } else if( nPos >= core->nLength ) { append( pData, nLen ); } else { // If we're going to flatten anyway, might as well for everyone flatten(); Chunk *p1 = core->newChunk( nPos ); Chunk *p2 = core->newChunk( nLen ); Chunk *p3 = core->newChunk( core->nLength-nPos ); memcpy( p1->pData, core->pFirst->pData, nPos ); memcpy( p2->pData, pData, nLen ); memcpy( p3->pData, core->pFirst->pData+nPos, core->nLength-nPos ); core->clear(); core->appendChunk( p1 ); core->appendChunk( p2 ); core->appendChunk( p3 ); } } void Bu::String::insert( long nPos, const String &str ) { if( nPos <= 0 ) { prepend( str ); } else if( nPos >= core->nLength ) { append( str ); } else { flatten(); Chunk *p1 = core->newChunk( nPos ); Chunk *p3 = core->newChunk( core->nLength-nPos ); memcpy( p1->pData, core->pFirst->pData, nPos ); memcpy( p3->pData, core->pFirst->pData+nPos, core->nLength-nPos ); core->clear(); core->appendChunk( p1 ); for( Chunk *pChnk = str.core->pFirst; pChnk; pChnk = pChnk->pNext ) { core->appendChunk( core->copyChunk( pChnk ) ); } core->appendChunk( p3 ); } } void Bu::String::insert( long nPos, const char *pData ) { insert( nPos, pData, strlen( pData ) ); } void Bu::String::remove( long nPos, long nLen ) { if( nLen <= 0 || nPos < 0 || nPos >= core->nLength ) return; if( nLen > core->nLength-nPos ) nLen = core->nLength-nPos; flatten(); memmove( core->pFirst->pData+nPos, core->pFirst->pData+nPos+nLen, core->nLength-nPos-nLen+1 ); core->nLength -= nLen; core->pFirst->nLength -= nLen; } void Bu::String::clear() { core->clear(); } const Bu::String &Bu::String::clone() const { return *this; } void Bu::String::unshare() { } Bu::String Bu::String::replace( const Bu::String &fnd, const Bu::String &rep ) const { String out; const_iterator o = begin(); while( true ) { const_iterator i = o.find( fnd, fnd.getSize() ); if( !i ) { out.append( o ); return out; } else { out.append( o, i ); out.append( rep ); o = i; o += fnd.getSize(); } } } void Bu::String::resize( long nNewSize ) { if( core->nLength == nNewSize ) return; if( nNewSize < 0 ) nNewSize = 0; flatten(); // TODO: This is bad Chunk *pNew = core->newChunk( nNewSize ); long nNewLen = (nNewSizenLength)?(nNewSize):(core->nLength); if( core->nLength > 0 ) { memcpy( pNew->pData, core->pFirst->pData, nNewLen ); delete[] core->pFirst->pData; delete core->pFirst; } pNew->pData[nNewLen] = (char)0; core->pFirst = core->pLast = pNew; core->nLength = nNewSize; } long Bu::String::getSize() const { return core->nLength; } char *Bu::String::getStr() { if( core->pFirst == NULL || core->nLength == 0 ) return (char *)""; flatten(); core->pFirst->pData[core->nLength] = (char)0; return core->pFirst->pData; } const char *Bu::String::getStr() const { if( core->pFirst == NULL || core->nLength == 0 ) return (char *)""; flatten(); core->pFirst->pData[core->nLength] = (char)0; return core->pFirst->pData; } char *Bu::String::getData() { return getStr(); } const char *Bu::String::getData() const { return getStr(); } const char *Bu::String::getConstStr() const { return getStr(); } Bu::String Bu::String::getSubStrIdx( long iStart, long iSize ) const { if( iStart < 0 ) iStart = 0; if( iStart >= core->nLength ) return (const char[]){(char)0}; if( iSize < 0 ) iSize = core->nLength; if( iStart+iSize > core->nLength ) iSize = core->nLength-iStart; if( iSize == 0 ) return (const char[]){(char)0}; flatten(); String ret( core->pFirst->pData+iStart, iSize ); return ret; } Bu::String Bu::String::getSubStr( const_iterator iBegin, const_iterator iEnd ) const { if( !iBegin.isValid() ) return String(); if( iBegin.pChunk == iEnd.pChunk ) { return String( iBegin.pChunk->pData+iBegin.iPos, iEnd.iPos-iBegin.iPos ); } else if( !iEnd.isValid() ) { String ret; ret.append( iBegin.pChunk->pData+iBegin.iPos, iBegin.pChunk->nLength-iBegin.iPos ); for( Chunk *pCur = iBegin.pChunk->pNext; pCur; pCur = pCur->pNext ) { ret.append( pCur->pData, pCur->nLength ); } return ret; } else { String ret; ret.append( iBegin.pChunk->pData+iBegin.iPos, iBegin.pChunk->nLength-iBegin.iPos ); for( Chunk *pCur = iBegin.pChunk->pNext; pCur != iEnd.pChunk; pCur = pCur->pNext ) { ret.append( pCur->pData, pCur->nLength ); } ret.append( iEnd.pChunk->pData, iEnd.iPos ); return ret; } } Bu::StringList Bu::String::split( const char c ) const { Bu::StringList ret; const_iterator l, r; l = begin(); for(r=l; l;) { for( r = l; r && r != c; r++ ) { } ret.append( String( l, r ) ); l = r; l++; } return ret; } Bu::String &Bu::String::operator+=( const char *pData ) { append( pData ); return (*this); } Bu::String &Bu::String::operator+=( const Bu::String &rSrc ) { append( rSrc ); return (*this); } Bu::String &Bu::String::operator+=( const Bu::String::const_iterator &i ) { append( i, i+1 ); return (*this); } Bu::String &Bu::String::operator+=( const char cData ) { if( core->pLast && core->pLast->nLength+1 < nMinSize ) { core->pLast->pData[core->pLast->nLength] = cData; ++core->pLast->nLength; ++core->nLength; // pLast->pData[pLast->nLength] = (char)0; } else { append( &cData, 1 ); } //append( pData ); return (*this); } Bu::String &Bu::String::operator=( const char *pData ) { set( pData ); return (*this); } Bu::String &Bu::String::operator=( const Bu::String &pData ) { set( pData ); return (*this); } Bu::String Bu::String::operator+( const Bu::String &rRight ) const { String ret( *this ); ret.append( rRight ); return ret; } Bu::String Bu::String::operator+( const char *pRight ) const { String ret( *this ); ret.append( pRight ); return ret; } Bu::String Bu::String::operator+( char *pRight ) const { String ret( *this ); ret.append( pRight ); return ret; } void Bu::String::set( const char *pData ) { clear(); append( pData ); } void Bu::String::set( const char *pData, long nSize ) { clear(); append( pData, nSize ); } void Bu::String::set( const char *pData, long nStart, long nSize ) { clear(); append( pData, nStart, nSize ); } void Bu::String::set( const String &rData ) { clear(); append( rData ); } void Bu::String::set( const String &rData, long nSize ) { clear(); append( rData, nSize ); } void Bu::String::set( const String &rData, long nStart, long nSize ) { clear(); append( rData, nStart, nSize ); } void Bu::String::set( const_iterator s ) { clear(); append( s ); } void Bu::String::set( const_iterator s, const_iterator e ) { clear(); append( s, e ); } void Bu::String::setSize( long iSize ) { core->clear(); core->appendChunk( core->newChunk( iSize ) ); } bool Bu::String::operator==( const char *pData ) const { if( core->pFirst == NULL || core->nLength == 0 ) { if( pData == NULL ) return true; if( pData[0] == (char)0 ) return true; return false; } flatten(); core->pFirst->pData[core->nLength] = (char)0; const char *a = pData; char *b = core->pFirst->pData; for( long j = 0; *a!=(char)0 || *b!=(char)0; j++, a++, b++ ) { if( *a != *b ) return false; if( *a == (char)0 && j < core->nLength ) return false; } return true; } bool Bu::String::operator==( const String &pData ) const { if( core == pData.core ) return true; if( core->pFirst == pData.core->pFirst ) return true; if( (core->nLength == 0 && pData.core->nLength == 0) ) return true; if( core->nLength != pData.core->nLength ) return false; if( pData.core->pFirst == NULL || core->pFirst == NULL ) return false; flatten(); pData.flatten(); const char *a = pData.core->pFirst->pData; char *b = core->pFirst->pData; for( long j = 0; j < core->nLength; j++, a++, b++ ) { if( *a != *b ) return false; } return true; } bool Bu::String::operator!=(const char *pData ) const { return !(*this == pData); } bool Bu::String::operator!=(const String &pData ) const { return !(*this == pData); } bool Bu::String::operator<(const String &pData ) const { flatten(); pData.flatten(); const char *a = core->pFirst->pData; char *b = pData.core->pFirst->pData; for( long j = 0; j < core->nLength; j++, a++, b++ ) { if( *a != *b ) return *a < *b; } return false; } bool Bu::String::operator<=(const String &pData ) const { flatten(); pData.flatten(); const char *a = core->pFirst->pData; char *b = pData.core->pFirst->pData; for( long j = 0; j < core->nLength; j++, a++, b++ ) { if( *a != *b ) return *a < *b; } return true; } bool Bu::String::operator>(const String &pData ) const { flatten(); pData.flatten(); const char *a = core->pFirst->pData; char *b = pData.core->pFirst->pData; for( long j = 0; j < core->nLength; j++, a++, b++ ) { if( *a != *b ) return *a > *b; } return false; } bool Bu::String::operator>=(const String &pData ) const { flatten(); pData.flatten(); const char *a = core->pFirst->pData; char *b = pData.core->pFirst->pData; for( long j = 0; j < core->nLength; j++, a++, b++ ) { if( *a != *b ) return *a > *b; } return true; } char &Bu::String::operator[]( long nIndex ) { if( nIndex < 0 || nIndex >= core->nLength ) throw Bu::ExceptionBase("Index out of range."); flatten(); return core->pFirst->pData[nIndex]; } const char &Bu::String::operator[]( long nIndex ) const { if( nIndex < 0 || nIndex >= core->nLength ) throw Bu::ExceptionBase("Index out of range."); flatten(); return core->pFirst->pData[nIndex]; } bool Bu::String::isSet() const { return (core->pFirst != NULL); } bool Bu::String::compareSub( const char *pData, long nIndex, long nLen ) const { if( core->pFirst == NULL || core->nLength == 0 ) { if( pData == NULL ) return true; if( nLen == 0 ) return true; if( pData[0] == (char)0 ) return true; return false; } if( nIndex+nLen > core->nLength ) return false; flatten(); core->pFirst->pData[core->nLength] = (char)0; const char *a = pData; char *b = core->pFirst->pData+nIndex; for( long j = 0; j < nLen; j++, a++, b++ ) { if( *a != *b ) return false; if( *a == (char)0 && j < core->nLength ) return false; } return true; } bool Bu::String::compareSub( const String &rData, long nIndex, long nLen ) const { if( core->pFirst == NULL || core->nLength == 0 || rData.core->pFirst == NULL || rData.core->nLength == 0 ) return false; if( nLen < 0 ) nLen = rData.core->nLength; if( nIndex+nLen > core->nLength ) return false; flatten(); rData.flatten(); const char *a = rData.core->pFirst->pData; char *b = core->pFirst->pData + nIndex; for( long j = 0; j < nLen; j++, a++, b++ ) { if( *a != *b ) return false; } return true; } bool Bu::String::isWS( long nIndex ) const { flatten(); return core->pFirst->pData[nIndex]==' ' || core->pFirst->pData[nIndex]=='\t' || core->pFirst->pData[nIndex]=='\r' || core->pFirst->pData[nIndex]=='\n'; } bool Bu::String::isAlpha( long nIndex ) const { flatten(); return (core->pFirst->pData[nIndex] >= 'a' && core->pFirst->pData[nIndex] <= 'z') || (core->pFirst->pData[nIndex] >= 'A' && core->pFirst->pData[nIndex] <= 'Z'); } Bu::String Bu::String::toLower() const { Bu::String sRet = *this; sRet.flatten(); for( long j = 0; j < sRet.core->nLength; j++ ) { if( sRet.core->pFirst->pData[j] >= 'A' && sRet.core->pFirst->pData[j] <= 'Z' ) sRet.core->pFirst->pData[j] -= 'A'-'a'; } return sRet; } Bu::String Bu::String::toUpper() const { Bu::String sRet = *this; sRet.flatten(); for( long j = 0; j < sRet.core->nLength; j++ ) { if( sRet.core->pFirst->pData[j] >= 'a' && sRet.core->pFirst->pData[j] <= 'z' ) sRet.core->pFirst->pData[j] += 'A'-'a'; } return sRet; } int16_t Bu::String::toInt16( int iRadix ) const { flatten(); if( core->pFirst == NULL || core->nLength == 0 ) return 0; return strtol( core->pFirst->pData, NULL, iRadix ); } int32_t Bu::String::toInt32( int iRadix ) const { flatten(); if( core->pFirst == NULL || core->nLength == 0 ) return 0; return strtol( core->pFirst->pData, NULL, iRadix ); } int64_t Bu::String::toInt64( int iRadix ) const { flatten(); if( core->pFirst == NULL || core->nLength == 0 ) return 0; return strtoll( core->pFirst->pData, NULL, iRadix ); } Bu::String::const_iterator Bu::String::find( const char cChar, Bu::String::const_iterator iStart ) const { if( !iStart ) iStart = begin(); for( ; iStart; iStart++ ) { if( cChar == *iStart ) return iStart; } return end(); } Bu::String::const_iterator Bu::String::find( const char *sText, int nLen, Bu::String::const_iterator iStart ) const { if( !iStart ) iStart = begin(); for( ; iStart; iStart++ ) { if( iStart.compare( sText, nLen ) ) return iStart; } return end(); } Bu::String::const_iterator Bu::String::find( const String &rStr, Bu::String::const_iterator iStart ) const { if( !iStart ) iStart = begin(); for( ; iStart; iStart++ ) { if( iStart.compare( rStr ) ) return iStart; } return end(); } Bu::String::const_iterator Bu::String::find( const String &rStr, int nLen, Bu::String::const_iterator iStart ) const { if( !iStart ) iStart = begin(); for( ; iStart; iStart++ ) { if( iStart.compare( rStr, nLen ) ) return iStart; } return end(); } Bu::String::iterator Bu::String::find( const char cChar, Bu::String::const_iterator iStart ) { if( !iStart ) iStart = begin(); for( ; iStart; iStart++ ) { if( cChar == *iStart ) return iterator( iStart.pChunk, iStart.iPos ); } return end(); } Bu::String::iterator Bu::String::find( const char *sText, int nLen, Bu::String::const_iterator iStart ) { if( !iStart ) iStart = begin(); for( ; iStart; iStart++ ) { if( iStart.compare( sText, nLen ) ) return iterator( iStart.pChunk, iStart.iPos ); } return end(); } Bu::String::iterator Bu::String::find( const String &rStr, Bu::String::const_iterator iStart ) { if( !iStart ) iStart = begin(); for( ; iStart; iStart++ ) { if( iStart.compare( rStr ) ) return iterator( iStart.pChunk, iStart.iPos ); } return end(); } Bu::String::iterator Bu::String::find( const String &rStr, int nLen, Bu::String::const_iterator iStart ) { if( !iStart ) iStart = begin(); for( ; iStart; iStart++ ) { if( iStart.compare( rStr, nLen ) ) return iterator( iStart.pChunk, iStart.iPos ); } return end(); } long Bu::String::findIdx( const char cChar, long iStart ) const { flatten(); for( long j = iStart; j < core->pFirst->nLength; j++ ) { if( core->pFirst->pData[j] == cChar ) return j; } return -1; } long Bu::String::findIdx( const char *sText, long iStart ) const { long nTLen = strlen( sText ); flatten(); for( long j = iStart; j < core->pFirst->nLength-nTLen; j++ ) { if( !strncmp( sText, core->pFirst->pData+j, nTLen ) ) return j; } return -1; } long Bu::String::rfindIdx( const char *sText ) const { long nTLen = strlen( sText ); flatten(); for( long j = core->pFirst->nLength-nTLen-1; j >= 0; j-- ) { if( !strncmp( sText, core->pFirst->pData+j, nTLen ) ) return j; } return -1; } void Bu::String::trimFront( long nAmnt ) { long nNewLen = core->nLength - nAmnt; flatten(); Chunk *pNew = core->newChunk( nNewLen ); memcpy( pNew->pData, core->pFirst->pData+nAmnt, nNewLen ); core->clear(); core->appendChunk( pNew ); } /* void Bu::String::trimBack( char c ) { if( core->pFirst == NULL || core->nLength == 0 ) return; flatten(); for( ; core->pFirst->nLength > 0 && core->pFirst->pData[core->pFirst->nLength-1] == c; core->pFirst->nLength--, core->nLength-- ) { } } */ void Bu::String::trimBack( long iAmnt ) { if( iAmnt < 0 ) return; if( core->nLength - iAmnt < 0 ) { clear(); return; } if( core->pFirst == NULL || core->nLength == 0 ) return; flatten(); core->pFirst->nLength -= iAmnt; core->nLength -= iAmnt; } Bu::String Bu::String::trimWhitespace() const { if( core->nLength == 0 ) return ""; const_iterator i = begin(); for( ; i && (*i == ' ' || *i == '\t' || *i == '\n' || *i == '\r'); i++ ) { } if( !i ) return ""; const_iterator e = i; for( ; e; e++ ) { if( *e == ' ' || *e == '\t' || *e == '\n' || *e == '\r' ) { const_iterator t = e; for( ; t && (*t == ' ' || *t == '\t' || *t == '\n' || *t == '\r'); t++ ) { } if( t ) { e = t; } else { break; } } } return Bu::String( i, e ); } Bu::String::iterator Bu::String::begin() { if( core->nLength == 0 ) return iterator( NULL, 0 ); return iterator( core->pFirst, 0 ); } Bu::String::const_iterator Bu::String::begin() const { if( core->nLength == 0 ) return const_iterator( NULL, 0 ); return const_iterator( core->pFirst, 0 ); } Bu::String::iterator Bu::String::end() { return iterator( NULL, 0 ); } Bu::String::const_iterator Bu::String::end() const { return const_iterator( NULL, 0 ); } bool Bu::String::isEmpty() const { if( core->nLength == 0 ) return true; return false; } void Bu::String::flatten() const { if( isFlat() ) return; if( core->pFirst == NULL || core->nLength == 0 ) return; Chunk *pNew = core->newChunk( core->nLength ); char *pos = pNew->pData; Chunk *i = core->pFirst; for(;;) { memcpy( pos, i->pData, i->nLength ); pos += i->nLength; i = i->pNext; if( i == NULL ) break; } core->clear(); core->pLast = core->pFirst = pNew; core->nLength = pNew->nLength; } bool Bu::String::isFlat() const { return (core->pFirst == core->pLast); } // // Sub-class Bu::String::FormatProxy // Bu::String::FormatProxy::FormatProxy( const String &rFmt, String::FormatProxyEndAction *pAct ) : rFmt( rFmt ), pAct( pAct ), bOpen( true ) { } Bu::String::FormatProxy::~FormatProxy() { if( pAct && bOpen ) end(); delete pAct; } Bu::String Bu::String::FormatProxy::end() const { int iCount = lArgs.getSize(); ArgList::const_iterator *aArg = new ArgList::const_iterator[iCount]; { int j = 0; for( ArgList::const_iterator i = lArgs.begin(); i; i++, j++ ) { aArg[j] = i; } } Bu::MemBuf mbOut; Bu::Formatter f( mbOut ); for( String::const_iterator s = rFmt.begin(); s; ) { if( *s == '%' ) { s++; if( *s == '%' ) f << *s; else { String sNum; if( *s == '{' ) { s++; while( s && *s >= '0' && *s <= '9' ) { sNum += *s; s++; } if( !s || *s != '}' ) { delete[] aArg; throw Bu::ExceptionBase( "Argument started with a '{' but terminated " "with a '%c' instead of '}'.", *s ); } s++; } else { while( s && *s >= '0' && *s <= '9' ) { sNum += *s; s++; } } int iIndex = strtol( sNum.getStr(), 0, 10 )-1; if( iIndex < 0 || iIndex >= iCount ) { delete[] aArg; throw Bu::ExceptionBase( "Argument index %d is outside of " "valid range (1-%d).", iIndex+1, iCount ); } f << (*aArg[iIndex]).format << (*aArg[iIndex]).value; continue; } } else { f << *s; } s++; } bOpen = false; delete[] aArg; if( pAct ) (*pAct)( mbOut.getString() ); return mbOut.getString(); } template<> uint32_t Bu::__calcHashCode( const Bu::String &k ) { long j, sz = k.getSize(); const char *s = k.getStr(); long nPos = 0; for( j = 0; j < sz; j++, s++ ) { nPos = *s + (nPos << 6) + (nPos << 16) - nPos; } return nPos; } template<> bool Bu::__cmpHashKeys( const Bu::String &a, const Bu::String &b ) { return a == b; } template<> void Bu::__tracer_format( const Bu::String &v ) { printf("(%ld)\"%s\"", v.getSize(), v.getStr() ); } bool &Bu::operator<<( bool &dst, const Bu::String &sIn ) { if( sIn == "true" || sIn == "yes" || sIn == "t" ) dst = true; else dst = false; return dst; } uint8_t &Bu::operator<<( uint8_t &dst, const Bu::String &sIn ) { sscanf( sIn.getStr(), "%hhu", &dst ); return dst; } int8_t &Bu::operator<<( int8_t &dst, const Bu::String &sIn ) { sscanf( sIn.getStr(), "%hhd", &dst ); return dst; } char &Bu::operator<<( char &dst, const Bu::String &sIn ) { sscanf( sIn.getStr(), "%hhd", &dst ); return dst; } uint16_t &Bu::operator<<( uint16_t &dst, const Bu::String &sIn ) { sscanf( sIn.getStr(), "%hu", &dst ); return dst; } int16_t &Bu::operator<<( int16_t &dst, const Bu::String &sIn ) { sscanf( sIn.getStr(), "%hd", &dst ); return dst; } uint32_t &Bu::operator<<( uint32_t &dst, const Bu::String &sIn ) { sscanf( sIn.getStr(), "%u", &dst ); return dst; } int32_t &Bu::operator<<( int32_t &dst, const Bu::String &sIn ) { sscanf( sIn.getStr(), "%d", &dst ); return dst; } uint64_t &Bu::operator<<( uint64_t &dst, const Bu::String &sIn ) { sscanf( sIn.getStr(), "%lu", &dst ); return dst; } int64_t &Bu::operator<<( int64_t &dst, const Bu::String &sIn ) { sscanf( sIn.getStr(), "%ld", &dst ); return dst; } float &Bu::operator<<( float &dst, const Bu::String &sIn ) { sscanf( sIn.getStr(), "%f", &dst ); return dst; } double &Bu::operator<<( double &dst, const Bu::String &sIn ) { sscanf( sIn.getStr(), "%lf", &dst ); return dst; } long double &Bu::operator<<( long double &dst, const Bu::String &sIn ) { sscanf( sIn.getStr(), "%Lf", &dst ); return dst; } Bu::String &Bu::operator<<( Bu::String &dst, const Bu::String &sIn ) { dst = sIn; return dst; } Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, const Bu::String &s ) { long n = s.getSize(); ar << n; ar.write( s.getConstStr(), n ); return ar; } Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, Bu::String &s ) { long n; ar >> n; s.setSize( n ); ar.read( s.getStr(), n ); return ar; }