summaryrefslogtreecommitdiffstats
path: root/akregator/src/mk4storage/metakit/src/remap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'akregator/src/mk4storage/metakit/src/remap.cpp')
-rw-r--r--akregator/src/mk4storage/metakit/src/remap.cpp1154
1 files changed, 1154 insertions, 0 deletions
diff --git a/akregator/src/mk4storage/metakit/src/remap.cpp b/akregator/src/mk4storage/metakit/src/remap.cpp
new file mode 100644
index 00000000..cc8175df
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/remap.cpp
@@ -0,0 +1,1154 @@
+// remap.cpp --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+* Mapping and remapping custom viewers
+*/
+
+#include "header.h"
+#include "remap.h"
+#include "handler.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_ReadOnlyViewer : public c4_CustomViewer
+{
+ c4_View _base;
+
+public:
+ c4_ReadOnlyViewer (c4_Sequence& seq_) : _base (&seq_) { }
+ virtual ~c4_ReadOnlyViewer () { }
+
+ virtual c4_View GetTemplate() { return _base.Clone(); }
+ virtual int GetSize() { return _base.GetSize(); }
+
+ virtual int Lookup(c4_Cursor key_, int& count_)
+ { int pos = 0; count_ = _base.GetSize();
+ return _base.RestrictSearch(*key_, pos, count_); }
+
+ virtual bool GetItem(int row_, int col_, c4_Bytes& buf_)
+ { return _base.GetItem(row_, col_, buf_); }
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_HashViewer : public c4_CustomViewer
+{
+ c4_View _base;
+ c4_View _map;
+ int _numKeys;
+
+ c4_IntProp _pHash;
+ c4_IntProp _pRow;
+
+ bool KeySame(int row_, c4_Cursor cursor_) const;
+ t4_i32 CalcHash(c4_Cursor cursor_) const;
+ int LookDict(t4_i32 hash_, c4_Cursor cursor_) const;
+ void InsertDict(int row_);
+ void RemoveDict(int pos_);
+ bool DictResize(int minused);
+
+ int Row(int i_) const { return _pRow (_map[i_]); }
+ int Hash(int i_) const { return _pHash (_map[i_]); }
+
+ void SetRow(int i_, int v_) { _pRow (_map[i_]) = v_; }
+ void SetHash(int i_, int v_) { _pHash (_map[i_]) = v_; }
+
+ bool IsUnused(int) const;
+ bool IsDummy(int) const;
+ bool IsActive(int i_) const { return Row(i_) >= 0; }
+
+ int GetPoly() const;
+ void SetPoly(int v_);
+ int GetSpare() const;
+ void SetSpare(int v_);
+
+public:
+ c4_HashViewer (c4_Sequence& seq_, int numKeys_,
+ c4_Sequence* map_=0);
+ virtual ~c4_HashViewer ();
+
+ virtual c4_View GetTemplate();
+ virtual int GetSize();
+ virtual int Lookup(c4_Cursor key_, int& count_);
+ virtual bool GetItem(int row_, int col_, c4_Bytes& buf_);
+ virtual bool SetItem(int row_, int col_, const c4_Bytes& buf_);
+ virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1);
+ virtual bool RemoveRows(int pos_, int count_=1);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// The following contains code derived froms Python's dictionaries, hence:
+// Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
+// The Netherlands.
+// Reduced and turned into a fast C++ class by Christian Tismer, hence:
+// Copyright 1999 by Christian Tismer.
+// Vectorized and reorganized further by Jean-Claude Wippler.
+/////////////////////////////////////////////////////////////////////////////
+
+// Table of irreducible polynomials to efficiently cycle through
+// GF(2^n)-{0}, 2<=n<=30.
+
+static long s_polys[] = {
+ 4 + 3,
+ 8 + 3,
+ 16 + 3,
+ 32 + 5,
+ 64 + 3,
+ 128 + 3,
+ 256 + 29,
+ 512 + 17,
+ 1024 + 9,
+ 2048 + 5,
+ 4096 + 83,
+ 8192 + 27,
+ 16384 + 43,
+ 32768 + 3,
+ 65536 + 45,
+ 131072 + 9,
+ 262144 + 39,
+ 524288 + 39,
+ 1048576 + 9,
+ 2097152 + 5,
+ 4194304 + 3,
+ 8388608 + 33,
+ 16777216 + 27,
+ 33554432 + 9,
+ 67108864 + 71,
+ 134217728 + 39,
+ 268435456 + 9,
+ 536870912 + 5,
+ 1073741824 + 83,
+ 0
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_HashViewer::c4_HashViewer (c4_Sequence& seq_, int numKeys_,
+ c4_Sequence* map_)
+ : _base (&seq_), _map (map_), _numKeys (numKeys_),
+ _pHash ("_H"), _pRow ("_R")
+{
+ if (_map.GetSize() == 0)
+ _map.SetSize(1);
+
+ int poly = GetPoly();
+ if (poly == 0 || _map.GetSize() <= _base.GetSize())
+ DictResize(_base.GetSize());
+}
+
+c4_HashViewer::~c4_HashViewer ()
+{
+}
+
+bool c4_HashViewer::IsUnused(int row_) const
+{
+ c4_RowRef r = _map[row_];
+ return _pRow (r) < 0 && _pHash (r) == 0;
+}
+
+bool c4_HashViewer::IsDummy(int row_) const
+{
+ c4_RowRef r = _map[row_];
+ return _pRow (r) < 0 && _pHash (r) < 0;
+}
+
+int c4_HashViewer::GetPoly() const
+{
+ return Hash(_map.GetSize()-1);
+}
+
+void c4_HashViewer::SetPoly(int v_)
+{
+ SetHash(_map.GetSize()-1, v_);
+}
+
+int c4_HashViewer::GetSpare() const
+{
+ return Row(_map.GetSize()-1);
+}
+
+void c4_HashViewer::SetSpare(int v_)
+{
+ SetRow(_map.GetSize()-1, v_);
+}
+
+bool c4_HashViewer::KeySame(int row_, c4_Cursor cursor_) const
+{
+ for (int i = 0; i < _numKeys; ++i)
+ {
+ c4_Bytes buffer;
+ _base.GetItem(row_, i, buffer);
+
+ c4_Handler& h = cursor_._seq->NthHandler(i);
+ if (h.Compare(cursor_._index, buffer) != 0)
+ return false;
+ }
+
+ return true;
+}
+
+/// Create mapped view which is uses a second view for hashing
+t4_i32 c4_HashViewer::CalcHash(c4_Cursor cursor_) const
+{
+ c4_Bytes buffer, buf2;
+ const t4_i32 endian = 0x03020100;
+ t4_i32 hash = 0;
+
+ for (int i = 0; i < _numKeys; ++i)
+ {
+ c4_Handler& h = cursor_._seq->NthHandler(i);
+ cursor_._seq->Get(cursor_._index, h.PropId(), buffer);
+
+ // this code borrows from Python's stringobject.c/string_hash()
+ int len = buffer.Size();
+ if (len > 0)
+ {
+ const t4_byte* p = buffer.Contents();
+
+ // 20030218: careful to avoid endian-ness sensitivity
+ if (*(const t4_byte*) &endian) // true on big-endian systems
+ switch (h.Property().Type())
+ {
+ case 'I': case 'L': case 'F': case 'D':
+ {
+ t4_byte* q = buf2.SetBuffer(len);
+ for (int j = 0; j < len; ++j)
+ q[len-j-1] = p[j];
+ p = q;
+ }
+ }
+
+ long x = *p << 7;
+
+ // modifications are risky, this code avoid scanning huge blobs
+ if (len > 200)
+ len = 100;
+
+ while (--len >= 0)
+ x = (1000003 * x) ^ *p++;
+
+ if (buffer.Size() > 200)
+ {
+ len = 100;
+ p += buffer.Size() - 200;
+ while (--len >= 0)
+ x = (1000003 * x) ^ *p++;
+ }
+
+ x ^= buffer.Size();
+ hash ^= x ^ i;
+ }
+ }
+
+ if (hash == 0)
+ hash = -1;
+
+ return hash;
+}
+
+/*
+ * Types of slots:
+ * Unused: row = -1, hash = 0
+ * Dummy: row = -1, hash = -1
+ * Active: row >= 0
+ * There must be at least one Unused slot at all times.
+ */
+
+int c4_HashViewer::LookDict(t4_i32 hash_, c4_Cursor cursor_) const
+{
+ const unsigned int mask = _map.GetSize() - 2;
+ /* We must come up with (i, incr) such that 0 <= i < _size
+ and 0 < incr < _size and both are a function of hash */
+ int i = mask & ~hash_;
+ /* We use ~hash_ instead of hash_, as degenerate hash functions, such
+ as for ints <sigh>, can have lots of leading zeros. It's not
+ really a performance risk, but better safe than sorry. */
+ if (IsUnused(i) || Hash(i) == hash_ && KeySame(Row(i), cursor_))
+ return i;
+
+ int freeslot = IsDummy(i) ? i : -1;
+
+ /* Derive incr from hash_, just to make it more arbitrary. Note that
+ incr must not be 0, or we will get into an infinite loop.*/
+ unsigned incr = (hash_ ^ ((unsigned long) hash_ >> 3)) & mask;
+ if (!incr)
+ incr = mask;
+
+ int poly = GetPoly();
+ for (;;)
+ {
+ i = (i+incr) & mask;
+ if (IsUnused(i))
+ break;
+ if (Hash(i) == hash_ && KeySame(Row(i), cursor_))
+ return i;
+ if (freeslot == -1 && IsDummy(i))
+ freeslot = i;
+ /* Cycle through GF(2^n)-{0} */
+ incr = incr << 1;
+ if (incr > mask)
+ incr ^= poly; /* This will implicitely clear the highest bit */
+ }
+
+ return freeslot != -1 ? freeslot : i;
+}
+
+void c4_HashViewer::InsertDict(int row_)
+{
+ c4_Cursor cursor = &_base[row_];
+
+ t4_i32 hash = CalcHash(cursor);
+ int i = LookDict(hash, cursor);
+
+ if (IsDummy(i))
+ {
+ int n = GetSpare();
+ d4_assert(n > 0);
+ SetSpare(n - 1);
+ }
+
+ SetHash(i, hash);
+ SetRow(i, row_);
+}
+
+void c4_HashViewer::RemoveDict(int pos_)
+{
+ c4_Cursor key = &_base[pos_];
+ t4_i32 hash = CalcHash(key);
+ int i = LookDict(hash, key);
+ d4_assert(i >= 0);
+
+ d4_assert(Row(i) == pos_);
+
+ SetHash(i, -1);
+ SetRow(i, -1);
+
+ SetSpare(GetSpare() + 1);
+}
+
+bool c4_HashViewer::DictResize(int minused)
+{
+ int i, newsize, newpoly;
+ for (i = 0, newsize = 4; ; i++, newsize <<= 1) {
+ if (s_polys[i] == 0)
+ return false;
+ else if (newsize > minused) {
+ newpoly = s_polys[i];
+ break;
+ }
+ }
+
+ _map.SetSize(0);
+
+ c4_Row empty;
+ _pRow (empty) = -1;
+ _map.InsertAt(0, empty, newsize + 1);
+
+ SetPoly(newpoly);
+ SetSpare(0);
+
+ for (int j = 0; j < _base.GetSize(); ++j)
+ InsertDict(j);
+
+ return true;
+}
+
+c4_View c4_HashViewer::GetTemplate()
+{
+ return _base.Clone();
+}
+
+int c4_HashViewer::GetSize()
+{
+ return _base.GetSize();
+}
+
+int c4_HashViewer::Lookup(c4_Cursor key_, int& count_)
+{
+ // can only use hashing if the properties match the query
+ // XXX it appears that this loop takes some 300 uS!
+ c4_View kv = (*key_).Container();
+ for (int k = 0; k < _numKeys; ++k)
+ if (kv.FindProperty(_base.NthProperty(k).GetId()) < 0)
+ return -1;
+
+ t4_i32 hash = CalcHash(key_); // TODO should combine with above loop
+ int i = LookDict(hash, key_);
+
+ int row = Row(i);
+ count_ = row >= 0 && KeySame(row, key_) ? 1 : 0;
+ return count_ ? row : 0; // don't return -1, we *know* it's not there
+}
+
+bool c4_HashViewer::GetItem(int row_, int col_, c4_Bytes& buf_)
+{
+ return _base.GetItem(row_, col_, buf_);
+}
+
+bool c4_HashViewer::SetItem(int row_, int col_, const c4_Bytes& buf_)
+{
+ if (col_ < _numKeys)
+ {
+ c4_Bytes temp;
+ _base.GetItem(row_, col_, temp);
+ if (buf_ == temp)
+ return true; // this call will have no effect, just ignore it
+
+ RemoveDict(row_);
+ }
+
+ _base.SetItem(row_, col_, buf_);
+
+ if (col_ < _numKeys)
+ {
+ // careful if changing a key to one which is already present:
+ // in that case, delete the other row to preserve uniqueness
+ //
+ // Note: this is a tricky and confusing issue, because now the
+ // mere act of *setting* a property value can *delete* a row!
+ //
+ // The big problem here is that setting the rest of the values
+ // in a loop can end up *wrong*, if the row has moved down!!!
+ int n;
+ int i = Lookup(&_base[row_], n);
+ if (i >= 0 && n > 0)
+ {
+ RemoveRows(i, 1);
+ if (i < row_)
+ --row_;
+ }
+
+ InsertDict(row_);
+ }
+
+ return true;
+}
+
+bool c4_HashViewer::InsertRows(int pos_, c4_Cursor value_, int count_)
+{
+ d4_assert(count_ > 0);
+
+ int n;
+ int i = Lookup(value_, n);
+ if (i >= 0 && n > 0)
+ {
+ _base.SetAt(i, *value_); // replace existing
+ return true;
+ }
+
+ // adjust row numbers if the insertion is not at the end
+ //
+ // TODO this could be optimized to go through the rows which
+ // were moved up, and then adjusting the map through a lookup
+ // (probably better than full scan if pos_ is relatively high)
+ if (pos_ < _base.GetSize())
+ {
+ for (int r = 0; r < _map.GetSize() - 1; ++r)
+ {
+ int n2 = Row(r);
+ if (n2 >= pos_)
+ SetRow(r, n2 + 1);
+ }
+ }
+
+ _base.InsertAt(pos_, *value_);
+ InsertDict(pos_);
+
+ int used = _base.GetSize();
+ int fill = used + GetSpare();
+ if (fill * 3 >= (_map.GetSize() - 1) * 2 && !DictResize(used * 2))
+ return false; // bail out
+
+ d4_assert(_base.GetSize() + GetSpare() < _map.GetSize() - 1);
+ return true;
+}
+
+bool c4_HashViewer::RemoveRows(int pos_, int count_)
+{
+ while (--count_ >= 0)
+ {
+ // since the map persists, be somewhat more aggressive than the
+ // original code in resizing down when the map is getting empty
+ if (_base.GetSize() * 3 < _map.GetSize() - 1 &&
+ !DictResize(_base.GetSize()))
+ return false; // bail out
+
+ RemoveDict(pos_);
+
+ // move rows down for now
+ //
+ // optionally: consider replacing with last entry (much faster)
+ for (int r = 0; r < _map.GetSize() - 1; ++r)
+ {
+ int n = Row(r);
+ if (n > pos_)
+ SetRow(r, n - 1);
+ }
+
+ _base.RemoveAt(pos_, 1);
+ }
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_BlockedViewer : public c4_CustomViewer
+{
+ enum { kLimit = 1000 };
+
+ c4_View _base;
+
+ c4_ViewProp _pBlock;
+ c4_DWordArray _offsets;
+
+ int Slot(int& pos_);
+ void Split(int block_, int row_);
+ void Merge(int block_);
+ void Validate() const;
+
+public:
+ c4_BlockedViewer (c4_Sequence& seq_);
+ virtual ~c4_BlockedViewer ();
+
+ virtual c4_View GetTemplate();
+ virtual int GetSize();
+ virtual bool GetItem(int row_, int col_, c4_Bytes& buf_);
+ virtual bool SetItem(int row_, int col_, const c4_Bytes& buf_);
+ virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1);
+ virtual bool RemoveRows(int pos_, int count_=1);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if q4_CHECK
+
+ // debugging version to verify that the internal data is consistent
+ void c4_BlockedViewer::Validate() const
+ {
+ d4_assert(_base.GetSize() >= 2);
+
+ int n = _base.GetSize() - 1;
+ d4_assert(_offsets.GetSize() == n);
+
+ int total = 0;
+ for (int i = 0; i < n; i++)
+ {
+ c4_View bv = _pBlock (_base[i]);
+ d4_assert(bv.GetSize() > 0 || i == 0);
+ total += bv.GetSize();
+ d4_assert((int) _offsets.GetAt(i) == total++);
+ }
+
+ c4_View be = _pBlock (_base[n]);
+ d4_assert(be.GetSize() == n - 1);
+ }
+
+#else
+
+ // nothing, so inline this thing to avoid even the calling overhead
+ d4_inline void c4_BlockedViewer::Validate() const
+ {
+ }
+
+#endif
+
+c4_BlockedViewer::c4_BlockedViewer (c4_Sequence& seq_)
+ : _base (&seq_), _pBlock ("_B")
+{
+ if (_base.GetSize() < 2)
+ _base.SetSize(2);
+
+ int n = _base.GetSize() - 1;
+ _offsets.SetSize(n);
+
+ int total = 0;
+ for (int i = 0; i < n; i++)
+ {
+ c4_View bv = _pBlock (_base[i]);
+ total += bv.GetSize();
+ _offsets.SetAt(i, total++);
+ }
+ Validate();
+}
+
+c4_BlockedViewer::~c4_BlockedViewer ()
+{
+}
+
+int c4_BlockedViewer::Slot(int& pos_)
+{
+ d4_assert(_offsets.GetSize() > 0);
+ d4_assert(pos_ <= (int) _offsets.GetAt(_offsets.GetSize() - 1));
+
+#if 0
+ const int n = _offsets.GetSize();
+
+ int h;
+ for (h = 0; h < n; ++h)
+ if (pos_ <= (t4_i32) _offsets.GetAt(h))
+ break;
+#else
+ // switch to binary search, adapted from code by Zhang Dehua, 28-3-2002
+ // slows down some 5%, but said to be much better with 5 million rows
+ int l = 0, h = _offsets.GetSize() - 1;
+ while (l < h) {
+ int m = l + (h - l) / 2;
+ if ((t4_i32) _offsets.GetAt(m) < pos_)
+ l = m + 1;
+ else
+ h = m;
+ }
+#endif
+
+ if (h > 0)
+ pos_ -= _offsets.GetAt(h-1) + 1;
+
+ return h;
+}
+
+void c4_BlockedViewer::Split(int bno_, int row_)
+{
+ int z = _base.GetSize() - 1;
+ d4_assert(bno_ < z);
+ c4_View bz = _pBlock (_base[z]);
+ c4_View bv = _pBlock (_base[bno_]);
+ d4_assert(row_ < bv.GetSize());
+
+ _offsets.InsertAt(bno_, _offsets.GetAt(bno_) - bv.GetSize() + row_);
+
+ _base.InsertAt(bno_+1, c4_Row ());
+ c4_View bn = _pBlock (_base[bno_+1]);
+
+ bv.RelocateRows(row_ + 1, -1, bn, 0);
+ bv.RelocateRows(row_, 1, bz, bno_);
+
+ Validate();
+}
+
+void c4_BlockedViewer::Merge(int bno_)
+{
+ int z = _base.GetSize() - 1;
+ c4_View bz = _pBlock (_base[z]);
+ c4_View bv1 = _pBlock (_base[bno_]);
+ c4_View bv2 = _pBlock (_base[bno_+1]);
+
+ _offsets.RemoveAt(bno_);
+
+ bz.RelocateRows(bno_, 1, bv1, -1);
+ bv2.RelocateRows(0, -1, bv1, -1);
+
+ _base.RemoveAt(bno_+1);
+
+ Validate();
+}
+
+c4_View c4_BlockedViewer::GetTemplate()
+{
+ c4_View bv = _pBlock (_base[0]);
+ return bv.Clone();
+}
+
+int c4_BlockedViewer::GetSize()
+{
+ int n = _offsets.GetAt(_offsets.GetSize() - 1);
+ d4_assert(n >= 0);
+ return n;
+}
+
+bool c4_BlockedViewer::GetItem(int row_, int col_, c4_Bytes& buf_)
+{
+ int orig = row_;
+
+ int i = Slot(row_);
+ d4_assert(0 <= i && i < _base.GetSize() - 1);
+
+ if ((t4_i32) _offsets.GetAt(i) == orig)
+ {
+ row_ = i;
+ i = _base.GetSize() - 1;
+ }
+
+ c4_View bv = _pBlock (_base[i]);
+ return bv.GetItem(row_, col_, buf_);
+}
+
+bool c4_BlockedViewer::SetItem(int row_, int col_, const c4_Bytes& buf_)
+{
+ int orig = row_;
+
+ int i = Slot(row_);
+ d4_assert(0 <= i && i < _base.GetSize() - 1);
+
+ if ((t4_i32) _offsets.GetAt(i) == orig)
+ {
+ row_ = i;
+ i = _base.GetSize() - 1;
+ }
+
+ c4_View bv = _pBlock (_base[i]);
+ bv.SetItem(row_, col_, buf_);
+ return true;
+}
+
+bool c4_BlockedViewer::InsertRows(int pos_, c4_Cursor value_, int count_)
+{
+ d4_assert(count_ > 0);
+
+ bool atEnd = pos_ == GetSize();
+
+ int z = _base.GetSize() - 1;
+ int i = Slot(pos_);
+ d4_assert(0 <= i && i < z);
+
+ c4_View bv = _pBlock (_base[i]);
+ d4_assert(0 <= pos_ && pos_ <= bv.GetSize());
+
+ bv.InsertAt(pos_, *value_, count_);
+ for (int j = i; j < z; ++j)
+ _offsets.SetAt(j, _offsets.GetAt(j) + count_);
+
+ // massive insertions are first split off
+ while (bv.GetSize() >= 2 * kLimit)
+ Split(i, bv.GetSize() - kLimit - 2);
+
+ if (bv.GetSize() > kLimit )
+ Split(i, atEnd ? kLimit - 1 : bv.GetSize() / 2); // 23-3-2002, from MB
+
+ Validate();
+
+ return true;
+}
+
+bool c4_BlockedViewer::RemoveRows(int pos_, int count_)
+{
+ d4_assert(count_ > 0);
+ d4_assert(pos_ + count_ <= GetSize());
+
+ int z = _base.GetSize() - 1;
+ int i = Slot(pos_);
+ d4_assert(0 <= i && i < z);
+
+ c4_View bv = _pBlock (_base[i]);
+ d4_assert(0 <= pos_ && pos_ <= bv.GetSize());
+
+ int todo = count_;
+
+ // optimize if the deletion goes past the end of this block...
+ int overshoot = pos_ + count_ - bv.GetSize();
+ if (overshoot > 0) {
+
+ // first, delete blocks which are going away completely
+ while (i+1 < _offsets.GetSize()) {
+ int nextsize = _offsets.GetAt(i+1) - _offsets.GetAt(i);
+ if (overshoot < nextsize)
+ break;
+ todo -= nextsize;
+ overshoot -= nextsize;
+
+ // drop the block and forget it ever existed
+ for (int j = i+1; j < z; ++j)
+ _offsets.SetAt(j, _offsets.GetAt(j) - nextsize);
+ _offsets.RemoveAt(i+1);
+
+ _base.RemoveAt(i+1);
+ --z;
+ c4_View bz = _pBlock (_base[z]);
+ bz.RemoveAt(i);
+
+ Validate();
+ }
+
+ // delete before merging, to avoid useless copying
+ if (overshoot > 1) {
+ c4_View bv2 = _pBlock (_base[i+1]);
+ bv2.RemoveAt(0, overshoot - 1);
+ todo -= overshoot - 1;
+
+ for (int j = i+1; j < z; ++j)
+ _offsets.SetAt(j, _offsets.GetAt(j) - (overshoot - 1));
+
+ // if the next block is filled enough, rotate the separator
+ // this avoids an expensive and unnecessary merge + split
+ if (bv2.GetSize() > kLimit / 2) {
+ c4_View bz = _pBlock (_base[z]);
+ bz[i] = bv2[0];
+ bv2.RemoveAt(0);
+ --todo;
+ d4_assert(pos_ + todo <= bv.GetSize());
+ d4_assert(i < _offsets.GetSize());
+
+ for (int j = i+1; j < z; ++j)
+ _offsets.SetAt(j, _offsets.GetAt(j) - 1);
+ }
+ }
+
+ // merge into one block
+ if (pos_ + todo > bv.GetSize()) {
+ d4_assert(i < z - 1);
+ Merge(i);
+ --z;
+ }
+ }
+ d4_assert(pos_ + todo <= bv.GetSize());
+
+ // now remove the rows and adjust offsets
+ if (todo > 0)
+ bv.RemoveAt(pos_, todo);
+
+ for (int j = i; j < z; ++j)
+ _offsets.SetAt(j, _offsets.GetAt(j) - todo);
+
+ // if the block underflows, merge it
+ if (bv.GetSize() < kLimit / 2) {
+ if (i > 0) // merge with predecessor, preferably
+ bv = _pBlock (_base[--i]);
+ if (i >= z - 1) // unless there is no successor to merge with
+ return true;
+ Merge(i);
+ }
+
+ // if the block overflows, split it
+ if (bv.GetSize() > kLimit )
+ Split(i, bv.GetSize() / 2);
+
+ Validate();
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_OrderedViewer : public c4_CustomViewer
+{
+ c4_View _base;
+ int _numKeys;
+
+ int KeyCompare(int row_, c4_Cursor cursor_) const;
+
+public:
+ c4_OrderedViewer (c4_Sequence& seq_, int numKeys_);
+ virtual ~c4_OrderedViewer ();
+
+ virtual c4_View GetTemplate();
+ virtual int GetSize();
+ virtual int Lookup(c4_Cursor key_, int& count_);
+ virtual bool GetItem(int row_, int col_, c4_Bytes& buf_);
+ virtual bool SetItem(int row_, int col_, const c4_Bytes& buf_);
+ virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1);
+ virtual bool RemoveRows(int pos_, int count_=1);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_OrderedViewer::c4_OrderedViewer (c4_Sequence& seq_, int numKeys_)
+ : _base (&seq_), _numKeys (numKeys_)
+{
+}
+
+c4_OrderedViewer::~c4_OrderedViewer ()
+{
+}
+
+int c4_OrderedViewer::KeyCompare(int row_, c4_Cursor cursor_) const
+{
+ for (int i = 0; i < _numKeys; ++i)
+ {
+ c4_Bytes buffer;
+ _base.GetItem(row_, i, buffer);
+
+ c4_Handler& h = cursor_._seq->NthHandler(i);
+ int f = h.Compare(cursor_._index, buffer);
+ if (f != 0)
+ return f;
+ }
+
+ return 0;
+}
+
+c4_View c4_OrderedViewer::GetTemplate()
+{
+ return _base.Clone();
+}
+
+int c4_OrderedViewer::GetSize()
+{
+ return _base.GetSize();
+}
+
+int c4_OrderedViewer::Lookup(c4_Cursor key_, int& count_)
+{
+ // can only use bsearch if the properties match the query
+ // XXX with ord1.tcl (dict words), this loop takes 300 uS!
+ c4_View kv = (*key_).Container();
+ for (int k = 0; k < _numKeys; ++k)
+ if (kv.FindProperty(_base.NthProperty(k).GetId()) < 0)
+ return -1;
+
+#if 0 // Locate gets the count wrong, it seems 2000-06-15
+ int pos;
+ count_ = _base.Locate(*key_, &pos);
+#else
+ int pos = _base.Search(*key_);
+ count_ = pos < _base.GetSize() && KeyCompare(pos, key_) == 0 ? 1 : 0;
+#endif
+ return pos;
+}
+
+bool c4_OrderedViewer::GetItem(int row_, int col_, c4_Bytes& buf_)
+{
+ return _base.GetItem(row_, col_, buf_);
+}
+
+bool c4_OrderedViewer::SetItem(int row_, int col_, const c4_Bytes& buf_)
+{
+ if (col_ < _numKeys)
+ {
+ c4_Bytes temp;
+ _base.GetItem(row_, col_, temp);
+ if (buf_ == temp)
+ return true; // this call will have no effect, just ignore it
+ }
+
+ _base.SetItem(row_, col_, buf_);
+
+ if (col_ < _numKeys)
+ {
+ c4_Row copy = _base[row_];
+ // have to remove the row because it messes up searching
+ // it would be more efficient to search *around* this row
+ // or perhaps figure out new pos before changing any data
+ RemoveRows(row_);
+ InsertRows(0, &copy); // position is ignored
+ }
+
+ return true;
+}
+
+bool c4_OrderedViewer::InsertRows(int, c4_Cursor value_, int count_)
+{
+ d4_assert(count_ > 0);
+
+ int n;
+ int i = Lookup(value_, n);
+
+ // XXX if the lookup does not work, then insert as first element (!?)
+ d4_assert(i >= 0);
+ if (i < 0)
+ i = 0;
+
+ if (n == 0)
+ _base.InsertAt(i, *value_);
+ else
+ {
+ d4_assert(i < _base.GetSize());
+ _base.SetAt(i, *value_); // replace existing
+ }
+
+ return true;
+}
+
+bool c4_OrderedViewer::RemoveRows(int pos_, int count_)
+{
+ _base.RemoveAt(pos_, count_);
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_IndexedViewer : public c4_CustomViewer
+{
+ c4_View _base;
+ c4_View _map;
+ c4_View _props;
+ bool _unique;
+ c4_IntProp _mapProp;
+
+ int KeyCompare(int row_, c4_Cursor cursor_) const;
+
+public:
+ c4_IndexedViewer (c4_Sequence& seq_, c4_Sequence& map_,
+ const c4_View& props_, bool unique_);
+ virtual ~c4_IndexedViewer ();
+
+ virtual c4_View GetTemplate();
+ virtual int GetSize();
+ virtual int Lookup(c4_Cursor key_, int& count_);
+ virtual bool GetItem(int row_, int col_, c4_Bytes& buf_);
+ virtual bool SetItem(int row_, int col_, const c4_Bytes& buf_);
+ virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1);
+ virtual bool RemoveRows(int pos_, int count_=1);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_IndexedViewer::c4_IndexedViewer (c4_Sequence& seq_, c4_Sequence& map_,
+ const c4_View& props_, bool unique_)
+ : _base (&seq_), _map (&map_), _props (props_), _unique (unique_),
+ _mapProp ((const c4_IntProp&) _map.NthProperty(0))
+{
+ int n = _base.GetSize();
+ if (_map.GetSize() != n)
+ {
+ c4_View sorted = _base.SortOn(_props);
+
+ _map.SetSize(n);
+ for (int i = 0; i < n; ++i)
+ _mapProp (_map[i]) = _base.GetIndexOf(sorted[i]);
+ }
+}
+
+c4_IndexedViewer::~c4_IndexedViewer ()
+{
+}
+
+int c4_IndexedViewer::KeyCompare(int row_, c4_Cursor cursor_) const
+{
+ int n = _props.NumProperties();
+ for (int i = 0; i < n; ++i)
+ {
+ c4_Bytes buffer;
+ _base.GetItem(row_, i, buffer);
+
+ c4_Handler& h = cursor_._seq->NthHandler(i);
+ int f = h.Compare(cursor_._index, buffer);
+ if (f != 0)
+ return f;
+ }
+
+ return 0;
+}
+
+c4_View c4_IndexedViewer::GetTemplate()
+{
+ return _base.Clone();
+}
+
+int c4_IndexedViewer::GetSize()
+{
+ return _base.GetSize();
+}
+
+int c4_IndexedViewer::Lookup(c4_Cursor key_, int& count_)
+{
+ // can only use bsearch if the properties match the query
+ // XXX with ord1.tcl (dict words), this loop takes 300 uS!
+ c4_View kv = (*key_).Container();
+ int n = _props.NumProperties();
+ for (int k = 0; k < n; ++k)
+ if (kv.FindProperty(_props.NthProperty(k).GetId()) < 0)
+ return -1;
+
+#if 0 // Locate gets the count wrong, it seems 2000-06-15
+ int pos;
+ count_ = _base.Locate(*key_, &pos);
+#else
+ int pos = _base.Search(*key_);
+ count_ = pos < _base.GetSize() && KeyCompare(pos, key_) == 0 ? 1 : 0;
+#endif
+ return pos;
+}
+
+bool c4_IndexedViewer::GetItem(int row_, int col_, c4_Bytes& buf_)
+{
+ return _base.GetItem(row_, col_, buf_);
+}
+
+bool c4_IndexedViewer::SetItem(int row_, int col_, const c4_Bytes& buf_)
+{
+ const int id = _base.NthProperty(col_).GetId();
+ const bool keyMod = _props.FindProperty(id) >= 0;
+
+ if (keyMod)
+ {
+ c4_Bytes temp;
+ _base.GetItem(row_, col_, temp);
+ if (buf_ == temp)
+ return true; // this call will have no effect, just ignore it
+ }
+
+ _base.SetItem(row_, col_, buf_);
+
+ if (keyMod)
+ {
+ // TODO adjust index
+ }
+
+ return true;
+}
+
+bool c4_IndexedViewer::InsertRows(int, c4_Cursor value_, int count_)
+{
+ d4_assert(count_ > 0);
+
+ if (_unique)
+ count_ = 1;
+
+ int n;
+ int i = Lookup(value_, n);
+
+ // XXX if the lookup does not work, then insert as first element (!?)
+ d4_assert(i >= 0);
+ if (i < 0)
+ i = 0;
+
+ if (n == 0)
+ _base.InsertAt(i, *value_);
+ else
+ {
+ d4_assert(i < _base.GetSize());
+ _base.SetAt(i, *value_); // replace existing
+ }
+
+ return true;
+}
+
+bool c4_IndexedViewer::RemoveRows(int pos_, int count_)
+{
+ _base.RemoveAt(pos_, count_);
+
+ int n = _map.GetSize();
+ while (--n >= 0)
+ {
+ int v = _mapProp (_map[n]);
+ if (v >= pos_)
+ if (v < pos_ + count_)
+ _map.RemoveAt(n);
+ else
+ _mapProp (_map[n]) = v - count_;
+ }
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_CustomViewer* f4_CreateReadOnly(c4_Sequence& seq_)
+{
+ return d4_new c4_ReadOnlyViewer (seq_);
+}
+
+c4_CustomViewer* f4_CreateHash(c4_Sequence& seq_, int nk_, c4_Sequence* map_)
+{
+ return d4_new c4_HashViewer (seq_, nk_, map_);
+}
+
+c4_CustomViewer* f4_CreateBlocked(c4_Sequence& seq_)
+{
+ return d4_new c4_BlockedViewer (seq_);
+}
+
+c4_CustomViewer* f4_CreateOrdered(c4_Sequence& seq_, int nk_)
+{
+ return d4_new c4_OrderedViewer (seq_, nk_);
+}
+
+c4_CustomViewer* f4_CreateIndexed(c4_Sequence& seq_, c4_Sequence& map_,
+ const c4_View& props_, bool unique_)
+{
+ return d4_new c4_IndexedViewer (seq_, map_, props_, unique_);
+}
+
+/////////////////////////////////////////////////////////////////////////////