From 469bbcf0701e1eb8a6670c23145b0da87357e178 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Sun, 25 Mar 2012 20:00:08 +0000 Subject: Code is all reorganized. We're about ready to release. I should write up a little explenation of the arrangement. --- src/unstable/bitstring.cpp | 479 ++++++++++++++++++++++++++++ src/unstable/bitstring.h | 224 ++++++++++++++ src/unstable/fifo.cpp | 162 ++++++++++ src/unstable/fifo.h | 72 +++++ src/unstable/itoserver.cpp | 242 +++++++++++++++ src/unstable/itoserver.h | 141 +++++++++ src/unstable/minimacro.cpp | 187 +++++++++++ src/unstable/minimacro.h | 130 ++++++++ src/unstable/myriad.cpp | 663 +++++++++++++++++++++++++++++++++++++++ src/unstable/myriad.h | 222 +++++++++++++ src/unstable/myriadfs.cpp | 703 ++++++++++++++++++++++++++++++++++++++++++ src/unstable/myriadfs.h | 203 ++++++++++++ src/unstable/myriadstream.cpp | 309 +++++++++++++++++++ src/unstable/myriadstream.h | 61 ++++ src/unstable/newline.cpp | 68 ++++ src/unstable/newline.h | 41 +++ src/unstable/udpsocket.cpp | 240 ++++++++++++++ src/unstable/udpsocket.h | 84 +++++ src/unstable/url.cpp | 285 +++++++++++++++++ src/unstable/url.h | 85 +++++ src/unstable/urn.cpp | 8 + src/unstable/urn.h | 8 + src/unstable/utfstring.cpp | 539 ++++++++++++++++++++++++++++++++ src/unstable/utfstring.h | 174 +++++++++++ src/unstable/uuid.cpp | 117 +++++++ src/unstable/uuid.h | 56 ++++ 26 files changed, 5503 insertions(+) create mode 100644 src/unstable/bitstring.cpp create mode 100644 src/unstable/bitstring.h create mode 100644 src/unstable/fifo.cpp create mode 100644 src/unstable/fifo.h create mode 100644 src/unstable/itoserver.cpp create mode 100644 src/unstable/itoserver.h create mode 100644 src/unstable/minimacro.cpp create mode 100644 src/unstable/minimacro.h create mode 100644 src/unstable/myriad.cpp create mode 100644 src/unstable/myriad.h create mode 100644 src/unstable/myriadfs.cpp create mode 100644 src/unstable/myriadfs.h create mode 100644 src/unstable/myriadstream.cpp create mode 100644 src/unstable/myriadstream.h create mode 100644 src/unstable/newline.cpp create mode 100644 src/unstable/newline.h create mode 100644 src/unstable/udpsocket.cpp create mode 100644 src/unstable/udpsocket.h create mode 100644 src/unstable/url.cpp create mode 100644 src/unstable/url.h create mode 100644 src/unstable/urn.cpp create mode 100644 src/unstable/urn.h create mode 100644 src/unstable/utfstring.cpp create mode 100644 src/unstable/utfstring.h create mode 100644 src/unstable/uuid.cpp create mode 100644 src/unstable/uuid.h (limited to 'src/unstable') diff --git a/src/unstable/bitstring.cpp b/src/unstable/bitstring.cpp new file mode 100644 index 0000000..bdd1bc2 --- /dev/null +++ b/src/unstable/bitstring.cpp @@ -0,0 +1,479 @@ +/* + * Copyright (C) 2007-2011 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/bitstring.h" +#include +#include +#include + +#include "bu/exceptionbase.h" + +#ifdef _WIN32 +#define random() rand() +#endif + +#define bitsToBytes( iBits ) (((iBits/8)+((iBits%8)?(1):(0)))); + +Bu::BitString::BitString() +{ + caData = NULL; + cTopByteMask = 0; + iBits = iBytes = 0; +} + +Bu::BitString::BitString( const Bu::BitString &xSrc ) +{ + iBits = xSrc.iBits; + iBytes = xSrc.iBytes; + cTopByteMask = xSrc.cTopByteMask; + caData = new unsigned char[iBytes]; + memcpy( caData, xSrc.caData, iBytes ); + + fixup(); +} + +Bu::BitString::BitString( long iNewBits, bool bFillRandomly ) +{ + long j; + iBits = iNewBits; + iBytes = bitsToBytes( iNewBits );//(iNewBits/8)+((iNewBits%8)?(1):(0)); + caData = new unsigned char[iBytes]; + + setMask(); + + if( bFillRandomly ) + { + // rand() only returns a value up to RAND_MAX (0x7FFF on my system) so + // I'll just use the low order byte) + for( j = 0; j < iBytes; j++ ) + { + caData[j] = (unsigned char)(random() & 0xFF); + } + } + else + { + clear(); + } + + fixup(); +} + +Bu::BitString::~BitString() +{ + if( caData != NULL ) delete[] caData; +} + +Bu::BitString &Bu::BitString::operator=( const Bu::BitString &xSrc ) +{ + if( caData != NULL ) + { + delete[] caData; + } + iBits = xSrc.iBits; + iBytes = xSrc.iBytes; + cTopByteMask = xSrc.cTopByteMask; + caData = new unsigned char[iBytes]; + memcpy( caData, xSrc.caData, iBytes ); + + fixup(); + + return *this; +} + +Bu::BitString Bu::BitString::operator~() +{ + Bu::BitString xRet( *this ); + + for( int j = 0; j < xRet.iBytes; j++ ) + { + xRet.caData[j] = ~xRet.caData[j]; + } + + xRet.fixup(); + + return xRet; +} + +Bu::BitString Bu::BitString::operator<<( const long iAmt ) +{ + if( iAmt == 0 ) + { + return (*this); + } + //int iByteShift = iAmt/8; + + Bu::BitString xSub( getSize() ); + + long shft = (iAmt%8); + long base = (iAmt/8); + unsigned char lowmask=0; + for( long j = 0; j < 8-shft; j++ ) + { + lowmask |= (1<>shft)&(lowmask)) | ((caData[j+1]<<(8-shft))&(~lowmask)); + } + xSub.fixup(); + + return xSub; +} + +Bu::BitString Bu::BitString::operator>>( const long iAmt ) +{ + if( iAmt == 0 ) + { + return (*this); + } + return (*this); +} + +void Bu::BitString::shiftLeft( long iAmt ) +{ + if( iAmt == 0 ) + { + return; + } + else if( iAmt < 0 ) + { + shiftRight( -iAmt ); + return; + } + + long iByteShift = iAmt/8; + long iBitShift = iAmt%8; + + long j; + for( j = iBytes-1; j >= 0; j-- ) + { + caData[j] = (((j-iByteShift)<0)?(0):((caData[j-iByteShift]<>(8-iBitShift)))); + } + + fixup(); +} + +void Bu::BitString::shiftRight( long iAmt ) +{ + if( iAmt == 0 ) + { + return; + } + else if( iAmt < 0 ) + { + shiftLeft( -iAmt ); + return; + } + + long iByteShift = iAmt/8; + long iBitShift = iAmt%8; + + long j; + for( j = 0; j < iBytes; j++ ) + { + caData[j] = (((j+iByteShift)>iBytes)?(0):((caData[j+iByteShift]>>iBitShift))) | (((j+iByteShift+1)>iBytes)?(0):((caData[j+iByteShift+1]<<(8-iBitShift)))); + } + + fixup(); +} +/* +long Bu::BitString::bitsToBytes( long iBits ) +{ + return (iBits/8)+((iBits%8)?(1):(0)); +} +*/ +void Bu::BitString::fixup() +{ + if( caData != NULL ) + { + caData[iBytes-1] &= cTopByteMask; + } +} + +void Bu::BitString::setBit( long iBit, bool bBitState ) +{ + if( iBit < 0 || iBit >= iBits ) + throw Bu::ExceptionBase("bit out of range: %d in (0-%d)", iBit, iBits ); + if( bBitState ) + { + caData[iBit/8] |= (1<<(iBit%8)); + } + else + { + caData[iBit/8] &= ~(1<<(iBit%8)); + } +} + +void Bu::BitString::flipBit( long iBit ) +{ + caData[iBit/8] ^= (1<<(iBit%8)); +} + +bool Bu::BitString::getBit( long iBit ) +{ + if( iBit >= iBits || iBit < 0 ) return false; + if( (caData[iBit/8] & (1<<(iBit%8))) == 0 ) + { + return false; + } + return true; +} + +long Bu::BitString::getBitLength() +{ + return iBits; +} + +long Bu::BitString::getSize() +{ + return iBits; +} + +class Bu::BitString Bu::BitString::getSubString( long iLower, long iUpper ) +{ + if( iUpper == 0 || iUpper < iLower ) iUpper = iBits; + + Bu::BitString xSub( iUpper-iLower+1 ); + + long shft = (iLower%8); + long base = (iLower/8); + unsigned char lowmask=0; + for( long j = 0; j < 8-shft; j++ ) + { + lowmask |= (1<>shft)&(lowmask)) | ((caData[base+j+1]<<(8-shft))&(~lowmask)); + } + xSub.fixup(); + + return xSub; +} + +long Bu::BitString::toLong( long iStart, long iSize ) +{ + if( iSize < 1 ) iSize = 1; + if( iSize > 32 ) iSize = 32; + if( iStart < 0 ) return 0; + if( iStart+iSize > getSize() ) return 0; + + Bu::BitString tmpo; + tmpo = getSubString( iStart, iStart+iSize-1 ); + long x = *((long *)tmpo.caData); + + return x; +} +/* +std::string Bu::BitString::toString( bool bAddSpacers ) +{ + long iSz = iBits; + if( bAddSpacers ) + { + iSz += (iBits/8); + if( iBits%8 == 0 ) iSz--; + } + std::string xStr; + + int bw=0; + int of=0; + for( int j = iBits-1; j >= 0; j-- ) + { + if( getBit( j ) ) + { + xStr += '1'; + } + else + { + xStr += '0'; + } + + if( bAddSpacers ) + { + bw++; + if( bw >= 8 && j < iBits-1 ) + { + bw = 0; + of++; + xStr += ' '; + } + } + } + + return xStr; +} +*/ +void Bu::BitString::clear() +{ + if( caData != NULL ) + { + memset( caData, 0, iBytes ); + } +} + +bool Bu::BitString::setBitLength( long iLength, bool bClear ) +{ + return setSize( iLength, bClear ); +} + +bool Bu::BitString::setSize( long iLength, bool bClear ) +{ + // First, if there's nothing, then allocate an empty one. + if( caData == NULL ) + { + iBits = iLength; + iBytes = bitsToBytes( iLength ); + caData = new unsigned char[iBytes]; + memset( caData, 0, iBytes ); + return true; + } + + // If the new length is the same as the old, don't do anything, but do + // check to see if we should still clear the data. + if( iBits != iLength ) + { + // Ok, we are changing the number if bits, but are we changing the + // number of bytes? + long iNewBytes = bitsToBytes( iLength ); + if( iBytes == iNewBytes ) + { + // No? That makes life easier + iBits = iLength; + setMask(); + if( bClear ) + { + clear(); + } + } + else + { + // Ok, reallocate and copy... + iBits = iLength; +// long iNewBytes = bitsToBytes( iLength ); + if( bClear ) + { + delete[] caData; + caData = new unsigned char[iNewBytes]; + memset( caData, 0, iNewBytes ); + } + else + { + unsigned char *tmp = caData; + caData = new unsigned char[iNewBytes]; + if( iNewBytes < iBytes ) + { + memcpy( caData, tmp, iNewBytes ); + } + else + { + memcpy( caData, tmp, iBytes ); + } + delete[] tmp; + } + iBytes = iNewBytes; + + setMask(); + } + + } + else if( bClear ) + { + clear(); + } + + return true; +} + +void Bu::BitString::setMask() +{ + // This can either mean that there are a multiple of eight bits or + // zero, if there are zero you're an idiot (zero can't happen, because + // we would allocate an extra byte and never use it) + if( (iBits%8 == 0) ) + { + cTopByteMask = 0xFF; + } + else + { + cTopByteMask = 0; + for( long j = 0; j < (iBits%8); j++ ) + { + cTopByteMask |= (1<= 0; j-- ) + { + if( getBit( j ) ) + { + return j; + } + } + + return -1; +} + +Bu::String Bu::BitString::toString() +{ + Bu::String sRet; + for( int j = iBits-1; j >= 0; j-- ) + sRet.append( getBit( j )?'1':'0' ); + return sRet; +} + +/* +bool Bu::BitString::writeToFile( FILE *fh ) +{ + fwrite( &iBits, sizeof(long), 1, fh ); + fwrite( caData, sizeof(char), iBytes, fh ); + + return true; +} + +bool Bu::BitString::readFromFile( FILE *fh ) +{ + fread( &iBits, sizeof(long), 1, fh ); + + iBytes = bitsToBytes( iBits ); + if( caData ) delete[] caData; + caData = new unsigned char[iBytes]; + + fread( caData, sizeof(char), iBytes, fh ); + + setMask(); + + fixup(); + + return true; +}*/ diff --git a/src/unstable/bitstring.h b/src/unstable/bitstring.h new file mode 100644 index 0000000..7a8fc48 --- /dev/null +++ b/src/unstable/bitstring.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2007-2011 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. + */ + +#ifndef BU_BITSTRING_H +#define BU_BITSTRING_H + +#include "bu/util.h" +#include "bu/string.h" + +namespace Bu +{ + /** + * Manages an arbitrarily sized string of bits, and allows basic interaction + * with them. This includes basic non-mathematical bitwise operations such + * as setting and testing bits, shifting the string, inversion and a few + * extras like randomization. On linux systems this takes advantage of long + * longs giving you a maximum size of about 2tb per string. + * + * For more general and mathematical type interaction see BitStringInt. + * + */ + class BitString + { + public: + /** + * Constructs a blank and basic BitString. This is actually useful + * since you can resize BitStrings at will, and even retain the data + * that was in them. + */ + BitString(); + + /** + * Constructs a BitString object as a copy of another BitString. This + * is a standard copy constructor and produces an exact duplicate of + * the original BitString object. + *@param xSrc Source BitString to copy data from. + */ + BitString( const BitString &xSrc ); + + /** + * Constructs a BitString with length iBits and optionally fills it with + * random data. The default setting, to not fill randomly, will produce + * a blank (all zeros) string of the specified size. + *@param iBits The length of the new BitString in bits. + *@param bFillRandomly Wether or not to randomize this BitString. + */ + BitString( long iBits, bool bFillRandomly=false ); + + /** + * Virtual deconstructor for the BitString. Takes care of cleanup for + * you. What more do you really want to know? + */ + virtual ~BitString(); + + // basic interaction + /** + * Sets a bit in the BitString. In it's normal mode it will always turn + * the given bit on, to clear a bit set bBitState to false instead of + * true. This operation runs in O(1). + *@param iBit The zero-based index of the bit to modify. + *@param bBitState Set to true to set the bit to 1, set to false to set + * the bit to 0. + */ + void setBit( long iBit, bool bBitState=true ); + + /** + * Reverses the state of the given bit. This will set the given bit + * to a 1 if it was 0, and to 0 if it was 1. This operation runs in + * O(1), and it should be noted that using this is marginally faster + * than doing the test and flip yourself with getBit and setBit since + * it uses a bitwise not operation and doesn't actually test the bit + * itself. + *@param iBit The index of the bit to flip. + */ + void flipBit( long iBit ); + + /** + * Gets the state of the given bit. This follows the standard + * convention used so far, a returned value of true means the bit in + * question is 1, and a value of flase means the bit is 0. All bits + * out of range of the BitString are treated as off, but are + * "accessable" in that this does not produce any kind of error + * message. This is intentional. This operation runs in O(1). + *@param iBit The index of the bit to test. + *@returns True for a 1, false for a 0. + */ + bool getBit( long iBit ); + + /** + * Inverts the entire BitString, in effect this calls flipBit on every + * bit in the string but is faster since it can operate on whole bytes + * at a time instead of individual bits. This operation runs in O(N). + */ + void invert(); + + /** + * Returns the number of bits allocated in this BitString. This + * operation runs in O(1) time since this value is cached and not + * computed. + *@returns The number of bits allocated in this BitString. + */ + DEPRECATED + long getBitLength(); + + long getSize(); + + /** + * Sets the entire BitString to zeros, but it does it very quickly. + * This operation runs in O(N). + */ + void clear(); + + /** + * Gets another BitString that is autonomous of the current one + * (contains a copy of the memory, not a pointer) and contains a subset + * of the data in the current BitString. This is an inclusive + * operation, so grabbing bits 0-5 will give you 6 bits. This is based + * on a very tricky bit-shifting algorithm and runs very quickly, in + * O(N) time. Passing in a value of zero for iUpper, or any value for + * iUpper that is less than iLower will set iUpper equal to the number + * of bits in the BitString. + *@param iLower The first bit in the current string, will be the first + * bit (0 index) in the new sub string. + *@param iUpper The last bit in the current string, will be the last + * bit in the new sub string. iUpper is included in the sub string. + *@returns A new BitString object ready to be used. Please note that + * managing this new object is up to whomever calls this function. + */ + class BitString getSubString( long iLower, long iUpper ); + + /** + * Sets the number of bits in the BitString, allocating more memory if + * necesarry, or freeing extra if able. The default operation of this + * function clears all data in the BitString while resizing it. If you + * would like to keep as much of the data that you had in your BitString + * as possible, then set bClear to false, and any data that will fit + * into the new BitString length will be retained. If increasing the + * number of bits, the new bits will come into existance cleared (set + * to 0). + *@param iLength The number of bits to set the BitString to. + *@param bClear When true, all data is eradicated and zeroed, when set + * to false an effort is made to retain the existing data. + *@returns true on success, false on failure. + */ + DEPRECATED + bool setBitLength( long iLength, bool bClear=true ); + bool setSize( long iLength, bool bClear=true ); + + /** + * Randomize the entire BitString, one bit at a time. This is actually + * the function called by the constructor when the user selects initial + * randomization. This function uses the system random() function, so + * srandom may be used to effect this process at will. + */ + void randomize(); + + /** + * Operates exactly like <<. All data in the BitString is shifted to + * the left by some number of bits, any data pushed off the edge of the + * BitString is lost, and all new data coming in will be zeroed. + * Using a negative value in the shiftLeft function will turn it into + * the shiftRight function. + *@param iAmt The number of bit positions to shift all data. + */ + void shiftLeft( long iAmt ); // just like << + + /** + * Operates exactly like >>. All data in the BitString is shifted to + * the right by some number of bits, any data pushed off the edge of the + * BitString is lost, and all new data coming in will be zeroed. + * Using a negative value in the shiftRight function will turn it into + * the shiftLeft function. + *@param iAmt The number of bit positions to shift all data. + */ + void shiftRight( long iAmt ); // just like >> + + /** + * Searches through the BitString and returns the index of the highest + * order bit position (the highest index) with an on bit (a bit set to + * 1). This is a handy helper function and rather faster than calling + * getBit() over and over again. + *@returns The index of the highest indexed on bit. + */ + long getHighestOrderBitPos(); + + // Conversion + /** + * Convert a block of data (no more than 32 bits) to a primitive long + * type. + * This is done in a little bit interesting way, so it may not always be + * the fastest way to access the data that you want, although it will + * always ensure that the long that is written makes numerical sense, as + * we write numbers, regaurdless of platform. + *@param iStart The first bit in the BitString to include in the long + *@param iSize THe number of bits to include, if this value is set over + * 32 it will be automatically truncated to, or however many bits there + * are in a long in your system. + *@returns A long converted from your raw BitString data. + */ + long toLong( long iStart = 0, long iSize = 32 ); + + Bu::String toString(); + + //operators + BitString &operator=( const BitString &xSrc ); + BitString operator~(); + BitString operator<<( const long iAmt ); + BitString operator>>( const long iAmt ); + + private: + void fixup(); + void setMask(); + unsigned char *caData; + long iBits; + long iBytes; + unsigned char cTopByteMask; + }; +}; + +#endif diff --git a/src/unstable/fifo.cpp b/src/unstable/fifo.cpp new file mode 100644 index 0000000..b0cf1c7 --- /dev/null +++ b/src/unstable/fifo.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2007-2011 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/fifo.h" +#include +#include +#include +#include +#include + +#include "bu/config.h" + +namespace Bu { subExceptionDef( FifoException ) } + +Bu::Fifo::Fifo( const Bu::String &sName, int iFlags, mode_t mAcc ) : + iFlags( iFlags ), + iIn( -1 ), + iOut( -1 ) +{ +#ifndef WIN32 + if( iFlags&Create ) + { + if( mkfifo( sName.getStr(), mAcc ) ) + { + throw FifoException("Error creating fifo: %s\n", strerror( errno ) ); + } + } + if( iFlags&Read ) + { + iIn = ::open( + sName.getStr(), + O_RDONLY|((iFlags&NonBlock)?O_NONBLOCK:0) + ); + } + if( iFlags&Write ) + { + iOut = ::open( + sName.getStr(), + O_WRONLY + ); + } +#else + #warning Bu::Fifo::Fifo IS A STUB for WIN32!!!! +#endif +} + +Bu::Fifo::~Fifo() +{ + close(); +} + +void Bu::Fifo::close() +{ + if( iIn > -1 ) + { + ::close( iIn ); + iIn = -1; + } + if( iOut > -1 ) + { + ::close( iOut ); + iOut = -1; + } +} + +Bu::size Bu::Fifo::read( void *pBuf, Bu::size nBytes ) +{ + if( iIn < 0 ) + throw FifoException("Fifo not open for reading."); + + return TEMP_FAILURE_RETRY( ::read( iIn, pBuf, nBytes ) ); +} + +Bu::size Bu::Fifo::write( const void *pBuf, Bu::size nBytes ) +{ + if( iOut < 0 ) + throw FifoException("Fifo not open for writing."); + + return TEMP_FAILURE_RETRY( ::write( iOut, pBuf, nBytes ) ); +} + +Bu::size Bu::Fifo::tell() +{ + return -1; +} + +void Bu::Fifo::seek( Bu::size ) +{ +} + +void Bu::Fifo::setPos( Bu::size ) +{ +} + +void Bu::Fifo::setPosEnd( Bu::size ) +{ +} + +bool Bu::Fifo::isEos() +{ + return false; +} + +bool Bu::Fifo::canRead() +{ + return (iIn>-1); +} + +bool Bu::Fifo::canWrite() +{ + return (iOut>-1); +} + +bool Bu::Fifo::isReadable() +{ + return (iIn>-1); +} + +bool Bu::Fifo::isWritable() +{ + return (iOut>-1); +} + +bool Bu::Fifo::isSeekable() +{ + return false; +} + +bool Bu::Fifo::isBlocking() +{ +#ifndef WIN32 + return ((fcntl( iIn, F_GETFL, 0 )&O_NONBLOCK) == O_NONBLOCK); +#else + #warning Bu::Fifo::isBlocking IS A STUB for WIN32!!!! +#endif +} + +void Bu::Fifo::setBlocking( bool bBlocking ) +{ +#ifndef WIN32 + if( bBlocking ) + fcntl( iIn, F_SETFL, fcntl( iIn, F_GETFL, 0 )&(~O_NONBLOCK) ); + else + fcntl( iIn, F_SETFL, fcntl( iIn, F_GETFL, 0 )|O_NONBLOCK ); +#else + #warning Bu::Fifo::setBlocking IS A STUB for WIN32!!!! +#endif +} + +void Bu::Fifo::flush() +{ +} + +bool Bu::Fifo::isOpen() +{ + return (iIn > -1) || (iOut > -1); +} + diff --git a/src/unstable/fifo.h b/src/unstable/fifo.h new file mode 100644 index 0000000..18a70ba --- /dev/null +++ b/src/unstable/fifo.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2007-2011 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. + */ + +#ifndef BU_FIFO_H +#define BU_FIFO_H + +#include +#include +#include + +#include "bu/stream.h" +#include "bu/string.h" +#include "bu/exceptionbase.h" + +namespace Bu +{ + subExceptionDecl( FifoException ); + + /** + * A fifo stream. + *@ingroup Streams + */ + class Fifo : public Bu::Stream + { + public: + Fifo( const Bu::String &sName, int iFlags, mode_t mAcc=-1 ); + virtual ~Fifo(); + + virtual void close(); + virtual Bu::size read( void *pBuf, Bu::size nBytes ); + virtual Bu::size write( const void *pBuf, Bu::size nBytes ); + using Stream::write; + + virtual Bu::size tell(); + virtual void seek( Bu::size offset ); + virtual void setPos( Bu::size pos ); + virtual void setPosEnd( Bu::size pos ); + virtual bool isEos(); + virtual bool isOpen(); + + virtual void flush(); + + virtual bool canRead(); + virtual bool canWrite(); + + virtual bool isReadable(); + virtual bool isWritable(); + virtual bool isSeekable(); + + virtual bool isBlocking(); + virtual void setBlocking( bool bBlocking=true ); + + enum { + Read = 0x01, + Write = 0x02, + Create = 0x04, + Delete = 0x08, + NonBlock = 0x10 + }; + + private: + int iFlags; + int iIn; + int iOut; + }; +} + +#endif diff --git a/src/unstable/itoserver.cpp b/src/unstable/itoserver.cpp new file mode 100644 index 0000000..c7165e2 --- /dev/null +++ b/src/unstable/itoserver.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2007-2011 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/itoserver.h" +#include +#include "bu/tcpserversocket.h" +#include "bu/client.h" +#include "bu/tcpsocket.h" + +#include "bu/config.h" + +Bu::ItoServer::ItoServer() : + nTimeoutSec( 1 ), + nTimeoutUSec( 0 ) +{ + FD_ZERO( &fdActive ); +} + +Bu::ItoServer::~ItoServer() +{ + while( !qClientCleanup.isEmpty() ) + { + ItoClient *pCli = qClientCleanup.dequeue(); + pCli->join(); + delete pCli; + } + // TODO: Make sure here that each client has shutdown it's socket, and + // maybe even written any extra data, we could put a timelimit on this... + // anyway, it's not as clean as it could be right now. + for( ClientHash::iterator i = hClients.begin(); i != hClients.end(); i++ ) + { + ItoClient *pCli = (*i); + pCli->join(); + delete pCli; + } +} + +void Bu::ItoServer::addPort( int nPort, int nPoolSize ) +{ + TcpServerSocket *s = new TcpServerSocket( nPort, nPoolSize ); + int nSocket = s->getSocket(); + FD_SET( nSocket, &fdActive ); + hServers.insert( nSocket, s ); +} + +void Bu::ItoServer::addPort( const String &sAddr, int nPort, int nPoolSize ) +{ + TcpServerSocket *s = new TcpServerSocket( sAddr, nPort, nPoolSize ); + int nSocket = s->getSocket(); + FD_SET( nSocket, &fdActive ); + hServers.insert( nSocket, s ); +} + +void Bu::ItoServer::setTimeout( int nTimeoutSec, int nTimeoutUSec ) +{ + this->nTimeoutSec = nTimeoutSec; + this->nTimeoutUSec = nTimeoutUSec; +} + +void Bu::ItoServer::addClient( int nSocket, int nPort ) +{ + ItoClient *pC = new ItoClient( *this, nSocket, nPort, nTimeoutSec, + nTimeoutUSec ); + + imClients.lock(); + hClients.insert( nSocket, pC ); + imClients.unlock(); + + pC->start(); +} + +void Bu::ItoServer::run() +{ + for(;;) + { + struct timeval xTimeout = { nTimeoutSec, nTimeoutUSec }; + + fd_set fdRead = fdActive; + //fd_set fdWrite = fdActive; + fd_set fdException = fdActive; + + if( TEMP_FAILURE_RETRY( select( FD_SETSIZE, &fdRead, NULL, &fdException, &xTimeout ) ) < 0 ) + { + throw ExceptionBase("Error attempting to scan open connections."); + } + + for( ServerHash::iterator i = hServers.begin(); i != hServers.end(); i++ ) + { + if( FD_ISSET( i.getKey(), &fdRead ) ) + { + TcpServerSocket *pSrv = i.getValue(); + addClient( pSrv->accept(), pSrv->getPort() ); + } + } + + while( !qClientCleanup.isEmpty() ) + { + ItoClient *pCli = qClientCleanup.dequeue(); + pCli->join(); + delete pCli; + } + } +} + +void Bu::ItoServer::clientCleanup( int iSocket ) +{ + imClients.lock(); + ItoClient *pCli = hClients.get( iSocket ); + imClients.unlock(); + qClientCleanup.enqueue( pCli ); +} + +Bu::ItoServer::ItoClient::ItoClient( ItoServer &rSrv, int iSocket, int iPort, + int nTimeoutSec, int nTimeoutUSec ) : + rSrv( rSrv ), + iSocket( iSocket ), + iPort( iPort ), + nTimeoutSec( nTimeoutSec ), + nTimeoutUSec( nTimeoutUSec ) +{ + FD_ZERO( &fdActive ); + FD_SET( iSocket, &fdActive ); + + pClient = new Client( + new Bu::TcpSocket( iSocket ), + new SrvClientLinkFactory( rSrv ) + ); +} + +Bu::ItoServer::ItoClient::~ItoClient() +{ +} + +void Bu::ItoServer::ItoClient::run() +{ + imProto.lock(); + rSrv.onNewConnection( pClient, iPort ); + pClient->processOutput(); + imProto.unlock(); + + for(;;) + { + struct timeval xTimeout = { nTimeoutSec, nTimeoutUSec }; + + fd_set fdRead = fdActive; + fd_set fdWrite; + fd_set fdException = fdActive; + + FD_ZERO( &fdWrite ); + if( pClient->hasOutput() ) + FD_SET( iSocket, &fdWrite ); + + if( TEMP_FAILURE_RETRY( select( FD_SETSIZE, + &fdRead, &fdWrite, &fdException, &xTimeout ) ) < 0 ) + { + throw ExceptionBase("Error attempting to scan open connections."); + } + + while( !qMsg.isEmpty() ) + { + imProto.lock(); + Bu::String *pMsg = qMsg.dequeue(); + pClient->onMessage( *pMsg ); + delete pMsg; + pClient->processOutput(); + imProto.unlock(); + } + + if( FD_ISSET( iSocket, &fdRead ) ) + { + imProto.lock(); + pClient->processInput(); + imProto.unlock(); + if( !pClient->isOpen() ) + { + imProto.lock(); + rSrv.onClosedConnection( pClient ); + imProto.unlock(); + + rSrv.clientCleanup( iSocket ); + + return; + } + } + + if( FD_ISSET( iSocket, &fdWrite ) ) + { + imProto.lock(); + pClient->processOutput(); + imProto.unlock(); + } + } +} + +Bu::ItoServer::SrvClientLink::SrvClientLink( ItoClient *pClient ) : + pClient( pClient ) +{ +} + +Bu::ItoServer::SrvClientLink::~SrvClientLink() +{ +} + +void Bu::ItoServer::SrvClientLink::sendMessage( const Bu::String &sMsg ) +{ + if( !pClient->imProto.trylock() ) + { + pClient->pClient->onMessage( sMsg ); + pClient->pClient->processOutput(); + pClient->imProto.unlock(); + } + else + { + Bu::String *pMsg = new Bu::String( sMsg ); + pClient->qMsg.enqueue( pMsg ); + } +} + +Bu::ItoServer::SrvClientLinkFactory::SrvClientLinkFactory( + Bu::ItoServer &rSrv ) : + rSrv( rSrv ) +{ +} + +Bu::ItoServer::SrvClientLinkFactory::~SrvClientLinkFactory() +{ +} + +Bu::ClientLink *Bu::ItoServer::SrvClientLinkFactory::createLink( + Bu::Client *pClient ) +{ + rSrv.imClients.lock(); + ItoClient *pCli = rSrv.hClients.get( *pClient->getSocket() ); + rSrv.imClients.unlock(); + + return new SrvClientLink( pCli ); +} + diff --git a/src/unstable/itoserver.h b/src/unstable/itoserver.h new file mode 100644 index 0000000..81db4e2 --- /dev/null +++ b/src/unstable/itoserver.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2007-2011 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. + */ + +#ifndef BU_ITO_SERVER_H +#define BU_ITO_SERVER_H + +#include + +#ifndef WIN32 + #include +#endif + +#include "bu/string.h" +#include "bu/list.h" +#include "bu/thread.h" +#include "bu/mutex.h" +#include "bu/synchroqueue.h" +#include "bu/hash.h" + +#include "bu/clientlink.h" +#include "bu/clientlinkfactory.h" + +namespace Bu +{ + class TcpServerSocket; + class TcpSocket; + class Client; + + /** + * Core of a network server. This class is distinct from a ServerSocket in + * that a ServerSocket is one listening socket, nothing more. Socket will + * manage a pool of both ServerSockets and connected Sockets along with + * their protocols and buffers. + * + * To start serving on a new port, use the addPort functions. Each call to + * addPort creates a new ServerSocket, starts it listening, and adds it to + * the server pool. + * + * All of the real work is done by scan, which will wait for up + * to the timeout set by setTimeout before returning if there is no data + * pending. scan should probably be called in some sort of tight + * loop, possibly in it's own thread, or in the main control loop. + * + * In order to use a Server you must subclass it and implement the pure + * virtual functions. These allow you to receive notification of events + * happening within the server itself, and actually makes it useful. + *@ingroup Threading Serving + */ + class ItoServer : public Thread + { + friend class ItoClient; + friend class SrvClientLinkFactory; + public: + ItoServer(); + virtual ~ItoServer(); + + void addPort( int nPort, int nPoolSize=40 ); + void addPort( const String &sAddr, int nPort, int nPoolSize=40 ); + + //void scan(); + void setTimeout( int nTimeoutSec, int nTimeoutUSec=0 ); + + void addClient( int nSocket, int nPort ); + + virtual void onNewConnection( Client *pClient, int nPort )=0; + virtual void onClosedConnection( Client *pClient )=0; + + protected: + virtual void run(); + + private: + class SrvClientLink; + class ItoClient : public Thread + { + friend class Bu::ItoServer::SrvClientLink; + public: + ItoClient( ItoServer &rSrv, int nSocket, int nPort, + int nTimeoutSec, int nTimeoutUSec ); + virtual ~ItoClient(); + + typedef SynchroQueue StringQueue; + StringQueue qMsg; + + protected: + virtual void run(); + + private: + ItoServer &rSrv; + Client *pClient; + fd_set fdActive; + int iSocket; + int iPort; + int nTimeoutSec; + int nTimeoutUSec; + Mutex imProto; + }; + + class SrvClientLink : public Bu::ClientLink + { + public: + SrvClientLink( ItoClient *pClient ); + virtual ~SrvClientLink(); + + virtual void sendMessage( const Bu::String &sMsg ); + + private: + ItoClient *pClient; + }; + + class SrvClientLinkFactory : public Bu::ClientLinkFactory + { + public: + SrvClientLinkFactory( ItoServer &rSrv ); + virtual ~SrvClientLinkFactory(); + + virtual Bu::ClientLink *createLink( Bu::Client *pClient ); + + private: + ItoServer &rSrv; + }; + + int nTimeoutSec; + int nTimeoutUSec; + fd_set fdActive; + typedef Hash ServerHash; + ServerHash hServers; + typedef Hash ClientHash; + typedef SynchroQueue ClientQueue; + ClientHash hClients; + ClientQueue qClientCleanup; + Mutex imClients; + + void clientCleanup( int iSocket ); + }; +} + +#endif diff --git a/src/unstable/minimacro.cpp b/src/unstable/minimacro.cpp new file mode 100644 index 0000000..b6fd6a8 --- /dev/null +++ b/src/unstable/minimacro.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2007-2011 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/minimacro.h" + +Bu::MiniMacro::MiniMacro() +{ + hFuncs.insert("toupper", new FuncToUpper() ); + hFuncs.insert("tolower", new FuncToLower() ); +} + +Bu::MiniMacro::MiniMacro( const StrHash &sVarSrc ) +{ + for( StrHash::const_iterator i = sVarSrc.begin(); i != sVarSrc.end(); i++ ) + { + addVar( i.getKey(), i.getValue() ); + } +} + +Bu::MiniMacro::~MiniMacro() +{ +} + +Bu::String Bu::MiniMacro::parse( const Bu::String &sIn ) +{ + bContinue = true; + Bu::String sOut; + for( sCur = sIn.getStr(); *sCur && bContinue; sCur++ ) + { + if( *sCur == '{' ) + { + switch( sCur[1] ) + { + case '=': + sCur += 2; + sOut += parseRepl(); + break; + + case '?': + sCur += 2; + sOut += parseCond(); + break; + + case ':': + sCur += 2; + sOut += parseCmd(); + break; + + default: + sOut += *sCur; + continue; + } + } + else + { + sOut += *sCur; + } + } + + iLastPos = (ptrdiff_t)sCur - (ptrdiff_t)sIn.getStr(); + + return sOut; +} + +Bu::String Bu::MiniMacro::parseRepl() +{ + Bu::String sOut; + bool bIsFirst = true; + for( const char *sNext = sCur;;) + { + for(; *sNext != ':' && *sNext != '}' && *sNext != '\0'; sNext++ ) { } + if( *sNext == '\0' ) + break; + Bu::String sName( sCur, (ptrdiff_t)sNext-(ptrdiff_t)sCur ); + if( bIsFirst ) + { + sOut = hVars[sName]; + bIsFirst = false; + //printf("Variable: \"%s\"\n", sName.getStr() ); + } + else + { + sOut = callFunc( sOut, sName ); + //printf("Filter: \"%s\"\n", sName.getStr() ); + } + if( *sNext == '}' ) + { + sCur = sNext; + break; + } + else if( *sNext == ':' ) + { + } + sNext++; + sCur = sNext; + } + return sOut; +} + +Bu::String Bu::MiniMacro::parseCond() +{ + Bu::String sOut; + //printf("%20s\n", sCur ); + return sOut; +} + +Bu::String Bu::MiniMacro::parseCmd() +{ + Bu::String sOut; + const char *sNext = sCur; + for(; *sNext != ':' && *sNext != '}' && *sNext != '\0'; sNext++ ) { } + if( *sNext != '\0' ) + { + Bu::String sName( sCur, (ptrdiff_t)sNext-(ptrdiff_t)sCur ); + if( sName == "end" ) + { + sCur = sNext; + bContinue = false; + return sOut; + } + else + { + throw Bu::ExceptionBase("Unknown command '%s'.", + sName.getStr() + ); + } + } + else + { + //printf("Uh...?\n"); + } + + //printf("%20s\n", sCur ); + return sOut; +} + +Bu::String Bu::MiniMacro::callFunc( + const Bu::String &sIn, const Bu::String &sFunc ) +{ + int i = sFunc.findIdx('('); + if( i < 0 ) + throw Bu::ExceptionBase("That doesn't look like a function call"); + Bu::String sName( sFunc.getStr(), i ); + StrList lsParams; + for( const char *s = sFunc.getStr()+i+1; *s && *s != ')'; s++ ) + { + for(; *s == ' ' || *s == '\t' || *s == '\r' || *s == '\n'; s++ ) { } + const char *sNext; + for( sNext = s; *sNext && *sNext != ')' && *sNext != ','; sNext++ ) { } + Bu::String p( s, (ptrdiff_t)sNext-(ptrdiff_t)s ); + lsParams.append( p ); + sNext++; + s = sNext; + } + return hFuncs.get( sName )->call( sIn, lsParams ); +} + +void Bu::MiniMacro::addVar( + const Bu::String &sName, const Bu::String &sValue ) +{ + hVars.insert( sName, sValue ); +} + +bool Bu::MiniMacro::hasVar( const Bu::String &sName ) +{ + return hVars.has( sName ); +} + +const Bu::String &Bu::MiniMacro::getVar( const Bu::String &sName ) +{ + return hVars.get( sName ); +} + +const Bu::StrHash &Bu::MiniMacro::getVars() +{ + return hVars; +} + +int Bu::MiniMacro::getPosition() +{ + return iLastPos; +} + diff --git a/src/unstable/minimacro.h b/src/unstable/minimacro.h new file mode 100644 index 0000000..b6c7c13 --- /dev/null +++ b/src/unstable/minimacro.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2007-2011 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. + */ + +#ifndef BU_MINI_MACRO_H +#define BU_MINI_MACRO_H + +#include "bu/hash.h" +#include "bu/string.h" + +namespace Bu +{ + typedef Bu::Hash StrHash; + /** + * A processor for Libbu++ brand Mini Macros. These are really simple, but + * still fairly flexible. It's mainly text replacement, but with a few + * extras like filter functions and conditional text segments. So far we + * don't support loops or anything, I'm not sure we ever will. + * + * Anatomy of a mini macro: + * - Every macro begins with a two character code, the first character is + * always '{', the second character determines the operation to perform. + * - If the '{' is followed by a character that is not valid it is not + * considered for expansion and the characters are copied to the output. + * - Every macro ends with a closing '}' + * - Macro types: + * - '=': variable replacement. The '=' is immediatley followed by the + * name of the variable to replace, then any number of optional filter + * segments. + * - '?': conditional text. The '?' is immediately followed by the + * variable to test. This works two ways, the variable can be alone, in + * which case it's existance is tested, or it can be followed by a "=" + * and a string to compare to. This is then followed by a text segment + * that will be used if the test is true, and an optional text segment + * to be used if the test is false. + * - ':': command. The ':' is immediately followed by a command string, + * of which there's only one right now, but that's ok. These are not + * put into the output stream, but instead mark something for the + * parser. Currently supported: + * - {:end}: end of parsing, stop here, also make note of how many input + * characters were used. + * - Segments: + * - Each segment is seperated by a colon. + * - Filter segments give the name of the filter, followed by + * parenthesies. Parameters may be provided within the parenthesies. + * - Text segments should always be quoted, but may contain any characters + * within the quotes, backslash is used as per C/ANSI/ISO standard. + * You can also quote any text using [' '] instead of quotes, which + * allows for nested strings. The [' token is only recognised within + * a macro. + * + *@verbatim + {=name:tolower()} + {=name:ccsplit("_"):toupper()} + {?name:"name exists and is {=name}"} + {?name:"{=name}":"no name!"} + {?name="bob":"You're named bob!":"Who are you? I only know bob..."} + @endverbatim + */ + class MiniMacro + { + public: + MiniMacro(); + MiniMacro( const StrHash &sVarSrc ); + virtual ~MiniMacro(); + + Bu::String parse( const Bu::String &sIn ); + void addVar( const Bu::String &sName, const Bu::String &sValue ); + bool hasVar( const Bu::String &sName ); + const Bu::String &getVar( const Bu::String &sName ); + const StrHash &getVars(); + int getPosition(); + + private: + const char *sCur; + Bu::String parseRepl(); + Bu::String parseCond(); + Bu::String parseCmd(); + Bu::String callFunc( + const Bu::String &sIn, const Bu::String &sFunc ); + + StrHash hVars; + bool bContinue; + int iLastPos; + + public: + typedef Bu::List StrList; + class Func + { + public: + Func(){} + virtual ~Func(){} + virtual Bu::String call( + const Bu::String &sIn, StrList &lsParam )=0; + }; + + class FuncToUpper : public Func + { + public: + FuncToUpper(){} + virtual ~FuncToUpper(){} + virtual Bu::String call( + const Bu::String &sIn, StrList & ) + { + return sIn.toUpper(); + } + }; + + class FuncToLower : public Func + { + public: + FuncToLower(){} + virtual ~FuncToLower(){} + virtual Bu::String call( + const Bu::String &sIn, StrList & ) + { + return sIn.toLower(); + } + }; + + private: + typedef Bu::Hash FuncHash; + FuncHash hFuncs; + }; +}; + +#endif diff --git a/src/unstable/myriad.cpp b/src/unstable/myriad.cpp new file mode 100644 index 0000000..de44930 --- /dev/null +++ b/src/unstable/myriad.cpp @@ -0,0 +1,663 @@ +/* + * Copyright (C) 2007-2011 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/myriad.h" +#include "bu/stream.h" +#include "bu/myriadstream.h" +#include + +#include "bu/sio.h" +using Bu::sio; +using Bu::Fmt; + +#define Myriad_MAGIC_CODE ((unsigned char *)"\x0a\xd3\xfa\x84") + +namespace Bu +{ + subExceptionDef( MyriadException ) + template t blkDiv( t total, t block ) { + return (total/block)+((total%block==0)?(0):(1)); + } +} + +Bu::Myriad::Myriad( Bu::Stream &sStore, int iBlockSize, int iPreallocate ) : + sStore( sStore ), + iBlockSize( iBlockSize ), + iBlocks( 0 ), + iUsed( 0 ), + bHeaderChanged( false ) +{ + try + { + initialize(); + } + catch( Bu::MyriadException &e ) + { + if( e.getErrorCode() == MyriadException::emptyStream ) + { + initialize( iBlockSize, iPreallocate ); + } + else + { + throw; + } + } +} + +Bu::Myriad::~Myriad() +{ + if( !hActiveBlocks.isEmpty() ) + { + sio << "Bu::Myriad::~Myriad(): Error: There are " + << hActiveBlocks.getSize() << " unsynced blocks!" << sio.nl; + } + sync(); + + for( StreamArray::iterator i = aStreams.begin(); i; i++ ) + { + delete *i; + } +} + +void Bu::Myriad::sync() +{ + updateHeader(); + + for( BlockHash::iterator i = hActiveBlocks.begin(); i; i++ ) + { + if( (*i)->bChanged ) + { + syncBlock( *i ); + } + } +} + +void Bu::Myriad::initialize() +{ + sStore.setPosEnd( 0 ); + int iSize = sStore.tell(); + sStore.setPos( 0 ); + + unsigned char buf[4]; + if( sStore.read( buf, 4 ) < 4 ) + throw MyriadException( MyriadException::emptyStream, + "Input stream appears to be empty."); + if( memcmp( buf, Myriad_MAGIC_CODE, 4 ) ) + { + throw MyriadException( MyriadException::invalidFormat, + "Stream does not appear to be a valid Myriad format."); + } + sStore.read( buf, 2 ); + if( buf[0] != 1 ) + throw MyriadException( MyriadException::badVersion, + "We can only handle version 1 for now."); + if( buf[1] != 32 ) + throw MyriadException( MyriadException::invalidWordSize, + "We can only handle 32-bit words at the moment."); + sStore.read( &iBlockSize, 4 ); + int iStreams; + sStore.read( &iStreams, 4 ); + + iBlocks = iSize/iBlockSize; + //sio << "Myriad: iSize=" << iSize << ", iBlockSize=" << iBlockSize + // << ", iBlocks=" << iBlocks << ", iStreams=" << iStreams << sio.nl; + + int iHeaderSize = 14 + 8 + 4; + int iHeaderBlocks = 0; //blkDiv( iHeaderSize+4, iBlockSize ); + + while( iHeaderSize > iHeaderBlocks*iBlockSize ) + { + iHeaderBlocks = blkDiv( iHeaderSize+4, iBlockSize ); + iHeaderSize = 14 + 8 + 4*iHeaderBlocks; + } + + //sio << "Myriad: iHeaderSize=" << iHeaderSize + // << ", iHeaderBlocks=" << iHeaderBlocks << sio.nl; + + Stream *pFakeHdr = new Stream; + pFakeHdr->iId = 0; + pFakeHdr->iSize = iHeaderSize; + for( int j = 0; j < iHeaderBlocks; j++ ) + { + pFakeHdr->aBlocks.append( j ); + } + + bsBlockUsed.setSize( iBlocks, true ); + +// bool bCanSkip = false; // Can skip around, post initial header stream i/o + MyriadStream *pIn = new MyriadStream( *this, pFakeHdr ); + pIn->setPos( sStore.tell() ); + for( int j = 0; j < iStreams; j++ ) + { + aStreams.append( new Stream() ); + Stream &s = *aStreams[j]; + pIn->read( &s.iId, 4 ); + pIn->read( &s.iSize, 4 ); + int iSBlocks = blkDiv(s.iSize, iBlockSize); + // sio << "Myriad: - Stream::iId=" << s.iId + // << ", Stream::iSize=" << s.iSize + // << ", Stream::aBlocks=" << iSBlocks + // << ", pIn->tell()=" << pIn->tell() << sio.nl; + for( int k = 0; k < iSBlocks; k++ ) + { + int iBId; + pIn->read( &iBId, 4 ); + // sio << "Myriad: - iBId=" << iBId + // << ", iStartPos=" << iBId*iBlockSize + // << ", pIn->tell()=" << pIn->tell() << sio.nl; + s.aBlocks.append( iBId ); + bsBlockUsed.setBit( iBId ); + iUsed++; + if( (j == 0 && k == iHeaderBlocks-1) ) + { + // sio << "Myriad: - End of prepartition, unlocking skipping." + // << sio.nl; +// bCanSkip = true; + MyriadStream *pTmp = new MyriadStream( *this, aStreams[0] ); + // sio << "Myriad - Position = " << pIn->tell() << sio.nl; + pTmp->setPos( pIn->tell() ); + delete pIn; + delete pFakeHdr; + pIn = pTmp; + } + } + } + delete pIn; + + //sio << "Myriad: Blocks used: " << bsBlockUsed.toString() << sio.nl; +} + +void Bu::Myriad::initialize( int iBlockSize, int iPreAllocate ) +{ + for( StreamArray::iterator i = aStreams.begin(); i; i++ ) + { + delete *i; + } + aStreams.clear(); + iUsed = 0; + + int iHeaderSize = 14 + 8 + 4; + int iHeaderBlocks = 0; //blkDiv( iHeaderSize+4, iBlockSize ); + char cBuf = 1; + int iBuf = 0; + + Stream *pStr = new Stream; + pStr->iId = 0; + + while( iHeaderSize > iHeaderBlocks*iBlockSize ) + { + iHeaderBlocks = blkDiv( iHeaderSize+4, iBlockSize ); + iHeaderSize = 14 + 8 + 4*iHeaderBlocks; + } + + iPreAllocate += iHeaderBlocks; + + //sio << "Myriad: iHeaderSize=" << iHeaderSize << ", iBlockSize=" + // << iBlockSize << ", iHeaderBlocks=" << iHeaderBlocks << sio.nl; + + bsBlockUsed.setSize( iPreAllocate, true ); + iUsed++; + + char *pBlock = new char[iBlockSize]; + memset( pBlock, 0, iBlockSize ); + for( int j = 0; j < iPreAllocate; j++ ) + { + sStore.write( pBlock, iBlockSize ); + } + delete[] (char *)pBlock; + + sStore.setPos( 0 ); + + // Magic number + sStore.write( Myriad_MAGIC_CODE, 4 ); + + // Version (0) + sStore.write( &cBuf, 1 ); + + // Bits per int + cBuf = 32; + sStore.write( &cBuf, 1 ); + + // The size of each block + sStore.write( &iBlockSize, 4 ); + + iBuf = 1; + // The number of streams + sStore.write( &iBuf, 4 ); + + // Stream header + iBuf = 0; + sStore.write( &iBuf, 4 ); + sStore.write( &iHeaderSize, 4 ); + for( iBuf = 0; iBuf < iHeaderBlocks; iBuf++ ) + { + sStore.write( &iBuf, 4 ); + } + + this->iBlockSize = iBlockSize; + this->iBlocks = iPreAllocate; + + pStr->iSize = sStore.tell(); +// sio << "Myriad: Actual end of header stream = " << pStr->iSize << sio.nl; + + pStr->iSize = iHeaderSize; + for( int j = 0; j < iHeaderBlocks; j++ ) + { + pStr->aBlocks.append( j ); + bsBlockUsed.setBit( j ); + iUsed++; + } + + aStreams.append( pStr ); + + //sio << bsBlockUsed.toString() << " - " << pStr->aBlocks << sio.nl; + + bHeaderChanged = true; + //hStreams.insert( 0, BlockArray( 0 ) ); +} + +void Bu::Myriad::updateHeader() +{ + if( bHeaderChanged == false ) + return; + if( !sStore.canWrite() ) + return; + + char cBuf; + int iBuf; + + //for( StreamArray::iterator i = aStreams.begin(); i; i++ ) + //{ + // sio << "Myriad: Stream " << Fmt(4) << (*i)->iId << ": " << (*i)->aBlocks << sio.nl; + //} + + // Compute the new size of the header. + int iHeaderSize = 14 + 8*aStreams.getSize(); +// sio << "Myriad: updateHeader: aStreams.getSize() = " << aStreams.getSize() +// << sio.nl; + for( StreamArray::iterator i = aStreams.begin(); i; i++ ) + { + iHeaderSize += 4*(*i)->aBlocks.getSize(); +// sio << "Myriad: updateHeader: (*i)->aBlocks.getSize() = " +// << (*i)->aBlocks.getSize() << sio.nl; + } + int iNewBlocks = blkDiv( iHeaderSize, iBlockSize ); + while( iNewBlocks > aStreams[0]->aBlocks.getSize() ) + { + int iBlock = findEmptyBlock(); +// sio << "Myriad: updateHeader: Appending block " << iBlock +// << " to header." << sio.nl; + aStreams[0]->aBlocks.append( iBlock ); + bsBlockUsed.setBit( iBlock ); + iUsed++; + iHeaderSize += 4; + iNewBlocks = blkDiv( iHeaderSize, iBlockSize ); + } + aStreams[0]->iSize = iHeaderSize; +// sio << "Myriad: updateHeader: iHeaderSize=" << iHeaderSize +// << ", iNewBlocks=" << iNewBlocks << ", curBlocks=" +// << aStreams[0]->aBlocks.getSize() << sio.nl; + + MyriadStream sHdr( *this, aStreams[0] ); + sHdr.write( Myriad_MAGIC_CODE, 4 ); + + // Version (1) + cBuf = 1; + sHdr.write( &cBuf, 1 ); + + // Bits per int + cBuf = 32; + sHdr.write( &cBuf, 1 ); + + // The size of each block + sHdr.write( &iBlockSize, 4 ); + + iBuf = aStreams.getSize(); + // The number of streams + sHdr.write( &iBuf, 4 ); + + for( StreamArray::iterator i = aStreams.begin(); i; i++ ) + { + sHdr.write( &(*i)->iId, 4 ); + sHdr.write( &(*i)->iSize, 4 ); + int iUsedBlocks = blkDiv( (*i)->iSize, iBlockSize ); +// for( BlockArray::iterator j = (*i)->aBlocks.begin(); j; j++ ) + for( int j = 0; j < iUsedBlocks; j++ ) + { + sHdr.write( &(*i)->aBlocks[j], 4 ); + } + } + + bHeaderChanged = false; +} + +int Bu::Myriad::createStream( int iPreAllocate ) +{ + Stream *pStr = new Stream(); + pStr->iId = aStreams.last()->iId+1; + //sio << "Myriad: New stream id=" << pStr->iId << ", iPreAllocate=" + // << iPreAllocate << sio.nl; + pStr->iSize = 0; + aStreams.append( pStr ); + + for( int j = 0; j < iPreAllocate; j++ ) + { + int iFreeBlock = findEmptyBlock(); +// sio << "Myriad: Adding block " << iFreeBlock << sio.nl; + pStr->aBlocks.append( iFreeBlock ); + bsBlockUsed.setBit( iFreeBlock ); + iUsed++; + } + + bHeaderChanged = true; + + return pStr->iId; +} + +int Bu::Myriad::createStreamWithId( int iId, int iPreAllocate ) +{ + try + { + findStream( iId ); + throw MyriadException( MyriadException::streamExists, + "There is already a stream with the given id."); + } + catch( MyriadException &e ) + { + Stream *pStr = new Stream(); + pStr->iId = iId; + //sio << "Myriad: New stream id=" << pStr->iId << ", iPreAllocate=" + // << iPreAllocate << sio.nl; + pStr->iSize = 0; + if( aStreams.last()->iId < iId ) + { + aStreams.append( pStr ); + } + else + { + for( StreamArray::iterator i = aStreams.begin(); i; i++ ) + { + if( (*i)->iId > iId ) + { + aStreams.insert( i, pStr ); + break; + } + } + } + + for( int j = 0; j < iPreAllocate; j++ ) + { + int iFreeBlock = findEmptyBlock(); + // sio << "Myriad: Adding block " << iFreeBlock << sio.nl; + pStr->aBlocks.append( iFreeBlock ); + bsBlockUsed.setBit( iFreeBlock ); + iUsed++; + } + + bHeaderChanged = true; + + return pStr->iId; + } +} + +int Bu::Myriad::findEmptyBlock() +{ + bHeaderChanged = true; + + for( int j = 0; j < bsBlockUsed.getSize(); j++ ) + { + if( bsBlockUsed.getBit( j ) == false ) + return j; + } +// sio << "Myriad: findEmptyBlock(): No empty blocks, adding new one." << sio.nl; + + bsBlockUsed.setSize( bsBlockUsed.getSize()+1, false ); + /* + sStore.setPos( iBlockSize*iBlocks ); + + char *pBlock = new char[iBlockSize]; + memset( pBlock, 0, iBlockSize ); + sStore.write( pBlock, iBlockSize ); + delete[] pBlock; + */ + + sStore.setSize( (iBlocks+1)*iBlockSize ); + + return iBlocks++; +} + +void Bu::Myriad::deleteStream( int iId ) +{ + if( iId < 0 ) + throw MyriadException( MyriadException::invalidStreamId, + "Invalid stream id."); + if( iId == 0 ) + throw MyriadException( MyriadException::protectedStream, + "You cannot delete stream zero, it is protected."); + for( StreamArray::iterator i = aStreams.begin(); i; i++ ) + { + if( (*i)->iId == iId ) + { + Stream *pStream = *i; + for( BlockArray::iterator j = pStream->aBlocks.begin(); j; j++ ) + { + bsBlockUsed.setBit( *j, false ); + iUsed--; + } + aStreams.erase( i ); + bHeaderChanged = true; + delete pStream; + return; + } + } +} + +Bu::Array Bu::Myriad::getStreamIds() +{ + Bu::Array aRet( aStreams.getSize() ); + for( StreamArray::iterator i = aStreams.begin(); i; i++ ) + { + aRet.append( (*i)->iId ); + } + + return aRet; +} + +int Bu::Myriad::getStreamSize( int iId ) +{ + return findStream( iId )->iSize; +} + +bool Bu::Myriad::hasStream( int iId ) +{ + try + { + findStream( iId ); + return true; + }catch(...) + { + return false; + } +} + +Bu::MyriadStream Bu::Myriad::openStream( int iId ) +{ + //sio << "Myriad: Request to open stream: " << iId << sio.nl; + return MyriadStream( *this, findStream( iId ) ); +} + +int Bu::Myriad::getNumStreams() +{ + return aStreams.getSize(); +} + +int Bu::Myriad::getBlockSize() +{ + return iBlockSize; +} + +int Bu::Myriad::getNumBlocks() +{ + return iBlocks; +} + +int Bu::Myriad::getNumUsedBlocks() +{ + return iUsed; +} + +int Bu::Myriad::getTotalUsedBytes() +{ + int iTotalSize = 0; + for( StreamArray::iterator i = aStreams.begin(); i; i++ ) + { + iTotalSize += (*i)->iSize; + } + return iTotalSize; +} + +int Bu::Myriad::getTotalUnusedBytes() +{ + int iTotalSize = (iBlocks-iUsed)*iBlockSize; + for( StreamArray::iterator i = aStreams.begin(); i; i++ ) + { + iTotalSize += iBlockSize - ((*i)->iSize%iBlockSize); + } + return iTotalSize; +} + +int Bu::Myriad::getTotalUnusedBytes( int iFakeBlockSize ) +{ + int iTotalSize = (iBlocks-iUsed)*iFakeBlockSize; + for( StreamArray::iterator i = aStreams.begin(); i; i++ ) + { + iTotalSize += iFakeBlockSize - ((*i)->iSize%iFakeBlockSize); + } + return iTotalSize; +} + +Bu::Myriad::Stream *Bu::Myriad::findStream( int iId ) +{ + for( StreamArray::iterator i = aStreams.begin(); i; i++ ) + { + if( (*i)->iId == iId ) + return *i; + } + + throw MyriadException( MyriadException::noSuchStream, + "The requested stream doesn't exist and cannot be opened." ); + + return NULL; +} + +Bu::Myriad::Block *Bu::Myriad::getBlock( int iBlock ) +{ +// sio << "Myriad: Reading block " << iBlock << ", bytes " +// << iBlockSize*iBlock << "-" << iBlockSize*(iBlock+1) << sio.nl; + Block *pBlock = new Block; + pBlock->pData = new char[iBlockSize]; + sStore.setPos( iBlockSize * iBlock ); + sStore.read( pBlock->pData, iBlockSize ); + pBlock->bChanged = false; + pBlock->iBlockIndex = iBlock; + + hActiveBlocks.insert( iBlock, pBlock ); + + return pBlock; +} + +void Bu::Myriad::releaseBlock( Bu::Myriad::Block *pBlock ) +{ + if( pBlock == NULL ) + return; +// sio << "Myriad: Releasing block " << pBlock->iBlockIndex << sio.nl; + syncBlock( pBlock ); + hActiveBlocks.erase( pBlock->iBlockIndex ); + delete[] pBlock->pData; + delete pBlock; +} + +void Bu::Myriad::syncBlock( Block *pBlock ) +{ + if( pBlock->bChanged ) + { +// sio << "Myriad: - Block changed, writing back to stream." << sio.nl; + sStore.setPos( iBlockSize * pBlock->iBlockIndex ); + sStore.write( pBlock->pData, iBlockSize ); + pBlock->bChanged = false; + } +} + +int Bu::Myriad::streamAddBlock( Stream *pStream ) +{ + int iBlock = findEmptyBlock(); + pStream->aBlocks.append( iBlock ); + bsBlockUsed.setBit( iBlock ); + bHeaderChanged = true; + iUsed++; + return iBlock; +} + +void Bu::Myriad::setStreamSize( Stream *pStream, long iSize ) +{ + if( pStream->iSize == iSize ) + { + return; + } + else if( pStream->iSize > iSize ) + { + // Shrink + for( int iNewSize = pStream->aBlocks.getSize()*iBlockSize; + iNewSize-iBlockSize > iSize; iNewSize -= iBlockSize ) + { + if( bsBlockUsed.getBit( pStream->aBlocks.last() ) ) + iUsed--; + bsBlockUsed.setBit( pStream->aBlocks.last(), false ); + pStream->aBlocks.eraseLast(); + } + pStream->iSize = iSize; + bHeaderChanged = true; + } + else + { + // Grow + for( int iNewSize = pStream->aBlocks.getSize()*iBlockSize; + iNewSize < iSize; iNewSize += iBlockSize ) + { + streamAddBlock( pStream ); + } + pStream->iSize = iSize; + bHeaderChanged = true; + } +} + +void Bu::Myriad::headerChanged() +{ + bHeaderChanged = true; +} + +bool Bu::Myriad::isMyriad( Bu::Stream &sStore ) +{ + sStore.setPos( 0 ); + + unsigned char buf[4]; + if( sStore.read( buf, 4 ) < 4 ) + throw MyriadException( MyriadException::emptyStream, + "Input stream appears to be empty."); + sStore.setPos( 0 ); + if( memcmp( buf, Myriad_MAGIC_CODE, 4 ) ) + { + return false; + } + return true; +} + +const Bu::BitString &Bu::Myriad::getBlocksUsed() const +{ + return bsBlockUsed; +} + diff --git a/src/unstable/myriad.h b/src/unstable/myriad.h new file mode 100644 index 0000000..3382ab5 --- /dev/null +++ b/src/unstable/myriad.h @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2007-2011 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. + */ + +#ifndef BU_MYRIAD_H +#define BU_MYRIAD_H + +#include +#include "bu/bitstring.h" +#include "bu/exceptionbase.h" +#include "bu/array.h" +#include "bu/hash.h" + +namespace Bu +{ + class Stream; + class MyriadStream; + + subExceptionDeclBegin( MyriadException ) + enum + { + emptyStream, + invalidFormat, + badVersion, + invalidWordSize, + noSuchStream, + streamExists, + invalidStreamId, + protectedStream + }; + subExceptionDeclEnd(); + + /** + * Myriad block-allocated stream multiplexing system. This is a system for + * creating streams that contain other streams in a flexible and lightweight + * manner. Basically, you can create a file (or any other stream) that can + * store any number of flexible, growing streams. The streams within the + * Myriad stream are automatically numbered, not named. This works more + * or less like a filesystem, but without the extra layer for managing + * file and directory links. This would actually be very easy to add + * on top of Myriad, but is not required. + * + * Header format is as follows: + * + * MMMMvBssssSSSS* + * M = Magic number (0AD3FA84) + * v = version number + * B = Bits per int + * s = Blocksize in bytes + * S = Number of Streams + * + * The * represents the Stream headers, one per stream, as follows: + * IIIIssss$ + * I = Id number of the stream + * s = size of stream in bytes + * + * The $ represents the Block headers, one per used block, as follows: + * IIII + * I = Index of the block + * + * The stream/block data is interleaved in the header, so all blocks stored + * with one stream are together. The block headers are in order, and the + * data in them is required to be "solid" you cannot fill partial blocks + * mid-way through a stream. + * + * The initial block starts with the nids header, and is both the zero block + * and the zero stream. For now, the minimum block size is the size needed + * to store the base header, the zero stream header, and the first two + * blocks of the zero stream, so 30 bytes. Since it's reccomended to use + * a size that will fit evenly into filesystem blocks, then a size of 32 is + * probably the smallest reccomended size because all powers of two equal + * to or greater than 32 are evenly divisible by 32. + * + * I have had a thought that if the block size were smaller than 42 bytes + * the header would consume the first N blocks where N * block size is + * enough space to house the initial header, the first stream header, and + * the first N block headers. This, of course, causes you to hit an + * infinite header if the block size is small enough. + */ + class Myriad + { + friend class MyriadStream; + public: + /** + * Create a Myriad object that uses the given stream to store data. + * This stream must be random access. The block size and preallocate + * values passed in are values that will be used if the given stream + * is empty. In that case the stream will be "formatted" for myriad + * with the specified block size. If there is already a viable Myriad + * format present in the stream, then the blocksize and preallocate + * values will be ignored and the values from the stream will be used + * instead. If the stream doesn't appear to be Myriad formatted an + * exception will be thrown. + */ + Myriad( Bu::Stream &sStore, int iBlockSize=512, int iPreallocate=8 ); + virtual ~Myriad(); + + /** + * Destroy whatever data may be in the base stream and create a new + * Myriad system there with the given blocksize. Use this with care, + * it will destroy anything that was already in the stream, and + * generally, should not ever have to be used. + */ + void initialize( int iBlockSize, int iPreAllocate=1 ); + + /** + * Create a new stream within the Myriad system. The ID of the new + * stream is returned. + */ + int createStream( int iPreAllocate=1 ); + + /** + * Create a new stream within the Myriad system with a given id. The + * id that you provide will be the new id of the stream unless it's + * already used, in which case an error is thrown. This is primarilly + * useful when copying an old Myriad file into a new one. + */ + int createStreamWithId( int iId, int iPreAllocate=1 ); + + /** + * Delete a stream that's already within the Myriad. + */ + void deleteStream( int iId ); + + /** + * Return a new Stream object assosiated with the given stream ID. + */ + MyriadStream openStream( int iId ); + + Bu::Array getStreamIds(); + int getStreamSize( int iId ); + bool hasStream( int iId ); + + int getNumStreams(); + int getBlockSize(); + int getNumBlocks(); + int getNumUsedBlocks(); + int getTotalUsedBytes(); + int getTotalUnusedBytes(); + int getTotalUnusedBytes( int iFakeBlockSize ); + + /** + * Syncronize the header data, etc. with the storage stream. It's not + * a bad idea to call this periodically. + */ + void sync(); + + /** + * Read the first few bytes from the given stream and return true/false + * depending on weather or not it's a Myriad stream. This will throw + * an exception if the stream is empty, or is not random access. + */ + static bool isMyriad( Bu::Stream &sStore ); + + const Bu::BitString &getBlocksUsed() const; + + private: + /** + * Initialize this object based on the data already in the assosiated + * stream. This will be called automatically for you if you forget, + * but if you want to pre-initialize for some reason, just call this + * once before you actually start doing anything with your Myriad. + */ + void initialize(); + + enum + { + blockUnused = 0xFFFFFFFFUL + }; + + typedef Bu::Array BlockArray; + class Stream + { + public: + int iId; + int iSize; + BlockArray aBlocks; + }; + typedef Bu::Array StreamArray; + + class Block + { + public: + char *pData; + bool bChanged; + int iBlockIndex; + }; + + void updateHeader(); + int findEmptyBlock(); + + /** + *@todo Change this to use a binary search, it's nicer. + */ + Stream *findStream( int iId ); + + Block *getBlock( int iBlock ); + void releaseBlock( Block *pBlock ); + void syncBlock( Block *pBlock ); + + int streamAddBlock( Stream *pStream ); + void setStreamSize( Stream *pStream, long iSize ); + + void headerChanged(); + + private: + Bu::Stream &sStore; + int iBlockSize; + int iBlocks; + int iUsed; + Bu::BitString bsBlockUsed; + StreamArray aStreams; + typedef Bu::Hash BlockHash; + BlockHash hActiveBlocks; + bool bHeaderChanged; + }; +}; + +#endif diff --git a/src/unstable/myriadfs.cpp b/src/unstable/myriadfs.cpp new file mode 100644 index 0000000..6884a31 --- /dev/null +++ b/src/unstable/myriadfs.cpp @@ -0,0 +1,703 @@ +/* + * Copyright (C) 2007-2011 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/myriadfs.h" +#include "bu/myriadstream.h" + +#include +#include +#include + +#include "bu/sio.h" +using Bu::sio; +using Bu::Fmt; + +namespace Bu { subExceptionDef( MyriadFsException ) } + +#define Myriad_Fs_MAGIC_CODE ((char *)"\xa7\x18\x8b\x39") + +Bu::MyriadFs::MyriadFs( Bu::Stream &rStore, int iBlockSize ) : + rStore( rStore ), + mStore( rStore, iBlockSize ), + iUser( 0 ), + iGroup( 0 ) +{ +#ifndef WIN32 + iUser = getuid(); + iGroup = getgid(); +#endif + + if( mStore.hasStream( 1 ) ) + { + // Check to see if this is a MyriadFs stream. + Bu::MyriadStream ms = mStore.openStream( 1 ); + char sMagic[4]; + if( ms.read( sMagic, 4 ) < 4 ) + throw MyriadFsException("The provided stream does not appear to be " + "a MyriadFs stream."); + if( ::strncmp( sMagic, Myriad_Fs_MAGIC_CODE, 4 ) ) + throw MyriadFsException("The provided stream does not appear to be " + "a MyriadFs stream."); + + int8_t iVer; + ms.read( &iVer, 1 ); + + int32_t iNumNodes; + ms.read( &iNumNodes, 4 ); + for( int32_t j = 0; j < iNumNodes; j++ ) + { + int32_t iNode; + int32_t iPos; + ms.read( &iNode, 4 ); + ms.read( &iPos, 4 ); + hNodeIndex.insert( iNode, iPos ); + } + } + else + { + // Create initial header stream + { + mStore.createStream( 1 ); + Bu::MyriadStream ms = mStore.openStream( 1 ); + ms.write( Myriad_Fs_MAGIC_CODE, 4 ); + int8_t iVer = 1; + int32_t iTmp = 1; + ms.write( &iVer, 1 ); + ms.write( &iTmp, 4 ); // iNumNodes + iTmp = 0; + ms.write( &iTmp, 4 ); // iInode + ms.write( &iTmp, 4 ); // iPosition + hNodeIndex.insert( 0, 0 ); + } + + // Create initial inode stream, with one root node. + { + mStore.createStream( 2 ); + Bu::MyriadStream ms = mStore.openStream( 2 ); + RawStat rs; + rs.iNode = 0; + rs.iUser = iUser; + rs.iGroup = iGroup; + rs.uPerms = 0755|typeDir; + rs.iLinks = 1; + rs.uStreamIndex = 3; + rs.iCTime = rs.iMTime = rs.iATime = time(NULL); + ms.write( &rs, sizeof(RawStat) ); + } + + // Create inode 0's storage stream. + { + mStore.createStream( 3 ); + Bu::MyriadStream ms = mStore.openStream( 3 ); + int32_t iTmp32 = 0; + ms.write( &iTmp32, 4 ); // iChildCount + } + } +} + +Bu::MyriadFs::~MyriadFs() +{ + writeHeader(); +} + +void Bu::MyriadFs::stat( const Bu::String &sPath, Bu::MyriadFs::Stat &rBuf ) +{ + int32_t iParent; + int32_t iNode = lookupInode( sPath, iParent ); + Bu::MyriadStream is = mStore.openStream( 2 ); + stat( iNode, rBuf, is ); +} + +Bu::MyriadStream Bu::MyriadFs::open( const Bu::String &sPath, int /*iMode*/, + uint16_t uPerms ) +{ + int32_t iParent = -1; + int32_t iNode; + try + { + iNode = lookupInode( sPath, iParent ); + sio << "File found." << sio.nl; + // The file was found + return openByInode( iNode ); + } + catch( Bu::MyriadFsException &e ) + { + if( iParent < 0 ) + throw; + + // This means that an intermediate path component couldn't be found + if( e.getErrorCode() == 1 ) + throw; + + // The file wasn't found, but the path leading up to it was. + // first, figure out the final path element... + Bu::String sName = filePart( sPath ); + sio << "End filename: " << sName << sio.nl; + sio << "Parent inode: " << iParent << sio.nl; + iNode = create( iParent, sName, (uPerms&permMask)|typeRegFile, 0 ); + sio << "New iNode: " << iNode << sio.nl; + return openByInode( iNode ); + } +} + +void Bu::MyriadFs::create( const Bu::String &sPath, uint16_t iPerms ) +{ + create( sPath, iPerms, 0 ); +} + +void Bu::MyriadFs::create( const Bu::String &sPath, uint16_t iPerms, + uint16_t iDevHi, uint16_t iDevLo ) +{ + create( sPath, iPerms, ((uint32_t)iDevHi<<16)|(uint32_t)iDevLo ); +} + +void Bu::MyriadFs::create( const Bu::String &sPath, uint16_t iPerms, + uint32_t uSpecial ) +{ + int32_t iParent = -1; + int32_t iNode; + try + { + iNode = lookupInode( sPath, iParent ); + sio << "File found." << sio.nl; + // The file was found + throw Bu::MyriadFsException("Path already exists."); + } + catch( Bu::MyriadFsException &e ) + { + if( iParent < 0 ) + throw; + + // This means that an intermediate path component couldn't be found + if( e.getErrorCode() == 1 ) + throw; + + // The file wasn't found, but the path leading up to it was. + // first, figure out the final path element... + Bu::String sName = filePart( sPath ); + sio << "End filename: " << sName << sio.nl; + sio << "Parent inode: " << iParent << sio.nl; + iNode = create( iParent, sName, iPerms, uSpecial ); + sio << "New iNode: " << iNode << sio.nl; + } +} + +void Bu::MyriadFs::mkDir( const Bu::String &sPath, uint16_t iPerms ) +{ + create( sPath, (iPerms&permMask)|typeDir, 0 ); +} + +void Bu::MyriadFs::mkSymLink( const Bu::String &sTarget, + const Bu::String &sPath ) +{ + int32_t iParent = -1; + int32_t iNode; + try + { + iNode = lookupInode( sPath, iParent ); + throw Bu::MyriadFsException("Path already exists."); + } + catch( Bu::MyriadFsException &e ) + { + if( iParent < 0 ) + throw; + + // This means that an intermediate path component couldn't be found + if( e.getErrorCode() == 1 ) + throw; + + // The file wasn't found, but the path leading up to it was. + // first, figure out the final path element... + Bu::String sName = filePart( sPath ); + sio << "End filename: " << sName << sio.nl; + sio << "Parent inode: " << iParent << sio.nl; + iNode = create( iParent, sName, 0777|typeSymLink, 0 ); + sio << "New iNode: " << iNode << sio.nl; + MyriadStream ms = openByInode( iNode ); + ms.write( sTarget ); + } +} + +void Bu::MyriadFs::mkHardLink( const Bu::String &sTarget, + const Bu::String &sPath ) +{ + int32_t iParent = -1; + int32_t iNode; + + iNode = lookupInode( sTarget, iParent ); + + try + { + lookupInode( sPath, iParent ); + throw Bu::MyriadFsException("Path already exists."); + } + catch( Bu::MyriadFsException &e ) + { + if( iParent < 0 ) + throw; + + // This means that an intermediate path component couldn't be found + if( e.getErrorCode() == 1 ) + throw; + + // The file wasn't found, but the path leading up to it was. + // first, figure out the final path element... + Bu::String sName = filePart( sPath ); + sio << "End filename: " << sName << sio.nl; + sio << "Parent inode: " << iParent << sio.nl; + addToDir( iParent, iNode, sName ); + MyriadStream is = mStore.openStream( 2 ); + RawStat rs; + readInode( iNode, rs, is ); + rs.iLinks++; + writeInode( rs, is ); + } +} + +Bu::String Bu::MyriadFs::readSymLink( const Bu::String &sPath ) +{ + int32_t iParent = -1; + int32_t iNode; + iNode = lookupInode( sPath, iParent ); + MyriadStream ms = openByInode( iNode ); + Bu::String sRet; + sRet.setSize( ms.getSize() ); + ms.read( sRet.getStr(), ms.getSize() ); + return sRet; +} + +Bu::MyriadFs::Dir Bu::MyriadFs::readDir( const Bu::String &sPath ) +{ + int32_t iParent = -1; + int32_t iNode = lookupInode( sPath, iParent ); + return readDir( iNode ); +} + +void Bu::MyriadFs::setTimes( const Bu::String &sPath, int64_t iATime, + int64_t iMTime ) +{ + int32_t iParent = -1; + int32_t iNode; + + iNode = lookupInode( sPath, iParent ); + + setTimes( iNode, iATime, iMTime ); +} + +void Bu::MyriadFs::unlink( const Bu::String &sPath ) +{ + int32_t iParent = -1; +// int32_t iNode; + + /*iNode =*/ lookupInode( sPath, iParent ); + + Dir lDir = readDir( iParent ); + + Bu::String sName = filePart( sPath ); + + for( Dir::iterator i = lDir.begin(); i; i++ ) + { + if( sName == (*i).sName ) + { + RawStat rs; + readInode( (*i).iNode, rs ); + if( (rs.uPerms&typeMask) == typeDir ) + { + MyriadStream msDir = mStore.openStream( rs.uStreamIndex ); + int32_t iCount; + msDir.read( &iCount, 4 ); + if( iCount > 0 ) + { + throw Bu::MyriadFsException("Directory not empty."); + } + } + if( --rs.iLinks == 0 ) + { + destroyNode( (*i).iNode ); + } + else + { + writeInode( rs ); + } + lDir.erase( i ); + break; + } + } + + Bu::MyriadStream ms = openByInode( iParent ); + int32_t iNumChildren = lDir.getSize(); + ms.write( &iNumChildren, 4 ); + for( Dir::iterator i = lDir.begin(); i; i++ ) + { + ms.write( &(*i).iNode, 4 ); + uint8_t iSize = (*i).sName.getSize(); + ms.write( &iSize, 1 ); + ms.write( (*i).sName.getStr(), iSize ); + } + ms.setSize( ms.tell() ); +} + +void Bu::MyriadFs::setFileSize( const Bu::String &sPath, int32_t iSize ) +{ + int32_t iParent = -1; + int32_t iNode; + iNode = lookupInode( sPath, iParent ); + MyriadStream ms = openByInode( iNode ); + ms.setSize( iSize ); +} + +void Bu::MyriadFs::rename( const Bu::String &sFrom, const Bu::String &sTo ) +{ + mkHardLink( sFrom, sTo ); + unlink( sFrom ); +} + +dev_t Bu::MyriadFs::devToSys( uint32_t uDev ) +{ + return (((uDev&0xFFFF0000)>>8)&0xFF00) | ((uDev&0xFF)); +} + +uint32_t Bu::MyriadFs::sysToDev( dev_t uDev ) +{ + return (((uint32_t)uDev&0xFF00)<<8) | ((uint32_t)uDev&0xFF); +} + +int32_t Bu::MyriadFs::lookupInode( const Bu::String &sPath, int32_t &iParent ) +{ + if( sPath == "/" ) + { + return 0; + } + if( sPath[0] == '/' ) + { + // Absolute lookup + return lookupInode( sPath.begin()+1, 0, iParent ); + } + else + { + // Relative lookup + throw Bu::ExceptionBase( + "Relative lookups in MyriadFs are not working yet."); + } +} + +int32_t Bu::MyriadFs::lookupInode( Bu::String::const_iterator iStart, + int32_t iNode, int32_t &iParent ) +{ + iParent = iNode; + + Bu::String::const_iterator iEnd = iStart.find('/'); + Bu::String sTok( iStart, iEnd ); + +// sio << "Direcotry component: " << sTok << sio.nl; + + Dir lDir = readDir( iNode ); + + for( Dir::iterator i = lDir.begin(); i; i++ ) + { + if( (*i).sName == sTok ) + { + // We found an item + if( !iEnd ) + { + // It's the last one in the requested path, return it + return (*i).iNode; + } + else + { + // Not the last one in our path, double check it's a dir + if( ((*i).uPerms&typeMask) == typeDir ) + { + return lookupInode( iEnd+1, (*i).iNode, iParent ); + } + else + { + iParent = -1; + throw Bu::MyriadFsException( + "Element '%s' in given path is not a directory.", + sTok.getStr() ); + } + } + } + } + + if( iEnd ) + throw Bu::MyriadFsException( 1, "Path not found"); + else + throw Bu::MyriadFsException( 2, "Path not found"); +} + +void Bu::MyriadFs::readInode( int32_t iNode, RawStat &rs, MyriadStream &rIs ) +{ + rIs.setPos( hNodeIndex.get( iNode )*sizeof(RawStat) ); + if( rIs.read( &rs, sizeof(RawStat) ) < sizeof(RawStat) ) + throw Bu::MyriadFsException("Filesystem corruption detected."); + if( rs.iNode != iNode ) + throw Bu::MyriadFsException("Filesystem corruption detected."); +} + +void Bu::MyriadFs::readInode( int32_t iNode, RawStat &rs ) +{ + MyriadStream ms = mStore.openStream( 2 ); + readInode( iNode, rs, ms ); +} + +void Bu::MyriadFs::writeInode( const RawStat &rs, + MyriadStream &rOs ) +{ + rOs.setSize( hNodeIndex.getSize()*sizeof(RawStat) ); + rOs.setPos( hNodeIndex.get( rs.iNode )*sizeof(RawStat) ); + if( rOs.write( &rs, sizeof(RawStat) ) < sizeof(RawStat) ) + throw Bu::MyriadFsException("Error writing inode to header stream."); +} + +void Bu::MyriadFs::writeInode( const RawStat &rs ) +{ + MyriadStream ms = mStore.openStream( 2 ); + writeInode( rs, ms ); +} + +Bu::MyriadFs::Dir Bu::MyriadFs::readDir( int32_t iNode ) +{ + Bu::MyriadStream ms = openByInode( iNode ); + int32_t iNumChildren = 0; + ms.read( &iNumChildren, 4 ); + + Bu::MyriadStream is = mStore.openStream( 2 ); + Dir lDir; +// sio << "Reading dir, " << iNumChildren << " entries:" << sio.nl; + for( int32_t j = 0; j < iNumChildren; j++ ) + { + int32_t iChildNode; + ms.read( &iChildNode, 4 ); + Stat s; + stat( iChildNode, s, is ); + uint8_t uLen; + ms.read( &uLen, 1 ); + s.sName.setSize( uLen ); + ms.read( s.sName.getStr(), uLen ); + lDir.append( s ); + +// sio << " " << s.sName << sio.nl; + } + + return lDir; +} + +Bu::MyriadStream Bu::MyriadFs::openByInode( int32_t iNode ) +{ + RawStat rs; + readInode( iNode, rs ); + switch( (rs.uPerms&typeMask) ) + { + case typeDir: + case typeSymLink: + case typeRegFile: + return mStore.openStream( rs.uStreamIndex ); + + default: + throw Bu::MyriadFsException( + "inode incorrect type for low-level openByInode."); + } +} + +void Bu::MyriadFs::addToDir( int32_t iDir, int32_t iNode, + const Bu::String &sName ) +{ + if( sName.getSize() > 255 ) + { + throw Bu::MyriadFsException("Filename too long, max is 255 bytes."); + } + Bu::MyriadStream ms = openByInode( iDir ); + int32_t iNumChildren = 0; + ms.read( &iNumChildren, 4 ); + iNumChildren++; + ms.setPos( 0 ); + ms.write( &iNumChildren, 4 ); + ms.setPosEnd( 0 ); + ms.write( &iNode, 4 ); + uint8_t uLen = sName.getSize(); + ms.write( &uLen, 1 ); + ms.write( sName.getStr(), uLen ); +} + +int32_t Bu::MyriadFs::create( int32_t iParent, const Bu::String &sName, + uint16_t uPerms, uint32_t uSpecial ) +{ + int32_t iNode = allocInode( uPerms, uSpecial ); + addToDir( iParent, iNode, sName ); + return iNode; +} + +int32_t Bu::MyriadFs::allocInode( uint16_t uPerms, uint32_t uSpecial ) +{ + int32_t iNode = 0; + for(; iNode < 0xfffffff; iNode++ ) + { + if( !hNodeIndex.has( iNode ) ) + { + hNodeIndex.insert( iNode, hNodeIndex.getSize() ); + RawStat rs; + rs.iNode = iNode; + rs.iUser = iUser; + rs.iGroup = iGroup; + rs.uPerms = uPerms; + rs.iLinks = 1; + switch( (uPerms&typeMask) ) + { + case typeRegFile: + case typeSymLink: + rs.uStreamIndex = mStore.createStream(); + break; + + case typeDir: + rs.uStreamIndex = mStore.createStream(); + sio << "Creating directory node, storage: " + << rs.uStreamIndex << sio.nl; + { + Bu::MyriadStream msDir = mStore.openStream( + rs.uStreamIndex + ); + uint32_t uSize = 0; + msDir.write( &uSize, 4 ); + } + break; + + case typeChrDev: + case typeBlkDev: + rs.uStreamIndex = uSpecial; + break; + + default: + rs.uStreamIndex = 0; + break; + } + rs.iATime = time(NULL); + rs.iMTime = time(NULL); + rs.iCTime = time(NULL); + writeInode( rs ); + + return iNode; + } + } + + throw Bu::MyriadFsException( + "No inode could be allocated. You've run out!"); +} + +void Bu::MyriadFs::stat( int32_t iNode, Stat &rBuf, MyriadStream &rIs ) +{ + RawStat rs; + readInode( iNode, rs, rIs ); + rBuf.iNode = iNode; + rBuf.iUser = rs.iUser; + rBuf.iGroup = rs.iGroup; + rBuf.uPerms = rs.uPerms; + rBuf.iLinks = rs.iLinks; + rBuf.iATime = rs.iATime; + rBuf.iMTime = rs.iMTime; + rBuf.iCTime = rs.iCTime; + rBuf.uDev = 0; + rBuf.iSize = 0; + switch( (rBuf.uPerms&typeMask) ) + { + case typeRegFile: + case typeSymLink: + rBuf.iSize = mStore.getStreamSize( rs.uStreamIndex ); + break; + + case typeChrDev: + case typeBlkDev: + rBuf.uDev = rs.uStreamIndex; + break; + + default: + rBuf.iSize = 0; + break; + } +} + +void Bu::MyriadFs::writeHeader() +{ + Bu::MyriadStream ms = mStore.openStream( 1 ); + ms.write( Myriad_Fs_MAGIC_CODE, 4 ); + int8_t iVer = 1; + int32_t iNumNodes = hNodeIndex.getSize(); + ms.write( &iVer, 1 ); + ms.write( &iNumNodes, 4 ); // iNumNodes + for( NodeIndex::iterator i = hNodeIndex.begin(); i; i++ ) + { + int32_t iNode = i.getKey(); + int32_t iPosition = i.getValue(); + ms.write( &iNode, 4 ); + ms.write( &iPosition, 4 ); + } + + // Truncate the stream afterwards so we don't use up too much space. + ms.setSize( ms.tell() ); +} + +void Bu::MyriadFs::setTimes( int32_t iNode, int64_t iATime, int64_t iMTime ) +{ + RawStat rs; + Bu::MyriadStream is = mStore.openStream( 2 ); + + readInode( iNode, rs, is ); + rs.iATime = iATime; + rs.iMTime = iMTime; + writeInode( rs, is ); +} + +void Bu::MyriadFs::destroyNode( int32_t iNode ) +{ + if( iNode == 0 ) + throw Bu::MyriadFsException("You cannot destroy the root."); + + Bu::MyriadStream is = mStore.openStream( 2 ); + + // This will be overwritten with the last node + uint32_t iPosition = hNodeIndex.get( iNode ); + RawStat rsOld; + readInode( iNode, rsOld, is ); + switch( (rsOld.uPerms&typeMask) ) + { + case typeRegFile: + case typeDir: + case typeSymLink: + mStore.deleteStream( rsOld.uStreamIndex ); + break; + } + + hNodeIndex.erase( iNode ); + + // Read the last node, can't use the helpers, because we don't know the + // iNode yet. + if( iPosition != hNodeIndex.getSize() ) + { + // If this is the last node, then we don't need to do anything, but + // this case handles what to do if we aren't on the last node + RawStat rs; + is.setPos( (hNodeIndex.getSize())*sizeof(RawStat) ); + is.read( &rs, sizeof(RawStat) ); + + hNodeIndex.get( rs.iNode ) = iPosition; + writeInode( rs, is ); + } + + is.setSize( hNodeIndex.getSize() * sizeof(RawStat) ); +} + +Bu::String Bu::MyriadFs::filePart( const Bu::String &sPath ) +{ + Bu::String::const_iterator iStart = sPath.begin(); + if( *iStart == '/' ) + iStart++; + for( Bu::String::const_iterator iEnd = iStart.find('/'); iEnd; + iStart = iEnd+1, iEnd = iStart.find('/') ) { } + return Bu::String( iStart, sPath.end() ); +} + diff --git a/src/unstable/myriadfs.h b/src/unstable/myriadfs.h new file mode 100644 index 0000000..cc9961a --- /dev/null +++ b/src/unstable/myriadfs.h @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2007-2011 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. + */ + +#ifndef MYRIAD_FS_H +#define MYRIAD_FS_H + +#include + +#include "bu/myriad.h" + +namespace Bu +{ + class Stream; + + subExceptionDecl( MyriadFsException ); + + /** + * A POSIX compliant, node based filesystem built on top of Myriad. + * + * A header is placed into stream 1. + * Header format: + * int32_t iMagicHeader (A7188B39) + * int8_t iVersion (1) + * int32_t iNumNodes + * NodeLookup[iNumNodes] nNode + * + * Node lookup: + * int32_t iInode + * int32_t iPosition + * + * The node headers or inode structures have a base size of 44 bytes. + * The name is stored in the directory format. + * Basic node header format: + * int32_t iNode + * int32_t iUser + * int32_t iGroup + * uint16_t uPerms + * int16_t iLinks + * uint32_t uStreamIndex + * int64_t iATime + * int64_t iMTime + * int64_t iCTime + * + * Some types get special formats for their assosiated data stream, or + * other special considerations, here's a list: + * + * - typeFifo: No stream, uStreamIndex unused (probably) + * - typeChrDev: No stream, uStreamIndex is device hi/lo + * - typeDir: The stream contains a directory contents listing, described + * below + * - typeBlkDev: No stream, uStreamIndex is device hi/lo + * - typeRegFile: The stream is the file data + * - typeSymLink: The stream is the destination of the symlink + * - typeSocket: No steram, uStreamIndex unused (probably) + * + * Directory streams have this simple listing format. They contain a list + * of all child elements, with no particular order at the moment. The . and + * .. entries are not listed, they are implicit: + * int32_t iNumNodes + * NodeTable[iNumNodes] nChildren + * + * NodeTable: + * int32_t iInode + * uint8_t uNameSize + * char[uNameSize] sName + */ + class MyriadFs + { + public: + MyriadFs( Bu::Stream &rStore, int iBlockSize=512 ); + virtual ~MyriadFs(); + + enum + { + permOthX = 0000001, + permOthW = 0000002, + permOthR = 0000004, + permGrpX = 0000010, + permGrpW = 0000020, + permGrpR = 0000040, + permUsrX = 0000100, + permUsrW = 0000200, + permUsrR = 0000400, + permSticky = 0001000, + permSetGid = 0002000, + permSetUid = 0004000, + permMask = 0007777, + typeFifo = 0010000, + typeChrDev = 0020000, + typeDir = 0040000, + typeBlkDev = 0060000, + typeRegFile = 0100000, + typeSymLink = 0120000, + typeSocket = 0140000, + typeMask = 0170000 + }; + + enum + { + Read = 0x01, ///< Open file for reading + Write = 0x02, ///< Open file for writing + Create = 0x04, ///< Create file if it doesn't exist + Truncate = 0x08, ///< Truncate file if it does exist + Append = 0x10, ///< Always append on every write + NonBlock = 0x20, ///< Open file in non-blocking mode + Exclusive = 0x44, ///< Create file, if it exists then fail + + // Helpful mixes + ReadWrite = 0x03, ///< Open for reading and writing + WriteNew = 0x0E ///< Create a file (or truncate) for writing. + /// Same as Write|Create|Truncate + }; + + class Stat + { + public: + int32_t iNode; + int32_t iUser; + int32_t iGroup; + uint16_t uPerms; + int16_t iLinks; + int64_t iATime; + int64_t iMTime; + int64_t iCTime; + int32_t iSize; + uint32_t uDev; + Bu::String sName; + }; + typedef Bu::List Dir; + + void stat( const Bu::String &sPath, Stat &rBuf ); + MyriadStream open( const Bu::String &sPath, int iMode, + uint16_t uPerms=0664 ); + void create( const Bu::String &sPath, uint16_t iPerms ); + void create( const Bu::String &sPath, uint16_t iPerms, + uint16_t iDevHi, uint16_t iDevLo ); + void create( const Bu::String &sPath, uint16_t iPerms, + uint32_t uSpecial ); + void mkDir( const Bu::String &sPath, uint16_t iPerms ); + void mkSymLink( const Bu::String &sTarget, const Bu::String &sPath ); + void mkHardLink( const Bu::String &sTarget, const Bu::String &sPath ); + Bu::String readSymLink( const Bu::String &sPath ); + Dir readDir( const Bu::String &sPath ); + void setTimes( const Bu::String &sPath, int64_t iATime, + int64_t iMTime ); + void unlink( const Bu::String &sPath ); + void setFileSize( const Bu::String &sPath, int32_t iSize ); + void rename( const Bu::String &sFrom, const Bu::String &sTo ); + + static dev_t devToSys( uint32_t uDev ); + static uint32_t sysToDev( dev_t uDev ); + + private: + class RawStat + { + public: + int32_t iNode; + int32_t iUser; + int32_t iGroup; + uint16_t uPerms; + int16_t iLinks; + uint32_t uStreamIndex; + int64_t iATime; + int64_t iMTime; + int64_t iCTime; + }; + typedef Bu::Hash NodeIndex; + + private: + int32_t lookupInode( const Bu::String &sPath, int32_t &iParent ); + int32_t lookupInode( Bu::String::const_iterator iStart, + int32_t iNode, int32_t &iParent ); + void readInode( int32_t iNode, RawStat &rs, MyriadStream &rIs ); + void readInode( int32_t iNode, RawStat &rs ); + void writeInode( const RawStat &rs ); + void writeInode( const RawStat &rs, MyriadStream &rOs ); + Dir readDir( int32_t iNode ); + MyriadStream openByInode( int32_t iNode ); + void addToDir( int32_t iDir, int32_t iNode, const Bu::String &sName ); + int32_t create( int32_t iParent, const Bu::String &sName, + uint16_t uPerms, uint32_t uSpecial ); + int32_t allocInode( uint16_t uPerms, uint32_t uSpecial ); + void stat( int32_t iNode, Stat &rBuf, MyriadStream &rIs ); + void writeHeader(); + void setTimes( int32_t iNode, int64_t iATime, int64_t iMTime ); + void destroyNode( int32_t iNode ); + + Bu::String filePart( const Bu::String &sPath ); + + private: + Bu::Stream &rStore; + Bu::Myriad mStore; + NodeIndex hNodeIndex; + int32_t iUser; + int32_t iGroup; + }; +}; + +#endif diff --git a/src/unstable/myriadstream.cpp b/src/unstable/myriadstream.cpp new file mode 100644 index 0000000..e6e94bc --- /dev/null +++ b/src/unstable/myriadstream.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2007-2011 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/myriadstream.h" + +#include + +// #define MYRIAD_STREAM_DEBUG 1 + +#ifdef MYRIAD_STREAM_DEBUG +#include "bu/sio.h" + +using Bu::sio; +using Bu::Fmt; +#endif + +Bu::MyriadStream::MyriadStream( Bu::Myriad &rMyriad, + Bu::Myriad::Stream *pStream ) : + rMyriad( rMyriad ), + pStream( pStream ), + pCurBlock( NULL ), + iPos( 0 ) +{ +#ifdef MYRIAD_STREAM_DEBUG + sio << "MyriadStream: " << __LINE__ << ": Created, iId=" << pStream->iId << ", iSize=" + << pStream->iSize << sio.nl; +#endif + //pCurBlock = rMyriad.newBlock(); + //rMyriad.getBlock( uStream, pCurBlock ); + //uSize = pCurBlock->uBytesUsed; +} + +Bu::MyriadStream::~MyriadStream() +{ + if( pCurBlock ) + rMyriad.releaseBlock( pCurBlock ); + //rMyriad.updateStreamSize( uStream, uSize ); + //rMyriad.deleteBlock( pCurBlock ); +} + +void Bu::MyriadStream::close() +{ +} + +Bu::size Bu::MyriadStream::read( void *pBuf, Bu::size nBytes ) +{ +#ifdef MYRIAD_STREAM_DEBUG + sio << "MyriadStream: read: " << __LINE__ << ": Started, asked to read " << nBytes << "b." + << sio.nl; +#endif + if( nBytes > (Bu::size)pStream->iSize-iPos ) + nBytes = pStream->iSize-iPos; + if( nBytes <= 0 ) + return 0; + int iLeft = nBytes; +#ifdef MYRIAD_STREAM_DEBUG + sio << "MyriadStream: read: " << __LINE__ << ": Started, going to read " << nBytes << "b." + << sio.nl; +#endif + if( pCurBlock == NULL ) + { +#ifdef MYRIAD_STREAM_DEBUG + sio << "MyriadStream: read: " << __LINE__ << ": No block loaded, loading initial block." + << sio.nl; +#endif + pCurBlock = rMyriad.getBlock( + pStream->aBlocks[iPos/rMyriad.iBlockSize] + ); + } + while( iLeft > 0 ) + { + int iCurBlock = pStream->aBlocks[iPos/rMyriad.iBlockSize]; + if( pCurBlock->iBlockIndex != iCurBlock ) + { +#ifdef MYRIAD_STREAM_DEBUG + sio << "MyriadStream: read: " << __LINE__ << ": Loading new block " << iCurBlock << "." + << sio.nl; +#endif + rMyriad.releaseBlock( pCurBlock ); + pCurBlock = rMyriad.getBlock( iCurBlock ); + } + + int iAmnt = Bu::min( + Bu::min( + rMyriad.iBlockSize - iPos%rMyriad.iBlockSize, + iLeft + ), + pStream->iSize-iPos + ); +#ifdef MYRIAD_STREAM_DEBUG + sio << "MyriadStream: read: " << __LINE__ << ": Copying out bytes: " + << iPos << "(" << (iPos%rMyriad.iBlockSize) << ")+" + << iAmnt + << ", " << iLeft << "b left." << sio.nl; +#endif + memcpy( + pBuf, + pCurBlock->pData+(iPos%rMyriad.iBlockSize), + iAmnt + ); + iPos += iAmnt; + pBuf = &((char *)pBuf)[iAmnt]; + iLeft -= iAmnt; + } + return nBytes; +} + +Bu::size Bu::MyriadStream::write( const void *pBuf, Bu::size nBytes ) +{ + if( nBytes <= 0 ) + return 0; + +#ifdef MYRIAD_STREAM_DEBUG + sio << "MyriadStream: write: " << __LINE__ << ": Started, asked to write " << nBytes << "b." + << sio.nl; +#endif + if( nBytes <= 0 ) + return 0; + int iLeft = nBytes; + /* + if( pCurBlock == NULL ) + { +#ifdef MYRIAD_STREAM_DEBUG + sio << "MyriadStream: write: No block loaded, loading initial block." + << sio.nl; +#endif + pCurBlock = rMyriad.getBlock( + pStream->aBlocks[iPos/rMyriad.iBlockSize] + ); + }*/ + + while( iLeft > 0 ) + { + int iCurBlock; + if( iPos/rMyriad.iBlockSize < pStream->aBlocks.getSize() ) + { + iCurBlock = pStream->aBlocks[iPos/rMyriad.iBlockSize]; + } + else + { + iCurBlock = rMyriad.streamAddBlock( pStream ); +#ifdef MYRIAD_STREAM_DEBUG + sio << "MyriadStream: write: " << __LINE__ << ": New block allocated and appended: " + << iCurBlock << "." << sio.nl; + +#endif + } + if( !pCurBlock || pCurBlock->iBlockIndex != iCurBlock ) + { +#ifdef MYRIAD_STREAM_DEBUG + sio << "MyriadStream: write: " << __LINE__ << ": Loading new block " << iCurBlock << "." + << sio.nl; +#endif + rMyriad.releaseBlock( pCurBlock ); + pCurBlock = rMyriad.getBlock( iCurBlock ); + } + pCurBlock->bChanged = true; + + // There are two main writing modes when it comes down to it. + // Overwrite mode and append mode. Append is what pretty much always + // happens when creating a new stream. + if( iPos < pStream->iSize ) + { + int iAmnt = Bu::min( + Bu::min( + rMyriad.iBlockSize - iPos%rMyriad.iBlockSize, + iLeft + ), + pStream->iSize-iPos + ); +#ifdef MYRIAD_STREAM_DEBUG + sio << "MyriadStream: write (ovr): " << __LINE__ << ": Copying in bytes: " + << (iPos%rMyriad.iBlockSize) << "+" + << iAmnt + << ", " << iLeft << "b left." << sio.nl; +#endif + memcpy( + pCurBlock->pData+(iPos%rMyriad.iBlockSize), + pBuf, + iAmnt + ); + iPos += iAmnt; + pBuf = &((char *)pBuf)[iAmnt]; + iLeft -= iAmnt; + } + else + { + int iAmnt = Bu::min( + rMyriad.iBlockSize - iPos%rMyriad.iBlockSize, + iLeft + ); +#ifdef MYRIAD_STREAM_DEBUG + sio << "MyriadStream: write (app): " << __LINE__ << ": Copying in bytes: " + << (iPos%rMyriad.iBlockSize) << "+" + << iAmnt + << ", " << iLeft << "b left." << sio.nl; +#endif + memcpy( + pCurBlock->pData+(iPos%rMyriad.iBlockSize), + pBuf, + iAmnt + ); + iPos += iAmnt; + pStream->iSize += iAmnt; + rMyriad.headerChanged(); + pBuf = &((char *)pBuf)[iAmnt]; + iLeft -= iAmnt; + } + } + + return nBytes; +} + +Bu::size Bu::MyriadStream::tell() +{ + return iPos; +} + +void Bu::MyriadStream::seek( Bu::size offset ) +{ + iPos += offset; +} + +void Bu::MyriadStream::setPos( Bu::size pos ) +{ + iPos = pos; +} + +void Bu::MyriadStream::setPosEnd( Bu::size pos ) +{ + iPos = pStream->iSize-pos; +} + +bool Bu::MyriadStream::isEos() +{ + return iPos >= pStream->iSize; +} + +bool Bu::MyriadStream::isOpen() +{ + return true; +} + +void Bu::MyriadStream::flush() +{ +} + +bool Bu::MyriadStream::canRead() +{ + return true; +} + +bool Bu::MyriadStream::canWrite() +{ + return true; +} + +bool Bu::MyriadStream::isReadable() +{ + return true; +} + +bool Bu::MyriadStream::isWritable() +{ + return true; +} + +bool Bu::MyriadStream::isSeekable() +{ + return true; +} + +bool Bu::MyriadStream::isBlocking() +{ + return true; +} + +void Bu::MyriadStream::setBlocking( bool /*bBlocking*/ ) +{ +} + +void Bu::MyriadStream::setSize( Bu::size iSize ) +{ + if( iSize < 0 ) + iSize = 0; + rMyriad.setStreamSize( pStream, iSize ); + if( iPos > iSize ) + iPos = iSize; +} + +Bu::size Bu::MyriadStream::getSize() const +{ + return pStream->iSize; +} + +Bu::size Bu::MyriadStream::getBlockSize() const +{ + return rMyriad.getBlockSize(); +} + +Bu::String Bu::MyriadStream::getLocation() const +{ + return Bu::String("%1").arg( pStream->iId ); +} + diff --git a/src/unstable/myriadstream.h b/src/unstable/myriadstream.h new file mode 100644 index 0000000..fdad669 --- /dev/null +++ b/src/unstable/myriadstream.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2007-2011 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. + */ + +#ifndef BU_MYRIAD_STREAM_H +#define BU_MYRIAD_STREAM_H + +#include "bu/stream.h" +#include "bu/myriad.h" + +namespace Bu +{ + class MyriadStream : public Bu::Stream + { + friend class Myriad; + private: + /** + * These can only be created by the Myriad class. + */ + MyriadStream( Myriad &rMyriad, Myriad::Stream *pStream ); + + public: + virtual ~MyriadStream(); + + virtual void close(); + virtual Bu::size read( void *pBuf, Bu::size nBytes ); + virtual Bu::size write( const void *pBuf, Bu::size nBytes ); + using Stream::write; + virtual Bu::size tell(); + virtual void seek( Bu::size offset ); + virtual void setPos( Bu::size pos ); + virtual void setPosEnd( Bu::size pos ); + virtual bool isEos(); + virtual bool isOpen(); + virtual void flush(); + virtual bool canRead(); + virtual bool canWrite(); + virtual bool isReadable(); + virtual bool isWritable(); + virtual bool isSeekable(); + virtual bool isBlocking(); + virtual void setBlocking( bool bBlocking=true ); + virtual void setSize( Bu::size iSize ); + + virtual size getSize() const; + virtual size getBlockSize() const; + virtual Bu::String getLocation() const; + + private: + Myriad &rMyriad; + Myriad::Stream *pStream; + Myriad::Block *pCurBlock; + int iBlockSize; + int iPos; + }; +}; + +#endif diff --git a/src/unstable/newline.cpp b/src/unstable/newline.cpp new file mode 100644 index 0000000..ffc9eb0 --- /dev/null +++ b/src/unstable/newline.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007-2011 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/newline.h" + +Bu::NewLine::NewLine( Bu::Stream &rNext ) : + Bu::Filter( rNext ), + bExChar( false ) +{ +} + +Bu::NewLine::~NewLine() +{ +} + +void Bu::NewLine::start() +{ +} + +Bu::size Bu::NewLine::stop() +{ + return 0; +} + +Bu::size Bu::NewLine::read( void *pBufV, Bu::size iAmnt ) +{ + Bu::size iTotal = 0; + Bu::size iOffset = 0; + Bu::size iRead = rNext.read( pBufV, iAmnt ); + char *pBuf = (char *)pBufV; + + for( Bu::size i = 0; i < iRead; i++ ) + { + if( pBuf[i] == '\r' ) + { + pBuf[i+iOffset] = '\n'; + if( pBuf[i+1] == '\n' ) + { + iOffset--; + } + } + else if( pBuf[i] == '\n' ) + { + pBuf[i+iOffset] = '\n'; + if( pBuf[i+1] == '\r' ) + { + iOffset--; + } + } + else if( iOffset ) + { + pBuf[i+iOffset] = pBuf[i]; + } + } + + iTotal += iRead + iOffset; + return iTotal; +} + +Bu::size Bu::NewLine::write( const void *, Bu::size ) +{ + return 0; +} + diff --git a/src/unstable/newline.h b/src/unstable/newline.h new file mode 100644 index 0000000..afe0a84 --- /dev/null +++ b/src/unstable/newline.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007-2011 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. + */ + +#ifndef BU_NEW_LINE_H +#define BU_NEW_LINE_H + +#include "bu/filter.h" + +namespace Bu +{ + /** + * Converts new-line characters from any standard convention into linefeeds + * (\\n) on reading, and converts them to either your OS's standard or a + * specified standard, depending on how you construct the class. + * + * If you're reading in a text file, then this filter is practically + * required. + */ + class NewLine : public Bu::Filter + { + public: + NewLine( Bu::Stream &rNext ); + virtual ~NewLine(); + + virtual void start(); + virtual Bu::size stop(); + + virtual Bu::size read( void *pBuf, Bu::size iAmnt ); + virtual Bu::size write( const void *pBuf, Bu::size iAmnt ); + + private: + bool bExChar; + char cExChar; + }; +}; + +#endif diff --git a/src/unstable/udpsocket.cpp b/src/unstable/udpsocket.cpp new file mode 100644 index 0000000..702840a --- /dev/null +++ b/src/unstable/udpsocket.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2007-2011 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. + */ + +#ifndef WIN32 //not on windows + +#include "bu/udpsocket.h" + +#include "bu/sio.h" +using namespace Bu; +#include + +#include +#include +#include +#include +#include + +namespace Bu { subExceptionDef( UdpSocketException ) } + +#define saTarget ( *((struct sockaddr_in *)paTarget) ) + +Bu::UdpSocket::UdpSocket( int iUdpSocket ) : + iUdpSocket( iUdpSocket ), + paTarget( NULL ), + bBound( false ) +{ +} + +Bu::UdpSocket::UdpSocket( const Bu::String &sAddr, int iPort, int iFlags ) : + iUdpSocket( 0 ), + paTarget( NULL ), + bBound( false ) +{ + iUdpSocket = socket( PF_INET, SOCK_DGRAM, 0 ); + if( iUdpSocket < 0 ) + { + throw UdpSocketException("Couldn't open udp socket: %s", + strerror( errno ) + ); + } + + if( (iFlags&Broadcast) ) + { + int broadcast = 1; + if( (setsockopt( iUdpSocket, SOL_SOCKET, SO_BROADCAST, + &broadcast, sizeof(broadcast) )) == -1) + { + throw UdpSocketException("Couldn't set udp socket to broadcast: %s", + strerror( errno ) + ); + } + } + + paTarget = new struct sockaddr_in; + saTarget.sin_family = AF_INET; + saTarget.sin_port = htons( iPort ); + saTarget.sin_addr.s_addr = inet_addr( sAddr.getStr() ); // INADDR_ANY; + memset( saTarget.sin_zero, '\0', sizeof(saTarget.sin_zero) ); + + if( (iFlags&Read) ) + { + if( bind( iUdpSocket, (struct sockaddr*)paTarget, sizeof(struct sockaddr_in) ) + == -1 ) + { + throw UdpSocketException("Couldn't bind port to udp socket: %s", + strerror( errno ) + ); + } + bBound = true; + } +} + +Bu::UdpSocket::~UdpSocket() +{ + close(); + delete (struct sockaddr_in *)paTarget; + paTarget = NULL; +} + +Bu::String Bu::UdpSocket::addrToStr( const addr &a ) +{ + return Bu::String("%1.%2.%3.%4"). + arg( (a&0xff) ). + arg( (a&0xff00)>>8 ). + arg( (a&0xff0000)>>16 ). + arg( (a&0xff000000)>>24 ); +} + +void Bu::UdpSocket::close() +{ + ::close( iUdpSocket ); +} + +Bu::size Bu::UdpSocket::read( void *pBuf, Bu::size nBytes ) +{ + return recv( iUdpSocket, pBuf, nBytes, 0 ); +} + +Bu::size Bu::UdpSocket::read( void *pBuf, Bu::size nBytes, + Bu::UdpSocket::addr &aHost, int &iPort ) +{ + sockaddr_in name; + socklen_t size = sizeof(name); + Bu::size ret = recvfrom( iUdpSocket, pBuf, nBytes, 0, + (struct sockaddr *)&name, &size ); + aHost = name.sin_addr.s_addr; + iPort = ntohs(name.sin_port); + return ret; +} + +Bu::size Bu::UdpSocket::write( const void *pBuf, Bu::size nBytes ) +{ + if( bBound ) + { + return sendto( iUdpSocket, pBuf, nBytes, 0, NULL, 0 ); + } + else + { + return sendto( iUdpSocket, pBuf, nBytes, 0, + (struct sockaddr*)paTarget, sizeof(struct sockaddr_in) ); + } +} + +Bu::size Bu::UdpSocket::tell() +{ + throw Bu::UnsupportedException(); +} + +void Bu::UdpSocket::seek( Bu::size ) +{ + throw Bu::UnsupportedException(); +} + +void Bu::UdpSocket::setPos( Bu::size ) +{ + throw Bu::UnsupportedException(); +} + +void Bu::UdpSocket::setPosEnd( Bu::size ) +{ + throw Bu::UnsupportedException(); +} + +bool Bu::UdpSocket::isEos() +{ + return false; +} + +bool Bu::UdpSocket::isOpen() +{ + return true; +} + +void Bu::UdpSocket::flush() +{ +} + +bool Bu::UdpSocket::canRead() +{ + return bBound; +} + +bool Bu::UdpSocket::canWrite() +{ + return true; +} + +bool Bu::UdpSocket::isReadable() +{ + return bBound; +} + +bool Bu::UdpSocket::isWritable() +{ + return true; +} + +bool Bu::UdpSocket::isSeekable() +{ + return false; +} + +bool Bu::UdpSocket::isBlocking() +{ + return true; +} + +void Bu::UdpSocket::setBlocking( bool bBlocking ) +{ +#ifndef WIN32 + if( bBlocking ) + { + fcntl( iUdpSocket, F_SETFL, fcntl( iUdpSocket, F_GETFL, 0 ) & (~O_NONBLOCK) ); + } + else + { + fcntl( iUdpSocket, F_SETFL, fcntl( iUdpSocket, F_GETFL, 0 ) | O_NONBLOCK ); + } +#else + u_long iMode; + if( bBlocking ) + iMode = 0; + else + iMode = 1; + //------------------------- + // Set the socket I/O mode: In this case FIONBIO + // enables or disables the blocking mode for the + // socket based on the numerical value of iMode. + // If iMode = 0, blocking is enabled; + // If iMode != 0, non-blocking mode is enabled. + bu_ioctlsocket(iUdpSocket, FIONBIO, &iMode); +#endif +} + +void Bu::UdpSocket::setSize( Bu::size ) +{ + throw Bu::UnsupportedException(); +} + +Bu::size Bu::UdpSocket::getSize() const +{ + throw Bu::UnsupportedException(); +} + +Bu::size Bu::UdpSocket::getBlockSize() const +{ + return 1500; +} + +Bu::String Bu::UdpSocket::getLocation() const +{ + throw Bu::UnsupportedException(); +} + +#endif + diff --git a/src/unstable/udpsocket.h b/src/unstable/udpsocket.h new file mode 100644 index 0000000..f228f08 --- /dev/null +++ b/src/unstable/udpsocket.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2007-2011 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. + */ +#ifndef WIN32 //not on windows + +#ifndef BU_UDP_SOCKET_H +#define BU_UDP_SOCKET_H + +#include + +#include "bu/stream.h" + +namespace Bu +{ + subExceptionDecl( UdpSocketException ); + + class UdpSocket : public Stream + { + public: + UdpSocket( int iUdpSocket ); + UdpSocket( const Bu::String &sAddr, int iPort, int iFlags ); + virtual ~UdpSocket(); + + typedef uint32_t addr; + + static Bu::String addrToStr( const addr &a ); + + virtual void close(); + virtual Bu::size read( void *pBuf, Bu::size nBytes ); + virtual Bu::size read( void *pBuf, Bu::size nBytes, + addr &sHost, int &iPort ); + virtual Bu::size write( const void *pBuf, Bu::size nBytes ); + using Stream::write; + + virtual Bu::size tell(); + virtual void seek( Bu::size offset ); + virtual void setPos( Bu::size pos ); + virtual void setPosEnd( Bu::size pos ); + virtual bool isEos(); + virtual bool isOpen(); + + virtual void flush(); + + virtual bool canRead(); + virtual bool canWrite(); + + virtual bool isReadable(); + virtual bool isWritable(); + virtual bool isSeekable(); + + virtual bool isBlocking(); + virtual void setBlocking( bool bBlocking=true ); + + virtual void setSize( Bu::size iSize ); + + enum { + // Flags + Read = 0x01, ///< Open udp socket for reading + Write = 0x02, ///< Open udp socket for writing + ReadWrite = 0x03, ///< Open for both read and write + Broadcast = 0x04, ///< Open for broadcast + }; + + virtual size getSize() const; + virtual size getBlockSize() const; + virtual Bu::String getLocation() const; + + private: +#ifdef WIN32 + unsigned int iUdpSocket; +#else + int iUdpSocket; +#endif + void *paTarget; + bool bBound; + }; +}; + +#endif + +#endif diff --git a/src/unstable/url.cpp b/src/unstable/url.cpp new file mode 100644 index 0000000..7b4a48e --- /dev/null +++ b/src/unstable/url.cpp @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2007-2011 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/url.h" +#ifndef WIN32 +# include +# include +#endif +#include + +char Bu::Url::hexcode[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' +}; + +Bu::Url::Url() +{ +} + +Bu::Url::Url( const Bu::String &sUrl ) +{ + parseUrl( sUrl ); +} + +Bu::Url::~Url() +{ +} + +void Bu::Url::parseUrl( const Bu::String &sUrl ) +{ + clear(); + + Bu::String::const_iterator i = sUrl.begin(); + parseProtocol( i ); + parseUserPass( i ); + parseHost( i ); + parsePath( i ); +} + +Bu::String Bu::Url::decode( const Bu::String &sStr ) +{ + Bu::String sRet; + char buf[3] = {0, 0, 0}; + for( Bu::String::const_iterator i = sStr.begin(); i; i++ ) + { + if( *i == '+' ) + { + sRet += ' '; + } + else if( *i == '%' ) + { + i++; + buf[0] = *i; + i++; + buf[1] = *i; + sRet += (char)((unsigned char)strtol( buf, NULL, 16 )); + } + else + { + sRet += *i; + } + } + return sRet; +} + +Bu::String Bu::Url::encode( const Bu::String &sStr ) +{ + Bu::String sRet; + for( Bu::String::const_iterator i = sStr.begin(); i; i++ ) + { + if( *i == ' ' ) + { + sRet += '+'; + } + else if( + (*i >= 'A' && *i <= 'Z') || + (*i >= 'a' && *i <= 'z') || + (*i >= '0' && *i <= '9') || + (*i == '-' || *i == '_' || *i == '.' || *i == '~') + ) + { + sRet += *i; + } + else + { + unsigned char b = *i; + sRet += '%'; + sRet += hexcode[(b>>4)&0xF]; + sRet += hexcode[b&0xF]; + } + } + return sRet; +} + +void Bu::Url::parseProtocol( Bu::String::const_iterator &i ) +{ + Bu::String::const_iterator s = i.find("://", 3); + if( !s ) + throw Bu::ExceptionBase("No :// in url"); + Bu::String sTmp( i, s ); + setProtocol( sTmp ); + i = s + 3; +} + +void Bu::Url::setProtocol( const Bu::String &sNewProto, bool bAutoSetPort ) +{ + sProtocol = sNewProto; +#ifndef WIN32 + if( bAutoSetPort ) + { + struct servent *se = getservbyname( sProtocol.getStr(), "tcp" ); + if( se ) + { + iPort = ntohs( se->s_port ); + } + } +#endif +} + +void Bu::Url::parseUserPass( Bu::String::const_iterator &i ) +{ + Bu::String::const_iterator s = i.find('@'); + if( !s ) + return; + + Bu::String::const_iterator p = i.find(':'); + if( p ) + { + sUser.set( i, p ); + sPass.set( p+1, s ); + } + else + { + sUser.set( i, s ); + } + + i = s + 1; +} + +void Bu::Url::parseHost( Bu::String::const_iterator &i ) +{ + Bu::String::const_iterator s = i; + for( ; s && *s != '/'; s++ ) + { + if( *s == ':' ) + { + sHost.set( i, s ); + i = s + 1; + s = i.find('/'); + Bu::String sPort( i, s ); + iPort = strtol( sPort.getStr(), NULL, 10 ); + i = s; + return; + } + } + sHost.set( i, s ); + i = s; +} + +void Bu::Url::parsePath( const Bu::String &sPath ) +{ + Bu::String::const_iterator i = sPath.begin(); + parsePath( i ); +} + +void Bu::Url::parsePath( Bu::String::const_iterator &i ) +{ + if( i ) + { + Bu::String::const_iterator s = i.find('?'); + sPath.set( i, s ); + i = s + 1; + if( s ) + { + parseParams( i ); + } + } + else + { + sPath = "/"; + } +} + +void Bu::Url::parseParams( const Bu::String &sQuery ) +{ + Bu::String::const_iterator i = sQuery.begin(); + parseParams( i ); +} + +void Bu::Url::parseParams( Bu::String::const_iterator &i ) +{ + bool bName = true; + Bu::String sName, sValue; + for( Bu::String::const_iterator s = i; s; s++ ) + { + if( bName ) + { + if( *s == '&' ) + { + sName.set( i, s ); + sValue.clear(); + i = s + 1; + addParam( decode( sName ), decode( sValue ) ); + } + else if( *s == '=' ) + { + sName.set( i, s ); + i = s + 1; + bName = false; + } + } + else + { + if( *s == '&' ) + { + sValue.set( i, s ); + i = s + 1; + bName = true; + addParam( decode( sName ), decode( sValue ) ); + } + } + } + if( i ) + { + if( bName ) + { + sName.set( i ); + sValue.clear(); + } + else + { + sValue.set( i ); + } + addParam( decode( sName ), decode( sValue ) ); + } +} + +void Bu::Url::addParam( const Bu::String &n, const Bu::String &v ) +{ + lParam.append( Param( n, v ) ); +} + +void Bu::Url::clear() +{ + sProtocol.clear(); + sUser.clear(); + sPass.clear(); + sHost.clear(); + sPath.clear(); + iPort.clear(); +} + +Bu::String Bu::Url::getFullPath() const +{ + Bu::String sBuf = sPath; + if( !lParam.isEmpty() ) + { + for( ParamList::const_iterator i = lParam.begin(); i; i++ ) + { + if( i == lParam.begin() ) + sBuf += "?"; + else + sBuf += "&"; + + sBuf += encode( (*i).sName ); + if( !(*i).sValue.isEmpty() ) + { + sBuf += "=" + encode( (*i).sValue ); + } + } + } + + return sBuf; +} + +Bu::String Bu::Url::getUrl() const +{ + Bu::String sBuf = sProtocol + "://" + sHost + getFullPath(); + return sBuf; +} + diff --git a/src/unstable/url.h b/src/unstable/url.h new file mode 100644 index 0000000..d751578 --- /dev/null +++ b/src/unstable/url.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2007-2011 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. + */ + +#ifndef BU_URL_H +#define BU_URL_H + +#include "bu/string.h" +#include "bu/atom.h" + +namespace Bu +{ + class Url + { + public: + typedef struct Param + { + Param() { } + Param( const Param &r ) : sName( r.sName ), sValue( r.sValue ) { } + Param( const Bu::String &n, const Bu::String &v ) : + sName( n ), sValue( v ) { } + Bu::String sName; + Bu::String sValue; + } Param; + typedef Bu::List ParamList; + + public: + Url(); + Url( const Bu::String &sUrl ); + virtual ~Url(); + + void parseUrl( const Bu::String &sUrl ); + void parseParams( const Bu::String &sQuery ); + void parseParams( Bu::String::const_iterator &i ); + void parsePath( const Bu::String &sPath ); + void parsePath( Bu::String::const_iterator &i ); + void clear(); + + Bu::String getUrl() const; + Bu::String getFullPath() const; + + const Bu::String &getProtocol() const { return sProtocol; } + const Bu::String &getUser() const { return sUser; } + const Bu::String &getPass() const { return sPass; } + const Bu::String &getHost() const { return sHost; } + const Bu::String &getPath() const { return sPath; } + int getPort() const { return iPort; } + ParamList::const_iterator getParamBegin() const + { return lParam.begin(); } + + void setProtocol( const Bu::String &sNewHost, bool bAutoSetPort=true ); + void setUser( const Bu::String &s ) { sUser = s; } + void setPass( const Bu::String &s ) { sPass = s; } + void setHost( const Bu::String &s ) { sHost = s; } + void setPath( const Bu::String &s ) { sPath = s; } + void setPort( int i ) { iPort = i; } + void addParam( const Bu::String &n, const Bu::String &v ); + + bool hasPort() const { return iPort.has(); } + + static Bu::String decode( const Bu::String &sStr ); + static Bu::String encode( const Bu::String &sStr ); + + private: // Parsing code + void parseProtocol( Bu::String::const_iterator &i ); + void parseUserPass( Bu::String::const_iterator &i ); + void parseHost( Bu::String::const_iterator &i ); + + private: + Bu::String sProtocol; + Bu::String sUser; + Bu::String sPass; + Bu::String sHost; + Bu::String sPath; + Bu::Atom iPort; + ParamList lParam; + + static char hexcode[16]; + }; +}; + +#endif diff --git a/src/unstable/urn.cpp b/src/unstable/urn.cpp new file mode 100644 index 0000000..106fb64 --- /dev/null +++ b/src/unstable/urn.cpp @@ -0,0 +1,8 @@ +/* + * Copyright (C) 2007-2011 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. + */ + + diff --git a/src/unstable/urn.h b/src/unstable/urn.h new file mode 100644 index 0000000..106fb64 --- /dev/null +++ b/src/unstable/urn.h @@ -0,0 +1,8 @@ +/* + * Copyright (C) 2007-2011 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. + */ + + diff --git a/src/unstable/utfstring.cpp b/src/unstable/utfstring.cpp new file mode 100644 index 0000000..19d3ddc --- /dev/null +++ b/src/unstable/utfstring.cpp @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2007-2011 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/utfstring.h" + +#include "bu/string.h" +#include "bu/stream.h" +#include "bu/config.h" +#include "bu/sio.h" +using Bu::sio; + +Bu::UtfString::UtfString() +{ +} + +Bu::UtfString::UtfString( const Bu::String &sInput, Encoding eEnc ) +{ + set( sInput, eEnc ); +} + +Bu::UtfString::~UtfString() +{ +} + +void Bu::UtfString::set( const Bu::String &sInput, Encoding eEnc ) +{ + switch( eEnc ) + { + case Utf8: + setUtf8( sInput ); + break; + + case Utf16: + setUtf16( sInput ); + break; + + case Utf16be: + setUtf16be( sInput ); + break; + + case Utf16le: + setUtf16le( sInput ); + break; + + case Utf32: + setUtf32( sInput ); + break; + + case Utf32be: + setUtf32be( sInput ); + break; + + case Utf32le: + setUtf32le( sInput ); + break; + + case Ucs2: + throw Bu::ExceptionBase("Ucs2 not supported yet."); + break; + + case Ucs4: + throw Bu::ExceptionBase("Ucs4 not supported yet."); + break; + + case GuessEncoding: + throw Bu::ExceptionBase("Guessing mode not supported yet."); + break; + } +} + +void Bu::UtfString::append( UtfChar ch ) +{ + if( ch >= 0x10000 ) + { + ch -= 0x10000; + append16( ((ch>>10)&0x3FF)| 0xD800u ); + append16( (ch&0x3FF)| 0xDC00u ); + } + else + { + append16( (uint16_t)(ch) ); + } +} + +void Bu::UtfString::setUtf8( const Bu::String &sInput ) +{ + static uint8_t lmask[8] = { + 0x00, + 0x01, + 0x03, + 0x07, + 0x0f, + 0x1f, + 0x3f, + 0x7f + }; + for( Bu::String::const_iterator i = sInput.begin(); i; i++ ) + { + if( ((int)(uint8_t)*i)&0x80 ) + { + int iBytes = 1; + for(; (((uint8_t)(*i))<= 1; iBytes-- ) + { + i++; + uPt |= ((*i)&lmask[6])<<(6*(iBytes-1)); + } + append( uPt ); + } + else + { + append( (Bu::UtfChar)(*i) ); + } + } +} + +void Bu::UtfString::setUtf16( const Bu::String &sInput ) +{ +// Bu::String::const_iterator i = sInput.begin(); + if( (uint8_t)*sInput.begin() == 0xFF && + (uint8_t)*(sInput.begin()+1) == 0xFE ) + { + setUtf16le( sInput ); + return; + } + setUtf16be( sInput ); +} + +void Bu::UtfString::setUtf16be( const Bu::String &sInput ) +{ + Bu::String::const_iterator i = sInput.begin(); + if( (uint8_t)*sInput.begin() == 0xFE && + (uint8_t)*(sInput.begin()+1) == 0xFF ) + + { + i += 2; + sio << "Verified big endian." << sio.nl; + } + else + { + sio << "Assuming big endian." << sio.nl; + } + uint16_t hi, lo; + for( ; i; i++ ) + { + hi = (((uint8_t)*i)<<8) | ((uint8_t)*(++i)); + append16( hi ); + if( (hi&0xD800u) == 0xD800u ) + { + lo = (((uint8_t)*(++i))<<8) | ((uint8_t)*(++i)); + append16( lo ); + } + } +} + +void Bu::UtfString::setUtf16le( const Bu::String &sInput ) +{ + Bu::String::const_iterator i = sInput.begin(); + if( (uint8_t)*sInput.begin() == 0xFF && + (uint8_t)*(sInput.begin()+1) == 0xFE ) + { + i += 2; + sio << "Verified little endian." << sio.nl; + } + else + { + sio << "Assuming little endian." << sio.nl; + } + uint16_t hi, lo; + for( ; i; i++ ) + { + hi = (((uint8_t)*i)) | ((uint8_t)*(++i)<<8); + append16( hi ); + if( (hi&0xD800u) == 0xD800u ) + { + lo = (((uint8_t)*(++i))) | ((uint8_t)*(++i)<<8); + append16( lo ); + } + } +} + +void Bu::UtfString::setUtf32( const Bu::String &sInput ) +{ + Bu::String::const_iterator i = sInput.begin(); + if( (uint8_t)*i == 0x00 && + (uint8_t)*(++i) == 0x00 && + (uint8_t)*(++i) == 0xFF && + (uint8_t)*(++i) == 0xFE ) + { + setUtf32le( sInput ); + return; + } + setUtf32be( sInput ); +} + +void Bu::UtfString::setUtf32be( const Bu::String &sInput ) +{ + Bu::String::const_iterator i = sInput.begin(); + if( (uint8_t)*i == 0x00 && + (uint8_t)*(++i) == 0x00 && + (uint8_t)*(++i) == 0xFE && + (uint8_t)*(++i) == 0xFF ) + { + i++; + sio << "Verified big endian." << sio.nl; + } + else + { + i = sInput.begin(); + sio << "Assuming big endian." << sio.nl; + } + for( ; i; i++ ) + { + append( (((uint8_t)*i)<<24) | + (((uint8_t)*(++i))<<16) | + (((uint8_t)*(++i))<<8) | + ((uint8_t)*(++i)) + ); + } +} + +void Bu::UtfString::setUtf32le( const Bu::String &sInput ) +{ + Bu::String::const_iterator i = sInput.begin(); + if( (uint8_t)*i == 0x00 && + (uint8_t)*(++i) == 0x00 && + (uint8_t)*(++i) == 0xFF && + (uint8_t)*(++i) == 0xFE ) + { + i++; + sio << "Verified little endian." << sio.nl; + } + else + { + i = sInput.begin(); + sio << "Assuming little endian." << sio.nl; + } + for( ; i; i++ ) + { + append( ((uint8_t)*i) | + (((uint8_t)*(++i))<<8) | + (((uint8_t)*(++i))<<16) | + (((uint8_t)*(++i))<<24) + ); + } +} + +void Bu::UtfString::write( Bu::Stream &sOut, Encoding eEnc ) +{ + switch( eEnc ) + { + case Utf8: + writeUtf8( sOut ); + break; + + case Utf16: +// writeUtf16( sOut ); +// break; + + case Utf16be: + writeUtf16be( sOut ); + break; + + case Utf16le: + writeUtf16le( sOut ); + break; + + case Utf32: +// writeUtf32( sOut ); +// break; + + case Utf32be: + writeUtf32be( sOut ); + break; + + case Utf32le: + writeUtf32le( sOut ); + break; + + case Ucs2: + throw Bu::ExceptionBase("Ucs2 not supported yet."); + break; + + case Ucs4: + throw Bu::ExceptionBase("Ucs4 not supported yet."); + break; + + case GuessEncoding: + throw Bu::ExceptionBase( + "GuessEncoding is incompatible with encoding."); + break; + + } +} + +void Bu::UtfString::writeUtf8( Bu::Stream &sOut ) +{ + int iPos = 0; + while( iPos < aData.getSize() ) + { + uint8_t uByte; + Bu::UtfChar chr = nextChar( iPos ); + if( chr >= 0x010000 ) + { + // Four bytes + // 111 111111 111111 111111 + uByte = (chr>>18)|0xF0; + sOut.write( &uByte, 1 ); + uByte = ((chr>>12)&0x3F)|0x80; + sOut.write( &uByte, 1 ); + uByte = ((chr>>6)&0x3F)|0x80; + sOut.write( &uByte, 1 ); + uByte = (chr&0x3F)|0x80; + sOut.write( &uByte, 1 ); + } + else if( chr >= 0x800 ) + { + // Three bytes + // 1111 111111 111111 + uByte = (chr>>12)|0xE0; + sOut.write( &uByte, 1 ); + uByte = ((chr>>6)&0x3F)|0x80; + sOut.write( &uByte, 1 ); + uByte = (chr&0x3F)|0x80; + sOut.write( &uByte, 1 ); + } + else if( chr >= 0x80 ) + { + // Two bytes + // 11111 111111 + uByte = (chr>>6)|0xC0; + sOut.write( &uByte, 1 ); + uByte = (chr&0x3F)|0x80; + sOut.write( &uByte, 1 ); + } + else + { + // One byte + uByte = chr; + sOut.write( &uByte, 1 ); + } + } +} +/* +void Bu::UtfString::writeUtf16( Bu::Stream &sOut ) +{ +} +*/ +void Bu::UtfString::writeUtf16be( Bu::Stream &sOut ) +{ +#if BYTE_ORDER == BIG_ENDIAN + uint16_t iTmp = 0xFEFF; // Byte Order Marker + sOut.write( &iTmp, 2 ); + for( Array::iterator i = aData.begin(); i; i++ ) + { + iTmp = *i; + sOut.write( &iTmp, 2 ); + } +#else + uint16_t iTmp = 0xFEFF; // Byte Order Marker + iTmp = (iTmp>>8) | (iTmp<<8); + sOut.write( &iTmp, 2 ); + for( Array::iterator i = aData.begin(); i; i++ ) + { + iTmp = *i; + iTmp = (iTmp>>8) | (iTmp<<8); + sOut.write( &iTmp, 2 ); + } +#endif +} + +void Bu::UtfString::writeUtf16le( Bu::Stream &sOut ) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + uint16_t iTmp = 0xFEFF; // Byte Order Marker + sOut.write( &iTmp, 2 ); + for( Array::iterator i = aData.begin(); i; i++ ) + { + iTmp = *i; + sOut.write( &iTmp, 2 ); + } +#else + uint16_t iTmp = 0xFEFF; // Byte Order Marker + iTmp = (iTmp>>8) | (iTmp<<8); + sOut.write( &iTmp, 2 ); + for( Array::iterator i = aData.begin(); i; i++ ) + { + iTmp = *i; + iTmp = (iTmp>>8) | (iTmp<<8); + sOut.write( &iTmp, 2 ); + } +#endif +} + +void Bu::UtfString::writeUtf32be( Bu::Stream &sOut ) +{ +#if BYTE_ORDER == BIG_ENDIAN + uint32_t iTmp = 0xFEFF; // Byte Order Marker + sOut.write( &iTmp, 4 ); + int i = 0; + while( i < aData.getSize() ) + { + iTmp = nextChar( i ); + sOut.write( &iTmp, 4 ); + } +#else + uint32_t iTmp = 0xFEFF; // Byte Order Marker + iTmp = (iTmp>>24)|(iTmp<<24)|((iTmp&0xff0000)>>8)|((iTmp&0xff00)<<8); + sOut.write( &iTmp, 4 ); + int i = 0; + while( i < aData.getSize() ) + { + iTmp = nextChar( i ); + iTmp = (iTmp>>24)|(iTmp<<24)|((iTmp&0xff0000)>>8)|((iTmp&0xff00)<<8); + sOut.write( &iTmp, 4 ); + } +#endif +} + +void Bu::UtfString::writeUtf32le( Bu::Stream &sOut ) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + uint32_t iTmp = 0xFEFF; // Byte Order Marker + sOut.write( &iTmp, 4 ); + int i = 0; + while( i < aData.getSize() ) + { + iTmp = nextChar( i ); + sOut.write( &iTmp, 4 ); + } +#else + uint32_t iTmp = 0xFEFF; // Byte Order Marker + iTmp = (iTmp>>24)|(iTmp<<24)|((iTmp&0xff0000)>>8)|((iTmp&0xff00)<<8); + sOut.write( &iTmp, 4 ); + int i = 0; + while( i < aData.getSize() ) + { + iTmp = nextChar( i ); + iTmp = (iTmp>>24)|(iTmp<<24)|((iTmp&0xff0000)>>8)|((iTmp&0xff00)<<8); + sOut.write( &iTmp, 4 ); + } +#endif +} + +Bu::UtfChar Bu::UtfString::get( int iIndex ) +{ + return nextChar( iIndex ); +} + +Bu::UtfChar Bu::UtfString::nextChar( int &iIndex ) +{ + Bu::UtfChar i = aData[iIndex++]; + switch( i&0xFC00 ) + { + case 0xD800: + return (((i&0x3FF)<<10) | ((aData[iIndex++]&0x3FF)))+0x10000; + + case 0xDC00: + return (((aData[iIndex-2]&0x3FF)<<10) | ((i&0x3FF)))+0x10000; + + default: + return i; + } +} + +void Bu::UtfString::debug() +{ + sio << "Raw Utf16: "; + for( int i = 0; i < aData.getSize(); i++ ) + { + if( i > 0 ) + sio << ", "; + sio << "0x" << Fmt::hex() << aData[i]; + } + sio << sio.nl; + sio << "Code Points: "; + for( int i = 0; i < aData.getSize(); i++ ) + { + if( i > 0 ) + sio << ", "; + sio << "0x" << Fmt::hex() << nextChar( i ); + } + sio << sio.nl; +} +/* +void Bu::UtfString::debugUtf8( const Bu::String &sUtf8 ) +{ + static uint8_t lmask[8] = { + 0x00, + 0x01, + 0x03, + 0x07, + 0x0f, + 0x1f, + 0x3f, + 0x7f + }; + for( Bu::String::const_iterator i = sUtf8.begin(); i; i++ ) + { + if( i != sUtf8.begin() ) + sio << ", "; + if( ((int)(uint8_t)*i)&0x80 ) + { +// sio << "Flag byte: " << Bu::Fmt().radix(2).width(8).fill('0') +// << (int)(uint8_t)*i << sio.nl; + int iBytes = 1; + for(; (((uint8_t)(*i))<= 1; iBytes-- ) + { +// sio << "iBytes = " << iBytes << ", shift = " << (6*(iBytes-1)) +// << sio.nl; +// sio << "next: " << Bu::Fmt().radix(2).width(8).fill('0') +// << (int)(uint8_t)*i << sio.nl +// << "mask: " << Bu::Fmt().radix(2).width(8).fill('0') +// << (int)lmask[6] << sio.nl; + i++; + uPt |= ((*i)&lmask[6])<<(6*(iBytes-1)); + } + sio << uPt; +// sio << " (" << Bu::Fmt( 8, 2 ).fill('0') +// << uPt << ")"; + } + else + { + sio << (int)((uint8_t)*i); + } + } + sio << sio.nl; +} +*/ diff --git a/src/unstable/utfstring.h b/src/unstable/utfstring.h new file mode 100644 index 0000000..477e272 --- /dev/null +++ b/src/unstable/utfstring.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2007-2011 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. + */ + +#ifndef BU_UTF_STRING_H +#define BU_UTF_STRING_H + +#include +#include "bu/array.h" + +namespace Bu +{ + class String; + class Stream; + + /** + * UtfChar isn't actually a character, unicode specifies "code points" not + * characters. The main reason for this is that not all code points define + * usable characters. Some control text directionality, some apply + * properties to other code points which are characters. However, most of + * these distinctions are only important when implementing displays that + * comply with the Unicode standard fully. + */ + typedef uint32_t UtfChar; + + /** + * A unicode string. This class represents a string of unicode code points. + * Every character in unicode can be represented with 21 bits, but we don't + * have a datatype that's 24 bits long, so we return all code points as a + * 32 bit unsigned value represented by Bu::UtfChar. However, the UtfString + * class, for efficiency purposes doesn't store 32 bit values internally. + * It represents all code points in the native utf16 encodeng. This means + * that it may be very difficult to quickly determine the length of a + * UtfString in code points. Unlike many Unicode handling systems, this + * one actually works with complete code points. When using this class you + * don't ever have to know about the inner workings of the different + * encoding schemes. All of the data is dealt with as whole code points. + * + * As an aside, this means that when encoding a UtfString to a Utf16 + * encoding that matches your archetecture this operation will be very + * fast since it will effectively be a raw dump of the internal data + * structures. However, it is highly reccomended that you DO NOT use the + * little endian encodings if you can possibly avoid it. They are not + * reccomended by the Unicode Consortium and are mainly supported as a + * means of communicating with other systems that encode their data + * incorrectly. That said, whenever UtfString encodes the contained string + * it always includes a BOM at the begining (the byte order marker) so that + * proper byte order can be easily determined by the program reading the + * data. + * + *@todo Investigate http://www.unicode.org/reports/tr6/ for compression. + */ + class UtfString + { + public: + enum Encoding + { + Utf8, + Utf16, + Utf16be, + Utf16le, + Utf32, + Utf32be, + Utf32le, + Ucs2, + Ucs4, + GuessEncoding + }; + + UtfString(); + UtfString( const Bu::String &sInput, Encoding eEnc=Utf8 ); + virtual ~UtfString(); + + class iterator + { + private: + iterator( UtfString *pSrc, int iCodePos ) : + pSrc( pSrc ), iCodePos( iCodePos ) + { + } + + public: + iterator() : + pSrc( NULL ), iCodePos( 0 ) + { + } + + UtfChar operator*() + { + if( !pSrc ) + throw Bu::ExceptionBase("invalid UtfString::iterator dereferenced."); + return pSrc->nextChar( iCodePos ); + } + + private: + UtfString *pSrc; + int iCodePos; + }; + + /** + * Append a UtfChar (A unicode code point) to the string. This can be + * any valid code point, and is just the value of the code point, no + * encoding necessary. + */ + void append( UtfChar ch ); + + /** + * Set the value of the entire string based on the given input and + * encoding. The default encoding is Utf8, which is compatible with + * 7-bit ascii, so it's a great choice for setting UtfStrings from + * string literals in code. + */ + void set( const Bu::String &sInput, Encoding eEnc=Utf8 ); + + /** + * This encodes the UtfString in the given encoding and outputs it to + * the provided stream. all Utf16 and Utf32 encodings will have the + * correct BOM (byte order marker) at the begining. + */ + void write( Bu::Stream &sOut, Encoding eEnc=Utf8 ); + + /** + * This encodes the UtfString in the given encoding and returns it as + * a binary Bu::String. Like write, this also includes the proper BOM + * at the begining. + */ + Bu::String get( Encoding eEnc=Utf8 ); + + void debug(); + + /** + * This may or may not stick around, given an index, this returns a + * codepoint, however there isn't necesarilly a 1:1 ratio between + * indexes and code points. + */ + UtfChar get( int iIndex ); + + /** + * This is what to use if you want to iterate through a section of the + * UtfString and you want to use a numerical index. In most cases it + * will be much easier to use an iterator, though. Given an index this + * will return the codepoint at that position and increment iIndex an + * appropriate amount for it to point to the next code point. + */ + UtfChar nextChar( int &iIndex ); + + private: + void append16( uint16_t i ) { aData.append( i ); } + + void setUtf8( const Bu::String &sInput ); + void setUtf16( const Bu::String &sInput ); + void setUtf16be( const Bu::String &sInput ); + void setUtf16le( const Bu::String &sInput ); + void setUtf32( const Bu::String &sInput ); + void setUtf32be( const Bu::String &sInput ); + void setUtf32le( const Bu::String &sInput ); + + void writeUtf8( Bu::Stream &sOut ); + void writeUtf16be( Bu::Stream &sOut ); + void writeUtf16le( Bu::Stream &sOut ); + void writeUtf32be( Bu::Stream &sOut ); + void writeUtf32le( Bu::Stream &sOut ); + + private: + Bu::Array aData; + int iRawLen; + int iCharLen; + }; +}; + +#endif diff --git a/src/unstable/uuid.cpp b/src/unstable/uuid.cpp new file mode 100644 index 0000000..088450b --- /dev/null +++ b/src/unstable/uuid.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2007-2011 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/uuid.h" +#include "bu/file.h" +#include "bu/formatter.h" +#include "bu/membuf.h" +#include + +Bu::Uuid::Uuid() +{ + clear(); +} + +Bu::Uuid::Uuid( const Uuid &src ) +{ + memcpy( data, src.data, 16 ); +} + +Bu::Uuid::Uuid( const Bu::String &sSrc ) +{ + if( sSrc.getSize() == 16 ) + { + memcpy( data, sSrc.getStr(), 16 ); + } + else if( sSrc.getSize() == 36 ) + { + // Parse it + set( sSrc ); + } +} + +Bu::Uuid::~Uuid() +{ +} + +Bu::String Bu::Uuid::toRawString() const +{ + return Bu::String( (char *)data, 16 ); +} + +Bu::String Bu::Uuid::toString() const +{ + Bu::MemBuf mb; + Bu::Formatter f( mb ); + + for( int j = 0; j < 16; j++ ) + { + if( j == 4 || j == 6 || j == 8 || j == 10 ) + f << '-'; + f << Bu::Fmt::hex(2).caps(false) << (unsigned int)data[j]; + } + + return mb.getString(); +} + +Bu::String Bu::Uuid::toUrn() const +{ + return "urn:uuid:" + toString(); +} + +int Bu::Uuid::getVersion() +{ + return (data[6]&((8|4|2|1)<<4))>>4; +} + +#define msb( i ) (1<<(7-i)) + +void Bu::Uuid::clear() +{ + data[7] = msb(0); +} + +Bu::Uuid Bu::Uuid::gen() +{ + Bu::File fIn( "/proc/sys/kernel/random/uuid", Bu::File::Read ); + char dat[36]; + fIn.read( dat, 36 ); + Uuid id; + id.set( dat ); + return id; +} + +void Bu::Uuid::set( const Bu::String &sSrc ) +{ + const char *dat = sSrc.getStr(); + int iNibble = 0; + memset( data, 0, 16 ); + for( int j = 0; j < 36; j++ ) + { + if( dat[j] == '-' ) + continue; + unsigned char c = (dat[j]>='0'&&dat[j]<='9')?(dat[j]-'0'):(dat[j]-'a'+10); + data[iNibble/2] |= (iNibble%2==0)?(c<<4):(c); + iNibble++; + } +} + +bool Bu::Uuid::operator==( const Uuid &rhs ) const +{ + return memcmp( data, rhs.data, 16 ) == 0; +} + +template<> uint32_t Bu::__calcHashCode( const Bu::Uuid &k ) +{ + return __calcHashCode( k.toRawString() ); +} + +template<> bool Bu::__cmpHashKeys( const Bu::Uuid &a, const Bu::Uuid &b ) +{ + return a == b; +} + diff --git a/src/unstable/uuid.h b/src/unstable/uuid.h new file mode 100644 index 0000000..261f653 --- /dev/null +++ b/src/unstable/uuid.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2007-2011 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. + */ + +#ifndef BU_UUID_H +#define BU_UUID_H + +#include "bu/string.h" + +namespace Bu +{ + class Uuid + { + public: + Uuid(); + Uuid( const Uuid &src ); + Uuid( const Bu::String &sSrc ); + virtual ~Uuid(); + + Bu::String toRawString() const; + Bu::String toString() const; + Bu::String toUrn() const; + + int getVersion(); + + static Uuid gen(); + static Uuid genV1(); + static Uuid genV2(); + static Uuid genV3(); + static Uuid genV4(); + static Uuid genV5(); + + void clear(); + + bool operator==( const Uuid &rhs ) const; + + private: + void set( const Bu::String &sSrc ); + unsigned char data[16]; + }; + + template + uint32_t __calcHashCode( const T &k ); + + template + bool __cmpHashKeys( const T &a, const T &b ); + + template<> uint32_t __calcHashCode( const Uuid &k ); + template<> bool __cmpHashKeys( + const Uuid &a, const Uuid &b ); +}; + +#endif -- cgit v1.2.3