diff options
Diffstat (limited to 'akregator/src/mk4storage/metakit/src/format.cpp')
-rw-r--r-- | akregator/src/mk4storage/metakit/src/format.cpp | 1341 |
1 files changed, 1341 insertions, 0 deletions
diff --git a/akregator/src/mk4storage/metakit/src/format.cpp b/akregator/src/mk4storage/metakit/src/format.cpp new file mode 100644 index 000000000..aa23e7391 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/format.cpp @@ -0,0 +1,1341 @@ +// format.cpp -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Format handlers deal with the representation of data + */ + +#include "header.h" +#include "handler.h" +#include "column.h" +#include "format.h" +#include "persist.h" + +///////////////////////////////////////////////////////////////////////////// + +class c4_FormatHandler : public c4_Handler +{ + c4_HandlerSeq& _owner; + +public: + c4_FormatHandler (const c4_Property& prop_, c4_HandlerSeq& owner_); + virtual ~c4_FormatHandler (); + + virtual bool IsPersistent() const; + +protected: + c4_HandlerSeq& Owner() const; +}; + +///////////////////////////////////////////////////////////////////////////// +// c4_FormatHandler + +c4_FormatHandler::c4_FormatHandler (const c4_Property& prop_, c4_HandlerSeq& owner_) + : c4_Handler (prop_), _owner (owner_) +{ +} + +c4_FormatHandler::~c4_FormatHandler () +{ +} + +d4_inline c4_HandlerSeq& c4_FormatHandler::Owner() const +{ + return _owner; +} + +bool c4_FormatHandler::IsPersistent() const +{ + return _owner.Persist() != 0; +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_FormatX : public c4_FormatHandler +{ +public: + c4_FormatX (const c4_Property& prop_, c4_HandlerSeq& seq_, + int width_ =sizeof (t4_i32)); + + virtual void Define(int, const t4_byte**); + virtual void OldDefine(char type_, c4_Persist&); + virtual void FlipBytes(); + + virtual int ItemSize(int index_); + virtual const void* Get(int index_, int& length_); + virtual void Set(int index_, const c4_Bytes& buf_); + + virtual void Insert(int index_, const c4_Bytes& buf_, int count_); + virtual void Remove(int index_, int count_); + + virtual void Commit(c4_SaveContext& ar_); + + virtual void Unmapped(); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); + +protected: + c4_ColOfInts _data; +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FormatX::c4_FormatX (const c4_Property& p_, c4_HandlerSeq& s_, int w_) + : c4_FormatHandler (p_, s_), _data (s_.Persist(), w_) +{ +} + +int c4_FormatX::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + return c4_ColOfInts::DoCompare(b1_, b2_); +} + +void c4_FormatX::Commit(c4_SaveContext& ar_) +{ + _data.FixSize(true); + ar_.CommitColumn(_data); + //_data.FixSize(false); +} + +void c4_FormatX::Define(int rows_, const t4_byte** ptr_) +{ + if (ptr_ != 0) + _data.PullLocation(*ptr_); + + _data.SetRowCount(rows_); +} + +void c4_FormatX::OldDefine(char, c4_Persist& pers_) +{ + pers_.FetchOldLocation(_data); + _data.SetRowCount(Owner().NumRows()); +} + +void c4_FormatX::FlipBytes() +{ + _data.FlipBytes(); +} + +int c4_FormatX::ItemSize(int index_) +{ + return _data.ItemSize(index_); +} + +const void* c4_FormatX::Get(int index_, int& length_) +{ + return _data.Get(index_, length_); +} + +void c4_FormatX::Set(int index_, const c4_Bytes& buf_) +{ + _data.Set(index_, buf_); +} + +void c4_FormatX::Insert(int index_, const c4_Bytes& buf_, int count_) +{ + _data.Insert(index_, buf_, count_); +} + +void c4_FormatX::Remove(int index_, int count_) +{ + _data.Remove(index_, count_); +} + +void c4_FormatX::Unmapped() +{ + _data.ReleaseAllSegments(); +} + +///////////////////////////////////////////////////////////////////////////// +#if !q4_TINY +///////////////////////////////////////////////////////////////////////////// + +class c4_FormatL : public c4_FormatX +{ +public: + c4_FormatL (const c4_Property& prop_, c4_HandlerSeq& seq_); + + virtual void Define(int, const t4_byte**); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FormatL::c4_FormatL (const c4_Property& prop_, c4_HandlerSeq& seq_) + : c4_FormatX (prop_, seq_, sizeof (t4_i64)) +{ + // force maximum size, autosizing more than 32 bits won't work + _data.SetAccessWidth(8 * sizeof (t4_i64)); +} + +int c4_FormatL::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + d4_assert(b1_.Size() == sizeof (t4_i64)); + d4_assert(b2_.Size() == sizeof (t4_i64)); + + t4_i64 v1 = *(const t4_i64*) b1_.Contents(); + t4_i64 v2 = *(const t4_i64*) b2_.Contents(); + + return v1 == v2 ? 0 : v1 < v2 ? -1 : +1; +} + +void c4_FormatL::Define(int rows_, const t4_byte** ptr_) +{ + if (ptr_ == 0 && rows_ > 0) { + d4_assert(_data.ColSize() == 0); + _data.InsertData(0, rows_ * sizeof (t4_i64), true); + } + + c4_FormatX::Define(rows_, ptr_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_FormatF : public c4_FormatX +{ +public: + c4_FormatF (const c4_Property& prop_, c4_HandlerSeq& seq_); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FormatF::c4_FormatF (const c4_Property& prop_, c4_HandlerSeq& seq_) + : c4_FormatX (prop_, seq_, sizeof (float)) +{ +} + +int c4_FormatF::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + d4_assert(b1_.Size() == sizeof (float)); + d4_assert(b2_.Size() == sizeof (float)); + + float v1 = *(const float*) b1_.Contents(); + float v2 = *(const float*) b2_.Contents(); + + return v1 == v2 ? 0 : v1 < v2 ? -1 : +1; +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_FormatD : public c4_FormatX +{ +public: + c4_FormatD (const c4_Property& prop_, c4_HandlerSeq& seq_); + + virtual void Define(int, const t4_byte**); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FormatD::c4_FormatD (const c4_Property& prop_, c4_HandlerSeq& seq_) + : c4_FormatX (prop_, seq_, sizeof (double)) +{ + // force maximum size, autosizing more than 32 bits won't work + _data.SetAccessWidth(8 * sizeof (double)); +} + +int c4_FormatD::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + d4_assert(b1_.Size() == sizeof (double)); + d4_assert(b2_.Size() == sizeof (double)); + + double v1 = *(const double*) b1_.Contents(); + double v2 = *(const double*) b2_.Contents(); + + return v1 == v2 ? 0 : v1 < v2 ? -1 : +1; +} + +void c4_FormatD::Define(int rows_, const t4_byte** ptr_) +{ + if (ptr_ == 0 && rows_ > 0) { + d4_assert(_data.ColSize() == 0); + _data.InsertData(0, rows_ * sizeof (double), true); + } + + c4_FormatX::Define(rows_, ptr_); +} + +///////////////////////////////////////////////////////////////////////////// +#endif // !q4_TINY +///////////////////////////////////////////////////////////////////////////// + +/* + Byte properties are used for raw bytes and for indirect (memo) data. + + There are two columns: the actual data and the item sizes. If the data + is indirect, then the size is stored as a negative value. + + In addition, there is an in-memory-only vector of columns (_memos). + Columns are created when asked for, and stay around until released with + a commit call. If the column object exists and is not dirty, then it + is either a real column (size < 0), or simply a duplicate of the data + stored inline as bytes. +*/ + +class c4_FormatB : public c4_FormatHandler +{ +public: + c4_FormatB (const c4_Property& prop_, c4_HandlerSeq& seq_); + virtual ~c4_FormatB (); + + virtual void Define(int, const t4_byte**); + virtual void OldDefine(char type_, c4_Persist&); + virtual void Commit(c4_SaveContext& ar_); + + virtual int ItemSize(int index_); + virtual const void* Get(int index_, int& length_); + virtual void Set(int index_, const c4_Bytes& buf_); + + virtual void Insert(int index_, const c4_Bytes& buf_, int count_); + virtual void Remove(int index_, int count_); + + virtual c4_Column* GetNthMemoCol(int index_, bool alloc_); + + virtual void Unmapped(); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); + +protected: + const void* GetOne(int index_, int& length_); + void SetOne(int index_, const c4_Bytes& buf_, bool ignoreMemos_ =false); + +private: + t4_i32 Offset(int index_) const; + bool ShouldBeMemo(int length_) const; + int ItemLenOffCol(int index_, t4_i32& off_, c4_Column*& col_); + bool CommitItem(c4_SaveContext& ar_, int index_); + void InitOffsets(c4_ColOfInts& sizes_); + + c4_Column _data; + c4_ColOfInts _sizeCol; // 2001-11-27: keep, to track position on disk + c4_Column _memoCol; // 2001-11-27: keep, to track position on disk + c4_DWordArray _offsets; + c4_PtrArray _memos; + bool _recalc; // 2001-11-27: remember when to redo _{size,memo}Col +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FormatB::c4_FormatB (const c4_Property& prop_, c4_HandlerSeq& seq_) + : c4_FormatHandler (prop_, seq_), _data (seq_.Persist()), + _sizeCol (seq_.Persist()), _memoCol (seq_.Persist()), _recalc (false) +{ + _offsets.SetSize(1, 100); + _offsets.SetAt(0, 0); +} + +c4_FormatB::~c4_FormatB () +{ + // cleanup allocated columns + //better? for (int i = _memos.GetSize(); --i >= 0 ;) + for (int i = 0; i < _memos.GetSize(); ++i) + delete (c4_Column*) _memos.GetAt(i); +} + +d4_inline t4_i32 c4_FormatB::Offset(int index_) const +{ + d4_assert((t4_i32) _offsets.GetAt(_offsets.GetSize() - 1) == _data.ColSize()); + d4_assert(_offsets.GetSize() == _memos.GetSize() + 1); + d4_assert(index_ < _offsets.GetSize()); + + // extend offset vectors for missing empty entries at end + int n = _offsets.GetSize(); + d4_assert(n > 0); + + if (index_ >= n) + index_ = n - 1; + + return _offsets.GetAt(index_); +} + +d4_inline bool c4_FormatB::ShouldBeMemo(int length_) const +{ + // items over 10000 bytes are always memos + // items up to 100 bytes are never memos + // + // else, memo only if the column would be under 1 Mb + // (assuming all items had the same size as this one) + // + // the effect is that as the number of rows increases, + // smaller and smaller items get turned into memos + // + // note that items which are no memo right now stay + // as is, and so do memos which have not been modified + + int rows = _memos.GetSize() + 1; // avoids divide by zero + return length_ > 10000 || length_ > 100 && length_ > 1000000 / rows; +} + +int c4_FormatB::ItemLenOffCol(int index_, t4_i32& off_, c4_Column*& col_) +{ + col_ = (c4_Column*) _memos.GetAt(index_); + if (col_ != 0) { + off_ = 0; + return col_->ColSize(); + } + + col_ = &_data; + off_ = Offset(index_); + return Offset(index_ + 1) - off_; +} + +c4_Column* c4_FormatB::GetNthMemoCol(int index_, bool alloc_) +{ + t4_i32 start; + c4_Column* col; + int n = ItemLenOffCol(index_, start, col); + + if (col == &_data && alloc_) { + col = d4_new c4_Column (_data.Persist()); + _memos.SetAt(index_, col); + + if (n > 0) + if (_data.IsDirty()) { + c4_Bytes temp; + _data.FetchBytes(start, n, temp, true); + col->SetBuffer(n); + col->StoreBytes(0, temp); + } + else + col->SetLocation(_data.Position() + start, n); + } + + return col; +} + +void c4_FormatB::Unmapped() +{ + _data.ReleaseAllSegments(); + _sizeCol.ReleaseAllSegments(); + _memoCol.ReleaseAllSegments(); + + for (int i = 0; i < _memos.GetSize(); ++i) { + c4_Column* cp = (c4_Column*) _memos.GetAt(i); + if (cp != 0) + cp->ReleaseAllSegments(); + } +} + +void c4_FormatB::Define(int, const t4_byte** ptr_) +{ + d4_assert(_memos.GetSize() == 0); + + if (ptr_ != 0) { + _data.PullLocation(*ptr_); + if (_data.ColSize() > 0) + _sizeCol.PullLocation(*ptr_); + _memoCol.PullLocation(*ptr_); + } + + // everything below this point could be delayed until use + // in that case, watch out that column space use is properly tracked + + InitOffsets(_sizeCol); + + if (_memoCol.ColSize() > 0) { + c4_Bytes walk; + _memoCol.FetchBytes(0, _memoCol.ColSize(), walk, true); + + const t4_byte* p = walk.Contents(); + + for (int row = 0; p < walk.Contents() + walk.Size(); ++row) { + row += c4_Column::PullValue(p); + d4_assert(row < _memos.GetSize()); + + c4_Column* mc = d4_new c4_Column (_data.Persist()); + d4_assert(mc != 0); + _memos.SetAt(row, mc); + + mc->PullLocation(p); + } + + d4_assert(p == walk.Contents() + walk.Size()); + } +} + +void c4_FormatB::OldDefine(char type_, c4_Persist& pers_) +{ + int rows = Owner().NumRows(); + + c4_ColOfInts sizes (_data.Persist()); + + if (type_ == 'M') { + InitOffsets(sizes); + + c4_ColOfInts szVec (_data.Persist()); + pers_.FetchOldLocation(szVec); + szVec.SetRowCount(rows); + + c4_ColOfInts posVec (_data.Persist()); + pers_.FetchOldLocation(posVec); + posVec.SetRowCount(rows); + + for (int r = 0; r < rows; ++r) { + t4_i32 sz = szVec.GetInt(r); + if (sz > 0) { + c4_Column* mc = d4_new c4_Column (_data.Persist()); + d4_assert(mc != 0); + _memos.SetAt(r, mc); + + mc->SetLocation(posVec.GetInt(r), sz); + } + } + } else { + pers_.FetchOldLocation(_data); + + if (type_ == 'B') { + pers_.FetchOldLocation(sizes); + +#if !q4_OLD_IS_ALWAYS_V2 + + // WARNING - HUGE HACK AHEAD - THIS IS NOT 100% FULLPROOF! + // + // The above is correct for MK versions 2.0 and up, but *NOT* + // for MK 1.8.6 datafiles, which store sizes first (OUCH!!!). + // This means that there is not a 100% safe way to auto-convert + // both 1.8.6 and 2.0 files - since there is no way to detect + // unambiguously which version a datafile is. All we can do, + // is to carefully check both vectors, and *hope* that only one + // of them is valid as sizes vector. This problem applies to + // the 'B' (bytes) property type only, and only pre 2.0 files. + // + // To build a version which *always* converts assuming 1.8.6, + // add flag "-Dq4_OLD_IS_PRE_V2" to the compiler command line. + // Conversely, "-Dq4_OLD_IS_ALWAYS_V2" forces 2.0 conversion. + + if (rows > 0) { + t4_i32 s1 = sizes.ColSize(); + t4_i32 s2 = _data.ColSize(); + +#if !q4_OLD_IS_PRE_V2 + // if the size vector is clearly impossible, swap vectors + bool fix = c4_ColOfInts::CalcAccessWidth(rows, s1) < 0; + + // if the other vector might be valid as well, check further + if (!fix && c4_ColOfInts::CalcAccessWidth(rows, s2) >= 0) { + sizes.SetRowCount(rows); + t4_i32 total = 0; + for (int i = 0; i < rows; ++i) { + t4_i32 w = sizes.GetInt(i); + if (w < 0 || total > s2) { + total = -1; + break; + } + total += w; + } + + // if the sizes don't add up, swap vectors + fix = total != s2; + } + + if (fix) +#endif + { + t4_i32 p1 = sizes.Position(); + t4_i32 p2 = _data.Position(); + _data.SetLocation(p1, s1); + sizes.SetLocation(p2, s2); + } + } +#endif + InitOffsets(sizes); + } else { + d4_assert(type_ == 'S'); + + sizes.SetRowCount(rows); + + t4_i32 pos = 0; + t4_i32 lastEnd = 0; + int k = 0; + + c4_ColIter iter (_data, 0, _data.ColSize()); + while (iter.Next()) { + const t4_byte* p = iter.BufLoad(); + for (int j = 0; j < iter.BufLen(); ++j) + if (!p[j]) { + sizes.SetInt(k++, pos + j + 1 - lastEnd); + lastEnd = pos + j + 1; + } + + pos += iter.BufLen(); + } + + d4_assert(pos == _data.ColSize()); + + if (lastEnd < pos) { // last entry had no zero byte + _data.InsertData(pos++, 1, true); + sizes.SetInt(k, pos - lastEnd); + } + + InitOffsets(sizes); + + // get rid of entries with just a null byte + for (int r = 0; r < rows; ++r) + if (c4_FormatB::ItemSize(r) == 1) + SetOne(r, c4_Bytes ()); + } + } +} + +void c4_FormatB::InitOffsets(c4_ColOfInts& sizes_) +{ + int rows = Owner().NumRows(); + + if (sizes_.RowCount() != rows) { + sizes_.SetRowCount(rows); + } + + _memos.SetSize(rows); + _offsets.SetSize(rows + 1); + + if (_data.ColSize() > 0) { + t4_i32 total = 0; + + for (int r = 0; r < rows; ++r) { + int n = sizes_.GetInt(r); + d4_assert(n >= 0); + total += n; + _offsets.SetAt(r + 1, total); + } + + d4_assert(total == _data.ColSize()); + } + +} + +int c4_FormatB::ItemSize(int index_) +{ + t4_i32 start; + c4_Column* col; + return ItemLenOffCol(index_, start, col); +} + +const void* c4_FormatB::GetOne(int index_, int& length_) +{ + t4_i32 start; + c4_Column* cp; + length_ = ItemLenOffCol(index_, start, cp); + d4_assert(length_ >= 0); + + if (length_ == 0) + return ""; + + return cp->FetchBytes(start, length_, Owner().Buffer(), false); +} + +const void* c4_FormatB::Get(int index_, int& length_) +{ + return GetOne(index_, length_); +} + +void c4_FormatB::SetOne(int index_, const c4_Bytes& xbuf_, bool ignoreMemos_) +{ + // this fixes bug in 2.4.0 when copying string from higher row + // TODO: this fix is very conservative, figure out when to copy + // (can probably look at pointer to see whether it's from us) + int sz = xbuf_.Size(); + c4_Bytes buf_ (xbuf_.Contents(), sz, 0 < sz && sz <= c4_Column::kSegMax); + + c4_Column* cp = &_data; + t4_i32 start = Offset(index_); + int len = Offset(index_ + 1) - start; + + if (!ignoreMemos_ && _memos.GetAt(index_) != 0) + len = ItemLenOffCol(index_, start, cp); + + int m = buf_.Size(); + int n = m - len; + + if (n > 0) + cp->Grow(start, n); + else if (n < 0) + cp->Shrink(start, - n); + else if (m == 0) + return; // no size change and no contents + + _recalc = true; + + cp->StoreBytes(start, buf_); + + if (n && cp == &_data) { // if size has changed + int k = _offsets.GetSize() - 1; + + // if filling in an empty entry at end: extend offsets first + if (m > 0 && index_ >= k) { + _offsets.InsertAt(k, _offsets.GetAt(k), index_ - k + 1); + + k = index_ + 1; + d4_assert(k == _offsets.GetSize() - 1); + } + + // adjust following entry offsets + while (++index_ <= k) + _offsets.ElementAt(index_) += n; + } + + d4_assert((t4_i32) _offsets.GetAt(_offsets.GetSize() - 1) == _data.ColSize()); +} + +void c4_FormatB::Set(int index_, const c4_Bytes& buf_) +{ + SetOne(index_, buf_); +} + +int c4_FormatB::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + int n = b1_.Size(); + if (n > b2_.Size()) + n = b2_.Size(); + + int f = memcmp(b1_.Contents(), b2_.Contents(), n); + return f ? f : b1_.Size() - b2_.Size(); +} + +void c4_FormatB::Insert(int index_, const c4_Bytes& buf_, int count_) +{ + d4_assert(count_ > 0); + + _recalc = true; + + int m = buf_.Size(); + t4_i32 off = Offset(index_); + + _memos.InsertAt(index_, 0, count_); + + // insert the appropriate number of bytes + t4_i32 n = count_ * (t4_i32) m; + if (n > 0) { + _data.Grow(off, n); + + // store as many copies as needed, but may have to do it in chunks + int spos = 0; + + c4_ColIter iter (_data, off, off + n); + while (iter.Next(m - spos)) { + memcpy(iter.BufSave(), buf_.Contents() + spos, iter.BufLen()); + + spos += iter.BufLen(); + if (spos >= m) + spos = 0; + } + + d4_assert(spos == 0); // must have copied an exact multiple of the data + } + + // define offsets of the new entries + _offsets.InsertAt(index_, 0, count_); + d4_assert(_offsets.GetSize() <= _memos.GetSize() + 1); + + while (--count_ >= 0) { + _offsets.SetAt(index_++, off); + off += m; + } + + d4_assert(index_ < _offsets.GetSize()); + + // adjust all following entries + while (index_ < _offsets.GetSize()) + _offsets.ElementAt(index_++) += n; + + d4_assert((t4_i32) _offsets.GetAt(index_ - 1) == _data.ColSize()); + d4_assert(index_ <= _memos.GetSize() + 1); +} + +void c4_FormatB::Remove(int index_, int count_) +{ + _recalc = true; + + t4_i32 off = Offset(index_); + t4_i32 n = Offset(index_ + count_) - off; + d4_assert(n >= 0); + + // remove the columns, if present + for (int i = 0; i < count_; ++i) + delete (c4_Column*) _memos.GetAt(index_ + i); + _memos.RemoveAt(index_, count_); + + if (n > 0) + _data.Shrink(off, n); + + _offsets.RemoveAt(index_, count_); + + d4_assert(index_ < _offsets.GetSize()); + + // adjust all following entries + while (index_ < _offsets.GetSize()) + _offsets.ElementAt(index_++) -= n; + + d4_assert((t4_i32) _offsets.GetAt(index_ - 1) == _data.ColSize()); + d4_assert(index_ <= _memos.GetSize() + 1); +} + +void c4_FormatB::Commit(c4_SaveContext& ar_) +{ + int rows = _memos.GetSize(); + d4_assert(rows > 0); + + bool full = _recalc || ar_.Serializing(); + + if (!full) + for (int i = 0; i < rows; ++i) { + c4_Column* col = (c4_Column*) _memos.GetAt(i); + if (col != 0) { + full = true; + break; + } + } + d4_assert(_recalc || _sizeCol.RowCount() == rows); + + if (full) { + _memoCol.SetBuffer(0); + _sizeCol.SetBuffer(0); + _sizeCol.SetAccessWidth(0); + _sizeCol.SetRowCount(rows); + + int skip = 0; + + c4_Column* saved = ar_.SetWalkBuffer(&_memoCol); + + for (int r = 0; r < rows; ++r) { + ++skip; + + t4_i32 start; + c4_Column* col; + int len = ItemLenOffCol(r, start, col); + + bool oldMemo = col != &_data; + bool newMemo = ShouldBeMemo(len); + + if (!oldMemo && newMemo) { + col = GetNthMemoCol(r, true); + d4_assert(col != &_data); + //? start = 0; + } + + c4_Bytes temp; + + if (newMemo) { // it now is a memo, inlined data will be empty + ar_.StoreValue(skip - 1); + skip = 0; + ar_.CommitColumn(*col); + } else if (!oldMemo) { // it was no memo, done if it hasn't become one + _sizeCol.SetInt(r, len); + continue; + } else { // it was a memo, but it no longer is + d4_assert(start == 0); + if (len > 0) + { + _sizeCol.SetInt(r, len); + col->FetchBytes(start, len, temp, true); + delete (c4_Column*) _memos.GetAt(r); // 28-11-2001: fix mem leak + _memos.SetAt(r, 0); // 02-11-2001: fix for use after commit + } + } + + SetOne(r, temp, true); // bypass current memo pointer + } + + ar_.SetWalkBuffer(saved); + } + + ar_.CommitColumn(_data); + + if (_data.ColSize() > 0) { + _sizeCol.FixSize(true); + ar_.CommitColumn(_sizeCol); + //_sizeCol.FixSize(false); + } + + ar_.CommitColumn(_memoCol); + + // need a way to find out when the data has been committed (on 2nd pass) + // both _sizeCol and _memoCol will be clean again when it has + // but be careful because dirty flag is only useful if size is nonzero + if (_recalc && !ar_.Serializing()) + _recalc = _sizeCol.ColSize() > 0 && _sizeCol.IsDirty() || + _memoCol.ColSize() > 0 && _memoCol.IsDirty(); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_FormatS : public c4_FormatB +{ +public: + c4_FormatS (const c4_Property& prop_, c4_HandlerSeq& seq_); + + virtual int ItemSize(int index_); + virtual const void* Get(int index_, int& length_); + virtual void Set(int index_, const c4_Bytes& buf_); + + virtual void Insert(int index_, const c4_Bytes& buf_, int count_); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FormatS::c4_FormatS (const c4_Property& prop_, c4_HandlerSeq& seq_) + : c4_FormatB (prop_, seq_) +{ +} + +int c4_FormatS::ItemSize(int index_) +{ + int n = c4_FormatB::ItemSize(index_) - 1; + return n >= 0 ? n : 0; +} + +const void* c4_FormatS::Get(int index_, int& length_) +{ + const void* ptr = GetOne(index_, length_); + + if (length_ == 0) { + length_ = 1; + ptr = ""; + } + + d4_assert(((const char*) ptr)[length_-1] == 0); + return ptr; +} + +void c4_FormatS::Set(int index_, const c4_Bytes& buf_) +{ + int m = buf_.Size(); + if (--m >= 0) { + d4_assert(buf_.Contents()[m] == 0); + if (m == 0) { + SetOne(index_, c4_Bytes ()); // don't store data for empty strings + return; + } + } + + SetOne(index_, buf_); +} + +int c4_FormatS::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + c4_String v1 ((const char*) b1_.Contents(), b1_.Size()); + c4_String v2 ((const char*) b2_.Contents(), b2_.Size()); + + return v1.CompareNoCase(v2); +} + +void c4_FormatS::Insert(int index_, const c4_Bytes& buf_, int count_) +{ + d4_assert(count_ > 0); + + int m = buf_.Size(); + if (--m >= 0) { + d4_assert(buf_.Contents()[m] == 0); + if (m == 0) { + c4_FormatB::Insert(index_, c4_Bytes (), count_); + return; + } + } + + c4_FormatB::Insert(index_, buf_, count_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_FormatV : public c4_FormatHandler +{ +public: + c4_FormatV (const c4_Property& prop_, c4_HandlerSeq& seq_); + virtual ~c4_FormatV (); + + virtual void Define(int rows_, const t4_byte** ptr_); + virtual void OldDefine(char type_, c4_Persist&); + virtual void Commit(c4_SaveContext& ar_); + + virtual void FlipBytes(); + + virtual int ItemSize(int index_); + virtual const void* Get(int index_, int& length_); + virtual void Set(int index_, const c4_Bytes& buf_); + + virtual void Insert(int index_, const c4_Bytes& buf_, int count_); + virtual void Remove(int index_, int count_); + + virtual void Unmapped(); + virtual bool HasSubview(int index_); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); + +private: + c4_HandlerSeq& At(int index_); + void Replace(int index_, c4_HandlerSeq* seq_); + void SetupAllSubviews(); + void ForgetSubview(int index_); + + c4_Column _data; + c4_PtrArray _subSeqs; + bool _inited; +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FormatV::c4_FormatV (const c4_Property& prop_, c4_HandlerSeq& seq_) + : c4_FormatHandler (prop_, seq_), _data (seq_.Persist()), _inited (false) +{ +} + +c4_FormatV::~c4_FormatV () +{ + for (int i = 0; i < _subSeqs.GetSize(); ++i) + ForgetSubview(i); +} + +c4_HandlerSeq& c4_FormatV::At(int index_) +{ + d4_assert(_inited); + + c4_HandlerSeq*& hs = (c4_HandlerSeq*&) _subSeqs.ElementAt(index_); + if (hs == 0) { + hs = d4_new c4_HandlerSeq (Owner(), this); + hs->IncRef(); + } + + return *hs; +} + +void c4_FormatV::SetupAllSubviews() +{ + d4_assert(!_inited); + _inited = true; + + if (_data.ColSize() > 0) { + c4_Bytes temp; + _data.FetchBytes(0, _data.ColSize(), temp, true); + const t4_byte* ptr = temp.Contents(); + + for (int r = 0; r < _subSeqs.GetSize(); ++r) { + // don't materialize subview if it is empty + // duplicates code which is in c4_HandlerSeq::Prepare + const t4_byte* p2 = ptr; + d4_dbgdef(t4_i32 sias =) + c4_Column::PullValue(p2); + d4_assert(sias == 0); // not yet + + if (c4_Column::PullValue(p2) > 0) + At(r).Prepare(&ptr, false); + else + ptr = p2; + } + + d4_assert(ptr == temp.Contents() + temp.Size()); + } +} + +void c4_FormatV::Define(int rows_, const t4_byte** ptr_) +{ + if (_inited) { + // big oops: a root handler already contains data + + for (int i = 0; i < _subSeqs.GetSize(); ++i) + ForgetSubview(i); + + _inited = false; + } + + _subSeqs.SetSize(rows_); + if (ptr_ != 0) + _data.PullLocation(*ptr_); +} + +void c4_FormatV::OldDefine(char, c4_Persist& pers_) +{ + int rows = Owner().NumRows(); + _subSeqs.SetSize(rows); + + for (int i = 0; i < rows; ++i) { + int n = pers_.FetchOldValue(); + if (n) { + // 14-11-2000: do not create again (this causes a mem leak) + // 04-12-2000: but do create if absent (fixes occasional crash) + c4_HandlerSeq* hs = (c4_HandlerSeq*) _subSeqs.GetAt(i); + if (hs == 0) { + hs = d4_new c4_HandlerSeq (Owner(), this); + _subSeqs.SetAt(i, hs); + hs->IncRef(); + } + hs->SetNumRows(n); + hs->OldPrepare(); + } + } +} + +void c4_FormatV::FlipBytes() +{ + if (!_inited) + SetupAllSubviews(); + + for (int i = 0; i < _subSeqs.GetSize(); ++i) + At(i).FlipAllBytes(); +} + +int c4_FormatV::ItemSize(int index_) +{ + if (!_inited) + SetupAllSubviews(); + + // 06-02-2002: avoid creating empty subview + c4_HandlerSeq* hs = (c4_HandlerSeq*&) _subSeqs.ElementAt(index_); + return hs == 0 ? 0 : hs->NumRows(); +} + +const void* c4_FormatV::Get(int index_, int& length_) +{ + if (!_inited) + SetupAllSubviews(); + + At(index_); // forces existence of a real entry + c4_HandlerSeq*& e = (c4_HandlerSeq*&) _subSeqs.ElementAt(index_); + + length_ = sizeof (c4_HandlerSeq**); + return &e; +} + +void c4_FormatV::Set(int index_, const c4_Bytes& buf_) +{ + d4_assert(buf_.Size() == sizeof (c4_Sequence*)); + + if (!_inited) + SetupAllSubviews(); + + c4_HandlerSeq* value = *(c4_HandlerSeq* const*) buf_.Contents(); + + if (value != & At(index_)) + Replace(index_, value); +} + +void c4_FormatV::Replace(int index_, c4_HandlerSeq* seq_) +{ + if (!_inited) + SetupAllSubviews(); + + c4_HandlerSeq*& curr = (c4_HandlerSeq*&) _subSeqs.ElementAt(index_); + if (seq_ == curr) + return; + + if (curr != 0) { + d4_assert(&curr->Parent() == &Owner()); + curr->DetachFromParent(); + curr->DetachFromStorage(true); + + curr->DecRef(); + curr = 0; + } + + if (seq_) { + int n = seq_->NumRows(); + + c4_HandlerSeq& t = At(index_); + d4_assert(t.NumRows() == 0); + + t.Resize(n); + + c4_Bytes data; + + // this dest seq has only the persistent handlers + // and maybe in a different order + // create any others we need as temporary properties + for (int i = 0; i < seq_->NumHandlers(); ++i) { + c4_Handler& h1 = seq_->NthHandler(i); + + int j = t.PropIndex(h1.Property()); + d4_assert(j >= 0); + + c4_Handler& h2 = t.NthHandler(j); + + for (int k = 0; k < n; ++k) + if (seq_->Get(k, h1.PropId(), data)) + h2.Set(k, data); + } + } +} + +int c4_FormatV::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + d4_assert(b1_.Size() == sizeof (c4_Sequence*)); + d4_assert(b2_.Size() == sizeof (c4_Sequence*)); + + c4_View v1 = *(c4_Sequence* const*) b1_.Contents(); + c4_View v2 = *(c4_Sequence* const*) b2_.Contents(); + + return v1.Compare(v2); +} + +void c4_FormatV::Insert(int index_, const c4_Bytes& buf_, int count_) +{ + d4_assert(buf_.Size() == sizeof (c4_Sequence*)); + d4_assert(count_ > 0); + + // can only insert an empty entry! + d4_assert(*(c4_Sequence* const*) buf_.Contents() == 0); + + if (!_inited) + SetupAllSubviews(); + + _subSeqs.InsertAt(index_, 0, count_); + _data.SetBuffer(0); // 2004-01-18 force dirty +} + +void c4_FormatV::Remove(int index_, int count_) +{ + d4_assert(count_ > 0); + + if (!_inited) + SetupAllSubviews(); + + for (int i = 0; i < count_; ++i) + ForgetSubview(index_ + i); + + _subSeqs.RemoveAt(index_, count_); + _data.SetBuffer(0); // 2004-01-18 force dirty +} + +void c4_FormatV::Unmapped() +{ + if (_inited) + for (int i = 0; i < _subSeqs.GetSize(); ++i) + if (HasSubview(i)) { + c4_HandlerSeq& hs = At(i); + hs.UnmappedAll(); + if (hs.NumRefs() == 1 && hs.NumRows() == 0) + ForgetSubview(i); + } + + _data.ReleaseAllSegments(); +} + +bool c4_FormatV::HasSubview(int index_) +{ + if (!_inited) + SetupAllSubviews(); + + return _subSeqs.ElementAt(index_) != 0; +} + +void c4_FormatV::ForgetSubview(int index_) +{ + c4_HandlerSeq*& seq = (c4_HandlerSeq*&) _subSeqs.ElementAt(index_); + if (seq != 0) { + d4_assert(&seq->Parent() == &Owner()); + seq->DetachFromParent(); + seq->DetachFromStorage(true); + seq->UnmappedAll(); + seq->DecRef(); + seq = 0; + } +} + +void c4_FormatV::Commit(c4_SaveContext& ar_) +{ + if (!_inited) + SetupAllSubviews(); + + int rows = _subSeqs.GetSize(); + d4_assert(rows > 0); + + c4_Column temp (0); + c4_Column* saved = ar_.SetWalkBuffer(&temp); + + for (int r = 0; r < rows; ++r) + if (HasSubview(r)) { + c4_HandlerSeq& hs = At(r); + ar_.CommitSequence(hs, false); + if (hs.NumRefs() == 1 && hs.NumRows() == 0) + ForgetSubview(r); + } else { + ar_.StoreValue(0); // sias + ar_.StoreValue(0); // row count + } + + ar_.SetWalkBuffer(saved); + + c4_Bytes buf; + temp.FetchBytes(0, temp.ColSize(), buf, true); + + bool changed = temp.ColSize() != _data.ColSize(); + + if (!changed) { + c4_Bytes buf2; + _data.FetchBytes(0, _data.ColSize(), buf2, true); + changed = buf != buf2; + } + + if (changed) { + _data.SetBuffer(buf.Size()); + _data.StoreBytes(0, buf); + } + + ar_.CommitColumn(_data); +} + +///////////////////////////////////////////////////////////////////////////// + +c4_Handler* f4_CreateFormat(const c4_Property& prop_, c4_HandlerSeq& seq_) +{ + switch (prop_.Type()) { + case 'I': return d4_new c4_FormatX (prop_, seq_); +#if !q4_TINY + case 'L': return d4_new c4_FormatL (prop_, seq_); + case 'F': return d4_new c4_FormatF (prop_, seq_); + case 'D': return d4_new c4_FormatD (prop_, seq_); +#endif + case 'B': return d4_new c4_FormatB (prop_, seq_); + case 'S': return d4_new c4_FormatS (prop_, seq_); + case 'V': return d4_new c4_FormatV (prop_, seq_); + } + + d4_assert(0); + // 2004-01-16 turn bad definition type into an int property to avoid crash + return d4_new c4_FormatX (c4_IntProp (prop_.Name()), seq_); +} + +///////////////////////////////////////////////////////////////////////////// + +int f4_ClearFormat(char type_) +{ + switch (type_) { + case 'I': return sizeof (t4_i32); +#if !q4_TINY + case 'L': return sizeof (t4_i64); + case 'F': return sizeof (float); + case 'D': return sizeof (double); +#endif + case 'S': return 1; + case 'V': return sizeof (c4_Sequence*); + } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////// + +int f4_CompareFormat(char type_, const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + switch (type_) { + case 'I': return c4_FormatX::DoCompare(b1_, b2_); +#if !q4_TINY + case 'L': return c4_FormatL::DoCompare(b1_, b2_); + case 'F': return c4_FormatF::DoCompare(b1_, b2_); + case 'D': return c4_FormatD::DoCompare(b1_, b2_); +#endif + case 'B': return c4_FormatB::DoCompare(b1_, b2_); + case 'S': return c4_FormatS::DoCompare(b1_, b2_); + case 'V': return c4_FormatV::DoCompare(b1_, b2_); + } + + d4_assert(0); + return 0; +} + +///////////////////////////////////////////////////////////////////////////// |