#ifndef BU_MYRIAD_H #define BU_MYRIAD_H #include "bu/stream.h" #include "bu/exceptionbase.h" #include "bu/mutex.h" #include "bu/array.h" #include "bu/hash.h" namespace Bu { class MyriadStream; subExceptionDeclBegin( MyriadException ) enum { emptyStream, invalidFormat, badVersion, invalidWordSize, noSuchStream, streamExists, invalidStreamId, protectedStream, invalidParameter, invalidBackingStream, badMode, }; subExceptionDeclEnd(); class Myriad { public: typedef int32_t StreamId; enum Mode { None = 0x00, // Flags 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 }; public: /** * Open existing Myriad stream, or initialize a new one if it doesn't * exist. * * Myriad format V0 * 0 - 3: Myriad_MAGIC_CODE (0ad3fa84) * 4 - 4: Version Id (1) * 5 - 5: Bits per integer (32) * 6 - 9: Block size in bytes. * 10 - 13: Number of streams. * 14 - ...: Stream Data * * Stream Data: * 0 - 3: Stream Id * 4 - 7: Size of stream in bytes * 8 - ...: List of blocks in stream (4 bytes per block */ Myriad( Bu::Stream &rBacking, int32_t iBlockSize=-1, int32_t iPreallocateBlocks=-1 ); virtual ~Myriad(); MyriadStream create( Mode eMode, int32_t iPreallocateBytes=-1 ); MyriadStream open( StreamId iStream, Mode eMode ); bool erase( StreamId iStream ); bool setSize( StreamId iStream, int32_t iNewSize ); Bu::String getLocation() const; private: bool loadMyriad(); void createMyriad( int32_t iBlockSize, int32_t iPreallocateBlocks ); int32_t allocateBlock(); void releaseBlock( int32_t iBlockId, bool bBlank=true ); void blankBlock( int32_t iBlockId ); void openStream( StreamId id ); void closeStream( StreamId id ); /** * Block restricted read, it will not read past the end of the block * that iStart places it in. */ int32_t blockRead( int32_t iBlock, int32_t iStart, void *pTarget, int32_t iSize ); /** * Block restricted write, it will not write past the end of the block * that iStart places it in. If this returns a non-zero number it's an * indication that you need to allocate a new block. */ int32_t blockWrite( int32_t iBlock, int32_t iStart, const void *pTarget, int32_t iSize ); public: /** * Bridge/communication/tracking class for individual Myriad streams. * Not for general use, this is used by Myriad and MyriadStream to * control access. */ class Stream { friend Bu::Myriad; private: Stream( Myriad &rParent, StreamId iStream, int32_t iSize ); virtual ~Stream(); public: int32_t getSize() const; int32_t getBlockSize() const; StreamId getStreamId() const; int32_t getOpenCount() const; void setSize( int32_t iNewSize ); int32_t read( int32_t iStart, void *pTarget, int32_t iSize ); int32_t write( int32_t iStart, const void *pTarget, int32_t iSize ); Bu::String getLocation() const; /** * Doesn't actually open, just increments the open counter. * If the open counter is non-zero then at least one stream has * a lock on this stream. */ void open(); /** * Doesn't actually close, just decrements the open counter. *@returns true if there are still handles open, false if no * streams have a lock. */ bool close(); private: mutable Bu::Mutex mAccess; Myriad &rParent; StreamId iStream; int32_t iSize; Bu::Array aBlocks; int32_t iOpenCount; bool bStructureChanged; }; private: typedef Bu::Hash StreamHash; typedef Bu::List IndexList; mutable Bu::Mutex mAccess; mutable Bu::Mutex mBacking; Bu::Stream &rBacking; int32_t iBlockSize; int32_t iBlockCount; bool bIsNewStream; StreamHash hStream; IndexList lFreeBlocks; }; }; #endif