// #define CDB_DEBUG // comment this macro to turn various error checking options off NAMESPACE_UPP #ifdef CDB_DEBUG #define CHECK_INTEGRITY() CheckIntegrity() #define CHECK_AVAILABILITY(a, b) CheckAvailability(a, b) #define CDB_ASSERT(x) ASSERT(x) #define CDB_CALL(x) x #else #define CHECK_INTEGRITY() #define CHECK_AVAILABILITY(a, b) #define CDB_ASSERT(x) #define CDB_CALL(x) #endif #define CRASH() __asm int 3 #define CDB_TIMING(n) RTIMING(n) typedef int foff_t; typedef int dfid_t; class DataFile : Moveable { public: typedef DataFile CLASSNAME; DataFile(); ~DataFile(); bool Open(const char *filename, bool read_only = false); bool Create(const char *filename); bool Close(); bool SaveAs(const char *new_file_name); bool IsReadOnly() const { return read_only; } bool IsOpen() const { return stream.IsOpen(); } const String& GetFileName() const { return filename; } Time GetLastWriteTime() const; int64 GetFileSize() const { return stream.GetSize(); } int GetOverhead() const; bool Compact(const char *backup_file_name = 0); void Commit(dfid_t id); void Rollback(dfid_t id); void BeginBlock(); void EndBlock(); bool Import(Stream& stream); void Export(Stream& stream); void SetCache(int large_block_size, int small_cache_size, int large_cache_size); int GetLargeBlockSize() const { return large_block_size; } int GetLargeCacheSize() const { return large_cache.size; } int GetSmallCacheSize() const { return small_cache.size; } int GetCount() const { return index.GetCount(); } void SetCount(int count); bool IsValidIndex(int object) const { return object >= 0 && object < GetCount(); } int GetLength(int object) const { return write_objects[object].length; } bool IsEmpty(int object) const { return GetLength(object) == 0; } String Get(int object); String operator [] (int object) { return Get(object); } void Set(int object, const String& data, dfid_t id); int Add(dfid_t id); void Remove(int object, dfid_t id); bool IsVoid(int object) const { return write_objects[object].length < 0; } void CheckIntegrity(); void CheckAvailability(foff_t start, int length); Vector FindObjects(foff_t start, int length); void DumpObjects(const Vector& index, bool dump_data = false); static void FileTest(); public: enum { LOG2_OFFSET_BYTES = 2, OFFSET_BYTES = 1 << (int)LOG2_OFFSET_BYTES, LOG2_BLOCK_BYTES = 12, BLOCK_BYTES = 1 << (int)LOG2_BLOCK_BYTES, LOG2_BLOCK_OFFSETS = LOG2_BLOCK_BYTES - LOG2_OFFSET_BYTES, BLOCK_OFFSETS = 1 << (int)LOG2_BLOCK_OFFSETS, LOG2_OBJECT_BYTES = LOG2_OFFSET_BYTES + 1, OBJECT_BYTES = 1 << (int)LOG2_OBJECT_BYTES, LOG2_BLOCK_OBJECTS = LOG2_BLOCK_BYTES - LOG2_OBJECT_BYTES, BLOCK_OBJECTS = 1 << (int)LOG2_BLOCK_OBJECTS, MIN_VERSION = 1, VERSION = 1, MAX_VERSION = 1, LOG2_GRANULARITY = 2, GRANULARITY = 1 << (int)LOG2_GRANULARITY, // defaults LARGE_BLOCK_SIZE = 4 * BLOCK_BYTES, SMALL_CACHE_SIZE = 1 << 20, LARGE_CACHE_SIZE = 1 << 20, }; private: struct Object { Object(foff_t offset = 0, int length = -1) : offset(offset), length(length) {} bool IsAlive() const { return offset && length > 0; } static int PtrLessLength(const Object *object, int length) { return object -> length < length; } static int LessOffset(const Object& object, foff_t offset) { return object.offset < offset; } static bool PtrLess(const Object *a, const Object *b) { return (a -> length != b -> length ? a -> length < b -> length : a -> offset < b -> offset); } static bool OffsetLess(const Object& a, const Object& b) { return a.offset < b.offset; } foff_t offset; int length; }; private: bool StreamHeader(bool write); bool ReadIndex(); String ReadBlock(foff_t offset, int length); foff_t AllocatePart(int block_size); void FreePart(foff_t offset, int length); foff_t WriteBlock(const String& data); void EnumBlock(Array& dest, foff_t offset, int length); void FreeBlock(foff_t offset, int length); void FreeBlock(const Object& o) { if(o.IsAlive()) FreeBlock(o.offset, o.length); } void FlushSmallCacheFor(dfid_t id); void FlushSmallCache() { FlushSmallCacheFor(0); } void FlushLargeCacheFor(dfid_t id); void FlushLargeCache() { FlushLargeCacheFor(0); } void FlushIndex(); static int GetBlockSize(int length); void Read(void *dest, foff_t offset, int length); void Write(const void *src, foff_t offset, int length); void ThrowRead() const; void ThrowWrite() const; void CreateFreeList(); void ReorderFreeItem(int pos); void Clear(); void CommitFile(); private: struct Header { Header(); bool Serialize(Stream& stream, bool write); enum { TOTAL = 256 }; char tag[128]; int version; int object_count; foff_t index_offset; }; friend struct Header; class Cache { public: Cache(int size); void Clear(); int Find(int object) const { return data.Find(object); } const String& operator [] (int i) const { return data[i]; } void Put(int object, const String& value, bool write); void Remove(int object); void RemoveAbove(int object); public: Callback WhenFlush; VectorMap data; Index dirty; int size; int total; }; FileStream stream; String filename; bool read_only; Header header; int large_block_size; Array index; Index write_id; Index write_index; Array write_objects; int block_level; Segtor free_upon_commit; Index free_objects; Array free_list; Vector free_list_index; foff_t free_offset; mutable Cache large_cache; mutable Cache small_cache; }; class ObjectExc : public Exc { public: ObjectExc(const String& s) { String::operator = (s); } }; template inline void CopyBackward(T *dst, const T *src, const T *lim) { while(src > lim) *--dst = *--src; } END_UPP_NAMESPACE