#include "TCoreDb.h" NAMESPACE_UPP static const char tag[] = "CDF: Cybex Data File version 1.0.\r\n" "Copyright ©2000 Cybex Development, spol. s r.o.\r\n" "\x1A"; DataFile::Header::Header() : version(VERSION) , object_count(0) , index_offset(0) { } bool DataFile::Header::Serialize(Stream& stream, bool write) { if(write) { stream.SetStoring(); memset(tag, 0, sizeof(tag)); CDB_ASSERT(sizeof(UPP::tag) < sizeof(tag)); strcpy(tag, UPP::tag); version = VERSION; } else stream.SetLoading(); stream.Seek(0); stream.SerializeRaw((byte *)tag, sizeof(UPP::tag)); if(!write && strcmp(tag, UPP::tag)) return false; StreamIL(stream, version); if(version < MIN_VERSION || version > MAX_VERSION) return false; StreamIL(stream, object_count); StreamIL(stream, index_offset); if(write) stream.Put(int(0), int(TOTAL - stream.GetPos())); return true; } DataFile::Cache::Cache(int size) : size(size) , total(0) { } void DataFile::Cache::Clear() { total = 0; data.Clear(); dirty.Clear(); } void DataFile::Cache::Put(int object, const String& value, bool write) { CDB_ASSERT(dirty.GetCount() <= data.GetCount()); int i = Find(object); total += value.GetLength() - (i >= 0 ? data[i].GetLength() : 0); if(total > size) { Remove(object); WhenFlush(); Clear(); total = value.GetLength(); data.Add(object, value); } else if(i >= 0) data[i] = value; else data.Add(object, value); if(write) dirty.FindAdd(object); CDB_ASSERT(dirty.GetCount() <= data.GetCount()); } void DataFile::Cache::Remove(int object) { CDB_ASSERT(dirty.GetCount() <= data.GetCount()); int i; if((i = data.Find(object)) >= 0) { total -= data[i].GetLength(); data.Remove(i); } if((i = dirty.Find(object)) >= 0) dirty.Remove(i); CDB_ASSERT(dirty.GetCount() <= data.GetCount()); } void DataFile::Cache::RemoveAbove(int object) { CDB_ASSERT(dirty.GetCount() <= data.GetCount()); int i; for(i = data.GetCount(); --i >= 0;) if(data.GetKey(i) >= object) { total -= data[i].GetLength(); data.Remove(i); } for(i = dirty.GetCount(); --i >= 0;) if(dirty[i] >= object) dirty.Remove(i); CDB_ASSERT(dirty.GetCount() <= data.GetCount()); } DataFile::DataFile() : large_block_size(LARGE_BLOCK_SIZE) , large_cache(LARGE_CACHE_SIZE) , small_cache(SMALL_CACHE_SIZE) , free_offset(Header::TOTAL) { large_cache.WhenFlush = THISBACK(FlushLargeCache); small_cache.WhenFlush = THISBACK(FlushSmallCache); } DataFile::~DataFile() { Close(); } bool DataFile::Open(const char *_filename, bool _read_only) { Close(); filename = GetFullPath(_filename); read_only = _read_only; if(!stream.Open(filename, read_only ? FileStream::READ : FileStream::READWRITE | FileStream::NOWRITESHARE)) return false; block_level = 0; if(!ReadIndex()) { Close(); return false; } // CHECK_INTEGRITY(); return true; } bool DataFile::Create(const char *_filename) { Close(); filename = GetFullPath(_filename); read_only = false; block_level = 0; if(!stream.Open(filename, FileStream::CREATE)) return false; header = Header(); StreamHeader(true); return true; } bool DataFile::Close() { if(!IsOpen()) return true; bool error = false; Clear(); stream.Close(); return !error && !stream.IsError(); } void DataFile::Clear() { small_cache.Clear(); large_cache.Clear(); index.Clear(); write_id.Clear(); write_index.Clear(); write_objects.Clear(); free_objects.Clear(); free_list.Clear(); free_list_index.Clear(); } Time DataFile::GetLastWriteTime() const { ASSERT(IsOpen()); #ifdef PLATFORM_WIN32 FileTime ft; if(!GetFileTime(stream.GetHandle(), 0, 0, &ft)) return Null; return ft; #endif #ifdef PLATFORM_POSIX struct stat sb; if(fstat(stream.GetHandle(), &sb)) return Null; return FileTime(sb.st_mtime); #endif//PLATFORM_POSIX } int DataFile::GetOverhead() const { foff_t size = Header::TOTAL + GetBlockSize(index.GetCount() * OBJECT_BYTES); for(int i = 0; i < index.GetCount(); i++) if(index[i].length > 0) size += GetBlockSize(index[i].length); return iscale(max(0, (int)stream.GetSize() - size), 100, max(size, 1)); } bool DataFile::SaveAs(const char *new_file_name) { Commit(0); DataFile nf; if(!nf.Create(new_file_name)) return false; nf.SetCount(index.GetCount()); for(int i = 0; i < index.GetCount(); i++) nf.Set(i, Get(i), 0); nf.Commit(0); if(!nf.Close()) { DeleteFile(new_file_name); return false; } Close(); return Open(new_file_name); } bool DataFile::Compact(const char *backup_file_name) { if(read_only) { NEVER(); return false; } String temp = ForceExt(filename, ".$$$"); DataFile aux; if(!aux.Create(temp)) return false; aux.SetCount(index.GetCount()); for(int i = 0; i < index.GetCount(); i++) aux.Set(i, Get(i), 0); aux.Commit(0); if(!aux.Close()) { DeleteFile(temp); return false; } if(backup_file_name && !FileCopy(temp, backup_file_name)) return false; Close(); FileDelete(filename); return FileMove(temp, filename) && Open(filename); } void DataFile::BeginBlock() { block_level++; } void DataFile::EndBlock() { ASSERT(block_level > 0); if(--block_level <= 0) CommitFile(); } void DataFile::CommitFile() { ASSERT(block_level <= 0); int start_msecs = msecs(); LOG("DataFile::CommitFile"); if(header.index_offset && header.object_count) free_upon_commit.Add(Object(header.index_offset, header.object_count * OBJECT_BYTES)); LOG("DataFile::CommitFile / FlushIndex: t + " << msecs(start_msecs)); FlushIndex(); LOG("DataFile::CommitFile / FileFlush: t + " << msecs(start_msecs)); FileFlush(stream); LOG("DataFile::CommitFile / StreamHeader: t + " << msecs(start_msecs)); StreamHeader(true); LOG("DataFile::CommitFile / FileFlush: t + " << msecs(start_msecs)); FileFlush(stream); // CHECK_INTEGRITY(); LOG("DataFile::CommitFile / FreeBlock: t + " << msecs(start_msecs)); while(!free_upon_commit.IsEmpty()) FreeBlock(free_upon_commit.Pop()); LOG("//DataFile::CommitFile: t + " << msecs(start_msecs)); // CHECK_INTEGRITY(); } void DataFile::Commit(dfid_t id) { CDB_TIMING("DataFile::Commit"); LOG("DataFile::Commit"); int start_msecs = msecs(); CDB_ASSERT(IsOpen()); if(read_only || id && write_id.Find(id) < 0) return; LOG("DataFile::Commit / FlushSmallCache: t + " << msecs(start_msecs)); FlushSmallCacheFor(id); LOG("DataFile::Commit / FlushLargeCache: t + " << msecs(start_msecs)); FlushLargeCacheFor(id); LOG("DataFile::Commit / live objects: t + " << msecs(start_msecs)); if(id == 0) { // commit everything for(int i = 0; i < write_index.GetCount(); i++) { int x = write_index[i]; CDB_ASSERT(x >= 0 && x < index.GetCount()); Object& o = index[x]; if(o.IsAlive()) free_upon_commit.Add(o); o = write_objects[x]; CDB_ASSERT(o.length <= 0 || o.offset >= Header::TOTAL); } write_id.Clear(); write_index.Clear(); } else { Vector to_remove; bool to_sort = false; for(int i = write_id.Find(id); i >= 0; i = write_id.FindNext(i)) { int x = write_index[i]; CDB_ASSERT(x >= 0 && x < index.GetCount()); Object& o = index[x]; if(o.IsAlive()) free_upon_commit.Add(o); o = write_objects[x]; to_sort = to_sort || (!to_remove.IsEmpty() && i < to_remove.Top()); to_remove.Add(i); } if(to_sort) Sort(to_remove); while(!to_remove.IsEmpty()) { int i = to_remove.Pop(); write_id.Remove(i); write_index.Remove(i); } } LOG("DataFile::Commit / CommitFile: t + " << msecs(start_msecs)); if(block_level <= 0) CommitFile(); LOG("//DataFile::Commit: t + " << msecs(start_msecs)); } void DataFile::Rollback(dfid_t id) { CHECK_INTEGRITY(); if(id == 0) { // full rollback for(int i = 0; i < write_index.GetCount(); i++) { int x = write_index[i]; FreeBlock(write_objects[x]); write_objects[x] = index[x]; } write_id.Clear(); write_index.Clear(); small_cache.Clear(); large_cache.Clear(); } else { Vector to_remove; bool to_sort = false; for(int i = write_id.Find(id); i >= 0; i = write_id.FindNext(i)) { int x = write_index[i]; small_cache.Remove(x); large_cache.Remove(x); CDB_ASSERT(x >= 0 && x < index.GetCount()); FreeBlock(write_objects[x]); write_objects[x] = index[x]; to_sort = to_sort || (!to_remove.IsEmpty() && i < to_remove.Top()); to_remove.Add(i); } if(to_sort) Sort(to_remove); while(!to_remove.IsEmpty()) { int i = to_remove.Pop(); write_id.Remove(i); write_index.Remove(i); } } CHECK_INTEGRITY(); } bool DataFile::Import(Stream& stream) { String line; for(;;) { const char *p = line; while(*p && *p != '#' && *p++ != '[') ; if(*p == ']') return true; // end of import if(*p == '=') { // item count int count = stou(p + 1); if(count < 0) return false; SetCount(count); } else if(*p == '-') { // delete item int item = stou(p + 1); if(IsValidIndex(item)) Remove(item, 0); } else if(IsDigit(*p)) { // item value int index = stou(p, &p); if(!IsValidIndex(index) || *p++ != ']') continue; String data; while(*p != '[') { while(*p && (byte)*p <= ' ') p++; byte c1, c2; while((c1 = ctoi(p[0])) < 16 && (c2 = ctoi(p[1])) < 16) { data.Cat(c1 * 16 + c2); p += 2; } if(stream.IsEof()) break; line = stream.GetLine(); p = line; } Set(index, data, 0); continue; } if(stream.IsEof()) return true; line = stream.GetLine(); } } void DataFile::Export(Stream& stream) { stream.PutLine("# Cybex DataFile export version 1.0"); stream.Put("# Export time: "); stream.Put(Format(GetSysTime())); stream.PutCrLf(); int c = GetCount(); stream.Put(Format("[=%d]", c)); stream.PutCrLf(); for(int i = 0; i < c; i++) if(!IsVoid(i)) { String item = Get(i); stream.Put(Format("[%d]", i)); const char *p = item.Begin(), *e = item.End(); if(p == e) stream.PutCrLf(); else do { const char *b = p + min(e - p, 32); stream.Put('\t'); static const char hex[] = "0123456789abcdef"; while(p < b) { stream.Put(hex[(*p >> 4) & 15]); stream.Put(hex[*p & 15]); p++; } stream.PutCrLf(); } while(p < e); } stream.PutLine("[] # end of export"); } void DataFile::SetCache(int lbs, int scs, int lcs) { FlushLargeCache(); FlushSmallCache(); large_block_size = lbs; small_cache.size = scs; large_cache.size = lcs; } void DataFile::SetCount(int count) { int old = GetCount(); if(count < old) { int i, x; for(i = count; i < old; i++) FreeBlock(index[i]); for(i = write_index.GetCount(); --i >= 0;) if((x = write_index[i]) >= count) { // remove item FreeBlock(write_objects[x]); write_id.Remove(i); write_index.Remove(i); } } write_objects.SetCountR(count); index.SetCountR(count); } String DataFile::Get(int object) { // if(object == 313 || object == 80) // __asm int 3 CDB_TIMING("DataFile::Get"); // CHECK_INTEGRITY(); int i; if((i = small_cache.Find(object)) >= 0) return small_cache[i]; if((i = large_cache.Find(object)) >= 0) return large_cache[i]; if(!IsValidIndex(object)) { NEVER(); return String::GetVoid(); } Object& o = write_objects[object]; i = o.length; if(i < 0) return String::GetVoid(); if(i == 0) return Null; CDB_ASSERT(o.offset >= Header::TOTAL); String s = ReadBlock(o.offset, i); Cache& cache = (i >= large_block_size ? large_cache : small_cache); if(i <= cache.size) cache.Put(object, s, false); return s; } void DataFile::Set(int object, const String& data, dfid_t id) { CDB_TIMING("DataFile::Set"); ASSERT(!IsReadOnly()); // CHECK_INTEGRITY(); LOG("Put(" << object << ", " << data.GetLength() << "); stream size = " << stream.GetSize()); if(object < 0) { NEVER(); return; } if(object >= GetCount()) SetCount(object + 1); int w = write_index.Find(object); if(w >= 0) { // object has been already written to - check id equality if(write_id[w] != id) { String s; s << ": nesouhlas ID při zápisu do datového souboru"; throw ObjectExc(s); } Object& o = write_objects[object]; foff_t offset = o.offset; o.offset = 0; if(offset && o.length > 0) FreeBlock(offset, o.length); } else { // add new update entry to write table w = write_index.GetCount(); write_id.Add(id); write_index.Add(object); ASSERT(write_id.GetCount() == write_index.GetCount()); } Object& o = write_objects[object]; o.offset = 0; int l = data.GetLength(); if(l == 0) { // clear item small_cache.Remove(object); large_cache.Remove(object); } else if(l >= large_block_size) { small_cache.Remove(object); if(l <= large_cache.size) large_cache.Put(object, data, true); else o.offset = WriteBlock(data); } else { large_cache.Remove(object); if(l <= small_cache.size) small_cache.Put(object, data, true); else o.offset = WriteBlock(data); } o.length = l; // CHECK_INTEGRITY(); } int DataFile::Add(dfid_t id) { CDB_TIMING("DataFile::Add"); if(!free_objects.IsEmpty()) return free_objects.Pop(); int c = GetCount(); SetCount(c + 1); int w = write_index.Find(c); if(w >= 0) write_id.Set(w, id); return c; } void DataFile::Remove(int object, dfid_t id) { CDB_TIMING("DataFile::Remove"); // CHECK_INTEGRITY(); if(!IsValidIndex(object)) { NEVER(); return; } large_cache.Remove(object); small_cache.Remove(object); int w = write_index.Find(object); if(w >= 0) { if(write_id[w] != id) { String s; s << " " << id << ">: nesouhlas ID při mazání objektu"; throw ObjectExc(s); } Object& o = write_objects[object]; FreeBlock(o); o.length = -1; } else { w = write_index.GetCount(); write_id.Add(id); write_index.Add(object); write_objects[object].length = -1; } // CHECK_INTEGRITY(); } bool DataFile::StreamHeader(bool write) { CDB_ASSERT(IsOpen()); CDB_ASSERT(!write || !read_only); return header.Serialize(stream, write); } bool DataFile::ReadIndex() { if(!StreamHeader(false)) return false; int index_block_length = header.object_count * OBJECT_BYTES; String raw_index = ReadBlock(header.index_offset, index_block_length); if(raw_index.GetLength() != index_block_length) return false; index.SetCount(header.object_count); foff_t sum_length = Header::TOTAL + GetBlockSize(index_block_length) - GRANULARITY + 1; foff_t file_length = (int)stream.GetSize(); for(int i = 0; i < index.GetCount(); i++) { const char *p = raw_index.Begin() + OBJECT_BYTES * i; Object& obj = index[i]; if((obj.length = Peek32le(p + OFFSET_BYTES)) > 0) { obj.offset = Peek32le(p); if(obj.offset < Header::TOTAL || obj.offset >= file_length) return false; // block damaged sum_length += GetBlockSize(obj.length); } } write_objects <<= index; if(sum_length > (foff_t)stream.GetSize()) return false; // file consistence violated CreateFreeList(); return true; } String DataFile::ReadBlock(foff_t offset, int length) { CDB_TIMING("DataFile::ReadBlock"); if(length <= 0) return Null; int total = length; int reserve = length + OFFSET_BYTES - 1; if(length <= BLOCK_BYTES) { // simple storage StringBuffer result(length); Read(result, offset, length); return result; } int buffer[10]; buffer[0] = length; buffer[1] = (length = ((length - 1) >> LOG2_BLOCK_BYTES) + 1) << LOG2_OFFSET_BYTES; int depth = 1; while(length > BLOCK_OFFSETS) { length = ((length - 1) >> LOG2_BLOCK_OFFSETS) + 1; buffer[++depth] = length << LOG2_OFFSET_BYTES; } if(buffer[depth] > reserve) ThrowRead(); StringBuffer result(reserve); char *ptr = result; char *off = ptr + reserve - buffer[depth]; Read(off, offset, buffer[depth]); do { // indirection step int dl = buffer[--depth]; const char *os = off; if(dl > reserve) ThrowRead(); char *od = off = (depth ? ptr + reserve - dl : ptr); while(dl > 0) { foff_t foff = Peek32le(os); os += OFFSET_BYTES; int part = min(dl, BLOCK_BYTES); Read(od, foff, part); od += part; dl -= part; } } while(depth); result.SetCount(total); return result; } void DataFile::EnumBlock(Array& dest, foff_t offset, int length) { CDB_TIMING("DataFile::EnumBlock"); if(length <= 0) return; if(length <= BLOCK_BYTES) { dest.Add(Object(offset, (length + GRANULARITY - 1) & -GRANULARITY)); return; } struct Layer : public Vector { int avail; int count; int index; }; Layer layers[10]; layers[0].count = length; layers[1].count = length = ((length - 1) >> LOG2_BLOCK_BYTES) + 1; layers[1].index = 0; layers[1].SetCount(BLOCK_BYTES); int layer = 1; while(length > BLOCK_OFFSETS) { layer++; layers[layer].count = length = ((length - 1) >> LOG2_BLOCK_OFFSETS) + 1; layers[layer].index = 0; layers[layer].SetCount(BLOCK_BYTES); } int len = ((layers[layer].avail = layers[layer].count) << LOG2_OFFSET_BYTES); Read(layers[layer].Begin(), offset, len); dest.Add(Object(offset, (len + GRANULARITY - 1) & -GRANULARITY)); int current = layer; for(;;) { while(layers[current].index >= layers[current].avail) // finished layer - step up one layer if(++current > layer) return; // done offset = Peek32le(layers[current].Begin() + OFFSET_BYTES * layers[current].index++); if(current == 1) { // final block - add to block list length = min(layers[0].count, BLOCK_BYTES); layers[0].count -= length; dest.Add(Object(offset, (length + GRANULARITY - 1) & -GRANULARITY)); } else { // pointer block - step down one block Layer& l = layers[--current]; l.index = 0; l.avail = min(l.count, BLOCK_OFFSETS); l.count -= l.avail; len = l.avail << LOG2_OFFSET_BYTES; Read(l.Begin(), offset, len); dest.Add(Object(offset, (len + GRANULARITY - 1) & -GRANULARITY)); } } } void DataFile::CheckIntegrity() { CDB_TIMING("DataFile::CheckIntegrity"); int i; //* int f; foff_t start = Header::TOTAL; Array raw; EnumBlock(raw, header.index_offset, header.object_count * OBJECT_BYTES); ASSERT(index.GetCount() == write_objects.GetCount()); for(i = 0; i < index.GetCount(); i++) { Object& o = index[i]; Object& w = write_objects[i]; if(o.IsAlive()) EnumBlock(raw, o.offset, o.length); if(w.IsAlive() && (!o.IsAlive() || o.offset != w.offset || o.length != w.length)) EnumBlock(raw, w.offset, w.length); } Sort(raw, &Object::OffsetLess); for(i = f = 0; i < raw.GetCount() || f < free_list.GetCount();) { if(i >= raw.GetCount() || f < free_list.GetCount() && free_list[f].offset < raw[i].offset) { // free item const Object& free = free_list[f++]; if(free.offset < start) { String dump; dump << "CheckIntegrity error\n" "\n" "free_list[" << (f - 1) << "].offset = " << free.offset << ", end of previous = " << start << " (length = " << free.length << ")\n"; fputs(dump, stderr); NEVER(); } start = free.offset + free.length; } else { const Object& o = raw[i++]; if(o.offset == 0 || o.length <= 0) continue; if(o.offset < start) { // object crash - find object index int i = index.GetCount(); while(--i >= 0 && index[i].offset != o.offset) ; String dump; dump << "CheckIntegrity error\n" "\n" "object[" << i << "].offset = " << o.offset << ", end of previous = " << start << " (length = " << o.length << ")\n"; fputs(dump, stderr); NEVER(); } start = o.offset + o.length; } } //*/ for(i = 1; i < free_list_index.GetCount(); i++) if(!Object::PtrLess(free_list_index[i - 1], free_list_index[i])) { String dump; dump << "CheckIntegrity error\n" "\n" "free_list_index[" << (i - 1) << "] > free_list_index[" << i << "]\n"; fputs(dump, stderr); NEVER(); } } void DataFile::CheckAvailability(foff_t start, int length) { CDB_TIMING("DataFile::CheckAvailability"); if(length <= 0) return; for(int i = 0; i < index.GetCount(); i++) { const Object& o = index[i]; if(o.length > 0 && o.offset) { Array aobj; EnumBlock(aobj, o.offset, o.length); for(int t = 0; t < aobj.GetCount(); t++) VERIFY(aobj[t].offset + aobj[t].length <= start || start + length <= aobj[t].offset); } } } foff_t DataFile::AllocatePart(int block_size) { CDB_TIMING("AllocatePart"); // CHECK_INTEGRITY(); block_size = (block_size + GRANULARITY - 1) & -GRANULARITY; int pos = BinFindIndex(free_list_index, block_size, &Object::PtrLessLength); if(pos < free_list_index.GetCount()) { // found in free list - allocate block Object *p = free_list_index[pos]; foff_t result = p -> offset; if(p -> length < block_size + GRANULARITY) { // allocate the whole block int ind = BinFindIndex(free_list, free_list_index[pos] -> offset, &Object::LessOffset); CDB_ASSERT(ind < free_list.GetCount()); free_list_index.Remove(pos); free_list.Remove(ind); } else { // modify block & move pointer around in free index p -> offset += block_size; p -> length -= block_size; ReorderFreeItem(pos); } // LOG("AllocatePart(" << block_size << ") -> free " << result); return result; } // CHECK_INTEGRITY(); foff_t foff = free_offset; free_offset += block_size; // LOG("AllocatePart(" << block_size << ") -> more " << foff); return foff; } void DataFile::FreePart(foff_t offset, int length) { CDB_TIMING("DataFile::FreePart"); if(length <= 0) return; // CHECK_INTEGRITY(); length = (length + GRANULARITY - 1) & -GRANULARITY; // LOG("FreePart(" << offset << ", " << length << ")"); int pos = BinFindIndex(free_list, offset, &Object::LessOffset); if(pos > 0) { Object& prev = free_list[pos - 1]; if(prev.offset + prev.length == offset) { // connect to previous block int ind = BinFindIndex(free_list_index, &prev, &Object::PtrLess); CDB_ASSERT(ind < free_list_index.GetCount()); int new_length = prev.length + length; if(pos < free_list.GetCount() && free_list[pos].offset == prev.offset + new_length) { // connect with the following block as well int ind2 = BinFindIndex(free_list_index, &free_list[pos], &Object::PtrLess); CDB_ASSERT(ind2 < free_list_index.GetCount() && ind2 != ind); prev.length += free_list[pos].length; free_list.Remove(pos); free_list_index.Remove(ind2); if(ind2 < ind) ind--; } else prev.length = new_length; ReorderFreeItem(ind); return; } } if(pos < free_list.GetCount() && free_list[pos].offset == offset + length) { // join with next Object& next = free_list[pos]; int ind = BinFindIndex(free_list_index, &next, &Object::PtrLess); CDB_ASSERT(ind < free_list_index.GetCount()); next.offset = offset; next.length += length; ReorderFreeItem(ind); return; } // CHECK_INTEGRITY(); // insert new entry Object *p = new Object(offset, length); free_list.Insert(pos, p); int ind = BinFindIndex(free_list_index, p, &Object::PtrLess); free_list_index.Insert(ind, p); // CHECK_INTEGRITY(); } int DataFile::WriteBlock(const String& data) { CDB_TIMING("DataFile::WriteBlock"); int length = data.GetLength(); if(length == 0) return 0; if(length <= BLOCK_BYTES) { // direct write // CHECK_INTEGRITY(); foff_t offset = AllocatePart(length); CHECK_AVAILABILITY(offset, length); Write(data, offset, length); return offset; } Vector< Vector > offset_map; const char *p = data; while(length > 0) { int part = min(length, BLOCK_BYTES); foff_t poff = AllocatePart(part); CHECK_AVAILABILITY(poff, part); Write(p, poff, part); p += part; length -= part; int pix = 0; for(;;) { if(pix >= offset_map.GetCount()) offset_map.Add().Reserve(BLOCK_BYTES); Vector& v = offset_map[pix]; int q = v.GetCount(); v.InsertN(q, OFFSET_BYTES); Poke32le(&v[q], poff); if(v.GetCount() < BLOCK_BYTES) break; poff = AllocatePart(BLOCK_BYTES); CHECK_AVAILABILITY(poff, BLOCK_BYTES); Write(v.Begin(), poff, BLOCK_BYTES); v.Clear(); pix++; } } int t = 0; while(t < offset_map.GetCount() && offset_map[t].IsEmpty()) t++; if(t >= offset_map.GetCount()) ThrowWrite(); // internal error int poff; for(;;) { Vector& v = offset_map[t]; poff = AllocatePart(v.GetCount()); CHECK_AVAILABILITY(poff, v.GetCount()); Write(v.Begin(), poff, v.GetCount()); if(++t >= offset_map.GetCount()) return poff; int q = offset_map[t].GetCount(); offset_map[t].InsertN(q, OFFSET_BYTES); Poke32le(&offset_map[t][q], poff); } } void DataFile::FlushSmallCacheFor(dfid_t id) { CDB_TIMING("DataFile::FlushSmallCache"); // CHECK_INTEGRITY(); CDB_ASSERT(small_cache.dirty.GetCount() <= small_cache.data.GetCount()); dfid_t mask = dfid_t(id ? -1 : 0); for(int i = small_cache.dirty.GetCount(); --i >= 0;) { int b = small_cache.dirty[i], t, w; if(IsValidIndex(b) && (t = small_cache.Find(b)) >= 0 && (w = write_index.Find(b)) >= 0) { if((write_id[w] & mask) == id) { CDB_ASSERT(write_objects[b].length == small_cache[t].GetLength()); write_objects[b].offset = WriteBlock(small_cache[t]); small_cache.dirty.Remove(i); } } else NEVER(); } // CHECK_INTEGRITY(); } void DataFile::FlushLargeCacheFor(dfid_t id) { CDB_TIMING("DataFile::FlushLargeCache"); // CHECK_INTEGRITY(); CDB_ASSERT(large_cache.dirty.GetCount() <= large_cache.data.GetCount()); dfid_t mask = dfid_t(id ? -1 : 0); for(int i = large_cache.dirty.GetCount(); --i >= 0;) { int b = large_cache.dirty[i], t, w; if(IsValidIndex(b) && (t = large_cache.Find(b)) >= 0 && (w = write_index.Find(b)) >= 0) { if((write_id[w] & mask) == id) { CDB_ASSERT(write_objects[b].length == large_cache[t].GetLength()); write_objects[b].offset = WriteBlock(large_cache[t]); large_cache.dirty.Remove(i); } } else NEVER(); } CHECK_INTEGRITY(); } int DataFile::GetBlockSize(int length) { int sum = (length + GRANULARITY - 1) & -GRANULARITY; if(length > BLOCK_BYTES) { length = ((length - 1) >> LOG2_BLOCK_BYTES) + 1; sum += length << LOG2_OFFSET_BYTES; while(length > BLOCK_OFFSETS) { length = ((length - 1) >> LOG2_BLOCK_OFFSETS) + 1; sum += length << LOG2_OFFSET_BYTES; } } return sum; } void DataFile::Read(void *dest, int offset, int length) { CDB_TIMING("DataFile::Read"); int fs = (int)stream.GetSize(); if(offset < 0 || length < 0 || length > fs || offset + length > fs) ThrowRead(); if(length <= 0) return; stream.Seek(offset); if(!stream.GetAll(dest, length) || stream.IsError()) ThrowRead(); } void DataFile::Write(const void *src, int offset, int length) { CDB_TIMING("DataFile::Write"); int fs = (int)stream.GetSize(); if(offset < 0 || length < 0) ThrowWrite(); if(length == 0) return; if(offset > fs) { stream.Seek(fs); stream.Put(0, offset - fs); } else stream.Seek(offset); stream.Put(src, length); if(stream.IsError()) ThrowWrite(); } void DataFile::ThrowRead() const { throw ObjectExc("Chyba při čtení souboru '" + filename + "'."); } void DataFile::ThrowWrite() const { throw ObjectExc("Chyba při zápisu do souboru '" + filename + "'."); } void DataFile::CreateFreeList() { free_list.Clear(); free_list_index.Clear(); free_offset = Header::TOTAL; Array data_blocks; EnumBlock(data_blocks, header.index_offset, header.object_count << LOG2_OBJECT_BYTES); int i; for(i = 0; i < index.GetCount(); i++) if(write_index.Find(i) < 0 && index[i].IsAlive()) EnumBlock(data_blocks, index[i].offset, index[i].length); for(i = 0; i < write_objects.GetCount(); i++) if(write_objects[i].IsAlive()) EnumBlock(data_blocks, write_objects[i].offset, write_objects[i].length); Sort(data_blocks, &Object::OffsetLess); if(data_blocks.IsEmpty()) return; // no data for(i = 0; i < data_blocks.GetCount(); i++) { const Object& o = data_blocks[i]; if(o.offset >= free_offset + GRANULARITY) free_list.Add(new Object(free_offset, (o.offset - free_offset) & -GRANULARITY)); free_offset = o.offset + ((o.length + GRANULARITY - 1) & -GRANULARITY); } free_list_index.SetCount(free_list.GetCount()); for(i = 0; i < free_list.GetCount(); i++) free_list_index[i] = &free_list[i]; Sort(free_list_index, &Object::PtrLess); // CHECK_INTEGRITY(); } void DataFile::FreeBlock(foff_t offset, int length) { CDB_TIMING("DataFile::FreeBlock"); if(offset == 0 || length <= 0) return; // CHECK_INTEGRITY(); Array parts; EnumBlock(parts, offset, length); for(int i = 0; i < parts.GetCount(); i++) FreePart(parts[i].offset, parts[i].length); // CHECK_INTEGRITY(); } void DataFile::ReorderFreeItem(int pos) { CDB_TIMING("DataFile::ReorderFreeItem"); CDB_ASSERT(pos >= 0 && pos < free_list_index.GetCount()); Object *p = free_list_index[pos]; bool lok = (pos == 0 || Object::PtrLess(free_list_index[pos - 1], p)); bool rok = (pos == free_list_index.GetCount() - 1 || Object::PtrLess(p, free_list_index[pos + 1])); if(!rok) { // move item up Object **op = free_list_index.Begin() + pos + 1, **oe = free_list_index.End(); int n = BinFindIndex(op, oe, p, &Object::PtrLess); Copy(op - 1, op, op + n); op[n - 1] = p; } else if(!lok) { // move item down Object **op = free_list_index.Begin() + pos, **ob = free_list_index.Begin(); int n = BinFindIndex(ob, op, p, &Object::PtrLess); CopyBackward(op + 1, op, ob + n); ob[n] = p; } // CHECK_INTEGRITY(); } void DataFile::FlushIndex() { CDB_TIMING("DataFile::FlushIndex"); // CHECK_INTEGRITY(); StringStream out; for(int i = 0; i < index.GetCount(); i++) { const Object& o = index[i]; CDB_ASSERT(o.length <= 0 || o.offset >= Header::TOTAL); out.PutIL(o.offset); out.PutIL(o.length); } header.index_offset = WriteBlock(out.GetResult()); header.object_count = index.GetCount(); // CHECK_INTEGRITY(); } Vector DataFile::FindObjects(foff_t start, int length) { Vector list; for(int i = 0; i < index.GetCount(); i++) { const Object& o = index[i]; if(!o.IsAlive()) continue; Array blocks; EnumBlock(blocks, o.offset, o.length); for(int t = 0; t < blocks.GetCount(); t++) if(blocks[t].offset + blocks[t].length > start && blocks[t].offset < start + length) { list.Add(i); break; } } return list; } void DataFile::DumpObjects(const Vector& list, bool dump_data) { for(int i = 0; i < list.GetCount(); i++) { int o = list[i]; if(!IsValidIndex(o)) RLOG("#" << o << " - invalid"); else { const Object& obj = index[o]; RLOG("#" << o << " - " << Format("[0x%08x +0x%08x)", obj.offset, obj.length)); if(dump_data && obj.IsAlive()) { String data = Get(o); RLOGHEXDUMP(data, data.GetLength()); } } } } END_UPP_NAMESPACE