summaryrefslogtreecommitdiffstats
path: root/akregator/src/mk4storage/metakit/src/format.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'akregator/src/mk4storage/metakit/src/format.cpp')
-rw-r--r--akregator/src/mk4storage/metakit/src/format.cpp1341
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;
+}
+
+/////////////////////////////////////////////////////////////////////////////