summaryrefslogtreecommitdiffstats
path: root/akregator/src/mk4storage/metakit/src
diff options
context:
space:
mode:
Diffstat (limited to 'akregator/src/mk4storage/metakit/src')
-rw-r--r--akregator/src/mk4storage/metakit/src/Makefile.am10
-rw-r--r--akregator/src/mk4storage/metakit/src/borc.h33
-rw-r--r--akregator/src/mk4storage/metakit/src/column.cpp1533
-rw-r--r--akregator/src/mk4storage/metakit/src/column.h212
-rw-r--r--akregator/src/mk4storage/metakit/src/column.inl89
-rw-r--r--akregator/src/mk4storage/metakit/src/custom.cpp1066
-rw-r--r--akregator/src/mk4storage/metakit/src/custom.h63
-rw-r--r--akregator/src/mk4storage/metakit/src/derived.cpp1003
-rw-r--r--akregator/src/mk4storage/metakit/src/derived.h25
-rw-r--r--akregator/src/mk4storage/metakit/src/field.cpp119
-rw-r--r--akregator/src/mk4storage/metakit/src/field.h64
-rw-r--r--akregator/src/mk4storage/metakit/src/field.inl37
-rw-r--r--akregator/src/mk4storage/metakit/src/fileio.cpp434
-rw-r--r--akregator/src/mk4storage/metakit/src/format.cpp1341
-rw-r--r--akregator/src/mk4storage/metakit/src/format.h23
-rw-r--r--akregator/src/mk4storage/metakit/src/gnuc.h13
-rw-r--r--akregator/src/mk4storage/metakit/src/handler.cpp500
-rw-r--r--akregator/src/mk4storage/metakit/src/handler.h150
-rw-r--r--akregator/src/mk4storage/metakit/src/handler.inl90
-rw-r--r--akregator/src/mk4storage/metakit/src/header.h215
-rw-r--r--akregator/src/mk4storage/metakit/src/mfc.h34
-rw-r--r--akregator/src/mk4storage/metakit/src/msvc.h39
-rw-r--r--akregator/src/mk4storage/metakit/src/mwcw.h31
-rw-r--r--akregator/src/mk4storage/metakit/src/persist.cpp1185
-rw-r--r--akregator/src/mk4storage/metakit/src/persist.h127
-rw-r--r--akregator/src/mk4storage/metakit/src/remap.cpp1154
-rw-r--r--akregator/src/mk4storage/metakit/src/remap.h26
-rw-r--r--akregator/src/mk4storage/metakit/src/std.cpp84
-rw-r--r--akregator/src/mk4storage/metakit/src/std.h63
-rw-r--r--akregator/src/mk4storage/metakit/src/store.cpp587
-rw-r--r--akregator/src/mk4storage/metakit/src/store.h114
-rw-r--r--akregator/src/mk4storage/metakit/src/store.inl20
-rw-r--r--akregator/src/mk4storage/metakit/src/string.cpp304
-rw-r--r--akregator/src/mk4storage/metakit/src/table.cpp160
-rw-r--r--akregator/src/mk4storage/metakit/src/univ.cpp227
-rw-r--r--akregator/src/mk4storage/metakit/src/univ.h115
-rw-r--r--akregator/src/mk4storage/metakit/src/univ.inl126
-rw-r--r--akregator/src/mk4storage/metakit/src/view.cpp1271
-rw-r--r--akregator/src/mk4storage/metakit/src/viewx.cpp857
-rw-r--r--akregator/src/mk4storage/metakit/src/win.h33
40 files changed, 13577 insertions, 0 deletions
diff --git a/akregator/src/mk4storage/metakit/src/Makefile.am b/akregator/src/mk4storage/metakit/src/Makefile.am
new file mode 100644
index 00000000..e428fe93
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/Makefile.am
@@ -0,0 +1,10 @@
+INCLUDES = -I$(srcdir)/../include
+
+noinst_LTLIBRARIES = libmetakitlocal.la
+
+noinst_HEADERS = borc.h column.h column.inl custom.h derived.h field.h field.inl format.h gnuc.h handler.h handler.inl header.h mfc.h \
+ msvc.h mwcw.h persist.h remap.h std.h store.h store.inl univ.h univ.inl win.h
+
+libmetakitlocal_la_SOURCES = column.cpp custom.cpp derived.cpp field.cpp fileio.cpp format.cpp handler.cpp persist.cpp remap.cpp std.cpp store.cpp \
+ string.cpp table.cpp univ.cpp view.cpp viewx.cpp
+
diff --git a/akregator/src/mk4storage/metakit/src/borc.h b/akregator/src/mk4storage/metakit/src/borc.h
new file mode 100644
index 00000000..2b881fc9
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/borc.h
@@ -0,0 +1,33 @@
+// borc.h --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Configuration header for Borland C++
+ */
+
+#define q4_BORC 1
+
+ // get rid of several common warning messages
+#if !q4_STRICT
+#pragma warn -aus // 'identifier' is assigned a value that is never used
+#pragma warn -par // Parameter 'parameter' is never used.
+#pragma warn -sig // Conversion may lose significant digits
+#pragma warn -use // 'identifier' declared but never used
+#endif
+
+#if __BORLANDC__ >= 0x500
+#define q4_BOOL 1 // supports the bool datatype
+ // undo previous defaults, because q4_BOOL is not set early enough
+#undef false
+#undef true
+#undef bool
+#endif
+
+#if !defined (q4_EXPORT)
+#define q4_EXPORT 1 // requires export/import specifiers
+#endif
+
+#if defined (__MT__)
+#define q4_MULTI 1 // uses multi-threading
+#endif
diff --git a/akregator/src/mk4storage/metakit/src/column.cpp b/akregator/src/mk4storage/metakit/src/column.cpp
new file mode 100644
index 00000000..2d191c64
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/column.cpp
@@ -0,0 +1,1533 @@
+// column.cpp --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Implements c4_Column, c4_ColOfInts, and c4_ColIter
+ */
+
+#include "header.h"
+#include "column.h"
+#include "persist.h"
+
+#if !q4_INLINE
+#include "column.inl"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if !HAVE_MEMMOVE && !HAVE_BCOPY
+ // in case we have no library memmove, or one that can't handle overlap
+
+ void f4_memmove (void* to_, const void* from_, int n_)
+ {
+ char* to = (char*) to_;
+ const char* from = (const char*) from_;
+
+ if (to + n_ <= from || from + n_ <= to)
+ memcpy(to, from, n_);
+ else if (to < from)
+ while (--n_ >= 0)
+ *to++ = *from++;
+ else if (to > from)
+ while (--n_ >= 0)
+ to[n_] = from[n_];
+ }
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_Column
+
+c4_Column::c4_Column (c4_Persist* persist_)
+ : _position (0), _size (0), _persist (persist_), _gap (0),
+ _slack (0), _dirty (false)
+{
+}
+
+#if q4_CHECK
+
+ // debugging version to verify that the internal data is consistent
+ void c4_Column::Validate() const
+ {
+ d4_assert(0 <= _slack && _slack < kSegMax);
+
+ if (_segments.GetSize() == 0)
+ return; // ok, not initialized
+
+ d4_assert(_gap <= _size);
+
+ int n = fSegIndex(_size + _slack);
+ d4_assert(n == _segments.GetSize() - 1);
+
+ t4_byte* p = (t4_byte*) _segments.GetAt(n);
+
+ if (fSegRest(_size + _slack) == 0)
+ d4_assert(p == 0);
+ else
+ d4_assert(p != 0);
+
+ while (--n >= 0) {
+ t4_byte* p = (t4_byte*) _segments.GetAt(n);
+ d4_assert(p != 0);
+ }
+ }
+
+#else
+
+ // nothing, so inline this thing to avoid even the calling overhead
+ d4_inline void c4_Column::Validate() const
+ {
+ }
+
+#endif
+
+c4_Column::~c4_Column ()
+{
+ Validate();
+ ReleaseAllSegments();
+
+ // this is needed to remove this column from the cache
+ d4_assert(_slack == 0);
+ FinishSlack();
+
+ _slack = -1; // bad value in case we try to set up again (!)
+}
+
+c4_Strategy& c4_Column::Strategy() const
+{
+ d4_assert(_persist != 0);
+
+ return _persist->Strategy();
+}
+
+bool c4_Column::IsMapped() const
+{
+ return _position > 1 && _persist != 0 && Strategy()._mapStart != 0;
+}
+
+bool c4_Column::UsesMap(const t4_byte* ptr_) const
+{
+ // the most common falsifying case is checked first
+ return _persist != 0 && ptr_ >= Strategy()._mapStart &&
+ Strategy()._dataSize != 0 && // added 2003-05-08, thx V DeMarco
+ ptr_ - Strategy()._mapStart < Strategy()._dataSize;
+}
+
+bool c4_Column::RequiresMap() const
+{
+ if (_persist != 0 && Strategy()._mapStart != 0)
+ for (int i = _segments.GetSize(); --i >= 0; )
+ if (UsesMap((t4_byte*) _segments.GetAt(i)))
+ return true;
+ return false;
+}
+
+void c4_Column::ReleaseSegment(int index_)
+{
+ t4_byte* p = (t4_byte*) _segments.GetAt(index_);
+ if (!UsesMap(p))
+ delete [] p;
+}
+
+void c4_Column::ReleaseAllSegments()
+{
+ //for (int i = 0; i < _segments.GetSize(); ++i)
+ for (int i = _segments.GetSize(); --i >= 0; )
+ ReleaseSegment(i); // last one might be a null pointer
+
+ _segments.SetSize(0);
+
+ _gap = 0;
+ _slack = 0;
+
+ if (_size == 0)
+ _position = 0;
+
+ _dirty = false;
+}
+
+ //@func Define where data is on file, or setup buffers (opt cleared).
+void c4_Column::SetLocation(t4_i32 pos_, t4_i32 size_)
+{
+ d4_assert(size_ > 0 || pos_ == 0);
+
+ ReleaseAllSegments();
+
+ _position = pos_;
+ _size = size_;
+
+ // There are two position settings:
+ //
+ // 0 = raw buffer, no file access
+ // >1 = file position from where data can be loaded on demand
+
+ _dirty = pos_ == 0;
+}
+
+void c4_Column::PullLocation(const t4_byte*& ptr_)
+{
+ d4_assert(_segments.GetSize() == 0);
+
+ _size = PullValue(ptr_);
+ _position = 0;
+ if (_size > 0) {
+ _position = PullValue(ptr_);
+ if (_position > 0) {
+ d4_assert(_persist != 0);
+ _persist->OccupySpace(_position, _size);
+ }
+ }
+
+ _dirty = false;
+}
+
+ //@func How many contiguous bytes are there at a specified position.
+int c4_Column::AvailAt(t4_i32 offset_) const
+{
+ d4_assert(offset_ <= _size);
+ d4_assert(_gap <= _size);
+
+ t4_i32 limit = _gap;
+
+ if (offset_ >= _gap) {
+ offset_ += _slack;
+ limit = _size + _slack;
+ }
+
+ int count = kSegMax - fSegRest(offset_);
+ if (offset_ + count > limit)
+ count = (int) (limit - offset_);
+
+ // either some real data or it must be at the very end of all data
+ d4_assert(0 < count && count <= kSegMax ||
+ count == 0 && offset_ == _size + _slack);
+ return count;
+}
+
+void c4_Column::SetupSegments()
+{
+ d4_assert(_segments.GetSize() == 0);
+ d4_assert(_gap == 0);
+ d4_assert(_slack == 0);
+
+ // The last entry in the _segments array is either a partial block
+ // or a null pointer, so calling "fSegIndex(_size)" is always allowed.
+
+ int n = fSegIndex(_size) + 1;
+ _segments.SetSize(n);
+
+ // treat last block differently if it is a partial entry
+ int last = n;
+ if (fSegRest(_size))
+ --last; // this block is partial, size is 1 .. kSegMax-1
+ else
+ --n; // the last block is left as a null pointer
+
+ int id = -1;
+ if (_position < 0) { // special aside id, figure out the real position
+ d4_assert(_persist != 0);
+ id = ~_position;
+ _position = _persist->LookupAside(id);
+ d4_assert(_position >= 0);
+ }
+
+ if (IsMapped()) {
+ // setup for mapped files is quick, just fill in the pointers
+ d4_assert(_position > 1);
+ d4_assert(_position + (n-1) * kSegMax <= Strategy()._dataSize);
+ const t4_byte* map = Strategy()._mapStart + _position;
+
+ for (int i = 0; i < n; ++i) {
+ _segments.SetAt(i, (t4_byte*) map); // loses const
+ map += kSegMax;
+ }
+ } else {
+ int chunk = kSegMax;
+ t4_i32 pos = _position;
+
+ // allocate buffers, load them if necessary
+ for (int i = 0; i < n; ++i) {
+ if (i == last)
+ chunk = fSegRest(_size);
+
+ t4_byte* p = d4_new t4_byte [chunk];
+ _segments.SetAt(i, p);
+
+ if (_position > 0) {
+ d4_dbgdef(int n =)
+ Strategy().DataRead(pos, p, chunk);
+ d4_assert(n == chunk);
+ pos += chunk;
+ }
+ }
+ }
+
+ if (id >= 0) {
+ d4_assert(_persist != 0);
+ _persist->ApplyAside(id, *this);
+ }
+
+ Validate();
+}
+
+ //@func Makes sure the requested data is in a modifiable buffer.
+t4_byte* c4_Column::CopyNow(t4_i32 offset_)
+{
+ d4_assert(offset_ <= _size);
+
+ _dirty = true;
+
+ const t4_byte* ptr = LoadNow(offset_);
+ if (UsesMap(ptr)) {
+ if (offset_ >= _gap)
+ offset_ += _slack;
+
+ // this will only force creation of a buffer
+ ptr = CopyData(offset_, offset_, 0);
+ d4_assert(!UsesMap(ptr));
+ }
+
+ return (t4_byte*) ptr;
+}
+
+ //@func Copies data, creating a buffer if needed. Must be in single segment.
+t4_byte* c4_Column::CopyData(t4_i32 to_, t4_i32 from_, int count_)
+{
+ int i = fSegIndex(to_);
+ t4_byte* p = (t4_byte*) _segments.GetAt(i);
+
+ if (UsesMap(p)) {
+ int n = kSegMax;
+ if (fSegOffset(i) + n > _size + _slack)
+ n = (int) (_size + _slack - fSegOffset(i));
+
+ d4_assert(n > 0);
+
+ t4_byte* q = d4_new t4_byte [n];
+ memcpy(q, p, n); // some copying can be avoided, overwritten below...
+ _segments.SetAt(i, q);
+
+ p = q;
+ }
+
+ p += fSegRest(to_);
+
+ if (count_ > 0) {
+ d4_assert(fSegIndex(to_ + count_ - 1) == i);
+
+ const t4_byte* src = (const t4_byte*) _segments.GetAt(fSegIndex(from_));
+ d4_memmove(p, src + fSegRest(from_), count_);
+ }
+
+ return p;
+}
+
+ /*
+ * Resizing a segmented vector can be a complicated operation.
+ * For now, simply making it work in all cases is the first priority.
+ *
+ * A major simplification - and good performance improvement - is caused
+ * by the trick of maintaining a "gap" in the data, which can be "moved"
+ * around to allow fast insertion as well as simple (delayed) deletion.
+ *
+ * The only complexity comes from the fact that the gap must end up being
+ * less than one full segment in size. Therefore, insertion and removal
+ * across segment boundaries needs to handle a variety of situations.
+ *
+ * Since complete segments can be inserted quickly, this approach avoids
+ * lots of copying when consecutive insertions/deletions are clustered.
+ * Even random changes move half as much (on average) as without a gap.
+ *
+ * The price is the overhead of up to one segment of empty space, and the
+ * complexity of this code (all the magic is within this c4_Column class).
+ */
+
+void c4_Column::MoveGapUp(t4_i32 dest_)
+{
+ d4_assert(dest_ <= _size);
+ d4_assert(_gap < dest_);
+ d4_assert(_slack > 0);
+
+ // forward loop to copy contents down, in little pieces if need be
+ while (_gap < dest_) {
+ int n = kSegMax - fSegRest(_gap);
+ t4_i32 curr = _gap + n;
+ if (curr > dest_)
+ curr = dest_;
+
+ // copy to [_gap..curr), which is inside one segment
+ d4_assert(_gap < curr);
+ d4_assert(fSegIndex(_gap) == fSegIndex(curr - 1));
+
+ // copy from [_gap + _slack .. curr + _slack), of the same size
+ t4_i32 fromBeg = _gap + _slack;
+ t4_i32 fromEnd = curr + _slack;
+
+ while (fromBeg < fromEnd) {
+ int k = kSegMax - fSegRest(fromBeg);
+ if (fromBeg + k > fromEnd)
+ k = (int) (fromEnd - fromBeg);
+
+ d4_assert(k > 0);
+
+ CopyData(_gap, fromBeg, k);
+
+ _gap += k;
+ fromBeg += k;
+ }
+
+ _gap = curr;
+ }
+
+ d4_assert(_gap == dest_);
+}
+
+void c4_Column::MoveGapDown(t4_i32 dest_)
+{
+ d4_assert(dest_ <= _size);
+ d4_assert(_gap > dest_);
+ d4_assert(_slack > 0);
+
+ // reverse loop to copy contents up, in little pieces if need be
+ t4_i32 toEnd = _gap + _slack;
+ t4_i32 toBeg = dest_ + _slack;
+
+ while (toEnd > toBeg) {
+ int n = fSegRest(toEnd);
+ t4_i32 curr = toEnd - (n ? n : kSegMax);
+ if (curr < toBeg)
+ curr = toBeg;
+
+ // copy to [curr..toEnd), which is inside one segment
+ d4_assert(curr < toEnd);
+ d4_assert(fSegIndex(curr) == fSegIndex(toEnd - 1));
+
+ // copy from [fromBeg .. _gap), which has the same size
+ t4_i32 fromBeg = _gap - (toEnd - curr);
+
+ while (_gap > fromBeg) {
+ int k = fSegRest(_gap);
+ if (k == 0)
+ k = kSegMax;
+ if (_gap - k < fromBeg)
+ k = (int) (_gap - fromBeg);
+
+ d4_assert(k > 0);
+
+ toEnd -= k;
+ _gap -= k;
+
+ CopyData(toEnd, _gap, k);
+ }
+ }
+
+ d4_assert(_gap == dest_);
+}
+
+void c4_Column::MoveGapTo(t4_i32 pos_)
+{
+ d4_assert(pos_ <= _size);
+
+ if (_slack == 0) // if there is no real gap, then just move it
+ _gap = pos_;
+ else if (_gap < pos_) // move the gap up, ie. some bytes down
+ MoveGapUp(pos_);
+ else if (_gap > pos_) // move the gap down, ie. some bytes up
+ if (_gap - pos_ > _size - _gap + fSegRest(pos_)) {
+ RemoveGap(); // it's faster to get rid of the gap instead
+ _gap = pos_;
+ }
+ else // normal case, move some bytes up
+ MoveGapDown(pos_);
+
+ d4_assert(_gap == pos_);
+
+ Validate();
+}
+
+void c4_Column::RemoveGap()
+{
+ if (_slack > 0) {
+ if (_gap < _size)
+ MoveGapUp(_size);
+
+ d4_assert(_gap == _size); // the gap is now at the end
+ d4_assert(_slack < kSegMax);
+
+ // Case 1: gap is at start of segment
+ // ==================================
+ //
+ // G G+S
+ //
+ // | |
+ // :----+xx:
+ // | |
+ //
+ // i i+1 (limit)
+ //
+ // Case 2: gap is inside segment
+ // =============================
+ //
+ // G G+S
+ //
+ // | |
+ // :--+--+x:
+ // | |
+ //
+ // i i+1 (limit)
+ //
+ // Case 3: gap runs to end of segment
+ // ==================================
+ //
+ // G G+S
+ //
+ // | |
+ // :--+----:0000000:
+ // | | |
+ //
+ // i i+1 i+2 (limit)
+ //
+ // Case 4: gap is across two segments
+ // ==================================
+ //
+ // G G+S
+ //
+ // | |
+ // :--+----:-+xxxxx:
+ // | | |
+ //
+ // i i+1 i+2 (limit)
+
+ int i = fSegIndex(_gap);
+ int n = fSegRest(_gap);
+
+ if (n == 0) { // case 1
+ ReleaseSegment(i);
+ _segments.SetAt(i, 0);
+ } else {
+ if (n + _slack > kSegMax) // case 4
+ ReleaseSegment(i+1);
+
+ // truncate rest of segment
+ t4_byte* p = d4_new t4_byte [n];
+ memcpy(p, _segments.GetAt(i), n);
+
+ ReleaseSegment(i);
+ _segments.SetAt(i, p);
+ _segments.SetSize(i + 1);
+ }
+
+ _slack = 0;
+ }
+
+ Validate();
+}
+
+void c4_Column::Grow(t4_i32 off_, t4_i32 diff_)
+{
+ d4_assert(off_ <= _size);
+ d4_assert(diff_ > 0);
+
+ if (_segments.GetSize() == 0)
+ SetupSegments();
+
+ Validate();
+
+ _dirty = true;
+
+ // move the gap so it starts where we want to insert
+ MoveGapTo(off_);
+
+ t4_i32 bigSlack = _slack;
+ if (bigSlack < diff_) { // only do more if this isn't good enough
+ // number of segments to insert
+ int n = fSegIndex(diff_ - _slack + kSegMax - 1);
+ d4_assert(n > 0);
+
+ int i1 = fSegIndex(_gap);
+ int i2 = fSegIndex(_gap + _slack);
+
+ bool moveBack = false;
+
+ if (i2 > i1) // cases 3 and 4
+ ++i1;
+ else if (fSegRest(_gap)) // case 2
+ moveBack = true;
+
+ _segments.InsertAt(i1, 0, n);
+ for (int i = 0; i < n; ++i)
+ _segments.SetAt(i1 + i, d4_new t4_byte [(int) kSegMax]);
+
+ bigSlack += fSegOffset(n);
+
+ if (moveBack) {
+ d4_assert(i1 == fSegIndex(_gap));
+
+ // we have inserted too low, move bytes in front of gap back
+ CopyData(fSegOffset(i1), fSegOffset(i1 + n), fSegRest(_gap));
+ }
+ }
+
+ d4_assert(diff_ <= bigSlack && bigSlack < diff_ + kSegMax);
+
+ _gap += diff_;
+ _slack = (int) (bigSlack - diff_);
+ _size += diff_;
+
+ FinishSlack();
+}
+
+void c4_Column::Shrink(t4_i32 off_, t4_i32 diff_)
+{
+ d4_assert(off_ <= _size);
+ d4_assert(diff_ > 0);
+
+ if (_segments.GetSize() == 0)
+ SetupSegments();
+
+ Validate();
+
+ _dirty = true;
+
+ // the simplification here is that we have in fact simply *two*
+ // gaps and we must merge them together and end up with just one
+
+ if (_slack > 0) {
+ if (_gap < off_) // if too low, move the gap up
+ MoveGapTo(off_);
+ else if (off_ + diff_ < _gap) // if too high, move down to end
+ MoveGapTo(off_ + diff_);
+
+ // the gap is now inside, or adjacent to, the deleted area
+ d4_assert(off_ <= _gap && _gap <= off_ + diff_);
+ }
+
+ _gap = off_;
+
+ // check whether the merged gap would cross a segment boundary
+ int i1 = fSegIndex(_gap);
+ int i2 = fSegIndex(_gap + _slack + diff_);
+
+ // drop complete segments, not a partially filled boundary
+ if (fSegRest(_gap))
+ ++i1;
+
+ // moved up (was after the next if in the 1.7 May 28 build)
+ _slack += diff_;
+ _size -= diff_;
+
+ int n = i2 - i1;
+ if (n > 0) {
+ for (int i = i1; i < i2; ++i)
+ ReleaseSegment(i);
+
+ _segments.RemoveAt(i1, n);
+
+ // the logic in 1.7 of May 28 was warped (the assert "fix" was wrong)
+ d4_assert(_slack >= fSegOffset(n));
+ _slack -= fSegOffset(n);
+ }
+
+ d4_assert(0 <= _slack && _slack < 2 * kSegMax);
+
+ // if the gap is at the end, get rid of a partial segment after it
+ if (_gap == _size) {
+ int i = fSegIndex(_size + _slack);
+ if (i != fSegIndex(_gap)) {
+ d4_assert(i == fSegIndex(_gap) + 1);
+ d4_assert(i == _segments.GetSize() - 1);
+
+ ReleaseSegment(i);
+ _segments.SetAt(i, 0);
+
+ _slack -= fSegRest(_size + _slack);
+
+ d4_assert(_slack < kSegMax);
+ d4_assert(fSegRest(_gap + _slack) == 0);
+ }
+ }
+
+ // the slack may still be too large to leave as is
+ if (_slack >= kSegMax) {
+ // move the bytes just after the end of the gap one segment down
+ int x = fSegRest(_gap + _slack);
+ int r = kSegMax - x;
+ if (_gap + r > _size)
+ r = (int) (_size - _gap);
+
+ CopyData(_gap, _gap + _slack, r);
+
+ int i = fSegIndex(_gap + kSegMax - 1);
+ ReleaseSegment(i);
+
+ if (r + x < kSegMax)
+ _segments.SetAt(i, 0);
+ else
+ _segments.RemoveAt(i);
+
+ _slack -= r + x;
+ _gap += r;
+ }
+
+ // if we have no data anymore, make sure not to use the file map either
+ if (_size == 0 && _slack > 0)
+ CopyNow(0);
+
+ FinishSlack();
+}
+
+void c4_Column::FinishSlack()
+{
+ Validate();
+
+ // optimization: if partial end segment easily fits in slack, move it down
+ t4_i32 gapEnd = _gap + _slack;
+ if (!fSegRest(gapEnd) && gapEnd >= _size + 500) {
+ // slack is at least 500 bytes more than the partial end segment
+ // also, the gap must end exactly on a segment boundary
+ int i = fSegIndex(gapEnd);
+ d4_assert(i == _segments.GetSize() - 1);
+
+ int n = _size - _gap;
+ CopyData(gapEnd - n, gapEnd, n);
+
+ ReleaseSegment(i);
+ _segments.SetAt(i, 0);
+
+ _slack -= n;
+ d4_assert(_slack >= 500);
+
+ Validate();
+ }
+}
+
+void c4_Column::SaveNow(c4_Strategy& strategy_, t4_i32 pos_)
+{
+ if (_segments.GetSize() == 0)
+ SetupSegments();
+
+ // write all segments
+ c4_ColIter iter (*this, 0, _size);
+ while (iter.Next(kSegMax)) {
+ int n = iter.BufLen();
+ strategy_.DataWrite(pos_, iter.BufLoad(), n);
+ if (strategy_._failure != 0)
+ break;
+ pos_ += n;
+ }
+}
+
+const t4_byte* c4_Column::FetchBytes(t4_i32 pos_, int len_, c4_Bytes& buffer_, bool forceCopy_)
+{
+ d4_assert(len_ > 0);
+ d4_assert(pos_ + len_ <= ColSize());
+ d4_assert(0 <= _slack && _slack < kSegMax);
+
+ c4_ColIter iter (*this, pos_, pos_ + len_);
+ iter.Next();
+
+ // most common case, all bytes are inside the same segment
+ if (!forceCopy_ && iter.BufLen() == len_)
+ return iter.BufLoad();
+
+ t4_byte* p = buffer_.SetBuffer(len_);
+ do {
+ d4_assert(iter.BufLen() > 0);
+ memcpy(p, iter.BufLoad(), iter.BufLen());
+ p += iter.BufLen();
+ } while (iter.Next());
+ d4_assert(p == buffer_.Contents() + len_);
+
+ return buffer_.Contents();
+}
+
+void c4_Column::StoreBytes(t4_i32 pos_, const c4_Bytes& buffer_)
+{
+ int n = buffer_.Size();
+ if (n > 0) {
+ d4_assert(pos_ + n <= ColSize());
+
+ c4_ColIter iter (*this, pos_, pos_ + n);
+
+ const t4_byte* p = buffer_.Contents();
+ while (iter.Next(n)) {
+ d4_assert(iter.BufLen() > 0);
+ memcpy(iter.BufSave(), p, iter.BufLen());
+ p += iter.BufLen();
+ }
+ d4_assert(p == buffer_.Contents() + n);
+ }
+}
+
+ /*
+ PushValue and PullValue deal with variable-sized storage of
+ one unsigned integer value of up to 32 bits. Depending on the
+ magnitude of the integer, 1..6 bytes are used to represent it.
+ Each byte holds 7 significant bits and one continuation bit.
+ This saves storage, but it is also byte order independent.
+ Negative values are stored as a zero byte plus positive value.
+ */
+
+t4_i32 c4_Column::PullValue(const t4_byte*& ptr_)
+{
+ t4_i32 mask = *ptr_ ? 0 : ~0;
+
+ t4_i32 v = 0;
+ for (;;) {
+ v = (v << 7) + *ptr_;
+ if (*ptr_++ & 0x80)
+ break;
+ }
+
+ return mask ^ (v - 0x80); // oops, last byte had bit 7 set
+}
+
+void c4_Column::PushValue(t4_byte*& ptr_, t4_i32 v_)
+{
+ if (v_ < 0) {
+ v_ = ~v_;
+ *ptr_++ = 0;
+ }
+
+ int n = 0;
+ do
+ n += 7;
+ while ((v_ >> n) && n < 32);
+
+ while (n) {
+ n -= 7;
+ t4_byte b = (t4_byte) ((v_ >> n) & 0x7F);
+ if (!n)
+ b |= 0x80; // set bit 7 on the last byte
+ *ptr_++ = b;
+ }
+}
+
+void c4_Column::InsertData(t4_i32 index_, t4_i32 count_, bool clear_)
+{
+ d4_assert(index_ <= ColSize());
+
+ if (count_ > 0) {
+ Grow(index_, count_);
+
+ // clear the contents, in separate chunks if necessary
+ if (clear_) {
+ c4_ColIter iter (*this, index_, index_ + count_);
+ while (iter.Next())
+ memset(iter.BufSave(), 0, iter.BufLen());
+ }
+ }
+}
+
+void c4_Column::RemoveData(t4_i32 index_, t4_i32 count_)
+{
+ d4_assert(index_ + count_ <= ColSize());
+
+ if (count_ > 0)
+ Shrink(index_, count_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void c4_ColOfInts::Get_0b(int)
+{
+ *(t4_i32*) _item = 0;
+}
+
+void c4_ColOfInts::Get_1b(int index_)
+{
+ t4_i32 off = index_ >> 3;
+ d4_assert(off < ColSize());
+
+ *(t4_i32*) _item = (*LoadNow(off) >> (index_ & 7)) & 0x01;
+}
+
+void c4_ColOfInts::Get_2b(int index_)
+{
+ t4_i32 off = index_ >> 2;
+ d4_assert(off < ColSize());
+
+ *(t4_i32*) _item = (*LoadNow(off) >> ((index_ & 3) << 1)) & 0x03;
+}
+
+void c4_ColOfInts::Get_4b(int index_)
+{
+ t4_i32 off = index_ >> 1;
+ d4_assert(off < ColSize());
+
+ *(t4_i32*) _item = (*LoadNow(off) >> ((index_ & 1) << 2)) & 0x0F;
+}
+
+void c4_ColOfInts::Get_8i(int index_)
+{
+ t4_i32 off = index_;
+ d4_assert(off < ColSize());
+
+ *(t4_i32*) _item = *(const signed char*) LoadNow(off);
+}
+
+void c4_ColOfInts::Get_16i(int index_)
+{
+ t4_i32 off = index_ * (t4_i32) 2;
+ d4_assert(off + 2 <= ColSize());
+
+ const t4_byte* vec = LoadNow(off);
+
+ _item[0] = vec[0];
+ _item[1] = vec[1];
+
+ *(t4_i32*) _item = *(const short*) _item;
+}
+
+void c4_ColOfInts::Get_16r(int index_)
+{
+ t4_i32 off = index_ * (t4_i32) 2;
+ d4_assert(off + 2 <= ColSize());
+
+ const t4_byte* vec = LoadNow(off);
+
+ // 2003-02-02 - gcc 3.2.1 on linux (!) fails to compile this
+ // sign-extension trick properly, use a temp buffer instead:
+ //*(t4_i32*) _item = *(const short*) _item;
+
+ t4_byte temp[2];
+ temp[1] = vec[0];
+ temp[0] = vec[1];
+ *(t4_i32*) _item = *(const short*) temp;
+}
+
+void c4_ColOfInts::Get_32i(int index_)
+{
+ t4_i32 off = index_ * (t4_i32) 4;
+ d4_assert(off + 4 <= ColSize());
+
+ const t4_byte* vec = LoadNow(off);
+
+ _item[0] = vec[0];
+ _item[1] = vec[1];
+ _item[2] = vec[2];
+ _item[3] = vec[3];
+}
+
+void c4_ColOfInts::Get_32r(int index_)
+{
+ t4_i32 off = index_ * (t4_i32) 4;
+ d4_assert(off + 4 <= ColSize());
+
+ const t4_byte* vec = LoadNow(off);
+
+ _item[3] = vec[0];
+ _item[2] = vec[1];
+ _item[1] = vec[2];
+ _item[0] = vec[3];
+}
+
+void c4_ColOfInts::Get_64i(int index_)
+{
+ t4_i32 off = index_ * (t4_i32) 8;
+ d4_assert(off + 8 <= ColSize());
+
+ const t4_byte* vec = LoadNow(off);
+
+ for (int i = 0; i < 8; ++i)
+ _item[i] = vec[i];
+}
+
+void c4_ColOfInts::Get_64r(int index_)
+{
+ t4_i32 off = index_ * (t4_i32) 8;
+ d4_assert(off + 8 <= ColSize());
+
+ const t4_byte* vec = LoadNow(off);
+
+ for (int i = 0; i < 8; ++i)
+ _item[7-i] = vec[i];
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+ static int fBitsNeeded(t4_i32 v)
+ {
+ if ((v >> 4) == 0) {
+ static int bits[] = { 0, 1, 2, 2, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4 };
+ return bits[(int) v];
+ }
+
+ if (v < 0) // first flip all bits if bit 31 is set
+ v = ~ v; // ... bit 31 is now always zero
+
+ // then check if bits 15-31 used (32b), 7-31 used (16b), else (8b)
+ return v >> 15 ? 32 : v >> 7 ? 16 : 8;
+ }
+
+bool c4_ColOfInts::Set_0b(int, const t4_byte* item_)
+{
+ t4_i32 v = *(const t4_i32*) item_;
+ return v == 0;
+}
+
+bool c4_ColOfInts::Set_1b(int index_, const t4_byte* item_)
+{
+ t4_i32 v = *(const t4_i32*) item_;
+
+ t4_i32 off = index_ >> 3;
+ d4_assert(off < ColSize());
+
+ index_ &= 7;
+
+ t4_byte* p = CopyNow(off);
+ *p = (*p & ~(1 << index_)) | (((t4_byte) v & 1) << index_);
+
+ return (v >> 1) == 0;
+}
+
+bool c4_ColOfInts::Set_2b(int index_, const t4_byte* item_)
+{
+ t4_i32 v = *(const t4_i32*) item_;
+
+ t4_i32 off = index_ >> 2;
+ d4_assert(off < ColSize());
+
+ const int n = (index_ & 3) << 1;
+
+ t4_byte* p = CopyNow(off);
+ *p = (*p & ~(0x03 << n)) | (((t4_byte) v & 0x03) << n);
+
+ return (v >> 2) == 0;
+}
+
+bool c4_ColOfInts::Set_4b(int index_, const t4_byte* item_)
+{
+ t4_i32 v = *(const t4_i32*) item_;
+
+ t4_i32 off = index_ >> 1;
+ d4_assert(off < ColSize());
+
+ const int n = (index_ & 1) << 2;
+
+ t4_byte* p = CopyNow(off);
+ *p = (*p & ~(0x0F << n)) | (((t4_byte) v & 0x0F) << n);
+
+ return (v >> 4) == 0;
+}
+
+// avoid a bug in MS EVC 3.0's code gen for ARM (i.e. WinCE)
+#ifdef _ARM_
+#pragma optimize("g",off)
+#endif
+
+bool c4_ColOfInts::Set_8i(int index_, const t4_byte* item_)
+{
+ t4_i32 v = *(const t4_i32*) item_;
+
+ t4_i32 off = index_;
+ d4_assert(off < ColSize());
+
+ *(char*) CopyNow(off) = (char) v;
+
+ return v == (signed char) v;
+}
+
+#ifdef _ARM_
+#pragma optimize("",on)
+#endif
+
+bool c4_ColOfInts::Set_16i(int index_, const t4_byte* item_)
+{
+ t4_i32 v = *(const t4_i32*) item_;
+
+ t4_i32 off = index_ * (t4_i32) 2;
+ d4_assert(off + 2 <= ColSize());
+
+ *(short*) CopyNow(off) = (short) v;
+
+ return v == (short) v;
+}
+
+bool c4_ColOfInts::Set_16r(int index_, const t4_byte* item_)
+{
+ t4_i32 v = *(const t4_i32*) item_;
+
+ t4_byte buf [2];
+ *(short*) buf = (short) v;
+
+ t4_i32 off = index_ * (t4_i32) 2;
+ d4_assert(off + 2 <= ColSize());
+
+ t4_byte* vec = CopyNow(off);
+
+ vec[1] = buf[0];
+ vec[0] = buf[1];
+
+ return v == (short) v;
+}
+
+bool c4_ColOfInts::Set_32i(int index_, const t4_byte* item_)
+{
+ t4_i32 v = *(const t4_i32*) item_;
+
+ t4_i32 off = index_ * (t4_i32) 4;
+ d4_assert(off + 4 <= ColSize());
+
+ *(t4_i32*) CopyNow(off) = (t4_i32) v;
+
+ return true;
+}
+
+bool c4_ColOfInts::Set_32r(int index_, const t4_byte* item_)
+{
+ t4_i32 off = index_ * (t4_i32) 4;
+ d4_assert(off + 4 <= ColSize());
+
+ t4_byte* vec = CopyNow(off);
+
+ vec[3] = item_[0];
+ vec[2] = item_[1];
+ vec[1] = item_[2];
+ vec[0] = item_[3];
+
+ return true;
+}
+
+bool c4_ColOfInts::Set_64i(int index_, const t4_byte* item_)
+{
+ t4_i32 off = index_ * (t4_i32) 8;
+ d4_assert(off + 8 <= ColSize());
+
+ t4_byte* vec = CopyNow(off);
+
+ for (int i = 0; i < 8; ++i)
+ vec[i] = item_[i];
+
+ return true;
+}
+
+bool c4_ColOfInts::Set_64r(int index_, const t4_byte* item_)
+{
+ t4_i32 off = index_ * (t4_i32) 8;
+ d4_assert(off + 8 <= ColSize());
+
+ t4_byte* vec = CopyNow(off);
+
+ for (int i = 0; i < 8; ++i)
+ vec[7-i] = item_[i];
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_ColOfInts::c4_ColOfInts (c4_Persist* persist_, int width_)
+ : c4_Column (persist_),
+ _getter (&c4_ColOfInts::Get_0b), _setter (&c4_ColOfInts::Set_0b),
+ _currWidth (0), _dataWidth (width_), _numRows (0), _mustFlip (false)
+{
+}
+
+void c4_ColOfInts::ForceFlip()
+{
+ _mustFlip = true;
+}
+
+int c4_ColOfInts::RowCount() const
+{
+ d4_assert(_numRows >= 0);
+
+ return _numRows;
+}
+
+int c4_ColOfInts::CalcAccessWidth(int numRows_, t4_i32 colSize_)
+{
+ d4_assert(numRows_ > 0);
+
+ int w = (int) ((colSize_ << 3) / numRows_);
+
+ // deduce sub-byte sizes for small vectors, see c4_ColOfInts::Set
+ if (numRows_ <= 7 && 0 < colSize_ && colSize_ <= 6) {
+ static t4_byte realWidth [][6] = {
+ // sz = 1: 2: 3: 4: 5: 6:
+ { 8, 16, 1, 32, 2, 4 }, // n = 1
+ { 4, 8, 1, 16, 2, 0 }, // n = 2
+ { 2, 4, 8, 1, 0, 16 }, // n = 3
+ { 2, 4, 0, 8, 1, 0 }, // n = 4
+ { 1, 2, 4, 0, 8, 0 }, // n = 5
+ { 1, 2, 4, 0, 0, 8 }, // n = 6
+ { 1, 2, 0, 4, 0, 0 }, // n = 7
+ };
+
+ w = realWidth [numRows_-1] [(int) colSize_ - 1];
+ d4_assert(w > 0);
+ }
+
+ return (w & (w - 1)) == 0 ? w : -1;
+}
+
+void c4_ColOfInts::SetRowCount(int numRows_)
+{
+ _numRows = numRows_;
+ if (numRows_ > 0) {
+ int w = CalcAccessWidth(numRows_, ColSize());
+ d4_assert(w >= 0);
+ SetAccessWidth(w);
+ }
+}
+
+void c4_ColOfInts::FlipBytes()
+{
+ if (_currWidth > 8) {
+ int step = _currWidth >> 3;
+
+ c4_ColIter iter (*this, 0, ColSize());
+ while (iter.Next(step)) {
+ t4_byte* data = iter.BufSave();
+ d4_assert(data != 0);
+
+ for (int j = 0; j < step; ++j) {
+ t4_byte c = data[j];
+ data[j] = data[step-j-1];
+ data[step-j-1] = c;
+ }
+ }
+ }
+}
+
+void c4_ColOfInts::SetAccessWidth(int bits_)
+{
+ d4_assert((bits_ & (bits_ - 1)) == 0);
+
+ int l2bp1 = 0; // "log2 bits plus one" needed to represent value
+ while (bits_) {
+ ++l2bp1;
+ bits_ >>= 1;
+ }
+ d4_assert(0 <= l2bp1 && l2bp1 < 8);
+
+ _currWidth = (1 << l2bp1) >> 1;
+
+ if (l2bp1 > 4 && (_mustFlip || Persist() != 0 && Strategy()._bytesFlipped))
+ l2bp1 += 3; // switch to the trailing entries for byte flipping
+
+ // Metrowerks Codewarrior 11 is dumb, it requires the "&c4_ColOfInts::"
+
+ static tGetter gTab [] =
+ {
+ &c4_ColOfInts::Get_0b, // 0: 0 bits/entry
+ &c4_ColOfInts::Get_1b, // 1: 1 bits/entry
+ &c4_ColOfInts::Get_2b, // 2: 2 bits/entry
+ &c4_ColOfInts::Get_4b, // 3: 4 bits/entry
+
+ &c4_ColOfInts::Get_8i, // 4: 8 bits/entry
+ &c4_ColOfInts::Get_16i, // 5: 16 bits/entry
+ &c4_ColOfInts::Get_32i, // 6: 32 bits/entry
+ &c4_ColOfInts::Get_64i, // 7: 64 bits/entry
+
+ &c4_ColOfInts::Get_16r, // 8: 16 bits/entry, reversed
+ &c4_ColOfInts::Get_32r, // 9: 32 bits/entry, reversed
+ &c4_ColOfInts::Get_64r, // 10: 64 bits/entry, reversed
+ };
+
+ static tSetter sTab [] =
+ {
+ &c4_ColOfInts::Set_0b, // 0: 0 bits/entry
+ &c4_ColOfInts::Set_1b, // 1: 1 bits/entry
+ &c4_ColOfInts::Set_2b, // 2: 2 bits/entry
+ &c4_ColOfInts::Set_4b, // 3: 4 bits/entry
+
+ &c4_ColOfInts::Set_8i, // 4: 8 bits/entry
+ &c4_ColOfInts::Set_16i, // 5: 16 bits/entry
+ &c4_ColOfInts::Set_32i, // 6: 32 bits/entry
+ &c4_ColOfInts::Set_64i, // 7: 64 bits/entry
+
+ &c4_ColOfInts::Set_16r, // 8: 16 bits/entry, reversed
+ &c4_ColOfInts::Set_32r, // 9: 32 bits/entry, reversed
+ &c4_ColOfInts::Set_64r, // 10: 64 bits/entry, reversed
+ };
+
+ d4_assert(l2bp1 < sizeof gTab / sizeof *gTab);
+
+ _getter = gTab[l2bp1];
+ _setter = sTab[l2bp1];
+
+ d4_assert(_getter != 0 && _setter != 0);
+}
+
+int c4_ColOfInts::ItemSize(int)
+{
+ return _currWidth >= 8 ? _currWidth >> 3 : - _currWidth;
+}
+
+const void* c4_ColOfInts::Get(int index_, int& length_)
+{
+ d4_assert(sizeof _item >= _dataWidth);
+
+ (this->*_getter)(index_);
+
+ length_ = _dataWidth;
+ return _item;
+}
+
+void c4_ColOfInts::Set(int index_, const c4_Bytes& buf_)
+{
+ d4_assert(buf_.Size() == _dataWidth);
+
+ if ((this->*_setter)(index_, buf_.Contents()))
+ return;
+
+ d4_assert(buf_.Size() == sizeof (t4_i32));
+
+ int n = fBitsNeeded(*(const t4_i32*) buf_.Contents());
+ if (n > _currWidth) {
+ int k = RowCount();
+
+ t4_i32 oldEnd = ColSize();
+ t4_i32 newEnd = ((t4_i32) k * n + 7) >> 3;
+
+ if (newEnd > oldEnd) {
+ InsertData(oldEnd, newEnd - oldEnd, _currWidth == 0);
+
+ // 14-5-2002: need to get rid of gap in case it risks not being a
+ // multiple of the increased size (bug, see s46 regression test)
+ //
+ // Example scenario: gap size is odd, data gets resized to 2/4-byte
+ // ints, data at end fits without moving gap to end, then we end
+ // up with a vector that has an int split *across* the gap - this
+ // commits just fine, but access to that split int is now bad.
+ //
+ // Lesson: need stricter/simpler consistency, it's way too complex!
+ if (n > 8)
+ RemoveGap();
+ }
+
+ // data value exceeds width, expand to new size and repeat
+ if (_currWidth > 0) {
+ d4_assert(n % _currWidth == 0); // must be expanding by a multiple
+
+ // To expand, we start by inserting a new appropriate chunk
+ // at the end, and expand the entries in place (last to first).
+
+ tGetter oldGetter = _getter;
+ SetAccessWidth(n);
+
+ d4_assert(sizeof _item >= _dataWidth);
+
+ // this expansion in place works because it runs backwards
+ while (--k >= 0) {
+ (this->*oldGetter)(k);
+ (this->*_setter)(k, _item);
+ }
+ } else {
+ if (_dataWidth > (int) sizeof (t4_i32))
+ n = _dataWidth << 3; // don't trust setter result, use max instead
+
+ SetAccessWidth(n);
+ }
+
+ // now repeat the failed call to _setter
+ /* bool f = */ (this->*_setter)(index_, buf_.Contents());
+ //? d4_assert(f);
+ }
+}
+
+t4_i32 c4_ColOfInts::GetInt(int index_)
+{
+ int n;
+ const void* p = Get(index_, n);
+ d4_assert(n == sizeof (t4_i32));
+ return *(const t4_i32*) p;
+}
+
+void c4_ColOfInts::SetInt(int index_, t4_i32 value_)
+{
+ Set(index_, c4_Bytes (&value_, sizeof value_));
+}
+
+int c4_ColOfInts::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_)
+{
+ d4_assert(b1_.Size() == sizeof (t4_i32));
+ d4_assert(b2_.Size() == sizeof (t4_i32));
+
+ t4_i32 v1 = *(const t4_i32*) b1_.Contents();
+ t4_i32 v2 = *(const t4_i32*) b2_.Contents();
+
+ return v1 == v2 ? 0 : v1 < v2 ? -1 : +1;
+}
+
+void c4_ColOfInts::Insert(int index_, const c4_Bytes& buf_, int count_)
+{
+ d4_assert(buf_.Size() == _dataWidth);
+ d4_assert(count_ > 0);
+
+ bool clear = true;
+ const t4_byte* ptr = buf_.Contents();
+
+ for (int i = 0; i < _dataWidth; ++i)
+ if (*ptr++) {
+ clear = false;
+ break;
+ }
+
+ ResizeData(index_, count_, clear);
+
+ if (!clear)
+ while (--count_ >= 0)
+ Set(index_++, buf_);
+}
+
+void c4_ColOfInts::Remove(int index_, int count_)
+{
+ d4_assert(count_ > 0);
+
+ ResizeData(index_, - count_);
+}
+
+void c4_ColOfInts::ResizeData(int index_, int count_, bool clear_)
+{
+ _numRows += count_;
+
+ if (!(_currWidth & 7)) { // not 1, 2, or 4
+ const t4_i32 w = (t4_i32) (_currWidth >> 3);
+ if (count_ > 0)
+ InsertData(index_ * w, count_ * w, clear_);
+ else
+ RemoveData(index_ * w, - count_ * w);
+ return;
+ }
+
+ d4_assert(_currWidth == 1 || _currWidth == 2 || _currWidth == 4);
+
+ /* _currwidth 1: 2: 4:
+ * shiftPos 3 2 1 shift the offset right this much
+ * maskPos 7 3 1 mask the offset with this
+ */
+
+ const int shiftPos = _currWidth == 4 ? 1 : 4 - _currWidth;
+ const int maskPos = (1 << shiftPos) - 1;
+
+ // the following code is similar to c4_Column::Resize, but at bit level
+
+ // turn insertion into deletion by inserting entire bytes
+ if (count_ > 0) {
+ unsigned off = (unsigned) index_ >> shiftPos;
+ int gapBytes = (count_ + maskPos) >> shiftPos;
+
+ InsertData(off, gapBytes, clear_);
+
+ // oops, we might have inserted too low by a few entries
+ const int bits = (index_ & maskPos) * _currWidth;
+ if (bits) {
+ const int maskLow = (1 << bits) - 1;
+
+ // move the first few bits to start of inserted range
+ t4_byte* p = CopyNow(off + gapBytes);
+ t4_byte one = *p & maskLow;
+ *p &= ~maskLow;
+
+ * CopyNow(off) = one;
+ }
+
+ index_ += count_;
+ count_ -= gapBytes << shiftPos;
+ d4_assert(count_ <= 0);
+ }
+
+ // now perform a deletion using a forward loop to copy down
+ if (count_ < 0) {
+ c4_Bytes temp;
+
+ while (index_ < _numRows) {
+ int length;
+ const void* ptr = Get(index_ - count_, length);
+ Set(index_++, c4_Bytes (ptr, length));
+ }
+ }
+ else {
+ d4_assert(count_ == 0);
+ }
+
+ FixSize(false);
+}
+
+void c4_ColOfInts::FixSize(bool fudge_)
+{
+ int n = RowCount();
+ t4_i32 needBytes = ((t4_i32) n * _currWidth + 7) >> 3;
+
+ // use a special trick to mark sizes less than 1 byte in storage
+ if (fudge_ && 1 <= n && n <= 4 && (_currWidth & 7)) {
+ const int shiftPos = _currWidth == 4 ? 1 : 4 - _currWidth;
+
+ static t4_byte fakeSizes [3][4] = { // n: 1: 2: 3: 4:
+ { 6, 1, 2, 2 }, // 4-bit entries: 4b 8b 12b 16b
+ { 5, 5, 1, 1 }, // 2-bit entries: 2b 4b 6b 8b
+ { 3, 3, 4, 5 }, // 1-bit entries: 1b 2b 3b 4b
+ };
+
+ // The idea is to use an "impossible" size (ie. 5, for n = 2)
+ // to give information about the current bit packing density.
+ d4_assert(needBytes <= 2);
+ needBytes = fakeSizes [shiftPos-1] [n-1];
+ }
+
+ t4_i32 currSize = ColSize();
+
+ if (needBytes < currSize)
+ RemoveData(needBytes, currSize - needBytes);
+ else if (needBytes > currSize)
+ InsertData(currSize, needBytes - currSize, true);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool c4_ColIter::Next()
+{
+ _pos += _len;
+
+ _len = _column.AvailAt(_pos);
+ _ptr = _column.LoadNow(_pos);
+
+ if (!_ptr)
+ _len = 0;
+ else if (_pos + _len >= _limit)
+ _len = _limit - _pos;
+ else { // 19990831 - optimization to avoid most copying
+ // while the end is adjacent to the next segment, extend it
+ while (_ptr + _len == _column.LoadNow(_pos + _len)) {
+ int n = _column.AvailAt(_pos + _len);
+ if (n == 0)
+ break; // may be a short column (strings)
+
+ _len += n;
+
+ if (_pos + _len >= _limit) {
+ _len = _limit - _pos;
+ break;
+ }
+ }
+ }
+
+ return _len > 0;
+}
+
+bool c4_ColIter::Next(int max_)
+{
+ _pos += _len;
+
+ _len = _column.AvailAt(_pos);
+ _ptr = _column.LoadNow(_pos);
+
+ if (!_ptr)
+ _len = 0;
+ else if (_pos + _len > _limit)
+ _len = _limit - _pos;
+
+ if (_len <= 0)
+ return false;
+
+ if (_len > max_)
+ _len = max_;
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/column.h b/akregator/src/mk4storage/metakit/src/column.h
new file mode 100644
index 00000000..3f6e4f15
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/column.h
@@ -0,0 +1,212 @@
+// column.h --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Definition of the column classes
+ */
+
+#ifndef __COLUMN_H__
+#define __COLUMN_H__
+
+/////////////////////////////////////////////////////////////////////////////
+// Declarations in this file
+
+ class c4_Column; // a column in a table
+ class c4_ColIter; // an iterator over column data
+ class c4_ColCache; // manages a cache for columns
+
+ class c4_Persist; // not defined here
+ class c4_Strategy; // not defined here
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_Column
+{
+ c4_PtrArray _segments;
+ t4_i32 _position;
+ t4_i32 _size;
+ c4_Persist* _persist;
+ t4_i32 _gap;
+ int _slack;
+ bool _dirty;
+
+public:
+ c4_Column (c4_Persist* persist_);
+ //: Constructs a column using the specified persistence manager.
+ ~c4_Column ();
+
+ void SetBuffer(t4_i32);
+ //: Allocate a new buffer of the specified size.
+
+ c4_Persist* Persist() const;
+ //: Returns persistence manager for this column, or zero.
+ c4_Strategy& Strategy() const;
+ //: Returns the associated strategy pointer.
+ t4_i32 Position() const;
+ //: Special access for the DUMP program.
+ t4_i32 ColSize() const;
+ //: Returns the number of bytes as stored on disk.
+ bool IsDirty() const;
+ //: Returns true if contents needs to be saved.
+
+ void SetLocation(t4_i32, t4_i32);
+ //: Sets the position and size of this column on file.
+ void PullLocation(const t4_byte*& ptr_);
+ //: Extract position and size of this column.
+
+ int AvailAt(t4_i32 offset_) const;
+ //: Returns number of bytes we can access at once.
+ const t4_byte* LoadNow(t4_i32);
+ //: Makes sure the data is loaded into memory.
+ t4_byte* CopyNow(t4_i32);
+ //: Makes sure a copy of the data is in memory.
+ void Grow(t4_i32, t4_i32);
+ //: Grows the buffer by inserting space.
+ void Shrink(t4_i32, t4_i32);
+ //: Shrinks the buffer by removing space.
+ void SaveNow(c4_Strategy&, t4_i32 pos_);
+ //: Save the buffer to file.
+
+ const t4_byte* FetchBytes(t4_i32 pos_, int len_, c4_Bytes& buffer_, bool forceCopy_);
+ //: Returns pointer to data, use buffer only if non-contiguous.
+ void StoreBytes(t4_i32 pos_, const c4_Bytes& buffer_);
+ //: Stores a copy of the buffer in the column.
+
+ bool RequiresMap() const;
+ void ReleaseAllSegments();
+
+ static t4_i32 PullValue(const t4_byte*& ptr_);
+ static void PushValue(t4_byte*& ptr_, t4_i32 v_);
+
+ void InsertData(t4_i32 index_, t4_i32 count_, bool clear_);
+ void RemoveData(t4_i32 index_, t4_i32 count_);
+ void RemoveGap();
+
+ enum { kSegBits = 12, kSegMax = 1 << kSegBits, kSegMask = kSegMax - 1 };
+
+private:
+ static int fSegIndex(t4_i32 offset_);
+ static t4_i32 fSegOffset(int index_);
+ static int fSegRest(t4_i32 offset_);
+
+ bool UsesMap(const t4_byte*) const;
+ bool IsMapped() const;
+
+ void ReleaseSegment(int);
+ void SetupSegments();
+ void Validate() const;
+ void FinishSlack();
+
+ void MoveGapUp(t4_i32 pos_);
+ void MoveGapDown(t4_i32 pos_);
+ void MoveGapTo(t4_i32 pos_);
+
+ t4_byte* CopyData(t4_i32, t4_i32, int);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_ColOfInts : public c4_Column
+{
+public:
+ c4_ColOfInts (c4_Persist* persist_, int width_ =sizeof (t4_i32));
+
+ int RowCount() const;
+ void SetRowCount(int numRows_);
+
+ void FlipBytes();
+
+ int ItemSize(int index_);
+ const void* Get(int index_, int& length_);
+ void Set(int index_, const c4_Bytes& buf_);
+
+ t4_i32 GetInt(int index_);
+ void SetInt(int index_, t4_i32 value_);
+
+ void Insert(int index_, const c4_Bytes& buf_, int count_);
+ void Remove(int index_, int count_);
+
+ static int CalcAccessWidth(int numRows_, t4_i32 colSize_);
+
+ void SetAccessWidth(int bits_);
+ void FixSize(bool fudge_);
+ void ForceFlip();
+
+ static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_);
+
+private:
+ typedef void (c4_ColOfInts::*tGetter) (int);
+ typedef bool (c4_ColOfInts::*tSetter) (int, const t4_byte*);
+
+ void Get_0b(int index_);
+ void Get_1b(int index_);
+ void Get_2b(int index_);
+ void Get_4b(int index_);
+ void Get_8i(int index_);
+ void Get_16i(int index_);
+ void Get_16r(int index_);
+ void Get_32i(int index_);
+ void Get_32r(int index_);
+ void Get_64i(int index_);
+ void Get_64r(int index_);
+
+ bool Set_0b(int index_, const t4_byte* item_);
+ bool Set_1b(int index_, const t4_byte* item_);
+ bool Set_2b(int index_, const t4_byte* item_);
+ bool Set_4b(int index_, const t4_byte* item_);
+ bool Set_8i(int index_, const t4_byte* item_);
+ bool Set_16i(int index_, const t4_byte* item_);
+ bool Set_16r(int index_, const t4_byte* item_);
+ bool Set_32i(int index_, const t4_byte* item_);
+ bool Set_32r(int index_, const t4_byte* item_);
+ bool Set_64i(int index_, const t4_byte* item_);
+ bool Set_64r(int index_, const t4_byte* item_);
+
+ void ResizeData(int index_, int count_, bool clear_ =false);
+
+ tGetter _getter;
+ tSetter _setter;
+
+ union {
+ t4_byte _item[8]; // holds temp result (careful with alignment!)
+ double _aligner; // needed for SPARC
+ };
+
+ int _currWidth; // number of bits used for one entry (0..64)
+ int _dataWidth; // number of bytes used for passing a value along
+ int _numRows;
+ bool _mustFlip;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_ColIter
+{
+ c4_Column& _column;
+ t4_i32 _limit;
+ t4_i32 _pos;
+ int _len;
+ const t4_byte* _ptr;
+
+public:
+ c4_ColIter (c4_Column& col_, t4_i32 offset_, t4_i32 limit_);
+// ~c4_ColIter ();
+
+ bool Next();
+ bool Next(int max_);
+
+ const t4_byte* BufLoad() const;
+ t4_byte* BufSave();
+ int BufLen() const;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if q4_INLINE
+#include "column.inl"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/akregator/src/mk4storage/metakit/src/column.inl b/akregator/src/mk4storage/metakit/src/column.inl
new file mode 100644
index 00000000..58f34437
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/column.inl
@@ -0,0 +1,89 @@
+// column.inl --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Inlined members of the column classes
+ */
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_Column
+
+d4_inline int c4_Column::fSegIndex(t4_i32 offset_)
+{
+ // limited by max array: 1 << (kSegBits + 15) with 16-bit ints
+ return (int) (offset_ >> kSegBits);
+}
+
+d4_inline t4_i32 c4_Column::fSegOffset(int index_)
+{
+ return (t4_i32) index_ << kSegBits;
+}
+
+d4_inline int c4_Column::fSegRest(t4_i32 offset_)
+{
+ return ((int) offset_ & kSegMask);
+}
+
+d4_inline c4_Persist* c4_Column::Persist() const
+{
+ return _persist;
+}
+
+d4_inline t4_i32 c4_Column::Position() const
+{
+ return _position;
+}
+
+d4_inline t4_i32 c4_Column::ColSize() const
+{
+ return _size;
+}
+
+d4_inline bool c4_Column::IsDirty() const
+{
+ return _dirty;
+}
+
+d4_inline void c4_Column::SetBuffer(t4_i32 length_)
+{
+ SetLocation(0, length_);
+ _dirty = true;
+}
+
+d4_inline const t4_byte* c4_Column::LoadNow(t4_i32 offset_)
+{
+ if (_segments.GetSize() == 0)
+ SetupSegments();
+
+ if (offset_ >= _gap)
+ offset_ += _slack;
+
+ t4_byte* ptr = (t4_byte*) _segments.GetAt(fSegIndex(offset_));
+ return ptr + fSegRest(offset_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_ColIter
+
+d4_inline c4_ColIter::c4_ColIter (c4_Column& col_, t4_i32 offset_, t4_i32 limit_)
+ : _column (col_), _limit (limit_), _pos (offset_), _len (0), _ptr (0)
+{
+}
+
+d4_inline const t4_byte* c4_ColIter::BufLoad() const
+{
+ return _ptr;
+}
+
+d4_inline t4_byte* c4_ColIter::BufSave()
+{
+ return _column.CopyNow(_pos);
+}
+
+d4_inline int c4_ColIter::BufLen() const
+{
+ return _len;
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/custom.cpp b/akregator/src/mk4storage/metakit/src/custom.cpp
new file mode 100644
index 00000000..a6275cea
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/custom.cpp
@@ -0,0 +1,1066 @@
+// custom.cpp --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Implementation of many custom viewer classes
+ */
+
+#include "header.h"
+
+#include "custom.h"
+#include "format.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_CustomHandler : public c4_Handler
+{
+ c4_CustomSeq* _seq;
+
+public:
+ c4_CustomHandler (const c4_Property& prop_, c4_CustomSeq* seq_);
+ virtual ~c4_CustomHandler ();
+
+ 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_);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_CustomHandler::c4_CustomHandler (const c4_Property& prop_,
+ c4_CustomSeq* seq_)
+ : c4_Handler (prop_), _seq (seq_)
+{
+ d4_assert(_seq != 0);
+}
+
+c4_CustomHandler::~c4_CustomHandler ()
+{
+}
+
+int c4_CustomHandler::ItemSize(int index_)
+{
+ c4_Bytes& buf = _seq->Buffer();
+
+ int colnum = _seq->PropIndex(Property().GetId());
+ d4_assert(colnum >= 0);
+
+ if (!_seq->DoGet(index_, colnum, buf))
+ return 0;
+
+ return buf.Size();
+}
+
+const void* c4_CustomHandler::Get(int index_, int& length_)
+{
+ c4_Bytes& buf = _seq->Buffer();
+
+ int colnum = _seq->PropIndex(Property().GetId());
+ d4_assert(colnum >= 0);
+
+ if (!_seq->DoGet(index_, colnum, buf))
+ ClearBytes(buf);
+
+ length_ = buf.Size();
+ return buf.Contents();
+}
+
+void c4_CustomHandler::Set(int index_, const c4_Bytes& buf_)
+{
+ int colnum = _seq->PropIndex(Property().GetId());
+ d4_assert(colnum >= 0);
+
+ _seq->DoSet(index_, colnum, buf_);
+}
+
+void c4_CustomHandler::Insert(int, const c4_Bytes&, int)
+{
+ d4_assert(0); //! not yet
+}
+
+void c4_CustomHandler::Remove(int, int)
+{
+ d4_assert(0); //! not yet
+}
+
+c4_Handler* c4_CustomSeq::CreateHandler(const c4_Property& prop_)
+{
+ return d4_new c4_CustomHandler (prop_, this);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_CustomSeq::c4_CustomSeq (c4_CustomViewer* viewer_)
+ : c4_HandlerSeq (0), _viewer (viewer_), _inited (false)
+{
+ d4_assert(_viewer != 0);
+
+ // set up handlers to match a template obtained from the viewer
+ c4_View v = viewer_->GetTemplate();
+
+ for (int i = 0; i < v.NumProperties(); ++i)
+ PropIndex(v.NthProperty(i));
+
+ _inited = true;
+}
+
+c4_CustomSeq::~c4_CustomSeq ()
+{
+ delete _viewer;
+}
+
+int c4_CustomSeq::NumRows() const
+{
+ return _inited ? _viewer->GetSize() : 0;
+}
+
+bool c4_CustomSeq::RestrictSearch(c4_Cursor cursor_, int& pos_, int& count_)
+{
+ if (count_ > 0)
+ {
+ int n;
+ int o = _viewer->Lookup(cursor_, n);
+ // a -1 result means: "don't know, please scan all"
+ if (o < 0)
+ return count_ > 0;
+
+ if (n > 0)
+ {
+ if (pos_ < o)
+ {
+ count_ -= o - pos_;
+ pos_ = o;
+ }
+
+ if (pos_ + count_ > o + n)
+ count_ = o + n - pos_;
+
+ if (count_ > 0)
+ return true;
+ }
+ }
+
+ count_ = 0;
+ return false;
+}
+
+void c4_CustomSeq::InsertAt(int p_, c4_Cursor c_, int n_)
+{
+ _viewer->InsertRows(p_, c_, n_);
+}
+
+void c4_CustomSeq::RemoveAt(int p_, int n_)
+{
+ _viewer->RemoveRows(p_, n_);
+}
+
+void c4_CustomSeq::Move(int, int)
+{
+ d4_assert(false); //! not yet
+}
+
+bool c4_CustomSeq::DoGet(int row_, int col_, c4_Bytes& buf_) const
+{
+ d4_assert(_inited);
+
+ return _viewer->GetItem(row_, col_, buf_);
+}
+
+void c4_CustomSeq::DoSet(int row_, int col_, const c4_Bytes& buf_)
+{
+ d4_assert(_inited);
+
+ d4_dbgdef(const bool f =)
+ _viewer->SetItem(row_, col_, buf_);
+ d4_assert(f);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+/** @class c4_CustomViewer
+ *
+ * Abstract base class for definition of custom views.
+ *
+ * A custom view is a view which can be accessed like any other view, using
+ * row and property operations, but which is fully managed by a customized
+ * "viewer" class. The viewer will eventually handle all requests for the
+ * view, such as defining its structure and size, as well as providing the
+ * actual data values when requested.
+ *
+ * Custom views cannot propagate changes.
+ *
+ * To implement a custom view, you must derive your viewer from this base
+ * class and define each of the virtual members. Then create a new object
+ * of this type on the heap and pass it to the c4_View constructor. Your
+ * viewer will automatically be destroyed when the last reference to its
+ * view goes away. See the DBF2MK sample code for an example of a viewer.
+ */
+
+c4_CustomViewer::~c4_CustomViewer ()
+{
+}
+
+ /// Locate a row in this view, try to use native searches
+int c4_CustomViewer::Lookup(c4_Cursor, int& count_)
+{
+ count_ = GetSize();
+ return 0; // not implemented, return entire view range
+}
+
+ /// Store one data item, supplied as a generic data value
+bool c4_CustomViewer::SetItem(int, int, const c4_Bytes&)
+{
+ return false; // default is not modifiable
+}
+
+ /// Insert one or more copies of a row (if possible)
+bool c4_CustomViewer::InsertRows(int, c4_Cursor, int)
+{
+ return false; // default is not modifiable
+}
+
+ /// Remove one or more rows (this is not always possible)
+bool c4_CustomViewer::RemoveRows(int, int)
+{
+ return false; // default is not modifiable
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_SliceViewer : public c4_CustomViewer
+{
+ c4_View _parent;
+ int _first, _limit, _step;
+
+public:
+ c4_SliceViewer (c4_Sequence& seq_, int first_, int limit_, int step_);
+ virtual ~c4_SliceViewer ();
+
+ virtual c4_View GetTemplate();
+ virtual int GetSize();
+ virtual bool GetItem(int row_, int col_, c4_Bytes& buf_);
+ 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_SliceViewer::c4_SliceViewer (c4_Sequence& seq_, int first_, int limit_, int step_)
+ : _parent (&seq_), _first (first_), _limit (limit_), _step (step_)
+{
+ d4_assert(_step != 0);
+}
+
+c4_SliceViewer::~c4_SliceViewer ()
+{
+}
+
+c4_View c4_SliceViewer::GetTemplate()
+{
+ return _parent.Clone(); // could probably return _parent just as well
+}
+
+int c4_SliceViewer::GetSize()
+{
+ int n = _limit >= 0 ? _limit : _parent.GetSize();
+ if (n < _first)
+ n = _first;
+
+ int k = _step < 0 ? -_step : _step;
+ return (n - _first + k - 1) / k;
+}
+
+bool c4_SliceViewer::GetItem(int row_, int col_, c4_Bytes& buf_)
+{
+ row_ = _first + _step * (_step > 0 ? row_ : row_ - GetSize() + 1);
+
+ return _parent.GetItem(row_, col_, buf_);
+}
+
+bool c4_SliceViewer::SetItem(int row_, int col_, const c4_Bytes& buf_)
+{
+ row_ = _first + _step * (_step > 0 ? row_ : row_ - GetSize() + 1);
+
+ _parent.SetItem(row_, col_, buf_);
+ return true;
+}
+
+bool c4_SliceViewer::InsertRows(int pos_, c4_Cursor value_, int count_)
+{
+ if (_step != 1)
+ return false;
+
+ pos_ = _first + _step * (_step > 0 ? pos_ : pos_ - GetSize() + 1);
+ if (_limit >= 0)
+ _limit += count_;
+
+ _parent.InsertAt(pos_, *value_, count_);
+ return true;
+}
+
+bool c4_SliceViewer::RemoveRows(int pos_, int count_)
+{
+ if (_step != 1)
+ return false;
+
+ pos_ = _first + _step * (_step > 0 ? pos_ : pos_ - GetSize() + 1);
+ if (_limit >= 0)
+ _limit -= count_;
+
+ _parent.RemoveAt(pos_, count_);
+ return true;
+}
+
+c4_CustomViewer* f4_CustSlice(c4_Sequence& seq_, int first_, int limit_, int step_)
+{
+ return d4_new c4_SliceViewer (seq_, first_, limit_, step_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_ProductViewer : public c4_CustomViewer
+{
+ c4_View _parent, _argView, _template;
+
+public:
+ c4_ProductViewer (c4_Sequence& seq_, const c4_View& view_);
+ virtual ~c4_ProductViewer ();
+
+ virtual c4_View GetTemplate();
+ virtual int GetSize();
+ virtual bool GetItem(int row_, int col_, c4_Bytes& buf_);
+};
+
+c4_ProductViewer::c4_ProductViewer (c4_Sequence& seq_, const c4_View& view_)
+ : _parent (&seq_), _argView (view_), _template (_parent.Clone())
+{
+ for (int i = 0; i < _argView.NumProperties(); ++i)
+ _template.AddProperty(_argView.NthProperty(i));
+}
+
+c4_ProductViewer::~c4_ProductViewer ()
+{
+}
+
+c4_View c4_ProductViewer::GetTemplate()
+{
+ return _template;
+}
+
+int c4_ProductViewer::GetSize()
+{
+ return _parent.GetSize() * _argView.GetSize();
+}
+
+bool c4_ProductViewer::GetItem(int row_, int col_, c4_Bytes& buf_)
+{
+ c4_View v = _parent;
+
+ if (col_ < v.NumProperties())
+ {
+ row_ /= _argView.GetSize();
+ }
+ else
+ {
+ v = _argView;
+ row_ %= _argView.GetSize();
+ col_ = v.FindProperty(_template.NthProperty(col_).GetId());
+
+ d4_assert(col_ >= 0);
+ }
+
+ return v.GetItem(row_, col_, buf_);
+}
+
+c4_CustomViewer* f4_CustProduct(c4_Sequence& seq_, const c4_View& view_)
+{
+ return d4_new c4_ProductViewer (seq_, view_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_RemapWithViewer : public c4_CustomViewer
+{
+ c4_View _parent, _argView;
+
+public:
+ c4_RemapWithViewer (c4_Sequence& seq_, const c4_View& view_);
+ virtual ~c4_RemapWithViewer ();
+
+ virtual c4_View GetTemplate();
+ virtual int GetSize();
+ virtual bool GetItem(int row_, int col_, c4_Bytes& buf_);
+ bool SetItem(int row_, int col_, const c4_Bytes& buf_);
+};
+
+c4_RemapWithViewer::c4_RemapWithViewer (c4_Sequence& seq_, const c4_View& view_)
+ : _parent (&seq_), _argView (view_)
+{
+}
+
+c4_RemapWithViewer::~c4_RemapWithViewer ()
+{
+}
+
+c4_View c4_RemapWithViewer::GetTemplate()
+{
+ return _parent.Clone(); // could probably return _parent just as well
+}
+
+int c4_RemapWithViewer::GetSize()
+{
+ return _argView.GetSize();
+}
+
+bool c4_RemapWithViewer::GetItem(int row_, int col_, c4_Bytes& buf_)
+{
+ const c4_Property& map = _argView.NthProperty(0);
+ d4_assert(map.Type() == 'I');
+
+ row_ = ((const c4_IntProp&) map) (_argView[row_]);
+
+ return _parent.GetItem(row_, col_, buf_);
+}
+
+bool c4_RemapWithViewer::SetItem(int row_, int col_, const c4_Bytes& buf_)
+{
+ const c4_Property& map = _argView.NthProperty(0);
+ d4_assert(map.Type() == 'I');
+
+ row_ = ((const c4_IntProp&) map) (_argView[row_]);
+
+ _parent.SetItem(row_, col_, buf_);
+ return true;
+}
+
+c4_CustomViewer* f4_CustRemapWith(c4_Sequence& seq_, const c4_View& view_)
+{
+ return d4_new c4_RemapWithViewer (seq_, view_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_PairViewer : public c4_CustomViewer
+{
+ c4_View _parent, _argView, _template;
+
+public:
+ c4_PairViewer (c4_Sequence& seq_, const c4_View& view_);
+ virtual ~c4_PairViewer ();
+
+ virtual c4_View GetTemplate();
+ virtual int GetSize();
+ virtual bool GetItem(int row_, int col_, c4_Bytes& buf_);
+ 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_PairViewer::c4_PairViewer (c4_Sequence& seq_, const c4_View& view_)
+ : _parent (&seq_), _argView (view_), _template (_parent.Clone())
+{
+ for (int i = 0; i < _argView.NumProperties(); ++i)
+ _template.AddProperty(_argView.NthProperty(i));
+}
+
+c4_PairViewer::~c4_PairViewer ()
+{
+}
+
+c4_View c4_PairViewer::GetTemplate()
+{
+ return _template;
+}
+
+int c4_PairViewer::GetSize()
+{
+ return _parent.GetSize();
+}
+
+bool c4_PairViewer::GetItem(int row_, int col_, c4_Bytes& buf_)
+{
+ c4_View v = _parent;
+
+ if (col_ >= v.NumProperties())
+ {
+ v = _argView;
+ col_ = v.FindProperty(_template.NthProperty(col_).GetId());
+ d4_assert(col_ >= 0);
+ }
+
+ return v.GetItem(row_, col_, buf_);
+}
+
+bool c4_PairViewer::SetItem(int row_, int col_, const c4_Bytes& buf_)
+{
+ c4_View v = _parent;
+
+ if (col_ >= v.NumProperties())
+ {
+ v = _argView;
+ col_ = v.FindProperty(_template.NthProperty(col_).GetId());
+ d4_assert(col_ >= 0);
+ }
+
+ v.SetItem(row_, col_, buf_);
+ return true;
+}
+
+bool c4_PairViewer::InsertRows(int pos_, c4_Cursor value_, int count_)
+{
+ _parent.InsertAt(pos_, *value_, count_);
+ _argView.InsertAt(pos_, *value_, count_);
+ return true;
+}
+
+bool c4_PairViewer::RemoveRows(int pos_, int count_)
+{
+ _parent.RemoveAt(pos_, count_);
+ _argView.RemoveAt(pos_, count_);
+ return true;
+}
+
+c4_CustomViewer* f4_CustPair(c4_Sequence& seq_, const c4_View& view_)
+{
+ return d4_new c4_PairViewer (seq_, view_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_ConcatViewer : public c4_CustomViewer
+{
+ c4_View _parent, _argView;
+
+public:
+ c4_ConcatViewer (c4_Sequence& seq_, const c4_View& view_);
+ virtual ~c4_ConcatViewer ();
+
+ virtual c4_View GetTemplate();
+ virtual int GetSize();
+ virtual bool GetItem(int row_, int col_, c4_Bytes& buf_);
+ bool SetItem(int row_, int col_, const c4_Bytes& buf_);
+};
+
+c4_ConcatViewer::c4_ConcatViewer (c4_Sequence& seq_, const c4_View& view_)
+ : _parent (&seq_), _argView (view_)
+{
+}
+
+c4_ConcatViewer::~c4_ConcatViewer ()
+{
+}
+
+c4_View c4_ConcatViewer::GetTemplate()
+{
+ return _parent.Clone(); // could probably return _parent just as well
+}
+
+int c4_ConcatViewer::GetSize()
+{
+ return _parent.GetSize() + _argView.GetSize();
+}
+
+bool c4_ConcatViewer::GetItem(int row_, int col_, c4_Bytes& buf_)
+{
+ c4_View v = _parent;
+
+ if (row_ >= _parent.GetSize())
+ {
+ v = _argView;
+ row_ -= _parent.GetSize();
+ col_ = v.FindProperty(_parent.NthProperty(col_).GetId());
+
+ if (col_ < 0)
+ return false;
+ }
+
+ return v.GetItem(row_, col_, buf_);
+}
+
+bool c4_ConcatViewer::SetItem(int row_, int col_, const c4_Bytes& buf_)
+{
+ c4_View v = _parent;
+
+ if (row_ >= _parent.GetSize())
+ {
+ v = _argView;
+ row_ -= _parent.GetSize();
+ col_ = v.FindProperty(_parent.NthProperty(col_).GetId());
+ d4_assert(col_ >= 0);
+ }
+
+ v.SetItem(row_, col_, buf_);
+ return true;
+}
+
+c4_CustomViewer* f4_CustConcat(c4_Sequence& seq_, const c4_View& view_)
+{
+ return d4_new c4_ConcatViewer (seq_, view_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_RenameViewer : public c4_CustomViewer
+{
+ c4_View _parent, _template;
+
+public:
+ c4_RenameViewer (c4_Sequence& seq_, const c4_Property& old_,
+ const c4_Property& new_);
+ virtual ~c4_RenameViewer ();
+
+ 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);
+};
+
+c4_RenameViewer::c4_RenameViewer (c4_Sequence& seq_, const c4_Property& old_,
+ const c4_Property& new_)
+ : _parent (&seq_)
+{
+ for (int i = 0; i < _parent.NumProperties(); ++i)
+ {
+ const c4_Property& prop = _parent.NthProperty(i);
+ _template.AddProperty(prop.GetId() == old_.GetId() ? new_ : prop);
+ }
+}
+
+c4_RenameViewer::~c4_RenameViewer ()
+{
+}
+
+c4_View c4_RenameViewer::GetTemplate()
+{
+ return _template;
+}
+
+int c4_RenameViewer::GetSize()
+{
+ return _parent.GetSize();
+}
+
+bool c4_RenameViewer::GetItem(int row_, int col_, c4_Bytes& buf_)
+{
+ return _parent.GetItem(row_, col_, buf_);
+}
+
+bool c4_RenameViewer::SetItem(int row_, int col_, const c4_Bytes& buf_)
+{
+ _parent.SetItem(row_, col_, buf_);
+ return true;
+}
+
+c4_CustomViewer* f4_CustRename(c4_Sequence& seq_, const c4_Property& old_,
+ const c4_Property& new_)
+{
+ return d4_new c4_RenameViewer (seq_, old_, new_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_GroupByViewer : public c4_CustomViewer
+{
+ c4_View _parent, _keys, _sorted, _temp;
+ c4_Property _result;
+ c4_DWordArray _map;
+
+ int ScanTransitions(int lo_, int hi_,
+ t4_byte* flags_, const c4_View& match_) const;
+
+public:
+ c4_GroupByViewer (c4_Sequence& seq_, const c4_View& keys_,
+ const c4_Property& result_);
+ virtual ~c4_GroupByViewer ();
+
+ virtual c4_View GetTemplate();
+ virtual int GetSize();
+ virtual bool GetItem(int row_, int col_, c4_Bytes& buf_);
+};
+
+c4_GroupByViewer::c4_GroupByViewer (c4_Sequence& seq_, const c4_View& keys_,
+ const c4_Property& result_)
+ : _parent (&seq_), _keys (keys_), _result (result_)
+{
+ _sorted = _parent.SortOn(_keys);
+ int n = _sorted.GetSize();
+
+ c4_Bytes temp;
+ t4_byte* buf = temp.SetBufferClear(n);
+
+ int groups = 0;
+ if (n > 0)
+ {
+ ++buf[0]; // the first entry is always a transition
+ groups = 1 + ScanTransitions(1, n, buf, _sorted.Project(_keys));
+ }
+
+ // set up a map pointing to each transition
+ _map.SetSize(groups + 1);
+ int j = 0;
+
+ for (int i = 0; i < n; ++i)
+ if (buf[i])
+ _map.SetAt(j++, i);
+
+ // also append an entry to point just past the end
+ _map.SetAt(j, n);
+
+ d4_assert(_map.GetAt(0) == 0);
+ d4_assert(j == groups);
+}
+
+c4_GroupByViewer::~c4_GroupByViewer ()
+{
+}
+
+int c4_GroupByViewer::ScanTransitions(int lo_, int hi_,
+ t4_byte* flags_, const c4_View& match_) const
+{
+ d4_assert(lo_ > 0);
+
+ int m = hi_ - lo_;
+ d4_assert(m >= 0);
+
+ // done if nothing left or if entire range is identical
+ if (m == 0 || match_ [lo_-1] == match_ [hi_-1])
+ return 0;
+
+ // range has a transition, done if it is exactly of size one
+ if (m == 1)
+ {
+ ++(flags_[lo_]);
+ return 1;
+ }
+
+ // use binary splitting if the range has enough entries
+ if (m >= 5)
+ return ScanTransitions(lo_, lo_ + m / 2, flags_, match_) +
+ ScanTransitions(lo_ + m / 2, hi_, flags_, match_);
+
+ // else use a normal linear scan
+ int n = 0;
+
+ for (int i = lo_; i < hi_; ++i)
+ if (match_ [i] != match_ [i-1])
+ {
+ ++(flags_[i]);
+ ++n;
+ }
+
+ return n;
+}
+
+c4_View c4_GroupByViewer::GetTemplate()
+{
+ c4_View v = _keys.Clone();
+ v.AddProperty(_result);
+
+ return v;
+}
+
+int c4_GroupByViewer::GetSize()
+{
+ d4_assert(_map.GetSize() > 0);
+
+ return _map.GetSize() - 1;
+}
+
+bool c4_GroupByViewer::GetItem(int row_, int col_, c4_Bytes& buf_)
+{
+ if (col_ < _keys.NumProperties())
+ return _sorted.GetItem(_map.GetAt(row_), col_, buf_);
+
+ d4_assert(col_ == _keys.NumProperties());
+
+ t4_i32 count;
+ switch (_result.Type())
+ {
+ case 'I': count = _map.GetAt(row_ + 1) - _map.GetAt(row_);
+ buf_ = c4_Bytes (&count, sizeof count, true);
+ break;
+ case 'V': _temp = _sorted.Slice(_map.GetAt(row_), _map.GetAt(row_ + 1))
+ .ProjectWithout(_keys);
+ buf_ = c4_Bytes (&_temp, sizeof _temp, true);
+ break;
+ default: d4_assert(0);
+ }
+
+ return true;
+}
+
+c4_CustomViewer* f4_CustGroupBy(c4_Sequence& seq_, const c4_View& template_,
+ const c4_Property& result_)
+{
+ return d4_new c4_GroupByViewer (seq_, template_, result_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_JoinPropViewer : public c4_CustomViewer
+{
+ c4_View _parent, _template;
+ c4_ViewProp _sub;
+ int _subPos, _subWidth;
+ c4_DWordArray _base, _offset;
+
+public:
+ c4_JoinPropViewer (c4_Sequence& seq_, const c4_ViewProp& sub_, bool outer_);
+ virtual ~c4_JoinPropViewer ();
+
+ virtual c4_View GetTemplate();
+ virtual int GetSize();
+ virtual bool GetItem(int row_, int col_, c4_Bytes& buf_);
+};
+
+c4_JoinPropViewer::c4_JoinPropViewer (c4_Sequence& seq_,
+ const c4_ViewProp& sub_, bool outer_)
+ : _parent (&seq_),
+ _sub (sub_), _subPos (_parent.FindProperty(sub_.GetId())), _subWidth (0)
+{
+ d4_assert(_subPos >= 0);
+
+ for (int k = 0; k < _parent.NumProperties(); ++k)
+ {
+ if (k != _subPos)
+ _template.AddProperty(_parent.NthProperty(k));
+ else // if there are no rows, then this join does very little anyway
+ //! OOPS: if this is an unattached view, then the subviews can differ
+ if (_parent.GetSize() > 0)
+ {
+ c4_View view = sub_ (_parent[0]);
+ for (int l = 0; l < view.NumProperties(); ++l)
+ {
+ _template.AddProperty(view.NthProperty(l));
+ ++_subWidth;
+ }
+ }
+ }
+
+ _base.SetSize(0, 5);
+ _offset.SetSize(0, 5);
+
+ for (int i = 0; i < _parent.GetSize(); ++i)
+ {
+ c4_View v = _sub (_parent[i]);
+
+ int n = v.GetSize();
+ if (n == 0 && outer_)
+ {
+ _base.Add(i);
+ _offset.Add(~ (t4_i32) 0); // special null entry for outer joins
+ }
+ else
+ for (int j = 0; j < n; ++j)
+ {
+ _base.Add(i);
+ _offset.Add(j);
+ }
+ }
+}
+
+c4_JoinPropViewer::~c4_JoinPropViewer ()
+{
+}
+
+c4_View c4_JoinPropViewer::GetTemplate()
+{
+ return _template;
+}
+
+int c4_JoinPropViewer::GetSize()
+{
+ return _base.GetSize();
+}
+
+bool c4_JoinPropViewer::GetItem(int row_, int col_, c4_Bytes& buf_)
+{
+ c4_View v = _parent;
+ int r = _base.GetAt(row_);
+
+ if (col_ >= _subPos)
+ if (col_ >= _subPos + _subWidth)
+ {
+ col_ -= _subWidth - 1;
+ }
+ else
+ {
+ v = _sub (_parent[r]);
+ r = _offset.GetAt(row_);
+ if (r < 0)
+ return false; // if this is a null row in an outer join
+
+ col_ = v.FindProperty(_template.NthProperty(col_).GetId());
+ if (col_ < 0)
+ return false; // if subview doesn't have all properties
+ }
+
+ return v.GetItem(r, col_, buf_);
+}
+
+c4_CustomViewer* f4_CustJoinProp(c4_Sequence& seq_,
+ const c4_ViewProp& sub_, bool outer_)
+{
+ return d4_new c4_JoinPropViewer (seq_, sub_, outer_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_JoinViewer : public c4_CustomViewer
+{
+ c4_View _parent, _argView, _template;
+ c4_DWordArray _base, _offset;
+
+public:
+ c4_JoinViewer (c4_Sequence& seq_, const c4_View& keys_,
+ const c4_View& view_, bool outer_);
+ virtual ~c4_JoinViewer ();
+
+ virtual c4_View GetTemplate();
+ virtual int GetSize();
+ virtual bool GetItem(int row_, int col_, c4_Bytes& buf_);
+};
+
+c4_JoinViewer::c4_JoinViewer (c4_Sequence& seq_,const c4_View& keys_,
+ const c4_View& view_, bool outer_)
+ : _parent (&seq_), _argView (view_.SortOn(keys_))
+{
+ // why not in GetTemplate, since we don't need to know this...
+ _template = _parent.Clone();
+ for (int l = 0; l < _argView.NumProperties(); ++l)
+ _template.AddProperty(_argView.NthProperty(l));
+
+ c4_View sorted = _parent.SortOn(keys_).Project(keys_);
+ c4_View temp = _argView.Project(keys_);
+
+ _base.SetSize(0, 5);
+ _offset.SetSize(0, 5);
+
+ int j = 0, n = 0;
+
+ for (int i = 0; i < sorted.GetSize(); ++i)
+ {
+ int orig = _parent.GetIndexOf(sorted[i]);
+ d4_assert(orig >= 0);
+
+ if (i > 0 && sorted[i] == sorted[i-1])
+ {
+ // if last key was same, repeat the same join
+ int last = _offset.GetSize() - n;
+ for (int k = 0; k < n; ++k)
+ {
+ _base.Add(orig);
+ _offset.Add(_offset.GetAt(last + k));
+ }
+ }
+ else // no, this is a new combination
+ {
+ bool match = false;
+
+ // advance until the temp view entry is >= this sorted entry
+ while (j < temp.GetSize())
+ if (sorted[i] <= temp[j])
+ {
+ match = sorted[i] == temp[j];
+ break;
+ }
+ else
+ ++j;
+
+ n = 0;
+
+ if (match)
+ {
+ do {
+ _base.Add(orig);
+ _offset.Add(j);
+ ++n;
+ } while (++j < temp.GetSize() && temp[j] == temp[j-1]);
+ }
+ else if (outer_)
+ {
+ // no match, add an entry anyway if this is an outer join
+ _base.Add(orig);
+ _offset.Add(~ (t4_i32) 0); // special null entry
+ ++n;
+ }
+ }
+ }
+}
+
+c4_JoinViewer::~c4_JoinViewer ()
+{
+}
+
+c4_View c4_JoinViewer::GetTemplate()
+{
+ return _template;
+}
+
+int c4_JoinViewer::GetSize()
+{
+ return _base.GetSize();
+}
+
+bool c4_JoinViewer::GetItem(int row_, int col_, c4_Bytes& buf_)
+{
+ c4_View v = _parent;
+ int r = _base.GetAt(row_);
+
+ if (col_ >= v.NumProperties())
+ {
+ v = _argView;
+ r = _offset.GetAt(row_);
+ if (r < 0)
+ return false; // if this is a null row in an outer join
+
+ col_ = v.FindProperty(_template.NthProperty(col_).GetId());
+ if (col_ < 0)
+ return false; // if second view doesn't have all properties
+ }
+
+ return v.GetItem(r, col_, buf_);
+}
+
+#if 0
+bool c4_JoinViewer::GetItem(int row_, int col_, c4_Bytes& buf_)
+{
+ c4_View v = _parent;
+
+ int o = 0;
+ int r = _offset.GetAt(row_);
+
+ if (r < 0)
+ {
+ o = ~r;
+ if (o == 0)
+ return false; // if this is a null row in an outer join
+ r -= o;
+ }
+
+ if (col_ >= v.NumProperties())
+ {
+ v = _argView;
+ r = _o;
+
+ col_ = v.FindProperty(_template.NthProperty(col_));
+ if (col_ < 0)
+ return false; // if second view doesn't have all properties
+ }
+
+ return v.GetItem(r, col_, buf_);
+}
+#endif
+
+c4_CustomViewer* f4_CustJoin(c4_Sequence& seq_, const c4_View& keys_,
+ const c4_View& view_, bool outer_)
+{
+ return d4_new c4_JoinViewer (seq_, keys_, view_, outer_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/custom.h b/akregator/src/mk4storage/metakit/src/custom.h
new file mode 100644
index 00000000..80555d3b
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/custom.h
@@ -0,0 +1,63 @@
+// custom.h --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Encapsulation of many custom viewer classes
+ */
+
+#ifndef __CUSTOM_H__
+#define __CUSTOM_H__
+
+#ifndef __FIELD_H__
+#include "field.h"
+#endif
+#ifndef __STORE_H__
+#include "handler.h"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_CustomSeq : public c4_HandlerSeq
+{
+ c4_CustomViewer* _viewer;
+ bool _inited;
+
+public:
+ c4_CustomSeq (c4_CustomViewer* viewer_);
+ virtual ~c4_CustomSeq ();
+
+ virtual int NumRows() const;
+
+ virtual bool RestrictSearch(c4_Cursor, int&, int&);
+
+ virtual void InsertAt(int, c4_Cursor, int =1);
+ virtual void RemoveAt(int, int =1);
+ virtual void Move(int from_, int);
+
+ bool DoGet(int row_, int col_, c4_Bytes& buf_) const;
+ void DoSet(int row_, int col_, const c4_Bytes& buf_);
+
+private: // this *is* used, as override
+ virtual c4_Handler* CreateHandler(const c4_Property&);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+ extern c4_CustomViewer* f4_CustSlice(c4_Sequence&, int, int , int);
+ extern c4_CustomViewer* f4_CustProduct(c4_Sequence&, const c4_View&);
+ extern c4_CustomViewer* f4_CustRemapWith(c4_Sequence&, const c4_View&);
+ extern c4_CustomViewer* f4_CustPair(c4_Sequence&, const c4_View&);
+ extern c4_CustomViewer* f4_CustConcat(c4_Sequence&, const c4_View&);
+ extern c4_CustomViewer* f4_CustRename(c4_Sequence&,
+ const c4_Property&, const c4_Property&);
+ extern c4_CustomViewer* f4_CustGroupBy(c4_Sequence&,
+ const c4_View&, const c4_Property&);
+ extern c4_CustomViewer* f4_CustJoinProp(c4_Sequence&,
+ const c4_ViewProp&, bool);
+ extern c4_CustomViewer* f4_CustJoin(c4_Sequence&,
+ const c4_View&, const c4_View&, bool);
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/akregator/src/mk4storage/metakit/src/derived.cpp b/akregator/src/mk4storage/metakit/src/derived.cpp
new file mode 100644
index 00000000..3baf5e3e
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/derived.cpp
@@ -0,0 +1,1003 @@
+// derived.cpp --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Derived views are virtual views which track changes
+ */
+
+#include "header.h"
+#include "handler.h"
+#include "store.h"
+#include "derived.h"
+
+#include <stdlib.h> // qsort
+
+/////////////////////////////////////////////////////////////////////////////
+// Implemented in this file
+
+// class c4_Sequence;
+ class c4_DerivedSeq;
+ class c4_FilterSeq;
+ class c4_SortSeq;
+ class c4_ProjectSeq;
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_FilterSeq : public c4_DerivedSeq
+{
+protected:
+ c4_DWordArray _rowMap;
+ c4_DWordArray _revMap;
+ c4_Row _lowRow;
+ c4_Row _highRow;
+ c4_Bytes _rowIds;
+
+protected:
+ c4_FilterSeq (c4_Sequence& seq_);
+ virtual ~c4_FilterSeq ();
+
+ void FixupReverseMap();
+ int PosInMap(int index_) const;
+ bool Match(int index_, c4_Sequence& seq_,
+ const int* =0, const int* =0) const;
+ bool MatchOne(int prop_, const c4_Bytes& data_) const;
+
+public:
+ c4_FilterSeq (c4_Sequence& seq_, c4_Cursor low_, c4_Cursor high_);
+
+ virtual int RemapIndex(int, const c4_Sequence*) const;
+
+ virtual int NumRows() const;
+
+ virtual int Compare(int, c4_Cursor) const;
+ virtual bool Get(int, int, c4_Bytes&);
+
+ virtual void InsertAt(int, c4_Cursor, int =1);
+ virtual void RemoveAt(int, int =1);
+ virtual void Set(int, const c4_Property&, const c4_Bytes&);
+ virtual void SetSize(int);
+
+ virtual c4_Notifier* PreChange(c4_Notifier& nf_);
+ virtual void PostChange(c4_Notifier& nf_);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_FilterSeq::c4_FilterSeq (c4_Sequence& seq_)
+ : c4_DerivedSeq (seq_)
+{
+ _rowMap.SetSize(_seq.NumRows());
+ _revMap.SetSize(_seq.NumRows());
+ d4_assert(NumRows() == _seq.NumRows());
+
+ for (int i = 0; i < NumRows(); ++i)
+ {
+ _rowMap.SetAt(i, i);
+ _revMap.SetAt(i, i);
+ }
+}
+
+c4_FilterSeq::c4_FilterSeq (c4_Sequence& seq_, c4_Cursor low_,
+ c4_Cursor high_)
+ : c4_DerivedSeq (seq_), _lowRow (*low_), _highRow (*high_)
+{
+ d4_assert((&_lowRow)._index == 0);
+ d4_assert((&_highRow)._index == 0);
+
+ // use a sneaky way to obtain the sequence pointers and indices
+ c4_Sequence* lowSeq = (& _lowRow)._seq;
+ c4_Sequence* highSeq = (& _highRow)._seq;
+ d4_assert(lowSeq && highSeq);
+
+ // prepare column numbers to avoid looking them up on every row
+ // lowCols is a vector of column numbers to use for the low limits
+ // highCols is a vector of column numbers to use for the high limits
+ int nl = lowSeq->NumHandlers(), nh = highSeq->NumHandlers();
+ c4_Bytes lowVec, highVec;
+ int* lowCols = (int*) lowVec.SetBufferClear(nl * sizeof (int));
+ int* highCols = (int*) highVec.SetBufferClear(nh * sizeof (int));
+
+ for (int il = 0; il < nl; ++il)
+ lowCols[il] = seq_.PropIndex(lowSeq->NthPropId(il));
+ for (int ih = 0; ih < nh; ++ih)
+ highCols[ih] = seq_.PropIndex(highSeq->NthPropId(ih));
+
+ // set _rowIds flag buffer for fast matching
+ {
+ int max = -1;
+
+ {
+ for (int i1 = 0; i1 < nl; ++i1)
+ {
+ int n = lowSeq->NthPropId(i1);
+ if (max < n)
+ max = n;
+ }
+ for (int i2 = 0; i2 < nh; ++i2)
+ {
+ int n = highSeq->NthPropId(i2);
+ if (max < n)
+ max = n;
+ }
+ }
+
+ t4_byte* p = _rowIds.SetBufferClear(max + 1);
+
+ {
+ for (int i1 = 0; i1 < nl; ++i1)
+ p[lowSeq->NthPropId(i1)] |= 1;
+ for (int i2 = 0; i2 < nh; ++i2)
+ p[highSeq->NthPropId(i2)] |= 2;
+ }
+ }
+
+ // now go through all rows and select the ones that are in range
+
+ _rowMap.SetSize(_seq.NumRows()); // avoid growing, use safe upper bound
+
+ int n = 0;
+
+ for (int i = 0; i < _seq.NumRows(); ++i)
+ if (Match(i, _seq, lowCols, highCols))
+ _rowMap.SetAt(n++, i);
+
+ _rowMap.SetSize(n);
+
+ FixupReverseMap();
+}
+
+c4_FilterSeq::~c4_FilterSeq ()
+{
+}
+
+void c4_FilterSeq::FixupReverseMap()
+{
+ int n = _seq.NumRows();
+
+ _revMap.SetSize(0);
+
+ if (n > 0)
+ {
+ _revMap.InsertAt(0, ~ (t4_i32) 0, n); //!
+
+ for (int i = 0; i < _rowMap.GetSize(); ++i)
+ _revMap.SetAt((int) _rowMap.GetAt(i), i);
+ }
+}
+
+bool c4_FilterSeq::MatchOne(int prop_, const c4_Bytes& data_) const
+{
+ d4_assert(prop_ < _rowIds.Size());
+
+ t4_byte flag = _rowIds.Contents()[prop_];
+ d4_assert(flag);
+
+ if (flag & 1)
+ {
+ c4_Sequence* lowSeq = (& _lowRow)._seq;
+
+ c4_Handler& h = lowSeq->NthHandler(lowSeq->PropIndex(prop_));
+ if (h.Compare(0, data_) > 0)
+ return false;
+ }
+
+ if (flag & 2)
+ {
+ c4_Sequence* highSeq = (& _highRow)._seq;
+
+ c4_Handler& h = highSeq->NthHandler(highSeq->PropIndex(prop_));
+ if (h.Compare(0, data_) < 0)
+ return false;
+ }
+
+ return true;
+}
+
+bool c4_FilterSeq::Match(int index_, c4_Sequence& seq_,
+ const int* lowCols_, const int* highCols_) const
+{
+ // use a sneaky way to obtain the sequence pointers and indices
+ c4_Sequence* lowSeq = (& _lowRow)._seq;
+ c4_Sequence* highSeq = (& _highRow)._seq;
+ d4_assert(lowSeq && highSeq);
+
+ int nl = lowSeq->NumHandlers(), nh = highSeq->NumHandlers();
+
+ c4_Bytes data;
+
+ // check each of the lower limits
+ for (int cl = 0; cl < nl; ++cl)
+ {
+ c4_Handler& hl = lowSeq->NthHandler(cl);
+
+ int n = lowCols_ ? lowCols_[cl]
+ : seq_.PropIndex(lowSeq->NthPropId(cl));
+ if (n >= 0)
+ {
+ c4_Handler& h = seq_.NthHandler(n);
+ const c4_Sequence* hc = seq_.HandlerContext(n);
+ int i = seq_.RemapIndex(index_, hc);
+
+ h.GetBytes(i, data);
+ }
+ else
+ hl.ClearBytes(data);
+
+ if (hl.Compare(0, data) > 0)
+ return false;
+ }
+
+ // check each of the upper limits
+ for (int ch = 0; ch < nh; ++ch)
+ {
+ c4_Handler& hh = highSeq->NthHandler(ch);
+
+ int n = highCols_ ? highCols_[ch]
+ : seq_.PropIndex(highSeq->NthPropId(ch));
+ if (n >= 0)
+ {
+ c4_Handler& h = seq_.NthHandler(n);
+ const c4_Sequence* hc = seq_.HandlerContext(n);
+ int i = seq_.RemapIndex(index_, hc);
+
+ h.GetBytes(i, data);
+ }
+ else
+ hh.ClearBytes(data);
+
+ if (hh.Compare(0, data) < 0)
+ return false;
+ }
+
+ return true;
+}
+
+int c4_FilterSeq::RemapIndex(int index_, const c4_Sequence* seq_) const
+{
+ return seq_ == this ? index_
+ : _seq.RemapIndex((int) _rowMap.GetAt(index_), seq_);
+}
+
+int c4_FilterSeq::NumRows() const
+{
+ return _rowMap.GetSize();
+}
+
+int c4_FilterSeq::Compare(int index_, c4_Cursor cursor_) const
+{
+ return _seq.Compare((int) _rowMap.GetAt(index_), cursor_);
+}
+
+bool c4_FilterSeq::Get(int index_, int propId_, c4_Bytes& bytes_)
+{
+ return _seq.Get((int) _rowMap.GetAt(index_), propId_, bytes_);
+}
+
+void c4_FilterSeq::InsertAt(int, c4_Cursor, int)
+{
+ d4_assert(0);
+}
+
+void c4_FilterSeq::RemoveAt(int, int)
+{
+ d4_assert(0);
+}
+
+void c4_FilterSeq::Set(int, const c4_Property&, const c4_Bytes&)
+{
+ d4_assert(0);
+}
+
+void c4_FilterSeq::SetSize(int)
+{
+ d4_assert(0);
+}
+
+int c4_FilterSeq::PosInMap(int index_) const
+{
+ int i = 0;
+
+ while (i < NumRows())
+ if ((int) _rowMap.GetAt(i) >= index_)
+ break;
+ else
+ ++i;
+
+ return i;
+}
+
+c4_Notifier* c4_FilterSeq::PreChange(c4_Notifier& nf_)
+{
+ if (!GetDependencies())
+ return 0;
+
+ c4_Notifier* chg = d4_new c4_Notifier (this);
+
+ bool pass = false;
+
+ switch (nf_._type)
+ {
+ case c4_Notifier::kSet:
+ pass = nf_._propId >= _rowIds.Size() ||
+ _rowIds.Contents()[nf_._propId] == 0;
+ // fall through...
+
+ case c4_Notifier::kSetAt:
+ {
+ int r = (int) _revMap.GetAt(nf_._index);
+
+ bool includeRow = r >= 0;
+ if (!pass)
+ if (nf_._type == c4_Notifier::kSetAt)
+ {
+ d4_assert(nf_._cursor != 0);
+ includeRow = Match(nf_._cursor->_index,
+ *nf_._cursor->_seq);
+ }
+ else // set just one property, and it's not in a row yet
+ includeRow = MatchOne(nf_._propId, *nf_._bytes);
+
+ if (r >= 0 && !includeRow)
+ chg->StartRemoveAt(r, 1);
+ else if (r < 0 && includeRow)
+ chg->StartInsertAt(PosInMap(nf_._index), *nf_._cursor, 1);
+ else if (includeRow)
+ {
+ d4_assert(r >= 0);
+
+ if (nf_._type == c4_Notifier::kSetAt)
+ chg->StartSetAt(r, *nf_._cursor);
+ else
+ chg->StartSet(r, nf_._propId, *nf_._bytes);
+ }
+ }
+ break;
+
+ case c4_Notifier::kInsertAt:
+ {
+ int i = PosInMap(nf_._index);
+
+ d4_assert(nf_._cursor != 0);
+ if (Match(nf_._cursor->_index, *nf_._cursor->_seq))
+ chg->StartInsertAt(i, *nf_._cursor, nf_._count);
+ }
+ break;
+
+ case c4_Notifier::kRemoveAt:
+ {
+ int i = PosInMap(nf_._index);
+ int j = PosInMap(nf_._index + nf_._count);
+ d4_assert(j >= i);
+
+ if (j > i)
+ chg->StartRemoveAt(i, j - i);
+ }
+ break;
+
+ case c4_Notifier::kMove:
+ {
+ int i = PosInMap(nf_._index);
+ bool inMap = i < NumRows() && (int) _rowMap.GetAt(i) == nf_._index;
+
+ if (inMap && nf_._index != nf_._count)
+ chg->StartMove(i, PosInMap(nf_._count));
+ }
+ break;
+ }
+
+ return chg;
+}
+
+void c4_FilterSeq::PostChange(c4_Notifier& nf_)
+{
+ bool pass = false;
+
+ switch (nf_._type)
+ {
+ case c4_Notifier::kSet:
+ pass = nf_._propId >= _rowIds.Size() ||
+ _rowIds.Contents()[nf_._propId] == 0;
+ // fall through...
+
+ case c4_Notifier::kSetAt:
+ {
+ int r = (int) _revMap.GetAt(nf_._index);
+
+ bool includeRow = r >= 0;
+ if (!pass)
+ if (nf_._type == c4_Notifier::kSetAt)
+ {
+ d4_assert(nf_._cursor != 0);
+ includeRow = Match(nf_._cursor->_index,
+ *nf_._cursor->_seq);
+ }
+ else // set just one property, and it's not in a row yet
+ includeRow = MatchOne(nf_._propId, *nf_._bytes);
+
+ if (r >= 0 && !includeRow)
+ _rowMap.RemoveAt(r);
+ else if (r < 0 && includeRow)
+ _rowMap.InsertAt(PosInMap(nf_._index), nf_._index);
+ else
+ break;
+
+ FixupReverseMap();
+ }
+ break;
+
+ case c4_Notifier::kInsertAt:
+ {
+ int i = PosInMap(nf_._index);
+
+ if (Match(nf_._index, _seq))
+ {
+ _rowMap.InsertAt(i, 0, nf_._count);
+
+ for (int j = 0; j < nf_._count; ++j)
+ _rowMap.SetAt(i++, nf_._index + j);
+ }
+
+ while (i < NumRows())
+ _rowMap.ElementAt(i++) += nf_._count;
+
+ FixupReverseMap();
+ }
+ break;
+
+ case c4_Notifier::kRemoveAt:
+ {
+ int i = PosInMap(nf_._index);
+ int j = PosInMap(nf_._index + nf_._count);
+ d4_assert(j >= i);
+
+ if (j > i)
+ _rowMap.RemoveAt(i, j - i);
+
+ while (i < NumRows())
+ _rowMap.ElementAt(i++) -= nf_._count;
+
+ FixupReverseMap();
+ }
+ break;
+
+ case c4_Notifier::kMove:
+ {
+ int i = PosInMap(nf_._index);
+ bool inMap = i < NumRows() && (int) _rowMap.GetAt(i) == nf_._index;
+
+ if (inMap && nf_._index != nf_._count)
+ {
+ int j = PosInMap(nf_._count);
+
+ _rowMap.RemoveAt(i);
+
+ if (j > i)
+ --j;
+
+ _rowMap.InsertAt(j, nf_._count);
+
+ FixupReverseMap();
+ }
+ }
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_SortSeq : public c4_FilterSeq
+{
+public:
+ typedef t4_i32 T;
+
+ c4_SortSeq (c4_Sequence& seq_, c4_Sequence* down_);
+ virtual ~c4_SortSeq ();
+
+ virtual c4_Notifier* PreChange(c4_Notifier& nf_);
+ virtual void PostChange(c4_Notifier& nf_);
+
+private:
+ struct c4_SortInfo
+ {
+ c4_Handler* _handler;
+ const c4_Sequence* _context;
+ c4_Bytes _buffer;
+
+ int CompareOne(c4_Sequence& seq_, T a, T b)
+ {
+ _handler->GetBytes(seq_.RemapIndex((int) b, _context), _buffer, true);
+ return _handler->Compare(seq_.RemapIndex((int) a, _context), _buffer);
+ }
+ };
+
+ bool LessThan(T a, T b);
+ bool TestSwap( T& first , T& second );
+ void MergeSortThis( T* ar, int size , T scratch[] );
+ void MergeSort( T ar[] , int size );
+
+ virtual int Compare(int, c4_Cursor) const;
+ int PosInMap(c4_Cursor cursor_) const;
+
+ c4_SortInfo* _info;
+ c4_Bytes _down;
+ int _width;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool c4_SortSeq::LessThan(T a, T b)
+{
+ if (a == b)
+ return false;
+
+ // go through each of the columns and compare values, but since
+ // handler access is used, we must be careful to remap indices
+
+ c4_SortInfo* info;
+
+ for (info = _info; info->_handler; ++info)
+ {
+ int f = info->CompareOne(_seq, a, b);
+ if (f)
+ {
+ int n = info - _info;
+ if (_width < n)
+ _width = n;
+
+ return (_down.Contents()[n] ? -f : f) < 0;
+ }
+ }
+
+ _width = info - _info;
+ return a < b;
+}
+
+inline bool c4_SortSeq::TestSwap( T& first , T& second )
+{
+ if ( LessThan( second , first ) )
+ {
+ T temp = first; first = second; second = temp;
+ return true;
+ }
+
+ return false;
+}
+
+void c4_SortSeq::MergeSortThis( T* ar, int size , T scratch[] )
+{
+ switch( size )
+ {
+ //Handle the special cases for speed:
+ case 2:
+ TestSwap( ar[ 0 ] , ar[ 1 ] );
+ break;
+
+ case 3:
+ TestSwap( ar[ 0 ] , ar[ 1 ] );
+ if ( TestSwap( ar[ 1 ] , ar[ 2 ] ) )
+ TestSwap( ar[ 0 ] , ar[ 1 ] );
+ break;
+
+ case 4:
+ //Gotta optimize this....
+ TestSwap( ar[ 0 ] , ar[ 1 ] );
+ TestSwap( ar[ 2 ] , ar[ 3 ] );
+ TestSwap( ar[ 0 ] , ar[ 2 ] );
+ TestSwap( ar[ 1 ] , ar[ 3 ] );
+ TestSwap( ar[ 1 ] , ar[ 2 ] );
+ break;
+
+ //Gotta do special case for list of five.
+
+ default:
+ //Subdivide the list, recurse, and merge
+ {
+ int s1 = size / 2;
+ int s2 = size - s1;
+ T* from1_ = scratch;
+ T* from2_ = scratch + s1;
+ MergeSortThis( from1_ , s1 , ar );
+ MergeSortThis( from2_ , s2 , ar + s1 );
+
+ T* to1_ = from1_ + s1;
+ T* to2_ = from2_ + s2;
+
+ for (;;)
+ {
+ if ( LessThan( *from1_, *from2_) )
+ {
+ *ar++ = *from1_++;
+
+ if (from1_ >= to1_)
+ {
+ while( from2_ < to2_ )
+ *ar++ = *from2_++;
+ break;
+ }
+ }
+ else
+ {
+ *ar++ = *from2_++;
+
+ if (from2_ >= to2_)
+ {
+ while( from1_ < to1_ )
+ *ar++ = *from1_++;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void c4_SortSeq::MergeSort( T ar[] , int size )
+{
+ if ( size > 1 )
+ {
+ T* scratch = d4_new T [size];
+ memcpy(scratch , ar , size * sizeof (T));
+ MergeSortThis(ar , size , scratch);
+ delete [] scratch;
+ }
+}
+
+c4_SortSeq::c4_SortSeq (c4_Sequence& seq_, c4_Sequence* down_)
+ : c4_FilterSeq (seq_), _info (0), _width (-1)
+{
+ d4_assert(NumRows() == seq_.NumRows());
+
+ if (NumRows() > 0)
+ {
+ // down is a vector of flags, true to sort in reverse order
+ char* down = (char*) _down.SetBufferClear(NumHandlers());
+
+ // set the down flag for all properties to be sorted in reverse
+ if (down_)
+ for (int i = 0; i < NumHandlers(); ++i)
+ if (down_->PropIndex(NthPropId(i)) >= 0)
+ down[i] = 1;
+
+ _width = -1;
+ int n = NumHandlers() + 1;
+ _info = d4_new c4_SortInfo [n];
+
+ int j;
+
+ for (j = 0; j < NumHandlers(); ++j)
+ {
+ _info[j]._handler = & _seq.NthHandler(j);
+ _info[j]._context = _seq.HandlerContext(j);
+ }
+
+ _info[j]._handler = 0;
+
+ // everything is ready, go sort the row index vector
+ MergeSort((T*) &_rowMap.ElementAt(0), NumRows());
+
+ delete [] _info;
+ _info = 0;
+
+ FixupReverseMap();
+ }
+}
+
+c4_SortSeq::~c4_SortSeq ()
+{
+ d4_assert(!_info);
+}
+
+int c4_SortSeq::Compare(int index_, c4_Cursor cursor_) const
+{
+ d4_assert(cursor_._seq != 0);
+
+ const char* down = (const char*) _down.Contents();
+ d4_assert(_down.Size() <= NumHandlers());
+
+ c4_Bytes data;
+
+ for (int colNum = 0; colNum < NumHandlers(); ++colNum)
+ {
+ c4_Handler& h = NthHandler(colNum);
+ const c4_Sequence* hc = HandlerContext(colNum);
+
+ if (!cursor_._seq->Get(cursor_._index, h.PropId(), data))
+ h.ClearBytes(data);
+
+ int f = h.Compare(RemapIndex(index_, hc), data);
+ if (f != 0)
+ return colNum < _down.Size() && down[colNum] ? -f : +f;
+ }
+
+ return 0;
+}
+
+int c4_SortSeq::PosInMap(c4_Cursor cursor_) const
+{
+ int i = 0;
+
+ while (i < NumRows())
+ if (Compare(i, cursor_) >= 0)
+ break;
+ else
+ ++i;
+
+ d4_assert(i == NumRows() || Compare(i, cursor_) >= 0);
+ return i;
+}
+
+c4_Notifier* c4_SortSeq::PreChange(c4_Notifier& /*nf_*/)
+{
+ if (!GetDependencies())
+ return 0;
+
+#if 0
+ c4_Notifier* chg = d4_new c4_Notifier (this);
+
+ switch (nf_._type)
+ {
+ case c4_Notifier::kSetAt:
+ case c4_Notifier::kSet:
+ {
+ d4_assert(0); // also needs nested propagation
+
+ /*
+ change can require a move *and* a change of contents
+ */
+ }
+ break;
+
+ case c4_Notifier::kInsertAt:
+ {
+ d4_assert(0); // this case isn't really difficult
+ }
+ break;
+
+ case c4_Notifier::kRemoveAt:
+ {
+ d4_assert(0); // nested propagation is too difficult for now
+ // i.e. can only use sort as last derived view
+ /*
+ possible solution:
+
+ if 1 row, simple
+ else if contig in map, also simple
+ else propagate reorder first, then delete contig
+
+ it can be done here, as multiple notifications,
+ by simulating n-1 SetAt's of first row in others
+ needs some map juggling, allow temp dup entries?
+
+ or perhaps more consistent with n separate removes
+ */
+ }
+ break;
+
+ case c4_Notifier::kMove:
+ {
+ // incorrect: may need to move if recnum matters (recs same)
+ }
+ break;
+ }
+
+ return chg;
+#endif
+
+// d4_assert(0); // fail, cannot handle a view dependent on this one yet
+ return 0;
+}
+
+void c4_SortSeq::PostChange(c4_Notifier& nf_)
+{
+ switch (nf_._type)
+ {
+ case c4_Notifier::kSet:
+ if (_seq.PropIndex(nf_._propId) > _width)
+ break; // cannot affect sort order, valuable optimization
+
+ case c4_Notifier::kSetAt:
+ {
+ int oi = (int) _revMap.GetAt(nf_._index);
+ d4_assert(oi >= 0);
+
+ c4_Cursor cursor (_seq, nf_._index);
+
+ // move the entry if the sort order has been disrupted
+ if ((oi > 0 && Compare(oi - 1, cursor) > 0) ||
+ (oi+1 < NumRows() && Compare(oi+1, cursor) < 0))
+ {
+ _rowMap.RemoveAt(oi);
+ _rowMap.InsertAt(PosInMap(cursor), nf_._index);
+
+ FixupReverseMap();
+ }
+
+ _width = NumHandlers(); // sorry, no more optimization
+ }
+ break;
+
+ case c4_Notifier::kInsertAt:
+ {
+ // if cursor was not set, it started out as a single Set
+ c4_Cursor cursor (_seq, nf_._index);
+ if (nf_._cursor)
+ cursor = *nf_._cursor;
+
+ for (int n = 0; n < NumRows(); ++n)
+ if ((int) _rowMap.GetAt(n) >= nf_._index)
+ _rowMap.ElementAt(n) += nf_._count;
+
+ int i = PosInMap(cursor);
+ _rowMap.InsertAt(i, 0, nf_._count);
+
+ for (int j = 0; j < nf_._count; ++j)
+ _rowMap.SetAt(i++, nf_._index + j);
+
+ FixupReverseMap();
+
+ _width = NumHandlers(); // sorry, no more optimization
+ }
+ break;
+
+ case c4_Notifier::kRemoveAt:
+ {
+ int lo = nf_._index;
+ int hi = nf_._index + nf_._count;
+
+ int j = 0;
+ for (int i = 0; i < NumRows(); ++i)
+ {
+ int n = (int) _rowMap.GetAt(i);
+
+ if (n >= hi)
+ _rowMap.ElementAt(i) -= nf_._count;
+
+ if (!(lo <= n && n < hi))
+ _rowMap.SetAt(j++, _rowMap.GetAt(i));
+ }
+
+ d4_assert(j + nf_._count == NumRows());
+ _rowMap.SetSize(j);
+
+ FixupReverseMap();
+
+ _width = NumHandlers(); // sorry, no more optimization
+ }
+ break;
+
+ case c4_Notifier::kMove:
+ {
+ // incorrect: may need to move if recnum matters (recs same)
+ }
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_ProjectSeq : public c4_DerivedSeq
+{
+ c4_DWordArray _colMap; // a bit large, but bytes would be too small
+ bool _frozen;
+ int _omitCount; // if > 0 then this is a dynamic "project without"
+
+public:
+ c4_ProjectSeq (c4_Sequence& seq_, c4_Sequence& in_, bool, c4_Sequence* out_);
+ virtual ~c4_ProjectSeq ();
+
+ virtual int NumHandlers() const;
+ virtual c4_Handler& NthHandler(int) const;
+ virtual const c4_Sequence* HandlerContext(int) const;
+ virtual int AddHandler(c4_Handler*);
+
+ virtual bool Get(int, int, c4_Bytes&);
+ virtual void Set(int, const c4_Property&, const c4_Bytes&);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_ProjectSeq::c4_ProjectSeq (c4_Sequence& seq_, c4_Sequence& in_,
+ bool reorder_, c4_Sequence* out_)
+ : c4_DerivedSeq (seq_), _frozen (!reorder_ && !out_), _omitCount (0)
+{
+ // build the array with column indexes
+ for (int j = 0; j < in_.NumHandlers(); ++j)
+ {
+ int propId = in_.NthPropId(j);
+ int idx = _seq.PropIndex(propId);
+
+ // if the j'th property is in the sequence, add it
+ if (idx >= 0)
+ {
+ // but only if it's not in the out_ view
+ if (out_ && out_->PropIndex(propId) >= 0)
+ ++_omitCount;
+ else
+ _colMap.Add(idx);
+ }
+ }
+
+ // if only reordering, append remaining columns from original view
+ if (reorder_)
+ {
+ for (int i = 0; i < _seq.NumHandlers(); ++i)
+ {
+ int propId = _seq.NthPropId(i);
+
+ // only consider properties we did not deal with before
+ if (in_.PropIndex(propId) < 0)
+ _colMap.Add(i);
+ }
+
+ d4_assert(_colMap.GetSize() == _seq.NumHandlers());
+ }
+}
+
+c4_ProjectSeq::~c4_ProjectSeq ()
+{
+}
+
+int c4_ProjectSeq::NumHandlers() const
+{
+ return _frozen ? _colMap.GetSize() : _seq.NumHandlers() - _omitCount;
+}
+
+c4_Handler& c4_ProjectSeq::NthHandler(int colNum_) const
+{
+ int n = colNum_ < _colMap.GetSize() ? _colMap.GetAt(colNum_) : colNum_;
+ return _seq.NthHandler(n);
+}
+
+const c4_Sequence* c4_ProjectSeq::HandlerContext(int colNum_) const
+{
+ int n = colNum_ < _colMap.GetSize() ? _colMap.GetAt(colNum_) : colNum_;
+ return _seq.HandlerContext(n);
+}
+
+int c4_ProjectSeq::AddHandler(c4_Handler* handler_)
+{
+ int n = _seq.AddHandler(handler_);
+ return _frozen ? _colMap.Add(n) : n - _omitCount;
+}
+
+bool c4_ProjectSeq::Get(int index_, int propId_, c4_Bytes& buf_)
+{
+ // fixed in 1.8: check that the property is visible
+ return PropIndex(propId_) >= 0 && _seq.Get(index_, propId_, buf_);
+}
+
+void c4_ProjectSeq::Set(int index_, const c4_Property& prop_, const c4_Bytes& bytes_)
+{
+ int n = _seq.NumHandlers();
+ _seq.Set(index_, prop_, bytes_);
+
+ // if the number of handlers changed, then one must have been added
+ if (n != _seq.NumHandlers())
+ {
+ d4_assert(n == _seq.NumHandlers() - 1);
+
+ if (_frozen)
+ _colMap.Add(n);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_Sequence* f4_CreateFilter(c4_Sequence& seq_, c4_Cursor l_, c4_Cursor h_)
+{
+ return d4_new c4_FilterSeq (seq_, l_, h_);
+}
+
+c4_Sequence* f4_CreateSort(c4_Sequence& seq_, c4_Sequence* down_)
+{
+ return d4_new c4_SortSeq (seq_, down_);
+}
+
+c4_Sequence* f4_CreateProject(c4_Sequence& seq_, c4_Sequence& in_,
+ bool reorder_, c4_Sequence* out_)
+{
+ return d4_new c4_ProjectSeq (seq_, in_, reorder_, out_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/derived.h b/akregator/src/mk4storage/metakit/src/derived.h
new file mode 100644
index 00000000..8bd934fc
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/derived.h
@@ -0,0 +1,25 @@
+// derived.h --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Encapsulation of derived view classes
+ */
+
+#ifndef __DERIVED_H__
+#define __DERIVED_H__
+
+/////////////////////////////////////////////////////////////////////////////
+// Declarations in this file
+
+ class c4_Cursor; // not defined here
+ class c4_Sequence; // not defined here
+
+ extern c4_Sequence* f4_CreateFilter(c4_Sequence&, c4_Cursor, c4_Cursor);
+ extern c4_Sequence* f4_CreateSort(c4_Sequence&, c4_Sequence* =0);
+ extern c4_Sequence* f4_CreateProject(c4_Sequence&, c4_Sequence&,
+ bool, c4_Sequence* =0);
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/akregator/src/mk4storage/metakit/src/field.cpp b/akregator/src/mk4storage/metakit/src/field.cpp
new file mode 100644
index 00000000..3867d6b5
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/field.cpp
@@ -0,0 +1,119 @@
+// field.cpp --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Implementation of the field structure tree
+ */
+
+#include "header.h"
+#include "field.h"
+
+#include <stdlib.h> // strtol
+
+#if !q4_INLINE
+#include "field.inl"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Implemented in this file
+
+ class c4_Field;
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_Field
+
+c4_Field::c4_Field (const char*& description_, c4_Field* parent_)
+ : _type (0)
+{
+ _indirect = this;
+
+ size_t n = strcspn(description_, ",[]");
+ const char* p = strchr(description_, ':');
+
+ if (p != 0 && p < description_ + n) {
+ _name = c4_String (description_, p - description_);
+ _type = p[1] & ~0x20; // force to upper case
+ } else {
+ _name = c4_String (description_, n);
+ _type = 'S';
+ }
+
+ description_ += n;
+
+ if (*description_ == '[') {
+ ++description_;
+ _type = 'V';
+
+ if (*description_ == '^') {
+ ++description_;
+ _indirect = parent_;
+ d4_assert(*description_ == ']');
+ }
+
+ if (*description_ == ']')
+ ++description_;
+ else
+ do {
+ // 2004-01-20 ignore duplicate property names
+ // (since there is no good way to report errors at this point)
+ c4_Field* sf = d4_new c4_Field (description_, this);
+ for (int i = 0; i < NumSubFields(); ++i)
+ if (SubField(i).Name().CompareNoCase(sf->Name()) == 0) {
+ delete sf;
+ sf = 0;
+ break;
+ }
+ if (sf != 0)
+ _subFields.Add(sf);
+ } while (*description_++ == ',');
+ }
+}
+
+c4_Field::~c4_Field ()
+{
+ if (_indirect == this) {
+ //better? for (int i = NumSubFields(); --i >= 0 ;)
+ for (int i = 0; i < NumSubFields(); ++i) {
+ c4_Field* sf = & SubField(i);
+ if (sf != this) // careful with recursive subfields
+ delete sf;
+ }
+ }
+}
+
+c4_String c4_Field::Description(bool anonymous_) const
+{
+ c4_String s = anonymous_ ? "?" : (const char*) Name();
+
+ if (Type() == 'V')
+ s += "[" + DescribeSubFields(anonymous_) + "]";
+ else {
+ s += ":";
+ s += (c4_String) Type();
+ }
+
+ return s;
+}
+
+c4_String c4_Field::DescribeSubFields(bool) const
+{
+ d4_assert(Type() == 'V');
+
+ if (_indirect != this)
+ return "^";
+
+ c4_String s;
+ char c = 0;
+
+ for (int i = 0; i < NumSubFields(); ++i) {
+ if (c != 0)
+ s += (c4_String) c;
+ s += SubField(i).Description();
+ c = ',';
+ }
+
+ return s;
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/field.h b/akregator/src/mk4storage/metakit/src/field.h
new file mode 100644
index 00000000..5dfc2573
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/field.h
@@ -0,0 +1,64 @@
+// field.h --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Core class to represent fields
+ */
+
+#ifndef __FIELD_H__
+#define __FIELD_H__
+
+#ifndef __K4CONF_H__
+#error Please include "k4conf.h" before this header file
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_Field
+{
+ c4_PtrArray _subFields;
+ c4_String _name;
+ char _type;
+ c4_Field* _indirect;
+
+public:
+/* Construction / destruction */
+ c4_Field (const char*&, c4_Field* =0);
+ //: Constructs a new field.
+ ~c4_Field ();
+
+/* Repeating and compound fields */
+ int NumSubFields() const;
+ //: Returns the number of subfields.
+ c4_Field& SubField(int) const;
+ //: Returns the description of each subfield.
+ bool IsRepeating() const;
+ //: Returns true if this field contains subtables.
+
+/* Field name and description */
+ const c4_String& Name() const;
+ //: Returns name of this field.
+ char Type() const;
+ //: Returns the type description of this field, if any.
+ char OrigType() const;
+ //: Similar, but report types which were originall 'M' as well.
+ c4_String Description(bool anonymous_ =false) const;
+ //: Describes the structure, omit names if anonymous.
+ c4_String DescribeSubFields(bool anonymous_ =false) const;
+ //: Describes just the subfields, omit names if anonymous.
+
+private:
+ c4_Field (const c4_Field&); // not implemented
+ void operator= (const c4_Field&); // not implemented
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if q4_INLINE
+#include "field.inl"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/akregator/src/mk4storage/metakit/src/field.inl b/akregator/src/mk4storage/metakit/src/field.inl
new file mode 100644
index 00000000..823e626e
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/field.inl
@@ -0,0 +1,37 @@
+// field.inl --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Inlined members of the field class
+ */
+
+d4_inline bool c4_Field::IsRepeating() const
+{
+ return _type == 'V';
+}
+
+d4_inline int c4_Field::NumSubFields() const
+{
+ return _indirect->_subFields.GetSize();
+}
+
+d4_inline c4_Field& c4_Field::SubField(int index_) const
+{
+ return *(c4_Field*) _indirect->_subFields.GetAt(index_);
+}
+
+d4_inline const c4_String& c4_Field::Name() const
+{
+ return _name;
+}
+
+d4_inline char c4_Field::OrigType() const
+{
+ return _type;
+}
+
+d4_inline char c4_Field::Type() const
+{
+ return _type == 'M' ? 'B' : _type;
+}
diff --git a/akregator/src/mk4storage/metakit/src/fileio.cpp b/akregator/src/mk4storage/metakit/src/fileio.cpp
new file mode 100644
index 00000000..28ff7dca
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/fileio.cpp
@@ -0,0 +1,434 @@
+// fileio.cpp --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Implementation of c4_FileStrategy and c4_FileStream
+ */
+
+#include "header.h"
+#include "mk4io.h"
+
+#if q4_WIN32
+#if q4_MSVC && !q4_STRICT
+#pragma warning(disable: 4201) // nonstandard extension used : ...
+#endif
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#if !defined (q4_WINCE)
+#include <io.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#endif
+#endif
+
+#if q4_UNIX && HAVE_MMAP
+#include <sys/types.h>
+#include <sys/mman.h>
+#endif
+
+#if q4_UNIX
+#include <unistd.h>
+#include <fcntl.h>
+#endif
+
+#if q4_WINCE
+#define _get_osfhandle(x) x
+#endif
+
+#ifndef _O_NOINHERIT
+#define _O_NOINHERIT 0
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// The "Carbon" version of a build on Macintosh supports running under
+// either MacOS 7..9 (which has no mmap), or MacOS X (which has mmap).
+// The logic below was adapted from a contribution by Paul Snively, it
+// decides at run time which case it is, and switches I/O calls to match.
+
+#if defined (q4_CARBON) && q4_CARBON
+//#if q4_MAC && !defined (__MACH__) && (!q4_MWCW || __MWERKS__ >= 0x3000)
+#undef HAVE_MMAP
+#define HAVE_MMAP 1
+
+#include <CFBundle.h>
+#include <Folders.h>
+
+#define PROT_NONE 0x00
+#define PROT_READ 0x01
+#define PROT_WRITE 0x02
+#define PROT_EXEC 0x04
+
+#define MAP_SHARED 0x0001
+#define MAP_PRIVATE 0x0002
+
+#define MAP_FIXED 0x0010
+#define MAP_RENAME 0x0020
+#define MAP_NORESERVE 0x0040
+#define MAP_INHERIT 0x0080
+#define MAP_NOEXTEND 0x0100
+#define MAP_HASSEMAPHORE 0x0200
+
+typedef unsigned long t4_u32;
+
+static t4_u32 sfwRefCount = 0;
+static CFBundleRef systemFramework = NULL;
+
+static char* fake_mmap(char*, t4_u32, int, int, int, long long)
+ { return (char*) -1L; }
+static int fake_munmap(char*, t4_u32)
+ { return 0; }
+
+static FILE* (*my_fopen)(const char*,const char*) = fopen;
+static int (*my_fclose)(FILE*) = fclose;
+static long (*my_ftell)(FILE*) = ftell;
+static int (*my_fseek)(FILE*,long,int) = fseek;
+static t4_u32 (*my_fread)(void* ptr,t4_u32,t4_u32,FILE*) = fread;
+static t4_u32 (*my_fwrite)(const void* ptr,t4_u32,t4_u32,FILE*) = fwrite;
+static int (*my_ferror)(FILE*) = ferror;
+static int (*my_fflush)(FILE*) = fflush;
+static int (*my_fileno)(FILE*) = fileno;
+static char* (*my_mmap)(char*,t4_u32,int,int,int,long long) = fake_mmap;
+static int (*my_munmap)(char*,t4_u32) = fake_munmap;
+
+static void InitializeIO()
+{
+ if (sfwRefCount++) return; // race condition, infinitesimal risk
+
+ FSRef theRef;
+ if (FSFindFolder(kOnAppropriateDisk, kFrameworksFolderType,
+ false, &theRef) == noErr) {
+ CFURLRef fw = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &theRef);
+ if (fw) {
+ CFURLRef bd =
+ CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault,
+ fw, CFSTR("System.framework"), false);
+ CFRelease(fw);
+ if (bd) {
+ systemFramework = CFBundleCreate(kCFAllocatorSystemDefault, bd);
+ CFRelease(bd);
+ }
+ }
+ if (!systemFramework || !CFBundleLoadExecutable(systemFramework))
+ return;
+#define F(x) CFBundleGetFunctionPointerForName(systemFramework, CFSTR(#x))
+ my_fopen = (FILE* (*)(const char*,const char*)) F(fopen);
+ my_fclose = (int (*)(FILE*)) F(fclose);
+ my_ftell = (long (*)(FILE*)) F(ftell);
+ my_fseek = (int (*)(FILE*,long,int)) F(fseek);
+ my_fread = (t4_u32 (*)(void* ptr,t4_u32,t4_u32,FILE*)) F(fread);
+ my_fwrite = (t4_u32 (*)(const void* ptr,t4_u32,t4_u32,FILE*)) F(fwrite);
+ my_ferror = (int (*)(FILE*)) F(ferror);
+ my_fflush = (int (*)(FILE*)) F(fflush);
+ my_fileno = (int (*)(FILE*)) F(fileno);
+ my_mmap = (char* (*)(char*,t4_u32,int,int,int,long long)) F(mmap);
+ my_munmap = (int (*)(char*,t4_u32)) F(munmap);
+#undef F
+ d4_assert(my_fopen && my_fclose && my_ftell && my_fseek &&
+ my_fread && my_fwrite && my_ferror && my_fflush &&
+ my_fileno && my_mmap && my_munmap);
+ }
+}
+
+static void FinalizeIO()
+{
+ if (--sfwRefCount) return; // race condition, infinitesimal risk
+
+ if (systemFramework) {
+ CFBundleUnloadExecutable(systemFramework);
+ CFRelease(systemFramework);
+ systemFramework = 0;
+ }
+}
+
+#define fopen my_fopen
+#define fclose my_fclose
+#define ftell my_ftell
+#define fseek my_fseek
+#define fread my_fread
+#define fwrite my_fwrite
+#define ferror my_ferror
+#define fflush my_fflush
+#define fileno my_fileno
+#define mmap my_mmap
+#define munmap my_munmap
+
+#else
+
+#define InitializeIO()
+#define FinalizeIO()
+
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if q4_CHECK
+#include <stdlib.h>
+
+void f4_AssertionFailed(const char* cond_, const char* file_, int line_)
+{
+ fprintf(stderr, "Assertion failed: %s (file %s, line %d)\n",
+ cond_, file_, line_);
+ abort();
+}
+
+#endif //q4_CHECK
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_FileStream
+
+c4_FileStream::c4_FileStream (FILE* stream_, bool owned_)
+ : _stream (stream_), _owned (owned_)
+{
+}
+
+c4_FileStream::~c4_FileStream ()
+{
+ if (_owned)
+ fclose(_stream);
+}
+
+int c4_FileStream::Read(void* buffer_, int length_)
+{
+ d4_assert(_stream != 0);
+
+ return (int) fread(buffer_, 1, length_, _stream);
+}
+
+bool c4_FileStream::Write(const void* buffer_, int length_)
+{
+ d4_assert(_stream != 0);
+
+ return (int) fwrite(buffer_, 1, length_, _stream) == length_;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_FileStrategy
+
+c4_FileStrategy::c4_FileStrategy (FILE* file_)
+ : _file (file_), _cleanup (0)
+{
+ InitializeIO();
+ ResetFileMapping();
+}
+
+c4_FileStrategy::~c4_FileStrategy ()
+{
+ _file = 0;
+ ResetFileMapping();
+
+ if (_cleanup)
+ fclose(_cleanup);
+
+ d4_assert(_mapStart == 0);
+ FinalizeIO();
+}
+
+bool c4_FileStrategy::IsValid() const
+{
+ return _file != 0;
+}
+
+t4_i32 c4_FileStrategy::FileSize()
+{
+ d4_assert(_file != 0);
+
+ long size = -1;
+
+ long old = ftell(_file);
+ if (old >= 0 && fseek(_file, 0, 2) == 0) {
+ long pos = ftell(_file);
+ if (fseek(_file, old, 0) == 0)
+ size = pos;
+ }
+
+ if (size < 0)
+ _failure = ferror(_file);
+
+ return size;
+}
+
+t4_i32 c4_FileStrategy::FreshGeneration()
+{
+ d4_assert(false);
+ return 0;
+}
+
+void c4_FileStrategy::ResetFileMapping()
+{
+#if q4_WIN32
+ if (_mapStart != 0) {
+ _mapStart -= _baseOffset;
+ d4_dbgdef(BOOL g =)
+ ::UnmapViewOfFile((char*) _mapStart);
+ d4_assert(g);
+ _mapStart = 0;
+ _dataSize = 0;
+ }
+
+ if (_file != 0) {
+ t4_i32 len = FileSize();
+
+ if (len > 0) {
+ FlushFileBuffers((HANDLE) _get_osfhandle(_fileno(_file)));
+ HANDLE h = ::CreateFileMapping((HANDLE) _get_osfhandle(_fileno(_file)),
+ 0, PAGE_READONLY, 0, len, 0);
+ d4_assert(h); // check for errors, but can continue without mapping
+
+ if (h) {
+ _mapStart = (t4_byte*) ::MapViewOfFile(h, FILE_MAP_READ, 0, 0, len);
+ d4_assert(_mapStart != 0);
+
+ if (_mapStart != 0) {
+ _mapStart += _baseOffset;
+ _dataSize = len - _baseOffset;
+ }
+
+ d4_dbgdef(BOOL f =)
+ ::CloseHandle(h);
+ d4_assert(f);
+ }
+ }
+ }
+#elif HAVE_MMAP
+ if (_mapStart != 0) {
+ _mapStart -= _baseOffset;
+ munmap((char*) _mapStart, _baseOffset + _dataSize); // also loses const
+ _mapStart = 0;
+ _dataSize = 0;
+ }
+
+ if (_file != 0) {
+ t4_i32 len = FileSize();
+
+ if (len > 0) {
+ _mapStart = (const t4_byte*) mmap(0, len, PROT_READ, MAP_SHARED,
+ fileno(_file), 0);
+ if (_mapStart != (void*) -1L) {
+ _mapStart += _baseOffset;
+ _dataSize = len - _baseOffset;
+ } else
+ _mapStart = 0;
+ }
+ }
+#endif
+}
+
+bool c4_FileStrategy::DataOpen(const char* fname_, int mode_)
+{
+ d4_assert(!_file);
+
+#if q4_WIN32 && !q4_BORC && !q4_WINCE
+ int flags = _O_BINARY | _O_NOINHERIT | (mode_ > 0 ? _O_RDWR : _O_RDONLY);
+ int fd = _open(fname_, flags);
+ if (fd != -1)
+ _cleanup = _file = _fdopen(fd, mode_ > 0 ? "r+b" : "rb");
+#else
+ _cleanup = _file = fopen(fname_, mode_ > 0 ? "r+b" : "rb");
+#if q4_UNIX
+ if (_file != 0)
+ fcntl(fileno(_file), F_SETFD, FD_CLOEXEC);
+#endif //q4_UNIX
+#endif //q4_WIN32 && !q4_BORC && !q4_WINCE
+
+ if (_file != 0) {
+ ResetFileMapping();
+ return true;
+ }
+
+ if (mode_ > 0) {
+#if q4_WIN32 && !q4_BORC && !q4_WINCE
+ fd = _open(fname_, flags | _O_CREAT, _S_IREAD | _S_IWRITE);
+ if (fd != -1)
+ _cleanup = _file = _fdopen(fd, "w+b");
+#else
+ _cleanup = _file = fopen(fname_, "w+b");
+#if q4_UNIX
+ if (_file != 0)
+ fcntl(fileno(_file), F_SETFD, FD_CLOEXEC);
+#endif //q4_UNIX
+#endif //q4_WIN32 && !q4_BORC && !q4_WINCE
+ }
+
+ //d4_assert(_file != 0);
+ return false;
+}
+
+int c4_FileStrategy::DataRead(t4_i32 pos_, void* buf_, int len_)
+{
+ d4_assert(_baseOffset + pos_ >= 0);
+ d4_assert(_file != 0);
+
+ //printf("DataRead at %d len %d\n", pos_, len_);
+ return fseek(_file, _baseOffset + pos_, 0) != 0 ? -1 :
+ (int) fread(buf_, 1, len_, _file);
+}
+
+void c4_FileStrategy::DataWrite(t4_i32 pos_, const void* buf_, int len_)
+{
+ d4_assert(_baseOffset + pos_ >= 0);
+ d4_assert(_file != 0);
+#if 0
+ if (_mapStart <= buf_ && buf_ < _mapStart + _dataSize) {
+ printf("DataWrite %08x at %d len %d (map %d)\n", buf_, pos_, len_,
+ (const t4_byte*) buf_ - _mapStart + _baseOffset);
+ } else {
+ printf("DataWrite %08x at %d len %d\n", buf_, pos_, len_);
+ }
+ fprintf(stderr, " _mapStart %08x _dataSize %d buf_ %08x len_ %d _baseOffset %d\n",
+ _mapStart, _dataSize, buf_, len_, _baseOffset);
+ printf(" _mapStart %08x _dataSize %d buf_ %08x len_ %d _baseOffset %d\n",
+ _mapStart, _dataSize, buf_, len_, _baseOffset);
+ fflush(stdout);
+#endif
+
+#if q4_WIN32 || __hpux || __MACH__
+// if (buf_ >= _mapStart && buf_ <= _mapLimit - len_)
+
+ // a horrendous hack to allow file mapping for Win95 on network drive
+ // must use a temp buf to avoid write from mapped file to same file
+ //
+ // 6-Feb-1999 -- this workaround is not thread safe
+ // 30-Nov-2001 -- changed to use the stack so now it is
+ // 28-Oct-2002 -- added HP/UX to the mix, to avoid hard lockup
+ char tempBuf [4096];
+ d4_assert(len_ <= sizeof tempBuf);
+ buf_ = memcpy(tempBuf, buf_, len_);
+#endif
+
+ if (fseek(_file, _baseOffset + pos_, 0) != 0 ||
+ (int) fwrite(buf_, 1, len_, _file) != len_) {
+ _failure = ferror(_file);
+ d4_assert(_failure != 0);
+ d4_assert(true); // always force an assertion failure in debug mode
+ }
+}
+
+void c4_FileStrategy::DataCommit(t4_i32 limit_)
+{
+ d4_assert(_file != 0);
+
+ if (fflush(_file) < 0) {
+ _failure = ferror(_file);
+ d4_assert(_failure != 0);
+ d4_assert(true); // always force an assertion failure in debug mode
+ return;
+ }
+
+ if (limit_ > 0) {
+#if 0 // can't truncate file in a portable way!
+ // unmap the file first, WinNT is more picky about this than Win95
+ FILE* save = _file;
+
+ _file = 0;
+ ResetFileMapping();
+ _file = save;
+
+ _file->SetLength(limit_); // now we can resize the file
+#endif
+ ResetFileMapping(); // remap, since file length may have changed
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/format.cpp b/akregator/src/mk4storage/metakit/src/format.cpp
new file mode 100644
index 00000000..aa23e739
--- /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;
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/format.h b/akregator/src/mk4storage/metakit/src/format.h
new file mode 100644
index 00000000..721e5548
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/format.h
@@ -0,0 +1,23 @@
+// format.h --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Encapsulation of all format handlers
+ */
+
+#ifndef __FORMAT_H__
+#define __FORMAT_H__
+
+/////////////////////////////////////////////////////////////////////////////
+// Declarations in this file
+
+ class c4_Handler; // not defined here
+
+ extern c4_Handler* f4_CreateFormat(const c4_Property&, c4_HandlerSeq&);
+ extern int f4_ClearFormat(char);
+ extern int f4_CompareFormat(char, const c4_Bytes&, const c4_Bytes&);
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/akregator/src/mk4storage/metakit/src/gnuc.h b/akregator/src/mk4storage/metakit/src/gnuc.h
new file mode 100644
index 00000000..da112ac6
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/gnuc.h
@@ -0,0 +1,13 @@
+// gnuc.h --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Configuration header for GNU C++
+ */
+
+#define q4_GNUC 1
+
+#if !defined (q4_BOOL)
+#define q4_BOOL 1
+#endif
diff --git a/akregator/src/mk4storage/metakit/src/handler.cpp b/akregator/src/mk4storage/metakit/src/handler.cpp
new file mode 100644
index 00000000..6c68c5c3
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/handler.cpp
@@ -0,0 +1,500 @@
+// handler.cpp --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Handlers store data in column-wise format
+ */
+
+#include "header.h"
+#include "handler.h"
+#include "format.h"
+#include "field.h"
+#include "column.h"
+#include "persist.h"
+
+#if !q4_INLINE
+#include "handler.inl"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_Handler
+
+void c4_Handler::ClearBytes(c4_Bytes& buf_) const
+{
+ static char zeros[8];
+
+ int n = f4_ClearFormat(Property().Type());
+ d4_assert(n <= sizeof zeros);
+
+ buf_ = c4_Bytes (zeros, n);
+}
+
+int c4_Handler::Compare(int index_, const c4_Bytes& buf_)
+{
+ // create a copy for small data, since ints use a common _item buffer
+ c4_Bytes copy (buf_.Contents(), buf_.Size(), buf_.Size() <= 8);
+
+ c4_Bytes data;
+ GetBytes(index_, data);
+
+ return f4_CompareFormat(Property().Type(), data, copy);
+}
+
+void c4_Handler::Commit(c4_SaveContext&)
+{
+ d4_assert(0);
+}
+
+void c4_Handler::OldDefine(char, c4_Persist&)
+{
+ d4_assert(0);
+}
+
+ // this is how the old "Get" was, keep it until no longer needed
+void c4_Handler::GetBytes(int index_, c4_Bytes& buf_, bool copySmall_)
+{
+ int n;
+ const void* p = Get(index_, n);
+ buf_ = c4_Bytes (p, n, copySmall_ && n <= 8);
+}
+
+void c4_Handler::Move(int from_, int to_)
+{
+ if (from_ != to_) {
+ c4_Bytes data;
+ GetBytes(from_, data);
+
+ Remove(from_, 1);
+
+ if (to_ > from_)
+ --to_;
+
+ Insert(to_, data, 1);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_HandlerSeq
+
+c4_HandlerSeq::c4_HandlerSeq (c4_Persist* persist_)
+ : _persist (persist_), _field (0), _parent (0), _numRows (0)
+{
+}
+
+c4_HandlerSeq::c4_HandlerSeq (c4_HandlerSeq& owner_, c4_Handler* handler_)
+ : _persist (owner_.Persist()), _field (owner_.FindField(handler_)),
+ _parent (&owner_), _numRows (0)
+{
+ for (int i = 0; i < NumFields(); ++i) {
+ c4_Field& field = Field(i);
+ c4_Property prop (field.Type(), field.Name());
+
+ d4_dbgdef(int n =)
+ AddHandler(f4_CreateFormat(prop, *this));
+ d4_assert(n == i);
+ }
+}
+
+c4_HandlerSeq::~c4_HandlerSeq ()
+{
+ const bool rootLevel = _parent == this;
+ c4_Persist* pers = _persist;
+
+ if (rootLevel && pers != 0)
+ pers->DoAutoCommit();
+
+ DetachFromParent();
+ DetachFromStorage(true);
+
+ for (int i = 0; i < NumHandlers(); ++i)
+ delete & NthHandler(i);
+ _handlers.SetSize(0);
+
+ ClearCache();
+
+ if (rootLevel) {
+ delete _field;
+
+ d4_assert(pers != 0);
+ delete pers;
+ }
+}
+
+c4_Persist* c4_HandlerSeq::Persist() const
+{
+ return _persist;
+}
+
+void c4_HandlerSeq::DefineRoot()
+{
+ d4_assert(_field == 0);
+ d4_assert(_parent == 0);
+
+ SetNumRows(1);
+
+ const char* desc = "[]";
+ _field = d4_new c4_Field (desc);
+ d4_assert(!*desc);
+
+ _parent = this;
+}
+
+c4_Handler* c4_HandlerSeq::CreateHandler(const c4_Property& prop_)
+{
+ return f4_CreateFormat(prop_, *this);
+}
+
+c4_Field& c4_HandlerSeq::Definition() const
+{
+ d4_assert(_field != 0);
+
+ return *_field;
+}
+
+void c4_HandlerSeq::DetachFromParent()
+{
+ if (_field != 0) {
+ const char* desc = "[]";
+ c4_Field f (desc);
+ d4_assert(!*desc);
+ Restructure(f, false);
+ _field = 0;
+ }
+
+ _parent = 0;
+}
+
+void c4_HandlerSeq::DetachFromStorage(bool full_)
+{
+ if (_persist != 0) {
+ int limit = full_ ? 0 : NumFields();
+
+ // get rid of all handlers which might do I/O
+ for (int c = NumHandlers(); --c >= 0; ) {
+ c4_Handler& h = NthHandler(c);
+
+ // all nested fields are detached recursively
+ if (IsNested(c))
+ for (int r = 0; r < NumRows(); ++r)
+ if (h.HasSubview(r))
+ SubEntry(c, r).DetachFromStorage(full_);
+
+ if (c >= limit) {
+ if (h.IsPersistent()) {
+ delete &h;
+ _handlers.RemoveAt(c);
+ ClearCache();
+ }
+ }
+ }
+
+ if (full_) {
+ //UnmappedAll();
+ _persist = 0;
+ }
+ }
+}
+
+void c4_HandlerSeq::DetermineSpaceUsage()
+{
+ for (int c = 0; c < NumFields(); ++c)
+ if (IsNested(c)) {
+ c4_Handler& h = NthHandler(c);
+ for (int r = 0; r < NumRows(); ++r)
+ if (h.HasSubview(r))
+ SubEntry(c, r).DetermineSpaceUsage();
+ }
+}
+
+void c4_HandlerSeq::SetNumRows(int numRows_)
+{
+ d4_assert(_numRows >= 0);
+
+ _numRows = numRows_;
+}
+
+int c4_HandlerSeq::AddHandler(c4_Handler* handler_)
+{
+ d4_assert(handler_ != 0);
+
+ return _handlers.Add(handler_);
+}
+
+const char* c4_HandlerSeq::Description()
+{
+ // 19-01-2003: avoid too dense code, since Sun CC seems to choke on it
+ //return _field != 0 ? UseTempBuffer(Definition().DescribeSubFields()) : 0;
+ if (_field == 0)
+ return 0;
+ c4_String s = _field->DescribeSubFields();
+ return UseTempBuffer(s);
+}
+
+void c4_HandlerSeq::Restructure(c4_Field& field_, bool remove_)
+{
+ //d4_assert(_field != 0);
+
+ // all nested fields must be set up, before we shuffle them around
+ for (int k = 0; k < NumHandlers(); ++k)
+ if (IsNested(k)) {
+ c4_Handler& h = NthHandler(k);
+ for (int n = 0; n < NumRows(); ++n)
+ if (h.HasSubview(n))
+ SubEntry(k, n);
+ }
+
+ for (int i = 0; i < field_.NumSubFields(); ++i) {
+ c4_Field& nf = field_.SubField(i);
+ c4_Property prop (nf.Type(), nf.Name());
+
+ int n = PropIndex(prop.GetId());
+ if (n == i)
+ continue;
+
+ if (n < 0) {
+ _handlers.InsertAt(i, f4_CreateFormat(prop, *this));
+ NthHandler(i).Define(NumRows(), 0);
+ } else {
+ // move the handler to the front
+ d4_assert(n > i);
+ _handlers.InsertAt(i, _handlers.GetAt(n));
+ _handlers.RemoveAt(++n);
+ }
+
+ ClearCache(); // we mess with the order of handler, keep clearing it
+
+ d4_assert(PropIndex(prop.GetId()) == i);
+ }
+
+ c4_Field* ofld = _field;
+ // special case if we're "restructuring a view out of persistence", see below
+
+ _field = remove_ ? 0 : &field_;
+
+ // let handler do additional init once all have been prepared
+ //for (int n = 0; n < NumHandlers(); ++n)
+ // NthHandler(n).Define(NumRows(), 0);
+
+ const char* desc = "[]";
+ c4_Field temp (desc);
+
+ // all nested fields are restructured recursively
+ for (int j = 0; j < NumHandlers(); ++j)
+ if (IsNested(j)) {
+ c4_Handler& h = NthHandler(j);
+ for (int n = 0; n < NumRows(); ++n)
+ if (h.HasSubview(n)) {
+ c4_HandlerSeq& seq = SubEntry(j, n);
+ if (j < NumFields())
+ seq.Restructure(field_.SubField(j), false);
+ else if (seq._field != 0)
+ seq.Restructure(temp, true);
+ }
+ }
+
+ if (_parent == this)
+ delete ofld; // the root table owns its field structure tree
+}
+
+int c4_HandlerSeq::NumFields() const
+{
+ return _field != 0 ? _field->NumSubFields() : 0;
+}
+
+char c4_HandlerSeq::ColumnType(int index_) const
+{
+ return NthHandler(index_).Property().Type();
+}
+
+bool c4_HandlerSeq::IsNested(int index_) const
+{
+ return ColumnType(index_) == 'V';
+}
+
+c4_Field& c4_HandlerSeq::Field(int index_) const
+{
+ d4_assert(_field != 0);
+
+ return _field->SubField(index_);
+}
+
+void c4_HandlerSeq::Prepare(const t4_byte** ptr_, bool selfDesc_)
+{
+ if (ptr_ != 0) {
+ d4_dbgdef(t4_i32 sias =)
+ c4_Column::PullValue(*ptr_);
+ d4_assert(sias == 0); // not yet
+
+ if (selfDesc_) {
+ t4_i32 n = c4_Column::PullValue(*ptr_);
+ if (n > 0) {
+ c4_String s = "[" + c4_String ((const char*) *ptr_, n) + "]";
+ const char* desc = s;
+
+ c4_Field* f = d4_new c4_Field (desc);
+ d4_assert(!*desc);
+
+ Restructure(*f, false);
+ *ptr_ += n;
+ }
+ }
+
+ int rows = (int) c4_Column::PullValue(*ptr_);
+ if (rows > 0) {
+ SetNumRows(rows);
+
+ for (int i = 0; i < NumFields(); ++i)
+ NthHandler(i).Define(rows, ptr_);
+ }
+ }
+}
+
+void c4_HandlerSeq::OldPrepare()
+{
+ d4_assert(_persist != 0);
+
+ for (int i = 0; i < NumFields(); ++i) {
+ char origType = _field->SubField(i).OrigType();
+ NthHandler(i).OldDefine(origType, *_persist);
+ }
+}
+
+void c4_HandlerSeq::FlipAllBytes()
+{
+ for (int i = 0; i < NumHandlers(); ++i) {
+ c4_Handler& h = NthHandler(i);
+ h.FlipBytes();
+ }
+}
+
+ // New 19990903: swap rows in tables without touching the memo fields
+ // or subviews on disk. This is used by the new c4_View::RelocateRows.
+
+void c4_HandlerSeq::ExchangeEntries(int srcPos_, c4_HandlerSeq& dst_, int dstPos_)
+{
+ d4_assert(NumHandlers() == dst_.NumHandlers());
+
+ c4_Bytes t1, t2;
+
+ for (int col = 0; col < NumHandlers(); ++col)
+ {
+ if (IsNested(col))
+ {
+ d4_assert(dst_.IsNested(col));
+
+ int n;
+ c4_HandlerSeq** e1 = (c4_HandlerSeq**) NthHandler(col).Get(srcPos_, n);
+ c4_HandlerSeq** e2 = (c4_HandlerSeq**) dst_.NthHandler(col).Get(dstPos_, n);
+ d4_assert(*e1 != 0 && *e2 != 0);
+
+ // swap the two entries
+ c4_HandlerSeq* e = *e1;
+ *e1 = *e2;
+ *e2 = e;
+
+ // shorthand, *after* the swap
+ c4_HandlerSeq& t1 = SubEntry(col, srcPos_);
+ c4_HandlerSeq& t2 = dst_.SubEntry(col, dstPos_);
+
+ // adjust the parents
+ t1._parent = this;
+ t2._parent = &dst_;
+
+ // reattach the proper field structures
+ t1.Restructure(Field(col), false);
+ t2.Restructure(dst_.Field(col), false);
+ }
+ else
+ {
+ d4_assert(ColumnType(col) == dst_.ColumnType(col));
+
+ c4_Handler& h1 = NthHandler(col);
+ c4_Handler& h2 = dst_.NthHandler(col);
+
+#if 0 // memo's are 'B' now, but tricky to deal with, so copy them for now
+ if (ColumnType(col) == 'M')
+ {
+ c4_Column* c1 = h1.GetNthMemoCol(srcPos_, true);
+ c4_Column* c2 = h2.GetNthMemoCol(dstPos_, true);
+
+ t4_i32 p1 = c1 ? c1->Position() : 0;
+ t4_i32 p2 = c2 ? c2->Position() : 0;
+
+ t4_i32 s1 = c1 ? c1->ColSize() : 0;
+ t4_i32 s2 = c2 ? c2->ColSize() : 0;
+
+ d4_assert(false); // broken
+ //!h1.SetNthMemoPos(srcPos_, p2, s2, c2);
+ //!h2.SetNthMemoPos(dstPos_, p1, s1, c1);
+ }
+#endif
+ // 10-4-2002: Need to use copies in case either item points into
+ // memory that could move, or if access re-uses a shared buffer.
+ // The special cases are sufficiently tricky that it's NOT being
+ // optimized for now (temp bufs, mmap ptrs, c4_Bytes buffering).
+
+ int n1, n2;
+ const void* p1 = h1.Get(srcPos_, n1);
+ const void* p2 = h2.Get(dstPos_, n2);
+
+ c4_Bytes t1 (p1, n1, true);
+ c4_Bytes t2 (p2, n2, true);
+
+ h1.Set(srcPos_, t2);
+ h2.Set(dstPos_, t1);
+ }
+ }
+}
+
+c4_HandlerSeq& c4_HandlerSeq::SubEntry(int col_, int row_) const
+{
+ d4_assert(IsNested(col_));
+
+ c4_Bytes temp;
+ NthHandler(col_).GetBytes(row_, temp);
+
+ d4_assert(temp.Size() == sizeof (c4_HandlerSeq**));
+ c4_HandlerSeq** p = (c4_HandlerSeq**) temp.Contents(); // loses const
+
+ d4_assert(p != 0 && *p != 0);
+
+ return **p;
+}
+
+c4_Field* c4_HandlerSeq::FindField(const c4_Handler* handler_)
+{
+ for (int i = 0; i < NumFields(); ++i)
+ if (handler_ == &NthHandler(i))
+ return &Field(i);
+ return 0;
+}
+
+void c4_HandlerSeq::UnmappedAll()
+{
+ for (int i = 0; i < NumFields(); ++i)
+ NthHandler(i).Unmapped();
+}
+
+ // construct meta view from a pre-parsed field tree structure
+ // this will one day be converted to directly parse the description string
+void c4_HandlerSeq::BuildMeta(int parent_, int colnum_, c4_View& meta_,
+ const c4_Field& field_)
+{
+ c4_IntProp pP ("P"), pC ("C");
+ c4_ViewProp pF ("F");
+ c4_StringProp pN ("N"), pT ("T");
+
+ int n = meta_.Add(pP [parent_] + pC [colnum_]);
+ c4_View fields = pF (meta_[n]);
+
+ for (int i = 0; i < field_.NumSubFields(); ++i) {
+ const c4_Field& f = field_.SubField(i);
+ char type = f.Type();
+ fields.Add(pN [f.Name()] + pT [c4_String (&type, 1)]);
+ if (type == 'V')
+ BuildMeta(n, i, meta_, f);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/handler.h b/akregator/src/mk4storage/metakit/src/handler.h
new file mode 100644
index 00000000..6003f625
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/handler.h
@@ -0,0 +1,150 @@
+// handler.h --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Definition of the main handler classes
+ */
+
+#ifndef __HANDLER_H__
+#define __HANDLER_H__
+
+/////////////////////////////////////////////////////////////////////////////
+// Declarations in this file
+
+ class c4_Handler; // data representation handler
+
+// class c4_Sequence;
+ class c4_HandlerSeq; // a sequence built from handlers
+
+ class c4_Column; // not defined here
+ class c4_Field; // not defined here
+ class c4_Persist; // not defined here
+ class c4_SaveContext; // not defined here
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_Handler
+{
+ c4_Property _property;
+
+public:
+ c4_Handler (const c4_Property& _prop);
+ //: Constructor (this is an abstract base class).
+ virtual ~c4_Handler ();
+
+ virtual void Define(int, const t4_byte**);
+ //: Called when the corresponding table has been fully defined.
+ virtual void FlipBytes();
+ //: Called to reverse the internal byte order of foreign data.
+ virtual void Commit(c4_SaveContext& ar_);
+ //: Commit the associated column(s) to file.
+ virtual void OldDefine(char, c4_Persist&);
+
+ const c4_Property& Property() const;
+ //: Returns the property associated with this handler.
+ int PropId() const;
+ //: Returns the id of the property associated with this handler.
+
+ void ClearBytes(c4_Bytes& buf_) const;
+ //: Returns the default value for items of this type.
+
+ virtual int ItemSize(int index_) = 0;
+ //: Return width of specified data item.
+ void GetBytes(int index_, c4_Bytes& buf_, bool copySmall_ =false);
+ //: Used for backward compatibility, should probably be replaced.
+ virtual const void* Get(int index_, int& length_) = 0;
+ //: Retrieves the data item at the specified index.
+ virtual void Set(int index_, const c4_Bytes& buf_) = 0;
+ //: Stores a new data item at the specified index.
+
+ int Compare(int index_, const c4_Bytes& buf_);
+ //: Compares an entry with a specified data item.
+
+ virtual void Insert(int index_, const c4_Bytes& buf_, int count_) = 0;
+ //: Inserts 1 or more data items at the specified index.
+ virtual void Remove(int index_, int count_) = 0;
+ //: Removes 1 or more data items at the specified index.
+ void Move(int from_, int to_);
+ //: Move a data item to another position.
+
+ virtual c4_Column* GetNthMemoCol(int index_, bool alloc_ =false);
+ //: Special access to underlying data of memo entries
+
+ virtual bool IsPersistent() const;
+ //: True if this handler might do I/O to satisfy fetches
+
+ virtual void Unmapped();
+ //: Make sure this handler stops using file mappings
+
+ virtual bool HasSubview(int index_);
+ //: True if this subview has materialized into an object
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_HandlerSeq : public c4_Sequence
+{
+ c4_PtrArray _handlers;
+ c4_Persist* _persist;
+ c4_Field* _field;
+ c4_HandlerSeq* _parent;
+ int _numRows;
+
+public:
+ c4_HandlerSeq (c4_Persist*);
+ c4_HandlerSeq (c4_HandlerSeq& owner_, c4_Handler* handler_);
+
+ virtual int NumRows() const;
+ virtual void SetNumRows(int);
+
+ virtual int NumHandlers() const;
+ virtual c4_Handler& NthHandler(int) const;
+ virtual const c4_Sequence* HandlerContext(int) const;
+ virtual int AddHandler(c4_Handler*);
+
+ void DefineRoot();
+ void Restructure(c4_Field&, bool remove_);
+ void DetachFromParent();
+ void DetachFromStorage(bool full_);
+ void DetermineSpaceUsage();
+
+ c4_Field& Definition() const;
+ const char* Description();
+ c4_HandlerSeq& Parent() const;
+ virtual c4_Persist* Persist() const;
+
+ c4_Field& Field(int) const;
+ int NumFields() const;
+ char ColumnType(int index_) const;
+ bool IsNested(int) const;
+
+ void Prepare(const t4_byte** ptr_, bool selfDesc_);
+ void OldPrepare();
+
+ void FlipAllBytes();
+ void ExchangeEntries(int srcPos_, c4_HandlerSeq& dst_, int dstPos_);
+
+ c4_HandlerSeq& SubEntry(int, int) const;
+
+ c4_Field* FindField(const c4_Handler* handler_);
+
+ void UnmappedAll();
+
+ static void BuildMeta(int, int, c4_View&, const c4_Field&);
+
+protected:
+ virtual c4_Handler* CreateHandler(const c4_Property&);
+
+ virtual ~c4_HandlerSeq ();
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if q4_INLINE
+#include "handler.inl"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/akregator/src/mk4storage/metakit/src/handler.inl b/akregator/src/mk4storage/metakit/src/handler.inl
new file mode 100644
index 00000000..75321f9c
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/handler.inl
@@ -0,0 +1,90 @@
+// handler.inl --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Inlined members of the handler classes
+ */
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_Handler
+
+d4_inline c4_Handler::c4_Handler (const c4_Property& prop_)
+ : _property (prop_)
+{
+}
+
+d4_inline c4_Handler::~c4_Handler ()
+{
+}
+
+d4_inline void c4_Handler::Define(int, const t4_byte**)
+{
+}
+
+d4_inline void c4_Handler::FlipBytes()
+{
+}
+
+d4_inline const c4_Property& c4_Handler::Property() const
+{
+ return _property;
+}
+
+d4_inline int c4_Handler::PropId() const
+{
+ return _property.GetId();
+}
+
+d4_inline c4_Column* c4_Handler::GetNthMemoCol(int, bool alloc_)
+{
+ return 0;
+}
+
+d4_inline bool c4_Handler::IsPersistent() const
+{
+ return false;
+}
+
+d4_inline void c4_Handler::Unmapped()
+{
+}
+
+d4_inline bool c4_Handler::HasSubview(int)
+{
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_HandlerSeq
+
+d4_inline int c4_HandlerSeq::NumRows() const
+{
+ d4_assert(_numRows >= 0);
+
+ return _numRows;
+}
+
+d4_inline int c4_HandlerSeq::NumHandlers() const
+{
+ return _handlers.GetSize();
+}
+
+d4_inline c4_Handler& c4_HandlerSeq::NthHandler(int index_) const
+{
+ d4_assert(_handlers.GetAt(index_) != 0);
+
+ return *(c4_Handler*) _handlers.GetAt(index_);
+}
+
+d4_inline const c4_Sequence* c4_HandlerSeq::HandlerContext(int) const
+{
+ return this;
+}
+
+d4_inline c4_HandlerSeq& c4_HandlerSeq::Parent() const
+{
+ return *_parent;
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/header.h b/akregator/src/mk4storage/metakit/src/header.h
new file mode 100644
index 00000000..2f8648b8
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/header.h
@@ -0,0 +1,215 @@
+// header.h --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * The internal header included in all source files
+ */
+
+#ifndef __HEADER_H__
+#define __HEADER_H__
+
+/////////////////////////////////////////////////////////////////////////////
+
+#include "config.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// A number of preprocessor options are used in the source code
+//
+// q4_DOS MS-DOS real-mode OS
+// q4_MAC Apple Macintosh OS
+// q4_UNIX Unix, any flavor
+// q4_VMS DEC OpenVMS OS
+// q4_WIN Microsoft Windows OS, any flavor
+// q4_WIN32 Microsoft Windows OS, 32-bit
+// q4_WINCE Microsoft Windows OS, embedded
+//
+// q4_MFC Microsoft MFC framework
+// q4_STD Standard STL version
+// q4_UNIV Universal version
+//
+// q4_BOOL compiler supports bool datatype
+// q4_CHECK enable assertion checks
+// q4_FIX manual header fix (see above)
+// q4_INLINE enable inline expansion
+// q4_KITDLL compile as DLL (shared library)
+// q4_MULTI compile for multi-threading
+// q4_NOLIB do not add automatic lib linkage (MSVC5)
+// q4_NO_NS don't use namespaces for STL
+// q4_OK assume all software is perfect
+// q4_STRICT do not disable any compiler warnings
+// q4_TINY small version, no floating point
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#define __K4CONF_H__ // skip section in "mk4.h", since we use "header.h"
+
+ // if neither MFC nor STD are specified, default to Universal version
+#if !q4_MFC && !q4_STD && !defined (q4_UNIV)
+#define q4_UNIV 1
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// You can either use '#define q4_xxx 1' to flag the choice of an OS, or
+// use a '#define d4_OS_H "file.h"' to force inclusion of a header later.
+
+#if defined (__MINGW32__)
+#define d4_OS_H "win.h"
+#elif defined (MSDOS) && defined (__GNUC__)
+#define q4_DOS 1
+#elif defined(unix) || defined(__unix__) || defined(__GNUC__) || \
+ defined(_AIX) || defined(__hpux)
+#define q4_UNIX 1
+#elif defined (__VMS)
+#define q4_VMS 1
+#elif defined (macintosh)
+#define q4_MAC 1
+#elif !defined (d4_OS_H)
+#define d4_OS_H "win.h"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Use '#define q4_xxx 1' to flag the choice of a CPU.
+
+#if defined (_M_I86) || defined (_M_IX86) || defined (i386)
+#define q4_I86 1
+#if defined (_M_I86SM)
+#define q4_TINY 1
+#endif
+#elif defined (__powerc)
+#define q4_PPC 1
+#elif defined (__alpha)
+#define q4_AXP 1
+#define q4_LONG64 1
+#elif defined (__VMS)
+#define q4_VAX 1
+#else
+#define q4_M68K 1
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Use '#define q4_xxx 1' to flag the choice of an IDE, and optionally also
+// add '#include "file.h"' to force inclusion of a header file right here.
+
+#if defined (__BORLANDC__) // Borland C++
+#include "borc.h"
+#elif defined (__DECCXX) // DEC C++
+#define q4_DECC 1
+#elif defined (__GNUC__) // GNU C++
+#include "gnuc.h"
+#elif defined (__MWERKS__) // Metrowerks CodeWarrior C++
+#include "mwcw.h"
+#elif defined (_MSC_VER) // Microsoft Visual C++
+#include "msvc.h"
+#elif defined (__SC__) // Symantec C++
+#define q4_SYMC 1
+#elif defined (__WATCOMC__) // Watcom C++
+#define q4_WATC 1
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Some of the options take precedence over others
+
+#if !q4_BOOL && !q4_STD // define a bool datatype
+#define false 0
+#define true 1
+#define bool int
+#endif
+
+#if !q4_CHECK // disable assertions
+#undef d4_assert
+#define d4_dbgdef(x)
+#define d4_assert(x)
+#endif
+
+#if q4_NO_NS // don't use namespaces
+#define d4_std
+#else
+#define d4_std std
+#endif
+
+#if HAVE_MEMMOVE
+#define d4_memmove(d,s,n) memmove(d,s,n)
+#elif HAVE_BCOPY
+#define d4_memmove(d,s,n) bcopy(s,d,n)
+#else
+#define d4_memmove f4_memmove
+extern void f4_memmove(void* d, const void* s, int n);
+#endif
+
+typedef unsigned char t4_byte; // create typedefs for t4_byte, etc.
+
+/////////////////////////////////////////////////////////////////////////////
+// Include header files which contain additional os/cpu/ide/fw specifics
+
+#ifdef d4_OS_H // operating system dependencies
+#include d4_OS_H
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Several defines should always be set
+
+#ifndef d4_assert // assertion macro
+#include <assert.h>
+#define d4_assert assert
+#endif
+
+#ifndef d4_dbgdef // conditionally compiled
+#ifdef NDEBUG
+#define d4_dbgdef(x)
+#else
+#define d4_dbgdef(x) x
+#endif
+#endif
+
+#ifndef d4_new // heap allocator
+#define d4_new new
+#endif
+
+#ifndef d4_reentrant // thread-local storage
+#define d4_reentrant
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Debug logging option, called internally where properties are modified
+
+#if q4_LOGPROPMODS
+void f4_DoLogProp(const c4_Handler*, int, const char*, int);
+#else
+#define f4_LogPropMods(a,b) 0
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Public definitions, plus a few more framework-specific ones
+
+#include "mk4.h"
+
+#if q4_MFC
+#include "mfc.h"
+#elif q4_STD
+#include "std.h"
+#elif q4_UNIV
+#include "univ.h"
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4100 4127 4135 4244 4511 4512 4514)
+#endif
+
+#include <string.h>
+
+/////////////////////////////////////////////////////////////////////////////
+// Report unexpected combinations of settings
+
+#if !q4_FIX
+#if (q4_DOS+q4_MAC+q4_UNIX+q4_VMS+q4_WIN) != 1
+#error Exactly one operating system should have been defined
+#endif
+#if (q4_MFC+q4_STD+q4_UNIV) != 1
+#error Exactly one container library should have been defined
+#endif
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/akregator/src/mk4storage/metakit/src/mfc.h b/akregator/src/mk4storage/metakit/src/mfc.h
new file mode 100644
index 00000000..6e6d3e24
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/mfc.h
@@ -0,0 +1,34 @@
+// mfc.h --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Configuration header for MFC-based builds
+ */
+
+#define q4_MFC 1
+
+#if q4_WIN && !q4_WIN32
+#include <afxwin.h>
+#else
+#include <afxcoll.h>
+#endif
+
+#undef d4_assert
+#define d4_assert ASSERT
+
+#undef d4_assertThis
+#define d4_assertThis d4_assert(AfxIsValidAddress(this, sizeof *this, FALSE))
+
+#undef d4_new
+#define d4_new DEBUG_NEW
+
+typedef class CString c4_String;
+typedef class CPtrArray c4_PtrArray;
+typedef class CDWordArray c4_DWordArray;
+typedef class CStringArray c4_StringArray;
+
+ // MSVC 1.52 thinks a typedef has no constructor, so use a define instead
+#if !q4_OK && q4_MSVC && _MSC_VER == 800
+#define c4_String CString
+#endif
diff --git a/akregator/src/mk4storage/metakit/src/msvc.h b/akregator/src/mk4storage/metakit/src/msvc.h
new file mode 100644
index 00000000..a53e8b6d
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/msvc.h
@@ -0,0 +1,39 @@
+// msvc.h --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Configuration header for Microsoft Visual C++
+ */
+
+#define q4_MSVC 1
+
+ // get rid of several common warning messages
+#if !q4_STRICT
+//#pragma warning(disable: 4244) // conversion ..., possible loss of data
+//#pragma warning(disable: 4135) // conversion between diff. integral types
+//#pragma warning(disable: 4511) // copy constructor could not be generated
+//#pragma warning(disable: 4512) // assignment op could not be generated
+//#pragma warning(disable: 4514) // unreferenced inline removed
+#pragma warning(disable: 4710) // function ... not inlined
+#pragma warning(disable: 4711) // ... selected for automatic inline expansion
+#pragma warning(disable: 4100) // unreferenced formal parameter
+#endif
+
+#if _MSC_VER >= 1100
+#define q4_BOOL 1 // 5.0 supports the bool datatype
+#else
+#define q4_NO_NS 1 // 4.x doesn't use namespaces for STL
+#endif
+
+#if defined (_MT)
+#define q4_MULTI 1 // uses multi-threading
+#endif
+
+#if defined (_DEBUG) && !defined (q4_CHECK) // use assertions in debug build
+#define q4_CHECK 1
+#endif
+
+#if !q4_STD && !q4_UNIV && !defined (q4_MFC)
+#define d4_FW_H "mfc.h" // default for MSVC is to use MFC
+#endif
diff --git a/akregator/src/mk4storage/metakit/src/mwcw.h b/akregator/src/mk4storage/metakit/src/mwcw.h
new file mode 100644
index 00000000..1c863b96
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/mwcw.h
@@ -0,0 +1,31 @@
+// mwcw.h --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Configuration header for Metrowerks CodeWarrior
+ */
+
+#define q4_MWCW 1
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if q4_68K
+#if !__option(IEEEdoubles)
+#error Cannot build Metakit with 10-byte doubles
+#endif
+#endif
+
+#if __option(bool)
+#define q4_BOOL 1
+ // undo previous defaults, because q4_BOOL is not set early enough
+#undef false
+#undef true
+#undef bool
+#endif
+
+#undef _MSC_VER
+
+#pragma export on
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/persist.cpp b/akregator/src/mk4storage/metakit/src/persist.cpp
new file mode 100644
index 00000000..65a9e94e
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/persist.cpp
@@ -0,0 +1,1185 @@
+// persist.cpp --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Implementation of the main file management classes
+ */
+
+#include "header.h"
+#include "column.h"
+#include "persist.h"
+#include "handler.h"
+#include "store.h"
+#include "field.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_FileMark
+{
+ enum {
+ kStorageFormat = 0x4C4A, // b0 = 'J', b1 = <4C> (on Intel)
+ kReverseFormat = 0x4A4C // b0 = <4C>, b1 = 'J'
+ };
+
+ t4_byte _data [8];
+
+public:
+ c4_FileMark ();
+ c4_FileMark (t4_i32 pos_, bool flipped_, bool extend_);
+ c4_FileMark (t4_i32 pos_, int len_);
+
+ t4_i32 Offset() const;
+ t4_i32 OldOffset() const;
+
+ bool IsHeader() const;
+ bool IsOldHeader() const;
+ bool IsFlipped() const;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_FileMark::c4_FileMark ()
+{
+ d4_assert(sizeof *this == 8);
+}
+
+c4_FileMark::c4_FileMark (t4_i32 pos_, bool flipped_, bool extend_)
+{
+ d4_assert(sizeof *this == 8);
+ *(short*) _data = flipped_ ? kReverseFormat : kStorageFormat;
+ _data[2] = extend_ ? 0x0A : 0x1A;
+ _data[3] = 0;
+ t4_byte* p = _data + 4;
+ for (int i = 24; i >= 0; i -= 8)
+ *p++ = (t4_byte) (pos_ >> i);
+ d4_assert(p == _data + sizeof _data);
+}
+
+c4_FileMark::c4_FileMark (t4_i32 pos_, int len_)
+{
+ d4_assert(sizeof *this == 8);
+ t4_byte* p = _data;
+ *p++ = 0x80;
+ for (int j = 16; j >= 0; j -= 8)
+ *p++ = (t4_byte) (len_ >> j);
+ for (int i = 24; i >= 0; i -= 8)
+ *p++ = (t4_byte) (pos_ >> i);
+ d4_assert(p == _data + sizeof _data);
+}
+
+t4_i32 c4_FileMark::Offset() const
+{
+ t4_i32 v = 0;
+ for (int i = 4; i < 8; ++i)
+ v = (v << 8) + _data[i];
+ return v;
+}
+
+t4_i32 c4_FileMark::OldOffset() const
+{
+ t4_i32 v = 0;
+ for (int i = 8; --i >= 4; )
+ v = (v << 8) + _data[i];
+ return v;
+}
+
+bool c4_FileMark::IsHeader() const
+{
+ return (_data[0] == 'J' || _data[0] == 'L') &&
+ (_data[0] ^ _data[1]) == ('J' ^ 'L') && _data[2] == 0x1A;
+}
+
+bool c4_FileMark::IsOldHeader() const
+{
+ return IsHeader() && _data[3] == 0x80;
+}
+
+bool c4_FileMark::IsFlipped() const
+{
+ return *(short*) _data == kReverseFormat;
+
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_Allocator : public c4_DWordArray
+{
+public:
+ c4_Allocator ();
+
+ void Initialize(t4_i32 first_ =1);
+
+ t4_i32 AllocationLimit() const;
+
+ t4_i32 Allocate(t4_i32 len_);
+ void Occupy(t4_i32 pos_, t4_i32 len_);
+ void Release(t4_i32 pos_, t4_i32 len_);
+ void Dump(const char* str_);
+
+private:
+ int Locate(t4_i32 pos_) const;
+ void InsertPair(int i_, t4_i32 from_, t4_i32 to_);
+ t4_i32 ReduceFrags(int goal_, int sHi_, int sLo_);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Allocation of blocks is maintained in a separate data structure.
+// There is no allocation overhead in the allocation arena itself.
+//
+// A single vector of "walls" is maintained, sorted by position:
+//
+// * Each transition between free and allocated is a single entry.
+// The number of entries is <num-free-ranges> + <num-used-ranges>.
+// * By definition, free areas start at the positions indicated
+// by the entries on even indices. Allocated ones use odd entries.
+// * There is an extra <0,0> free slot at the very beginning. This
+// simplifies boundary conditions at the start of the arena.
+// * Position zero cannot be allocated, first slot starts at 1.
+//
+// Properties of this approach:
+//
+// * No allocation overhead for adjacent allocated areas. On the
+// other hand, the allocator does not know the size of used slots.
+// * Alternate function allows marking a specific range as occupied.
+// * Allocator can be initialized as either all free or all in-use.
+// * Allocation info contains only integers, it could be stored.
+// * To extend allocated slots: "occupy" extra bytes at the end.
+// * Generic: can be used for memory, disk files, and array entries.
+
+c4_Allocator::c4_Allocator ()
+{
+ Initialize();
+}
+
+void c4_Allocator::Initialize(t4_i32 first_)
+{
+ SetSize(0, 1000); // empty, and growing in large chunks
+ Add(0); // fake block at start
+ Add(0); // ... only used to avoid merging
+
+ // if occupied, add a tiny free slot at the end, else add entire range
+ const t4_i32 kMaxInt = 0x7fffffff;
+ if (first_ == 0)
+ first_ = kMaxInt;
+
+ Add(first_); // start at a nicely aligned position
+ Add(kMaxInt); // ... there is no limit on file size
+}
+
+t4_i32 c4_Allocator::Allocate(t4_i32 len_)
+{
+ // zero arg is ok, it simply returns first allocatable position
+ for (int i = 2; i < GetSize(); i += 2)
+ if (GetAt(i+1) >= GetAt(i) + len_) {
+ t4_i32 pos = GetAt(i);
+ if ((t4_i32) GetAt(i+1) > pos + len_)
+ ElementAt(i) += len_;
+ else
+ RemoveAt(i, 2);
+ return pos;
+ }
+
+ d4_assert(0);
+ return 0; // not reached
+}
+
+void c4_Allocator::Occupy(t4_i32 pos_, t4_i32 len_)
+{
+ d4_assert(pos_ > 0);
+ // note that zero size simply checks if there is any space to extend
+
+ int i = Locate(pos_);
+ d4_assert(0 < i && i < GetSize());
+
+ if (i % 2) { // allocation is not at start of free block
+ d4_assert((t4_i32) GetAt(i-1) < pos_);
+
+ if ((t4_i32) GetAt(i) == pos_ + len_) // allocate from end of free block
+ SetAt(i, pos_);
+ else // split free block in two
+ InsertPair(i, pos_, pos_ + len_);
+ }
+ else if ((t4_i32) GetAt(i) == pos_)
+/*
+ This side of the if used to be unconditional, but that was
+ incorrect if ReduceFrags gets called (which only happens with
+ severely fragmented files) - there are cases when allocation
+ leads to an occupy request of which the free space list knows
+ nothing about because it dropped small segments. The solution
+ is to silently "allow" such allocations - fixed 29-02-2000
+ Thanks to Andrew Kuchling for his help in chasing this bug.
+*/
+ { // else extend tail of allocated area
+ if ((t4_i32) GetAt(i+1) > pos_ + len_)
+ ElementAt(i) += len_; // move start of next free up
+ else
+ RemoveAt(i, 2); // remove this slot
+ }
+}
+
+void c4_Allocator::Release(t4_i32 pos, t4_i32 len)
+{
+ int i = Locate(pos + len);
+ d4_assert(0 < i && i < GetSize());
+ d4_assert(i % 2 == 0); // don't release inside a free block
+
+ if ((t4_i32) GetAt(i) == pos) // move start of next free down
+ ElementAt(i) -= len;
+ else if ((t4_i32) GetAt(i-1) == pos) // move end of previous free up
+ ElementAt(i-1) += len;
+ else // insert a new entry
+ InsertPair(i, pos, pos + len);
+
+ if (GetAt(i-1) == GetAt(i)) // merge if adjacent free
+ RemoveAt(i-1, 2);
+}
+
+t4_i32 c4_Allocator::AllocationLimit() const
+{
+ d4_assert(GetSize() >= 2);
+
+ return GetAt(GetSize() - 2);
+}
+
+int c4_Allocator::Locate(t4_i32 pos) const
+{
+ int lo = 0, hi = GetSize() - 1;
+
+ while (lo < hi) {
+ int i = (lo + hi) / 2;
+ if (pos < (t4_i32) GetAt(i))
+ hi = i - 1;
+ else if (pos > (t4_i32) GetAt(i))
+ lo = i + 1;
+ else
+ return i;
+ }
+
+ return lo < GetSize() && pos > (t4_i32) GetAt(lo) ? lo + 1 : lo;
+}
+
+void c4_Allocator::InsertPair(int i_, t4_i32 from_, t4_i32 to_)
+{
+ d4_assert(0 < i_);
+ d4_assert(i_ < GetSize());
+
+ d4_assert(from_ < to_);
+ d4_assert((t4_i32) GetAt(i_-1) < from_);
+ //!d4_assert(to_ < GetAt(i_));
+
+ if (to_ >= (t4_i32) GetAt(i_))
+ return; // ignore 2nd allocation of used area
+
+ InsertAt(i_, from_, 2);
+ SetAt(i_+1, to_);
+
+ // it's ok to have arrays up to some 30000 bytes
+ if (GetSize() > 7500)
+ ReduceFrags(5000, 12, 6);
+}
+
+t4_i32 c4_Allocator::ReduceFrags(int goal_, int sHi_, int sLo_)
+{
+ // drastic fail-safe measure: remove small gaps if vec gets too long
+ // this will cause some lost free space but avoids array overflow
+ // the lost space will most probably be re-used after the next commit
+
+ int limit = GetSize() - 2;
+ t4_i32 loss = 0;
+
+ // go through all entries and remove gaps under the given threshold
+ for (int shift = sHi_; shift >= sLo_; --shift) {
+ // the threshold is a fraction of the current size of the arena
+ t4_i32 threshold = AllocationLimit() >> shift;
+ if (threshold == 0)
+ continue;
+
+ int n = 2;
+ for (int i = n; i < limit; i += 2)
+ if ((t4_i32) GetAt(i+1) - (t4_i32) GetAt(i) > threshold) {
+ SetAt(n++, GetAt(i));
+ SetAt(n++, GetAt(i+1));
+ }
+ else
+ loss += GetAt(i+1) - GetAt(i);
+
+ limit = n;
+
+ // if (GetSize() < goal_) - suboptimal, fixed 29-02-2000
+ if (limit < goal_)
+ break; // got rid of enough entries, that's enough
+ }
+
+ int n = GetSize() - 2;
+ SetAt(limit++, GetAt(n++));
+ SetAt(limit++, GetAt(n));
+ SetSize(limit);
+
+ return loss;
+}
+
+#if q4_CHECK
+#include <stdio.h>
+
+void c4_Allocator::Dump(const char* str_)
+{
+ fprintf(stderr, "c4_Allocator::Dump, %d entries <%s>\n", GetSize(), str_);
+ for (int i = 2; i < GetSize(); i += 2)
+ fprintf(stderr, " %10ld .. %ld\n", GetAt(i-1), GetAt(i));
+ fprintf(stderr, "END\n");
+}
+
+#else
+
+void c4_Allocator::Dump(const char* str_) { }
+
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_Differ
+{
+public:
+ c4_Differ (c4_Storage& storage_);
+ ~c4_Differ ();
+
+ int NewDiffID();
+ void CreateDiff(int id_, c4_Column& col_);
+ t4_i32 BaseOfDiff(int id_);
+ void ApplyDiff(int id_, c4_Column& col_) const;
+
+ void GetRoot(c4_Bytes& buffer_);
+
+ c4_Storage _storage;
+ c4_View _diffs;
+ c4_View _temp;
+
+private:
+ void AddEntry(t4_i32, t4_i32, const c4_Bytes&);
+
+ c4_ViewProp pCols; // column info:
+ c4_IntProp pOrig; // original position
+ c4_ViewProp pDiff; // difference chunks:
+ c4_IntProp pKeep; // offset
+ c4_IntProp pResize; // length
+ c4_BytesProp pBytes; // data
+};
+
+c4_Differ::c4_Differ (c4_Storage& storage_)
+ : _storage (storage_), pCols ("_C"), pOrig ("_O"),
+ pDiff ("_D"), pKeep ("_K"), pResize ("_R"), pBytes ("_B")
+{
+ // weird names, to avoid clashing with existing ones (capitalization!)
+ _diffs = _storage.GetAs("_C[_O:I,_D[_K:I,_R:I,_B:B]]");
+}
+
+c4_Differ::~c4_Differ ()
+{
+ _diffs = c4_View ();
+}
+
+void c4_Differ::AddEntry(t4_i32 off_, t4_i32 len_, const c4_Bytes& data_)
+{
+ int n = _temp.GetSize();
+ _temp.SetSize(n + 1);
+ c4_RowRef r = _temp[n];
+
+ pKeep (r) = (t4_i32) off_;
+ pResize (r) = (t4_i32) len_;
+ pBytes (r).SetData(data_);
+}
+
+int c4_Differ::NewDiffID()
+{
+ int n = _diffs.GetSize();
+ _diffs.SetSize(n + 1);
+ return n;
+}
+
+void c4_Differ::CreateDiff(int id_, c4_Column& col_)
+{
+ _temp.SetSize(0);
+#if 0
+ t4_i32 offset = 0;
+ t4_i32 savedOff = 0;
+ t4_i32 savedLen = 0;
+
+ c4_Strategy* strat = col_.Persist() != 0 ? &col_.Strategy() : 0;
+
+ c4_ColIter iter (col_, 0, col_.ColSize());
+ while (iter.Next()) {
+ const t4_byte* p = iter.BufLoad();
+ if (strat != 0 && strat->_mapStart != 0 && p >= strat->_mapStart &&
+ p - strat->_mapStart < strat->_dataSize)
+ {
+ t4_i32 nextOff = p - strat->_mapStart;
+ if (savedLen == 0)
+ savedOff = nextOff;
+ if (nextOff == savedOff + savedLen) {
+ savedLen += iter.BufLen();
+ continue;
+ }
+
+ if (savedLen > 0)
+ AddEntry(savedOff, savedLen, c4_Bytes ());
+
+ savedOff = nextOff;
+ savedLen = iter.BufLen();
+ } else {
+ AddEntry(savedOff, savedLen, c4_Bytes (p, iter.BufLen()));
+ savedLen = 0;
+ }
+
+ offset += iter.BufLen();
+ }
+
+ c4_View diff = pDiff (_diffs[id_]);
+ if (_temp.GetSize() != diff.GetSize() || _temp != diff)
+#else
+ c4_Bytes t1;
+ const t4_byte* p = col_.FetchBytes(0, col_.ColSize(), t1, false);
+ AddEntry(0, 0, c4_Bytes (p, col_.ColSize()));
+#endif
+ pDiff (_diffs[id_]) = _temp;
+
+ pOrig (_diffs[id_]) = col_.Position();
+}
+
+t4_i32 c4_Differ::BaseOfDiff(int id_)
+{
+ d4_assert(0 <= id_ && id_ < _diffs.GetSize());
+
+ return pOrig (_diffs[id_]);
+}
+
+void c4_Differ::ApplyDiff(int id_, c4_Column& col_) const
+{
+ d4_assert(0 <= id_ && id_ < _diffs.GetSize());
+
+ c4_View diff = pDiff (_diffs[id_]);
+ t4_i32 offset = 0;
+
+ for (int n = 0; n < diff.GetSize(); ++n) {
+ c4_RowRef row (diff[n]);
+ offset += pKeep (row);
+
+ c4_Bytes data;
+ pBytes(row).GetData(data);
+
+ // the following code is a lot like c4_MemoRef::Modify
+ const t4_i32 change = pResize (row);
+ if (change < 0)
+ col_.Shrink(offset, -change);
+ else if (change > 0)
+ col_.Grow(offset, change);
+
+ col_.StoreBytes(offset, data);
+ offset += data.Size();
+ }
+
+ if (offset > col_.ColSize())
+ col_.Shrink(offset, offset - col_.ColSize());
+}
+
+void c4_Differ::GetRoot(c4_Bytes& buffer_)
+{
+ int last = _diffs.GetSize() - 1;
+ if (last >= 0) {
+ c4_Bytes temp;
+ c4_View diff = pDiff (_diffs[last]);
+ if (diff.GetSize() > 0)
+ pBytes (diff[0]).GetData(buffer_);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_SaveContext::c4_SaveContext (c4_Strategy& strategy_, bool fullScan_,
+ int mode_, c4_Differ* differ_, c4_Allocator* space_)
+ : _strategy (strategy_), _walk (0), _differ (differ_), _space (space_),
+ _cleanup (0), _nextSpace (0), _preflight (true), _fullScan (fullScan_),
+ _mode (mode_), _nextPosIndex (0), _bufPtr (_buffer), _curr (_buffer),
+ _limit (_buffer)
+{
+ if (_space == 0)
+ _space = _cleanup = d4_new c4_Allocator;
+
+ _nextSpace = _mode == 1 ? d4_new c4_Allocator : _space;
+}
+
+c4_SaveContext::~c4_SaveContext ()
+{
+ delete _cleanup;
+ if (_nextSpace != _space)
+ delete _nextSpace;
+}
+
+bool c4_SaveContext::IsFlipped() const
+{
+ return _strategy._bytesFlipped;
+}
+
+bool c4_SaveContext::Serializing() const
+{
+ return _fullScan;
+}
+
+void c4_SaveContext::AllocDump(const char* str_, bool next_)
+{
+ c4_Allocator* ap = next_ ? _nextSpace : _space;
+ if (ap != 0)
+ ap->Dump(str_);
+}
+
+void c4_SaveContext::FlushBuffer()
+{
+ int n = _curr - _bufPtr;
+ if (_walk != 0 && n > 0) {
+ t4_i32 end = _walk->ColSize();
+ _walk->Grow(end, n);
+ _walk->StoreBytes(end, c4_Bytes (_bufPtr, n));
+ }
+
+ _curr = _bufPtr = _buffer;
+ _limit = _buffer + sizeof _buffer;
+}
+
+c4_Column* c4_SaveContext::SetWalkBuffer(c4_Column* col_)
+{
+ FlushBuffer();
+
+ c4_Column* prev = _walk;
+ _walk = col_;
+ return prev;
+}
+
+void c4_SaveContext::Write(const void* buf_, int len_)
+{
+ // use buffering if possible
+ if (_curr + len_ <= _limit) {
+ memcpy(_curr, buf_, len_);
+ _curr += len_;
+ } else {
+ FlushBuffer();
+ _bufPtr = (t4_byte*) buf_; // also loses const
+ _curr = _limit = _bufPtr + len_;
+ FlushBuffer();
+ }
+}
+
+void c4_SaveContext::StoreValue(t4_i32 v_)
+{
+ if (_walk == 0)
+ return;
+
+ if (_curr + 10 >= _limit)
+ FlushBuffer();
+
+ d4_assert(_curr + 10 < _limit);
+ c4_Column::PushValue(_curr, v_);
+}
+
+void c4_SaveContext::SaveIt(c4_HandlerSeq& root_, c4_Allocator** spacePtr_,
+ c4_Bytes& rootWalk_)
+{
+ d4_assert(_space != 0);
+
+ const t4_i32 size = _strategy.FileSize();
+ if (_strategy._failure != 0)
+ return;
+
+ const t4_i32 end = _fullScan ? 0 : size - _strategy._baseOffset;
+
+ if (_differ == 0) {
+ if (_mode != 1)
+ _space->Initialize();
+
+ // don't allocate anything inside the file in extend mode
+ if (_mode == 2 && end > 0) {
+ _space->Occupy(1, end - 1);
+ _nextSpace->Occupy(1, end - 1);
+ }
+
+ // the header is always reserved
+ _space->Occupy(1, 7);
+ _nextSpace->Occupy(1, 7);
+
+ if (end > 0) {
+ d4_assert(end >= 16);
+ _space->Occupy(end - 16, 16);
+ _nextSpace->Occupy(end - 16, 16);
+ _space->Occupy(end, 8);
+ _nextSpace->Occupy(end, 8);
+ }
+ }
+
+ //AllocDump("a1", false);
+ //AllocDump("a2", true);
+
+ // first pass allocates columns and constructs shallow walks
+ c4_Column walk (root_.Persist());
+ SetWalkBuffer(&walk);
+ CommitSequence(root_, true);
+ SetWalkBuffer(0);
+ CommitColumn(walk);
+
+ c4_Bytes tempWalk;
+ walk.FetchBytes(0, walk.ColSize(), tempWalk, true);
+
+ t4_i32 limit = _nextSpace->AllocationLimit();
+ d4_assert(limit >= 8 || _differ != 0);
+
+ bool changed = _fullScan || tempWalk != rootWalk_;
+
+ rootWalk_ = c4_Bytes (tempWalk.Contents(), tempWalk.Size(), true);
+
+ _preflight = false;
+
+ // special-case to avoid saving data if file is logically empty
+ // in that case, the data is 0x80 0x81 0x80 (plus the header)
+ if (!_fullScan && limit <= 11 && _differ == 0) {
+ _space->Initialize();
+ _nextSpace->Initialize();
+ changed = false;
+ }
+
+ if (!changed)
+ return;
+
+ //AllocDump("b1", false);
+ //AllocDump("b2", true);
+
+ if (_differ != 0) {
+ int n = _differ->NewDiffID();
+ _differ->CreateDiff(n, walk);
+ return;
+ }
+
+ d4_assert(_mode != 0 || _fullScan);
+
+ // this is the place where writing may start
+
+ // figure out where the new file ends and write a skip tail there
+ t4_i32 end0 = end;
+
+ // true if the file need not be extended due to internal free space
+ bool inPlace = end0 == limit - 8;
+ if (inPlace) {
+ d4_assert(!_fullScan);
+ _space->Release(end0, 8);
+ _nextSpace->Release(end0, 8);
+ end0 -= 16; // overwrite existing tail markers
+ } else {
+ c4_FileMark head (limit + 16 - end, _strategy._bytesFlipped, end > 0);
+ _strategy.DataWrite(end, &head, sizeof head);
+
+ if (end0 < limit)
+ end0 = limit; // create a gap
+ }
+
+ t4_i32 end1 = end0 + 8;
+ t4_i32 end2 = end1 + 8;
+
+ if (!_fullScan && !inPlace) {
+ c4_FileMark mark1 (end0, 0);
+ _strategy.DataWrite(end0, &mark1, sizeof mark1);
+#if q4_WIN32
+ /* March 8, 2002
+ * On at least NT4 with NTFS, extending a file can cause it to be
+ * rounded up further than expected. To prevent creating a bad
+ * file (since the file does then not end with a marker), the
+ * workaround it so simply accept the new end instead and rewrite.
+ * Note that between these two writes, the file is in a bad state.
+ */
+ t4_i32 realend = _strategy.FileSize() - _strategy._baseOffset;
+ if (realend > end1) {
+ end0 = limit = realend - 8;
+ end1 = realend;
+ end2 = realend + 8;
+ c4_FileMark mark1a (end0, 0);
+ _strategy.DataWrite(end0, &mark1a, sizeof mark1a);
+ }
+#endif
+ d4_assert(_strategy.FileSize() == _strategy._baseOffset + end1);
+ }
+
+ _space->Occupy(end0, 16);
+ _nextSpace->Occupy(end0, 16);
+
+ // strategy.DataCommit(0); // may be needed, need more info on how FS's work
+ // but this would need more work, since we can't adjust file-mapping here
+
+ // second pass saves the columns and structure to disk
+ CommitSequence(root_, true); // writes changed columns
+ CommitColumn(walk);
+
+ //! d4_assert(_curr == 0);
+ d4_assert(_nextPosIndex == _newPositions.GetSize());
+
+ if (_fullScan) {
+ c4_FileMark mark1 (limit, 0);
+ _strategy.DataWrite(_strategy.FileSize() - _strategy._baseOffset,
+ &mark1, sizeof mark1);
+
+ c4_FileMark mark2 (limit - walk.ColSize(), walk.ColSize());
+ _strategy.DataWrite(_strategy.FileSize() - _strategy._baseOffset,
+ &mark2, sizeof mark2);
+
+ return;
+ }
+
+ if (inPlace)
+ d4_assert(_strategy.FileSize() == _strategy._baseOffset + end2);
+ else {
+ // make sure the allocated size hasn't changed
+ d4_assert(_nextSpace->AllocationLimit() == limit + 16);
+ d4_assert(end0 >= limit);
+ d4_assert(_strategy.FileSize() - _strategy._baseOffset == end1);
+ }
+
+ if (walk.Position() == 0 || _strategy._failure != 0)
+ return;
+
+ _strategy.DataCommit(0);
+
+ c4_FileMark mark2 (walk.Position(), walk.ColSize());
+ _strategy.DataWrite(end1, &mark2, sizeof mark2);
+ d4_assert(_strategy.FileSize() - _strategy._baseOffset == end2);
+
+ // do not alter the file header in extend mode, unless it is new
+ if (!_fullScan && (_mode == 1 || end == 0)) {
+ _strategy.DataCommit(0);
+
+ c4_FileMark head (end2, _strategy._bytesFlipped, false);
+ d4_assert(head.IsHeader());
+ _strategy.DataWrite(0, &head, sizeof head);
+
+ // if the file became smaller, we could shrink it
+ if (limit + 16 < end0) {
+/*
+ Not yet, this depends on the strategy class being able to truncate, but
+ there is no way to find out whether it does (the solution is to write tail
+ markers in such a way that the file won't grow unnecessarily if it doesn't).
+
+ The logic will probably be:
+
+ * write new skip + commit "tails" at limit (no visible effect on file)
+ * overwrite commit tail at end with a skip to this new one (equivalent)
+ * replace header with one pointing to that internal new one (equivalent)
+ * flush (now the file is valid both truncated and not-yet-truncated
+
+ end = limit;
+*/
+ }
+ }
+
+ // if using memory mapped files, make sure the map is no longer in use
+ if (_strategy._mapStart != 0)
+ root_.UnmappedAll();
+
+ // commit and tell strategy object what the new file size is, this
+ // may be smaller now, if old data at the end is no longer referenced
+ _strategy.DataCommit(end2);
+
+ d4_assert(_strategy.FileSize() - _strategy._baseOffset == end2);
+
+ if (spacePtr_ != 0 && _space != _nextSpace) {
+ d4_assert(*spacePtr_ == _space);
+ delete *spacePtr_;
+ *spacePtr_ = _nextSpace;
+ _nextSpace = 0;
+ }
+}
+
+bool c4_SaveContext::CommitColumn(c4_Column& col_)
+{
+ bool changed = col_.IsDirty() || _fullScan;
+
+ t4_i32 sz = col_.ColSize();
+ StoreValue(sz);
+ if (sz > 0) {
+ t4_i32 pos = col_.Position();
+
+ if (_differ) {
+ if (changed) {
+ int n = pos < 0 ? ~pos : _differ->NewDiffID();
+ _differ->CreateDiff(n, col_);
+
+ d4_assert(n >= 0);
+ pos = ~n;
+ }
+ } else if (_preflight) {
+ if (changed)
+ pos = _space->Allocate(sz);
+
+ _nextSpace->Occupy(pos, sz);
+ _newPositions.Add(pos);
+ } else {
+ pos = _newPositions.GetAt(_nextPosIndex++);
+
+ if (changed)
+ col_.SaveNow(_strategy, pos);
+
+ if (!_fullScan)
+ col_.SetLocation(pos, sz);
+ }
+
+ StoreValue(pos);
+ }
+
+ return changed;
+}
+
+void c4_SaveContext::CommitSequence(c4_HandlerSeq& seq_, bool selfDesc_)
+{
+ StoreValue(0); // sias prefix
+
+ if (selfDesc_) {
+ c4_String desc = seq_.Description();
+ int k = desc.GetLength();
+ StoreValue(k);
+ Write((const char*) desc, k);
+ }
+
+ StoreValue(seq_.NumRows());
+ if (seq_.NumRows() > 0)
+ for (int i = 0; i < seq_.NumFields(); ++i)
+ seq_.NthHandler(i).Commit(*this);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+ // used for on-the-fly conversion of old-format datafiles
+ t4_byte* _oldBuf;
+ const t4_byte* _oldCurr;
+ const t4_byte* _oldLimit;
+ t4_i32 _oldSeek;
+
+
+c4_Persist::c4_Persist (c4_Strategy& strategy_, bool owned_, int mode_)
+ : _space (0), _strategy (strategy_), _root (0), _differ (0),
+ _fCommit (0), _mode (mode_), _owned (owned_), _oldBuf (0),
+ _oldCurr (0), _oldLimit (0), _oldSeek (-1)
+{
+ if (_mode == 1)
+ _space = d4_new c4_Allocator;
+}
+
+c4_Persist::~c4_Persist ()
+{
+ delete _differ;
+
+ if (_owned) {
+ if (_root != 0)
+ _root->UnmappedAll();
+ delete &_strategy;
+ }
+
+ delete _space;
+
+ if (_oldBuf != 0)
+ delete [] _oldBuf;
+}
+
+c4_HandlerSeq& c4_Persist::Root() const
+{
+ d4_assert(_root != 0);
+ return *_root;
+}
+
+void c4_Persist::SetRoot(c4_HandlerSeq* root_)
+{
+ d4_assert(_root == 0);
+ _root = root_;
+}
+
+c4_Strategy& c4_Persist::Strategy() const
+{
+ return _strategy;
+}
+
+bool c4_Persist::AutoCommit(bool flag_)
+{
+ bool prev = _fCommit != 0;
+ if (flag_)
+ _fCommit = &c4_Persist::Commit;
+ else
+ _fCommit = 0;
+ return prev;
+}
+
+void c4_Persist::DoAutoCommit()
+{
+ if (_fCommit != 0)
+ (this->*_fCommit)(false);
+}
+
+bool c4_Persist::SetAside(c4_Storage& aside_)
+{
+ delete _differ;
+ _differ = d4_new c4_Differ (aside_);
+ Rollback(false);
+ return true; //! true if the generation matches
+}
+
+c4_Storage* c4_Persist::GetAside() const
+{
+ return _differ != 0 ? &_differ->_storage : 0;
+}
+
+bool c4_Persist::Commit(bool full_)
+{
+ // 1-Mar-1999, new semantics! return success status of commits
+ _strategy._failure = 0;
+
+ if (!_strategy.IsValid())
+ return false;
+
+ if (_mode == 0 && (_differ == 0 || full_)) // can't commit to r/o file
+ return false; // note that _strategy._failure is *zero* in this case
+
+ c4_SaveContext ar (_strategy, false, _mode, full_ ? 0 : _differ, _space);
+
+ // get rid of temp properties which still use the datafile
+ if (_mode == 1)
+ _root->DetachFromStorage(false);
+
+ // 30-3-2001: moved down, fixes "crash every 2nd call of mkdemo/dbg"
+ ar.SaveIt(*_root, &_space, _rootWalk);
+ return _strategy._failure == 0;
+}
+
+bool c4_Persist::Rollback(bool full_)
+{
+ _root->DetachFromParent();
+ _root->DetachFromStorage(true);
+ _root = 0;
+
+ if (_space != 0)
+ _space->Initialize();
+
+ c4_HandlerSeq* seq = d4_new c4_HandlerSeq (this);
+ seq->DefineRoot();
+ SetRoot(seq);
+
+ if (full_) {
+ delete _differ;
+ _differ = 0;
+ }
+
+ LoadAll();
+
+ return _strategy._failure == 0;
+}
+
+bool c4_Persist::LoadIt(c4_Column& walk_)
+{
+ t4_i32 limit = _strategy.FileSize();
+ if (_strategy._failure != 0)
+ return false;
+
+ if (_strategy.EndOfData(limit) < 0) {
+ _strategy.SetBase(limit);
+ d4_assert(_strategy._failure == 0); // file is ok, but empty
+ return false;
+ }
+
+ if (_strategy._rootLen > 0)
+ walk_.SetLocation(_strategy._rootPos, _strategy._rootLen);
+
+ // if the file size has increased, we must remap
+ if (_strategy._mapStart != 0 &&
+ _strategy.FileSize() > _strategy._baseOffset + _strategy._dataSize)
+ _strategy.ResetFileMapping();
+
+ return true;
+}
+
+void c4_Persist::LoadAll()
+{
+ c4_Column walk (this);
+ if (!LoadIt(walk))
+ return;
+
+ if (_strategy._rootLen < 0) {
+ _oldSeek = _strategy._rootPos;
+ _oldBuf = d4_new t4_byte [512];
+ _oldCurr = _oldLimit = _oldBuf;
+
+ t4_i32 n = FetchOldValue();
+ d4_assert(n == 0);
+ n = FetchOldValue();
+ d4_assert(n > 0);
+
+ c4_Bytes temp;
+ t4_byte* buf = temp.SetBuffer(n);
+ d4_dbgdef(int n2 =)
+ OldRead(buf, n);
+ d4_assert(n2 == n);
+
+ c4_String s = "[" + c4_String ((const char*) buf, n) + "]";
+ const char* desc = s;
+
+ c4_Field* f = d4_new c4_Field (desc);
+ d4_assert(!*desc);
+
+ //?_root->DefineRoot();
+ _root->Restructure(*f, false);
+
+ _root->OldPrepare();
+
+ // don't touch data inside while converting the file
+ if (_strategy.FileSize() >= 0)
+ OccupySpace(1, _strategy.FileSize());
+ } else {
+ walk.FetchBytes(0, walk.ColSize(), _rootWalk, true);
+ if (_differ)
+ _differ->GetRoot(_rootWalk);
+
+ // define and fill the root table
+ const t4_byte* ptr = _rootWalk.Contents();
+ _root->Prepare(&ptr, true);
+ d4_assert(ptr == _rootWalk.Contents() + _rootWalk.Size());
+ }
+}
+
+t4_i32 c4_Persist::FetchOldValue()
+{
+ d4_assert(_oldSeek >= 0);
+
+ if (_oldCurr == _oldLimit) {
+ int n = OldRead(_oldBuf, 500);
+ _oldLimit = _oldCurr + n;
+ _oldBuf[n] = 0x80; // to force end
+ }
+
+ const t4_byte* p = _oldCurr;
+ t4_i32 value = c4_Column::PullValue(p);
+
+ if (p > _oldLimit) {
+ int k = _oldLimit - _oldCurr;
+ d4_assert(0 < k && k < 10);
+ memcpy(_oldBuf, _oldCurr, k);
+
+ int n = OldRead(_oldBuf + k, 500);
+ _oldCurr = _oldBuf + k;
+ _oldLimit = _oldCurr + n;
+ _oldBuf[n+k] = 0x80; // to force end
+
+ p = _oldCurr;
+ value = c4_Column::PullValue(p);
+ d4_assert(p <= _oldLimit);
+ }
+
+ _oldCurr = p;
+ return value;
+}
+
+void c4_Persist::FetchOldLocation(c4_Column& col_)
+{
+ d4_assert(_oldSeek >= 0);
+
+ t4_i32 sz = FetchOldValue();
+ if (sz > 0)
+ col_.SetLocation(FetchOldValue(), sz);
+}
+
+int c4_Persist::OldRead(t4_byte* buf_, int len_)
+{
+ d4_assert(_oldSeek >= 0);
+
+ t4_i32 newSeek = _oldSeek + _oldCurr - _oldLimit;
+ int n = _strategy.DataRead(newSeek, buf_, len_);
+ d4_assert(n > 0);
+ _oldSeek = newSeek + n;
+ _oldCurr = _oldLimit = _oldBuf;
+ return n;
+}
+
+c4_HandlerSeq* c4_Persist::Load(c4_Stream* stream_)
+{
+ d4_assert(stream_ != 0);
+
+ c4_FileMark head;
+ if (stream_->Read(&head, sizeof head) != sizeof head || !head.IsHeader())
+ return 0; // no data in file
+
+ //_oldStyle = head._data[3] == 0x80;
+ d4_assert(!head.IsOldHeader());
+
+ t4_i32 limit = head.Offset();
+
+ c4_StreamStrategy* strat = d4_new c4_StreamStrategy (limit);
+ strat->_bytesFlipped = head.IsFlipped();
+ strat->DataWrite(strat->FileSize() - strat->_baseOffset, &head, sizeof head);
+
+ while (strat->FileSize() - strat->_baseOffset < limit) {
+ char buffer [4096];
+ int n = stream_->Read(buffer, sizeof buffer);
+ d4_assert(n > 0);
+ strat->DataWrite(strat->FileSize() - strat->_baseOffset, buffer, n);
+ }
+
+ c4_Persist* pers = d4_new c4_Persist (*strat, true, 0);
+ c4_HandlerSeq* seq = d4_new c4_HandlerSeq (pers);
+ seq->DefineRoot();
+ pers->SetRoot(seq);
+
+ c4_Column walk (pers);
+ if (!pers->LoadIt(walk)) {
+ seq->IncRef();
+ seq->DecRef(); // a funny way to delete
+ return 0;
+ }
+
+ c4_Bytes tempWalk;
+ walk.FetchBytes(0, walk.ColSize(), tempWalk, true);
+
+ const t4_byte* ptr = tempWalk.Contents();
+ seq->Prepare(&ptr, true);
+ d4_assert(ptr == tempWalk.Contents() + tempWalk.Size());
+
+ return seq;
+}
+
+void c4_Persist::Save(c4_Stream* stream_, c4_HandlerSeq& root_)
+{
+ d4_assert(stream_ != 0);
+
+ c4_StreamStrategy strat (stream_);
+
+ // 31-01-2002: streaming must adopt byte order of origin datafile
+ c4_Persist* p = root_.Persist();
+ if (p != 0)
+ strat._bytesFlipped = p->Strategy()._bytesFlipped;
+
+ c4_SaveContext ar (strat, true, 0, 0, 0);
+ c4_Bytes tempWalk;
+ ar.SaveIt(root_, 0, tempWalk);
+}
+
+t4_i32 c4_Persist::LookupAside(int id_)
+{
+ d4_assert(_differ != 0);
+
+ return _differ->BaseOfDiff(id_);
+}
+
+void c4_Persist::ApplyAside(int id_, c4_Column& col_)
+{
+ d4_assert(_differ != 0);
+
+ _differ->ApplyDiff(id_, col_);
+}
+
+void c4_Persist::OccupySpace(t4_i32 pos_, t4_i32 len_)
+{
+ d4_assert(_mode != 1 || _space != 0);
+
+ if (_space != 0)
+ _space->Occupy(pos_, len_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/persist.h b/akregator/src/mk4storage/metakit/src/persist.h
new file mode 100644
index 00000000..61ef261e
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/persist.h
@@ -0,0 +1,127 @@
+// persist.h --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Definition of the core file management classes
+ */
+
+#ifndef __PERSIST_H__
+#define __PERSIST_H__
+
+/////////////////////////////////////////////////////////////////////////////
+// Declarations in this file
+
+ class c4_SaveContext; // wraps file commits
+ class c4_Persist; // persistent table storage
+
+ class c4_Allocator; // not defined here
+ class c4_Column; // not defined here
+ class c4_Differ; // not defined here
+ class c4_FileMark; // not defined here
+ class c4_Strategy; // not defined here
+ class c4_HandlerSeq; // not defined here
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_SaveContext
+{
+ c4_Strategy& _strategy;
+ c4_Column* _walk;
+ c4_Differ* _differ;
+
+ c4_Allocator* _space;
+ c4_Allocator* _cleanup;
+ c4_Allocator* _nextSpace;
+
+ bool _preflight;
+ bool _fullScan;
+ int _mode;
+
+ c4_DWordArray _newPositions;
+ int _nextPosIndex;
+
+ t4_byte* _bufPtr;
+ t4_byte* _curr;
+ t4_byte* _limit;
+ t4_byte _buffer [512];
+
+public:
+ c4_SaveContext (c4_Strategy& strategy_, bool fullScan_, int mode_,
+ c4_Differ* differ_, c4_Allocator* space_);
+ ~c4_SaveContext ();
+
+ void SaveIt(c4_HandlerSeq& root_, c4_Allocator** spacePtr_,
+ c4_Bytes& rootWalk_);
+
+ void StoreValue(t4_i32 v_);
+ bool CommitColumn(c4_Column& col_);
+ void CommitSequence(c4_HandlerSeq& seq_, bool selfDesc_);
+
+ c4_Column* SetWalkBuffer(c4_Column* walk_);
+ bool IsFlipped() const;
+
+ bool Serializing() const;
+ void AllocDump(const char*, bool =false);
+
+private:
+ void FlushBuffer();
+ void Write(const void* buf_, int len_);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_Persist
+{
+ c4_Allocator* _space;
+ c4_Strategy& _strategy;
+ c4_HandlerSeq* _root;
+ c4_Differ* _differ;
+ c4_Bytes _rootWalk;
+ bool (c4_Persist::*_fCommit) (bool);
+ int _mode;
+ bool _owned;
+
+ // used for on-the-fly conversion of old-format datafiles
+ t4_byte* _oldBuf;
+ const t4_byte* _oldCurr;
+ const t4_byte* _oldLimit;
+ t4_i32 _oldSeek;
+
+ int OldRead(t4_byte* buf_, int len_);
+
+public:
+ c4_Persist (c4_Strategy&, bool owned_, int mode_);
+ ~c4_Persist ();
+
+ c4_HandlerSeq& Root() const;
+ void SetRoot(c4_HandlerSeq* root_);
+ c4_Strategy& Strategy() const;
+
+ bool AutoCommit(bool =true);
+ void DoAutoCommit();
+
+ bool SetAside(c4_Storage& aside_);
+ c4_Storage* GetAside() const;
+
+ bool Commit(bool full_);
+ bool Rollback(bool full_);
+
+ bool LoadIt(c4_Column& walk_);
+ void LoadAll();
+
+ t4_i32 LookupAside(int id_);
+ void ApplyAside(int id_, c4_Column& col_);
+
+ void OccupySpace(t4_i32 pos_, t4_i32 len_);
+
+ t4_i32 FetchOldValue();
+ void FetchOldLocation(c4_Column& col_);
+
+ static c4_HandlerSeq* Load(c4_Stream*);
+ static void Save(c4_Stream*, c4_HandlerSeq& root_);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif
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_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/remap.h b/akregator/src/mk4storage/metakit/src/remap.h
new file mode 100644
index 00000000..a435f853
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/remap.h
@@ -0,0 +1,26 @@
+// remap.h --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Encapsulation of the (re)mapping viewers
+ */
+
+#ifndef __REMAP_H__
+#define __REMAP_H__
+
+/////////////////////////////////////////////////////////////////////////////
+// Declarations in this file
+
+ class c4_Sequence; // not defined here
+
+ extern c4_CustomViewer* f4_CreateReadOnly(c4_Sequence&);
+ extern c4_CustomViewer* f4_CreateHash(c4_Sequence&, int, c4_Sequence* =0);
+ extern c4_CustomViewer* f4_CreateBlocked(c4_Sequence&);
+ extern c4_CustomViewer* f4_CreateOrdered(c4_Sequence&, int);
+ extern c4_CustomViewer* f4_CreateIndexed(c4_Sequence&, c4_Sequence&,
+ const c4_View&, bool =false);
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/akregator/src/mk4storage/metakit/src/std.cpp b/akregator/src/mk4storage/metakit/src/std.cpp
new file mode 100644
index 00000000..135fe776
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/std.cpp
@@ -0,0 +1,84 @@
+// std.cpp --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Implementation of STL-based strings and containers
+ */
+
+#include "header.h"
+
+#if q4_STD // until end of source
+/////////////////////////////////////////////////////////////////////////////
+
+#include "column.h" // c4_ColCache
+
+#if !q4_INLINE
+static char _mk4stdInl[] = "mk4str.inl";
+#include "mk4str.inl"
+#endif
+
+#if !q4_NO_NS
+using namespace std;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Implemented in this file
+
+ class c4_String;
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if !q4_MSVC && !q4_WATC
+
+ // MS C/C++ has this handy stricmp: a case-insensitive version of strcmp
+ // This version only works with 7-bit ASCII characters 0x00 through 0x7F
+
+ static int stricmp(const char* p1, const char* p2)
+ {
+ int c1, c2;
+
+#ifdef d4_USE_UNOPTIMIZED_CODE
+ do
+ {
+ c1 = tolower(*p1++);
+ c2 = tolower(*p2++);
+ } while (c1 != 0 && c1 == c2);
+#else
+ do
+ {
+ c1 = *p1++;
+ c2 = *p2++;
+ } while (c1 != 0 && (c1 == c2 || tolower(c1) == tolower(c2)));
+
+ c1 = tolower(c1);
+ c2 = tolower(c2);
+#endif
+
+ return c1 - c2;
+ }
+
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_String
+
+c4_String c4_String::Mid(int nFirst_, int nCount_) const
+{
+ int n = length();
+ if (nFirst_ > n)
+ nFirst_ = n;
+ if (nFirst_ + nCount_ > n)
+ nCount_ = n - nFirst_;
+
+ return substr(nFirst_, nCount_);
+}
+
+int c4_String::CompareNoCase(const char* str_) const
+{
+ // this is not very "standard library-ish" ...
+ return *(const string*) this == str_ ? 0 : stricmp(c_str(), str_);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // q4_STD
diff --git a/akregator/src/mk4storage/metakit/src/std.h b/akregator/src/mk4storage/metakit/src/std.h
new file mode 100644
index 00000000..1d1937be
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/std.h
@@ -0,0 +1,63 @@
+// std.h --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Configuration header for STL-based builds
+ */
+
+#define q4_STD 1
+
+#include "mk4str.h"
+
+#include <vector>
+
+/////////////////////////////////////////////////////////////////////////////
+
+template<class T>
+class c4_ArrayT
+{
+#ifdef _MSC_VER
+ d4_std::vector< T, d4_std::allocator<T> > _vector;
+#else
+ d4_std::vector< T, d4_std::alloc > _vector;
+#endif
+
+public:
+ c4_ArrayT () { }
+ ~c4_ArrayT () { }
+
+ int GetSize() const { return _vector.size(); }
+ void SetSize(int nNewSize, int =-1) { _vector.resize(nNewSize); }
+
+ T GetAt(int nIndex) const { return _vector[nIndex]; }
+ T& ElementAt(int nIndex) { return _vector[nIndex]; }
+
+ void SetAt(int nIndex, const T& newElement)
+ {
+ _vector[nIndex] = newElement;
+ }
+
+ int Add(const T& newElement)
+ {
+ int n = _vector.size();
+ _vector.push_back(newElement);
+ return n;
+ }
+
+ void InsertAt(int nIndex, const T& newElement, int nCount =1)
+ {
+ _vector.insert(&_vector[nIndex], nCount, newElement);
+ }
+
+ void RemoveAt(int nIndex, int nCount =1)
+ {
+ _vector.erase(&_vector[nIndex], &_vector[nIndex+nCount]);
+ }
+};
+
+typedef c4_ArrayT<t4_i32> c4_DWordArray;
+typedef c4_ArrayT<void*> c4_PtrArray;
+typedef c4_ArrayT<c4_String> c4_StringArray;
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/store.cpp b/akregator/src/mk4storage/metakit/src/store.cpp
new file mode 100644
index 00000000..a32de665
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/store.cpp
@@ -0,0 +1,587 @@
+// store.cpp --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Storage management and several other loose ends
+ */
+
+#include "header.h"
+#include "handler.h" // 19990906
+#include "store.h"
+#include "field.h"
+#include "persist.h"
+#include "format.h" // 19990906
+
+#include "mk4io.h" // 19991104
+
+#if !q4_INLINE
+#include "store.inl"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_Dependencies::c4_Dependencies ()
+{
+ _refs.SetSize(0, 3); // a little optimization
+}
+
+c4_Dependencies::~c4_Dependencies ()
+{
+}
+
+void c4_Dependencies::Add(c4_Sequence* seq_)
+{
+ for (int i = 0; i < _refs.GetSize(); ++i)
+ d4_assert(_refs.GetAt(i) != seq_);
+
+ _refs.Add(seq_);
+}
+
+bool c4_Dependencies::Remove(c4_Sequence* seq_)
+{
+ int n = _refs.GetSize() - 1;
+ d4_assert(n >= 0);
+
+ for (int i = 0; i <= n; ++i)
+ if (_refs.GetAt(i) == seq_) {
+ _refs.SetAt(i, _refs.GetAt(n));
+ _refs.SetSize(n);
+ return n > 0;
+ }
+
+ d4_assert(0); // dependency not found
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_Notifier::~c4_Notifier ()
+{
+ if (_type > kNone && _origin->GetDependencies()) {
+ c4_PtrArray& refs = _origin->GetDependencies()->_refs;
+
+ for (int i = 0; i < refs.GetSize(); ++i) {
+ c4_Sequence* seq = (c4_Sequence*) refs.GetAt(i);
+ d4_assert(seq != 0);
+
+ seq->PostChange(*this);
+
+ if (_chain && _chain->_origin == seq) {
+ c4_Notifier* next = _chain->_next;
+ _chain->_next = 0;
+
+ delete _chain;
+
+ _chain = next;
+ }
+ }
+ }
+
+ d4_assert(!_chain);
+ d4_assert(!_next);
+}
+
+void c4_Notifier::StartSetAt(int index_, c4_Cursor& cursor_)
+{
+ _type = kSetAt;
+ _index = index_;
+ _cursor = &cursor_;
+
+ Notify();
+}
+
+void c4_Notifier::StartInsertAt(int i_, c4_Cursor& cursor_, int n_)
+{
+ _type = kInsertAt;
+ _index = i_;
+ _cursor = &cursor_;
+ _count = n_;
+
+ Notify();
+}
+
+void c4_Notifier::StartRemoveAt(int index_, int count_)
+{
+ _type = kRemoveAt;
+ _index = index_;
+ _count = count_;
+
+ Notify();
+}
+
+void c4_Notifier::StartMove(int from_, int to_)
+{
+ _type = kMove;
+ _index = from_;
+ _count = to_;
+
+ Notify();
+}
+
+void c4_Notifier::StartSet(int i_, int propId_, const c4_Bytes& buf_)
+{
+ _type = kSet;
+ _index = i_;
+ _propId = propId_;
+ _bytes = &buf_;
+
+ Notify();
+}
+
+void c4_Notifier::Notify()
+{
+ d4_assert(_origin->GetDependencies() != 0);
+ c4_PtrArray& refs = _origin->GetDependencies()->_refs;
+
+ int n = refs.GetSize();
+ d4_assert(n > 0);
+
+ c4_Notifier** rover = &_chain;
+
+ for (int i = 0; i < n; ++i) {
+ c4_Sequence* seq = (c4_Sequence*) refs.GetAt(i);
+ d4_assert(seq != 0);
+
+ c4_Notifier* ptr = seq->PreChange(*this);
+ if (ptr) {
+ d4_assert(ptr->_origin == seq);
+
+ d4_assert(!*rover);
+ *rover = ptr;
+ rover = &ptr->_next;
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+/** @class c4_Storage
+ *
+ * Manager for persistent storage of view structures.
+ *
+ * The storage class uses a view, with additional functionality to be able
+ * to store and reload the data it contains (including nested subviews).
+ *
+ * By default, data is loaded on demand, i.e. whenever data which has
+ * not yet been referenced is used for the first time. Loading is limited
+ * to the lifetime of this storage object, since the storage object carries
+ * the file descriptor with it that is needed to access the data file.
+ *
+ * To save changes, call the Commit member. This is the only time
+ * that data is written to file - when using a read-only file simply avoid
+ * calling Commit.
+ *
+ * The LoadFromStream and SaveToStream members can be used to
+ * serialize the contents of this storage row using only sequential I/O
+ * (no seeking, only read or write calls).
+ *
+ * The data storage mechanism implementation provides fail-safe operation:
+ * if anything prevents Commit from completing its task, the last
+ * succesfully committed version of the saved data will be recovered on
+ * the next open. This also includes changes made to the table structure.
+ *
+ * The following code creates a view with 1 row and stores it on file:
+ * @code
+ * c4_StringProp pName ("Name");
+ * c4_IntProp pAge ("Age");
+ *
+ * c4_Storage storage ("myfile.dat", true);
+ * c4_View myView = storage.GetAs("Musicians[Name:S,Age:I]");
+ *
+ * myView.Add(pName ["John Williams"] + pAge [43]);
+ *
+ * storage.Commit();
+ * @endcode
+ */
+
+c4_Storage::c4_Storage ()
+{
+ // changed to r/o, now that commits don't crash on it anymore
+ Initialize(*d4_new c4_Strategy, true, 0);
+}
+
+c4_Storage::c4_Storage (c4_Strategy& strategy_, bool owned_, int mode_)
+{
+ Initialize(strategy_, owned_, mode_);
+ Persist()->LoadAll();
+}
+
+c4_Storage::c4_Storage (const char* fname_, int mode_)
+{
+ c4_FileStrategy* strat = d4_new c4_FileStrategy;
+ strat->DataOpen(fname_, mode_);
+
+ Initialize(*strat, true, mode_);
+ if (strat->IsValid())
+ Persist()->LoadAll();
+}
+
+c4_Storage::c4_Storage (const c4_View& root_)
+{
+ if (root_.Persist() != 0) // only restore if view was indeed persistent
+ *(c4_View*) this = root_;
+ else // if this was not possible, start with a fresh empty storage
+ Initialize(*d4_new c4_Strategy, true, 0);
+}
+
+c4_Storage::~c4_Storage ()
+{
+ // cannot unmap here, because there may still be an autocommit pending
+ //((c4_HandlerSeq*) _seq)->UnmapAll();
+}
+
+void c4_Storage::Initialize(c4_Strategy& strategy_, bool owned_, int mode_)
+{
+ c4_Persist* pers = d4_new c4_Persist (strategy_, owned_, mode_);
+ c4_HandlerSeq* seq = d4_new c4_HandlerSeq (pers);
+ seq->DefineRoot();
+ *(c4_View*) this = seq;
+ pers->SetRoot(seq);
+}
+
+ /// Get or set a named view in this storage object
+c4_ViewRef c4_Storage::View(const char* name_)
+{
+ /*
+ The easy solution would seem to be:
+
+ c4_ViewProp prop (name_);
+ return prop (Contents());
+
+ But this does not work, because the return value would point to
+ an object allocated on the stack.
+
+ Instead, make sure the view *has* such a property, and use the
+ one inside the c4_Handler for it (since this will stay around).
+ */
+
+// int n = _root->PropIndex(c4_ViewProp (name_));
+
+ c4_ViewProp prop (name_);
+ int n = AddProperty(prop);
+ d4_assert(n >= 0);
+
+ // the following is an expression of the form "property (rowref)"
+ return NthProperty(n) (GetAt(0));
+}
+
+ /// Get a named view, redefining it to match the given structure
+c4_View c4_Storage::GetAs(const char* description_)
+{
+ d4_assert(description_ != 0);
+
+ // Dec 2001: now that GetAs is being used so much more frequently,
+ // add a quick check to see whether restructuring is needed at all
+ const char* q = strchr(description_, '[');
+ if (q != 0) {
+ c4_String vname (description_, q - description_);
+ const char* d = Description(vname);
+ if (d != 0) {
+ c4_String desc (d);
+ if (("[" + desc + "]").CompareNoCase(q) == 0)
+ return View(vname);
+ }
+ }
+
+ c4_Field* field = d4_new c4_Field (description_);
+ d4_assert(field != 0);
+
+ d4_assert(!*description_);
+
+ c4_String name = field->Name();
+ d4_assert(!name.IsEmpty());
+
+ c4_Field& curr = Persist()->Root().Definition();
+
+ c4_String newField = "," + field->Description();
+ bool keep = newField.Find('[') >= 0;
+
+ c4_String newDef;
+
+ // go through all subfields
+ for (int i = 0; i < curr.NumSubFields(); ++i) {
+ c4_Field& of = curr.SubField(i);
+ if (of.Name().CompareNoCase(name) == 0) {
+ if (field->IsRepeating())
+ newDef += newField;
+ // else new is not a repeating entry, so drop this entire field
+
+ newField.Empty(); // don't append it later on
+ continue;
+ }
+
+ newDef += "," + of.Description(); // keep original field
+ }
+
+ if (keep) // added 19990824 ignore if deletion
+ newDef += newField; // appends new definition if not found earlier
+
+ delete field;
+
+ const char* p = newDef;
+ SetStructure(*p ? ++p : p); // skip the leading comma
+
+ if (!keep) // 19990916: avoid adding an empty view again
+ return c4_View ();
+
+ return View(name);
+}
+
+ /// Define the complete view structure of the storage
+void c4_Storage::SetStructure(const char* description_)
+{
+ d4_assert(description_ != 0);
+
+ if (description_ != Description()) {
+ c4_String s = "[" + c4_String (description_) + "]";
+ description_ = s;
+
+ c4_Field* field = d4_new c4_Field (description_);
+ d4_assert(!*description_);
+
+ d4_assert(field != 0);
+ Persist()->Root().Restructure(*field, false);
+ }
+}
+
+ /// Return the strategy object associated with this storage
+c4_Strategy& c4_Storage::Strategy() const
+{
+ return Persist()->Strategy();
+}
+
+ /// Return a description of the view structure (default is all)
+const char* c4_Storage::Description(const char* name_)
+{
+ if (name_ == 0 || *name_ == 0)
+ return c4_View::Description();
+
+ c4_View v = View(name_);
+ return v.Description();
+}
+
+ /// Define the storage to use for differential commits
+bool c4_Storage::SetAside(c4_Storage& aside_)
+{
+ c4_Persist* pers = Persist();
+ bool f = pers->SetAside(aside_);
+ // adjust our copy when the root view has been replaced
+ *(c4_View*) this = &pers->Root();
+ return f;
+}
+
+ /// Return storage used for differential commits, or null
+c4_Storage* c4_Storage::GetAside() const
+{
+ return Persist()->GetAside();
+}
+
+ /// Flush pending changes to file right now
+bool c4_Storage::Commit(bool full_)
+{
+ return Strategy().IsValid() && Persist()->Commit(full_);
+}
+
+/** (Re)initialize for on-demand loading
+ *
+ * Calling Rollback will cancel all uncommitted changes.
+ */
+bool c4_Storage::Rollback(bool full_)
+{
+ c4_Persist* pers = Persist();
+ bool f = Strategy().IsValid() && pers->Rollback(full_);
+ // adjust our copy when the root view has been replaced
+ *(c4_View*) this = &pers->Root();
+ return f;
+}
+
+ /// Set storage up to always call Commit in the destructor
+bool c4_Storage::AutoCommit(bool flag_)
+{
+ return Persist()->AutoCommit(flag_);
+}
+
+ /// Load contents from the specified input stream
+bool c4_Storage::LoadFrom(c4_Stream& stream_)
+{
+ c4_HandlerSeq* newRoot = c4_Persist::Load(&stream_);
+ if (newRoot == 0)
+ return false;
+
+ // fix commit-after-load bug, by using a full view copy
+ // this is inefficient, but avoids mapping/strategy problems
+ c4_View temp (newRoot);
+
+ SetSize(0);
+ SetStructure(temp.Description());
+ InsertAt(0, temp);
+
+ return true;
+}
+
+ /// Save contents to the specified output stream
+void c4_Storage::SaveTo(c4_Stream& stream_)
+{
+ c4_Persist::Save(&stream_, Persist()->Root());
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_DerivedSeq::c4_DerivedSeq (c4_Sequence& seq_)
+ : _seq (seq_)
+{
+ _seq.Attach(this);
+}
+
+c4_DerivedSeq::~c4_DerivedSeq ()
+{
+ _seq.Detach(this);
+}
+
+int c4_DerivedSeq::RemapIndex(int index_, const c4_Sequence* seq_) const
+{
+ return seq_ == this ? index_ : _seq.RemapIndex(index_, seq_);
+}
+
+int c4_DerivedSeq::NumRows() const
+{
+ return _seq.NumRows();
+}
+
+int c4_DerivedSeq::NumHandlers() const
+{
+ return _seq.NumHandlers();
+}
+
+c4_Handler& c4_DerivedSeq::NthHandler(int colNum_) const
+{
+ return _seq.NthHandler(colNum_);
+}
+
+const c4_Sequence* c4_DerivedSeq::HandlerContext(int colNum_) const
+{
+ return _seq.HandlerContext(colNum_);
+}
+
+int c4_DerivedSeq::AddHandler(c4_Handler* handler_)
+{
+ return _seq.AddHandler(handler_);
+}
+
+c4_Handler* c4_DerivedSeq::CreateHandler(const c4_Property& prop_)
+{
+ return _seq.CreateHandler(prop_);
+}
+
+void c4_DerivedSeq::SetNumRows(int size_)
+{
+ _seq.SetNumRows(size_);
+}
+
+c4_Notifier* c4_DerivedSeq::PreChange(c4_Notifier& nf_)
+{
+ if (!GetDependencies())
+ return 0;
+
+ c4_Notifier* chg = d4_new c4_Notifier (this);
+
+ switch (nf_._type)
+ {
+ case c4_Notifier::kSetAt:
+ chg->StartSetAt(nf_._index, *nf_._cursor);
+ break;
+
+ case c4_Notifier::kSet:
+ chg->StartSet(nf_._index, nf_._propId, *nf_._bytes);
+ break;
+
+ case c4_Notifier::kInsertAt:
+ chg->StartInsertAt(nf_._index, *nf_._cursor, nf_._count);
+ break;
+
+ case c4_Notifier::kRemoveAt:
+ chg->StartRemoveAt(nf_._index, nf_._count);
+ break;
+
+ case c4_Notifier::kMove:
+ chg->StartMove(nf_._index, nf_._count);
+ break;
+ }
+
+ return chg;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_StreamStrategy::c4_StreamStrategy (t4_i32 buflen_)
+ : _stream (0), _buffer (d4_new t4_byte [buflen_]), _buflen (buflen_), _position (0)
+{
+ _mapStart = _buffer;
+ _dataSize = buflen_;
+}
+
+c4_StreamStrategy::c4_StreamStrategy (c4_Stream* stream_)
+ : _stream (stream_), _buffer (0), _buflen (0), _position (0)
+{
+}
+
+c4_StreamStrategy::~c4_StreamStrategy ()
+{
+ _mapStart = 0;
+ _dataSize = 0;
+
+ if (_buffer != 0)
+ delete [] _buffer;
+}
+
+bool c4_StreamStrategy::IsValid() const
+{
+ return true;
+}
+
+int c4_StreamStrategy::DataRead(t4_i32 pos_, void* buffer_, int length_)
+{
+ if (_buffer != 0) {
+ d4_assert(pos_ <= _buflen);
+ _position = pos_ + _baseOffset;
+
+ if (length_ > _buflen - _position)
+ length_ = _buflen - _position;
+ if (length_ > 0)
+ memcpy(buffer_, _buffer + _position, length_);
+ } else {
+ d4_assert(_position == pos_ + _baseOffset);
+ length_ = _stream != 0 ? _stream->Read(buffer_, length_) : 0;
+ }
+
+ _position += length_;
+ return length_;
+}
+
+void c4_StreamStrategy::DataWrite(t4_i32 pos_, const void* buffer_, int length_)
+{
+ if (_buffer != 0) {
+ d4_assert(pos_ <= _buflen);
+ _position = pos_ + _baseOffset;
+
+ int n = length_;
+ if (n > _buflen - _position)
+ n = _buflen - _position;
+ if (n > 0)
+ memcpy(_buffer + _position, buffer_, n);
+ } else {
+ d4_assert(_position == pos_ + _baseOffset);
+ if (_stream != 0 && !_stream->Write(buffer_, length_))
+ ++_failure;
+ }
+
+ _position += length_;
+}
+
+t4_i32 c4_StreamStrategy::FileSize()
+{
+ return _position;
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/store.h b/akregator/src/mk4storage/metakit/src/store.h
new file mode 100644
index 00000000..c45d258e
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/store.h
@@ -0,0 +1,114 @@
+// store.h --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Definition of auxiliary storage management classes
+ */
+
+#ifndef __STORE_H__
+#define __STORE_H__
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_Dependencies
+{
+ c4_PtrArray _refs;
+
+public:
+ c4_Dependencies ();
+ ~c4_Dependencies ();
+
+ void Add(c4_Sequence* seq_);
+ bool Remove(c4_Sequence* seq_);
+
+ friend class c4_Notifier;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_Notifier
+{
+ c4_Sequence* _origin;
+ c4_Notifier* _chain;
+ c4_Notifier* _next;
+
+public:
+ enum { kNone, kSetAt, kInsertAt, kRemoveAt, kMove, kSet, kLimit };
+
+ c4_Notifier (c4_Sequence* origin_);
+ ~c4_Notifier ();
+
+ bool HasDependents() const;
+
+ void StartSetAt(int index_, c4_Cursor& cursor_);
+ void StartInsertAt(int index_, c4_Cursor& cursor_, int count_);
+ void StartRemoveAt(int index_, int count_);
+ void StartMove(int from_, int to_);
+ void StartSet(int index_, int propId_, const c4_Bytes& buf_);
+
+ int _type;
+ int _index;
+ int _propId;
+ int _count;
+ c4_Cursor* _cursor;
+ const c4_Bytes* _bytes;
+
+private:
+ void Notify();
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_DerivedSeq : public c4_Sequence
+{
+protected:
+ c4_Sequence& _seq;
+
+protected:
+ c4_DerivedSeq (c4_Sequence& seq_);
+ virtual ~c4_DerivedSeq ();
+
+public:
+ virtual int RemapIndex(int, const c4_Sequence*) const;
+
+ virtual int NumRows() const;
+ virtual void SetNumRows(int size_);
+
+ virtual int NumHandlers() const;
+ virtual c4_Handler& NthHandler(int) const;
+ virtual const c4_Sequence* HandlerContext(int) const;
+ virtual int AddHandler(c4_Handler*);
+ virtual c4_Handler* CreateHandler(const c4_Property&);
+
+ virtual c4_Notifier* PreChange(c4_Notifier& nf_);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_StreamStrategy : public c4_Strategy
+{
+ c4_Stream* _stream;
+ t4_byte* _buffer;
+ t4_i32 _buflen;
+ t4_i32 _position;
+public:
+ c4_StreamStrategy (t4_i32 buflen_);
+ c4_StreamStrategy (c4_Stream* stream_);
+ virtual ~c4_StreamStrategy ();
+
+ virtual bool IsValid() const;
+ virtual int DataRead(t4_i32 pos_, void* buffer_, int length_);
+ virtual void DataWrite(t4_i32 pos_, const void* buffer_, int length_);
+ virtual t4_i32 FileSize();
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if q4_INLINE
+#include "store.inl"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/akregator/src/mk4storage/metakit/src/store.inl b/akregator/src/mk4storage/metakit/src/store.inl
new file mode 100644
index 00000000..191ee7de
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/store.inl
@@ -0,0 +1,20 @@
+// store.inl --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Inlined members of the storage management classes
+ */
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_Notifier
+
+d4_inline c4_Notifier::c4_Notifier (c4_Sequence* origin_)
+ : _origin (origin_), _chain (0), _next (0),
+ _type (kNone), _index (0), _propId (0), _count (0),
+ _cursor (0), _bytes (0)
+{
+ d4_assert(_origin != 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/string.cpp b/akregator/src/mk4storage/metakit/src/string.cpp
new file mode 100644
index 00000000..33c8836c
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/string.cpp
@@ -0,0 +1,304 @@
+// string.cpp --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * yet another string implementation
+ */
+
+#include "header.h"
+
+/* these definitions could be used instead of header.h ...
+ #define q4_UNIV 1
+ #define d4_inline
+ #include "mk4str.h"
+ #define d4_reentrant
+ #define d4_assert(x)
+*/
+
+#if q4_UNIV // until end of source
+/////////////////////////////////////////////////////////////////////////////
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _AIX
+#include <strings.h>
+#endif
+
+#if !q4_INLINE
+#include "mk4str.inl"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if q4_MSVC || q4_WATC || q4_BORC || (q4_MWCW && __MWERKS__ < 0x3000)
+#define strcasecmp stricmp
+#elif q4_WINCE
+
+ // MS C/C++ has this handy stricmp: a case-insensitive version of strcmp
+ // This version only works with 7-bit ASCII characters 0x00 through 0x7F
+
+ static int stricmp(const char* p1, const char* p2)
+ {
+ int c1, c2;
+
+#ifdef d4_USE_UNOPTIMIZED_CODE
+ do
+ {
+ c1 = tolower(*p1++);
+ c2 = tolower(*p2++);
+ } while (c1 != 0 && c1 == c2);
+#else
+ do
+ {
+ c1 = *p1++;
+ c2 = *p2++;
+ } while (c1 != 0 && (c1 == c2 || tolower(c1) == tolower(c2)));
+
+ c1 = tolower(c1);
+ c2 = tolower(c2);
+#endif
+
+ return c1 - c2;
+ }
+
+#endif
+
+#if q4_WINCE
+ const char* strrchr(const char* p, char ch)
+ {
+ const char* q = 0;
+ while (*p)
+ if (*p++ == ch)
+ q = p;
+ return q;
+ }
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// This string class implement functionality which is very similar to that
+// provided by the CString class of the Microsoft Framework Classes (MFC).
+//
+// There are also several major differences:
+//
+// 1) This class uses reference counting to avoid massive copying.
+// Consequently, function return as well as assignment is very fast.
+// 2) Strings of up to 255 bytes can contain any data, even null bytes.
+// Longer strings can not contain any null bytes past position 255.
+// 3) This class can produce a "const char*" without overhead, but it
+// can also cast to the byte-counted "const unsigned char*" used
+// everywhere in Macintosh applications (as StringPtr, Str255, etc).
+// 4) This source code is not derived from Microsoft's code in any way.
+//
+// A good way to use this class, is to always use c4_String for function
+// return values and "const [unsigned] char*" for all parameters. Together,
+// these two choices will remove the need for nearly any messy casts.
+//
+// Note: MFC 4.0 has now adopted refcounts, and is a good alternative to
+// this code (but a bit bulkier, it also has Unicode support).
+
+ // 2001-11-27, stop releasing nullvec, to allow MT use
+ d4_reentrant static unsigned char* nullVec = 0;
+
+static int fInc(unsigned char* p)
+{
+ ++*p;
+ if (*p)
+ return 1;
+
+ --*p;
+ return 0;
+}
+
+inline static void fDec(unsigned char* p)
+{
+ --*p;
+ if (!*p && p != nullVec)
+ delete [] p;
+}
+
+c4_String::c4_String (char ch, int n /* =1 */)
+{
+ if (n < 0)
+ n = 0;
+
+ _value = new unsigned char [n + 3];
+
+ _value[0] = 1; // see Init() member
+ memset(_value + 2, ch, n);
+ _value[1] = (unsigned char) (n <= 255 ? n : 255);
+ _value[n+2] = 0;
+}
+
+c4_String::c4_String (const char* p)
+{
+ Init(p, p != 0 ? strlen(p) : 0);
+}
+
+c4_String::c4_String (const c4_String& s)
+{
+ if (fInc(s._value))
+ _value = s._value;
+ else
+ Init(s.Data(), s.GetLength());
+}
+
+c4_String::~c4_String ()
+{
+ fDec(_value);
+}
+
+const c4_String& c4_String::operator= (const c4_String& s)
+{
+ unsigned char* oldVal = _value;
+ if (fInc(s._value))
+ _value = s._value;
+ else
+ Init(s.Data(), s.GetLength());
+ fDec(oldVal);
+
+ return *this;
+}
+
+c4_String operator+ (const c4_String& a, const c4_String& b)
+{
+ const int aCnt = a.GetLength();
+ int sum = aCnt + b.GetLength();
+
+ c4_String result ('\0', sum); // set up correct size, then fix contents
+ memcpy(result._value + 2, a.Data(), aCnt);
+ memcpy(result._value + 2 + aCnt, b.Data(), sum - aCnt);
+
+ return result;
+}
+
+void c4_String::Init(const void* p, int n)
+{
+ if (p == NULL || n <= 0)
+ {
+ // Optimization to significantly speed-up init of empty strings:
+ // share a common entry, which avoids *LOTS* of tiny mem allocs.
+ //
+ // Especially "new [...] c4_String" will benefit a lot, as well as:
+ //
+ // c4_String s; // this would have caused a new allocation
+ // s = ... // then immediately drops the count back
+ //
+ // 2001/11/27: changed to never release this empty vector, for MT use
+ // the new logic is to completely ignore its ref count
+
+ if (!nullVec)
+ {
+ // obtain a valid new empty string buffer to keep around
+ unsigned char* nv = new unsigned char [3];
+ nv[0] = nv[1] = nv[2] = 0;
+ // only set static value after item is fully inited (avoid MT race)
+ nullVec = nv;
+ }
+
+ _value = nullVec; // use this buffer as our empty string
+ return; // done... that was quick, wasn't it?
+ }
+
+ _value = new unsigned char [n + 3];
+
+ _value[0] = 1; // many assumptions here: set the reference count to 1
+
+ if (n > 0)
+ memcpy(_value + 2, p, n);
+ _value[1] = (unsigned char) (n <= 255 ? n : 255);
+ _value[n+2] = 0;
+}
+
+int c4_String::FullLength() const
+{
+ int n = _value[1];
+ return n < 255 ? n : n + strlen((const char*) _value + 2 + 255);
+}
+
+c4_String c4_String::Mid(int nFirst, int nCount) const
+{
+ if (nFirst >= GetLength())
+ return c4_String ();
+
+ if (nFirst + nCount > GetLength())
+ nCount = GetLength() - nFirst;
+
+ if (nFirst == 0 && nCount == GetLength())
+ return *this;
+
+ return c4_String (Data() + nFirst, nCount);
+}
+
+c4_String c4_String::Left(int nCount) const
+{
+ if (nCount >= GetLength())
+ return *this;
+
+ return c4_String (Data(), nCount);
+}
+
+c4_String c4_String::Right(int nCount) const
+{
+ if (nCount >= GetLength())
+ return *this;
+
+ return c4_String (Data() + GetLength() - nCount, nCount);
+}
+
+bool operator== (const c4_String& a, const c4_String& b)
+{
+ return a._value == b._value || a.GetLength() == b.GetLength() &&
+ memcmp(a.Data(), b.Data(), a.GetLength()) == 0;
+}
+
+int c4_String::Compare(const char* str) const
+{
+ return Data() == str ? 0 : strcmp(Data(), str);
+}
+
+int c4_String::CompareNoCase(const char* str) const
+{
+ return Data() == str ? 0 : strcasecmp(Data(), str);
+}
+
+int c4_String::Find(char ch) const
+{
+ const char* p = strchr(Data(), ch);
+ return p != 0 ? p - Data() : -1;
+}
+
+int c4_String::ReverseFind(char ch) const
+{
+ const char* p = strrchr(Data(), ch);
+ return p != 0 ? p - Data() : -1;
+}
+
+int c4_String::FindOneOf(const char* set) const
+{
+ const char* p = strpbrk(Data(), set);
+ return p != 0 ? p - Data() : -1;
+}
+
+int c4_String::Find(const char* sub) const
+{
+ const char* p = strstr(Data(), sub);
+ return p != 0 ? p - Data() : -1;
+}
+
+c4_String c4_String::SpanIncluding(const char* set) const
+{
+ return Left(strspn(Data(), set));
+}
+
+c4_String c4_String::SpanExcluding(const char* set) const
+{
+ return Left(strcspn(Data(), set));
+}
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // q4_UNIV
diff --git a/akregator/src/mk4storage/metakit/src/table.cpp b/akregator/src/mk4storage/metakit/src/table.cpp
new file mode 100644
index 00000000..a857cee0
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/table.cpp
@@ -0,0 +1,160 @@
+// table.cpp --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Loose ends, these should be moved
+ */
+
+#include "header.h"
+#include "handler.h"
+#include "store.h"
+#include "field.h"
+#include "format.h"
+#include "persist.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// Implemented in this file
+
+ class c4_Bytes;
+ class c4_HandlerSeq;
+
+/////////////////////////////////////////////////////////////////////////////
+
+/** @class c4_Bytes
+ *
+ * Generic data buffer, with optional automatic clean up.
+ *
+ * These objects are used to pass around untyped data without concern about
+ * clean-up. They know whether the bytes need to be de-allocated when these
+ * objects go out of scope. Small amounts of data are stored in the object.
+ *
+ * Objects of this class are used a lot within Metakit to manipulate its own
+ * data items generically. The c4_BytesProp class allows storing binary
+ * data explicitly in a file. If such data files must be portable, then the
+ * application itself must define a generic format to deal with byte order.
+ *
+ * How to store an object in binary form in a row (this is not portable):
+ * @code
+ * struct MyStruct { ... };
+ * MyStruct something;
+ *
+ * c4_BytesProp pData ("Data");
+ * c4_Row row;
+ *
+ * pData (row) = c4_Bytes (&something, sizeof something);
+ * @endcode
+ */
+
+ /// Construct an object with contents, optionally as a copy
+c4_Bytes::c4_Bytes (const void* buf_, int len_, bool copy_)
+ : _size (len_), _copy (copy_)
+{
+ _contents = (t4_byte*) buf_; // moved out of intializers for DEC CXX 5.7
+ if (_copy)
+ _MakeCopy();
+}
+
+ /// Copy constructor
+c4_Bytes::c4_Bytes (const c4_Bytes& src_)
+ : _size (src_._size), _copy (src_._copy)
+{
+ _contents = src_._contents; // moved out of intializers for DEC CXX 5.7
+ if (_copy || _contents == src_._buffer)
+ _MakeCopy();
+}
+
+ /// Assignment, this may make a private copy of contents
+c4_Bytes& c4_Bytes::operator= (const c4_Bytes& src_)
+{
+ if (&src_ != this)
+ {
+ _LoseCopy();
+
+ _contents = src_._contents;
+ _size = src_._size;
+ _copy = src_._copy;
+
+ if (_copy || _contents == src_._buffer)
+ _MakeCopy();
+ }
+
+ return *this;
+}
+
+ /// Swap the contents and ownership of two byte objects
+void c4_Bytes::Swap(c4_Bytes& bytes_)
+{
+ t4_byte* p = _contents;
+ int s = _size;
+ bool c = _copy;
+
+ _contents = bytes_._contents;
+ _size = bytes_._size;
+ _copy = bytes_._copy;
+
+ bytes_._contents = p;
+ bytes_._size = s;
+ bytes_._copy = c;
+
+ // if either one is using its local buffer, swap those too
+ if (_contents == bytes_._buffer || p == _buffer)
+ {
+ t4_byte t [sizeof _buffer];
+
+ memcpy(t, _buffer, sizeof _buffer);
+ memcpy(_buffer, bytes_._buffer, sizeof _buffer);
+ memcpy(bytes_._buffer, t, sizeof _buffer);
+
+ if (_contents == bytes_._buffer)
+ _contents = _buffer;
+
+ if (bytes_._contents == _buffer)
+ bytes_._contents = bytes_._buffer;
+ }
+}
+
+/// Define contents as a freshly allocated buffer of given size
+t4_byte* c4_Bytes::SetBuffer(int length_)
+{
+/* No substantial improvement measured:
+ Perhaps keep a correctly sized c4_Bytes object in each property?
+ It means c4_...Ref objects would need to store a pointer, not an id.
+
+ if (length_ == _size)
+ return _contents; // no work needed, get out fast
+*/
+ _LoseCopy();
+
+ _size = length_;
+ _copy = _size > (int) sizeof _buffer;
+
+ return _contents = _copy ? d4_new t4_byte [_size] : _buffer;
+}
+
+/// Allocate a buffer and fills its contents with zero bytes
+t4_byte* c4_Bytes::SetBufferClear(int length_)
+{
+ return (t4_byte*) memset(SetBuffer(length_), 0, length_);
+}
+
+void c4_Bytes::_MakeCopy()
+{
+ d4_assert(_contents != 0);
+
+ _copy = _size > (int) sizeof _buffer;
+
+ if (_size > 0)
+ _contents = (t4_byte*) memcpy(_copy ? d4_new t4_byte [_size]
+ : _buffer, _contents, _size);
+}
+
+/// Return true if the contents of both objects are equal
+bool operator== (const c4_Bytes& a_, const c4_Bytes& b_)
+{
+ return a_._contents == b_._contents ||
+ (a_._size == b_._size &&
+ memcmp(a_._contents, b_._contents, a_._size) == 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/univ.cpp b/akregator/src/mk4storage/metakit/src/univ.cpp
new file mode 100644
index 00000000..81a73015
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/univ.cpp
@@ -0,0 +1,227 @@
+// univ.cpp --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * A simple implementation of dynamic arrays
+ */
+
+#include "header.h"
+
+#if q4_UNIV // until end of source
+/////////////////////////////////////////////////////////////////////////////
+
+#include <stdlib.h> // malloc
+
+#if !q4_INLINE
+#include "univ.inl"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if q4_UNIX || __MINGW32__
+#define _strdup strdup
+#elif !q4_BORC && !q4_MSVC && !q4_WATC && !(q4_MWCW && defined(_WIN32)) && \
+ !(q4_MWCW && __MWERKS__ >= 0x3000)
+
+ static char* _strdup(const char* p)
+ {
+ if (!p)
+ return 0;
+
+ char* s = (char*) malloc(strlen(p) + 1);
+ return strcpy(s, p);
+ }
+
+#endif
+
+ // The Borland C++ RTL does not want file handle objects to cross
+ // DLL boundaries, so we add special fopen/fclose hooks to this DLL.
+
+#if q4_BORC
+ #include <stdio.h>
+
+#if q4_WIN32
+ __declspec(dllexport) FILE*
+#else
+ FILE* __export
+#endif
+ f4_FileOpenInDLL(const char* name_, const char* mode_)
+ {
+ return fopen(name_, mode_);
+ }
+
+#if q4_WIN32
+ __declspec(dllexport)
+#else
+ int __export
+#endif
+ f4_FileCloseInDLL(FILE* file_)
+ {
+ return fclose(file_);
+ }
+
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_BaseArray
+
+c4_BaseArray::c4_BaseArray ()
+ : _data (0), _size (0)
+{
+}
+
+c4_BaseArray::~c4_BaseArray ()
+{
+ SetLength(0);
+}
+
+void c4_BaseArray::SetLength(int nNewSize)
+{
+ // 2001-11-25: use more granular allocation, as optimization
+ const int bits = 6;
+
+ if (((_size - 1) ^ (nNewSize - 1)) >> bits) {
+ const int n = (nNewSize + (1<<bits) - 1) & -(1<<bits);
+ _data = _data == 0 ? n == 0 ? (char*) 0
+ : (char*) malloc(n)
+ : n == 0 ? (free(_data), (char*) 0)
+ : (char*) realloc(_data, n);
+ }
+
+ d4_assert(_data != 0 || nNewSize == 0);
+
+ int n = _size;
+ _size = nNewSize;
+
+ if (nNewSize > n)
+ memset(GetData(n), 0, nNewSize - n);
+}
+
+void c4_BaseArray::Grow(int nIndex)
+{
+ if (nIndex > _size)
+ SetLength(nIndex);
+}
+
+void c4_BaseArray::InsertAt(int nIndex, int nCount)
+{
+ SetLength(_size + nCount);
+
+ int to = nIndex + nCount;
+ if (_size > to)
+ d4_memmove(GetData(to), GetData(nIndex), _size - to);
+}
+
+void c4_BaseArray::RemoveAt(int nIndex, int nCount)
+{
+ int from = nIndex + nCount;
+ if (_size > from)
+ d4_memmove(GetData(nIndex), GetData(from), _size - from);
+
+ SetLength(_size - nCount);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_DWordArray
+
+int c4_DWordArray::Add(t4_i32 newElement)
+{
+ int n = GetSize();
+ _vector.Grow(Off(n + 1));
+ SetAt(n, newElement);
+ return n;
+}
+
+void c4_DWordArray::InsertAt(int nIndex, t4_i32 newElement, int nCount)
+{
+ _vector.InsertAt(Off(nIndex), nCount * sizeof (t4_i32));
+
+ while (--nCount >= 0)
+ SetAt(nIndex++, newElement);
+}
+
+void c4_DWordArray::RemoveAt(int nIndex, int nCount)
+{
+ _vector.RemoveAt(Off(nIndex), nCount * sizeof (t4_i32));
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_PtrArray
+
+int c4_PtrArray::Add(void* newElement)
+{
+ int n = GetSize();
+ _vector.Grow(Off(n + 1));
+ SetAt(n, newElement);
+ return n;
+}
+
+void c4_PtrArray::InsertAt(int nIndex, void* newElement, int nCount)
+{
+ _vector.InsertAt(Off(nIndex), nCount * sizeof (void*));
+
+ while (--nCount >= 0)
+ SetAt(nIndex++, newElement);
+}
+
+void c4_PtrArray::RemoveAt(int nIndex, int nCount)
+{
+ _vector.RemoveAt(Off(nIndex), nCount * sizeof (void*));
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_StringArray
+
+c4_StringArray::~c4_StringArray()
+{
+ SetSize(0);
+}
+
+void c4_StringArray::SetSize(int nNewSize, int)
+{
+ int i = nNewSize;
+
+ while (i < GetSize())
+ SetAt(i++, 0);
+
+ _ptrs.SetSize(nNewSize);
+
+ while (i < GetSize())
+ _ptrs.SetAt(i++, "");
+}
+
+void c4_StringArray::SetAt(int nIndex, const char* newElement)
+{
+ char* s = (char*) _ptrs.GetAt(nIndex);
+ if (s && *s)
+ free(s);
+
+ _ptrs.SetAt(nIndex, newElement && *newElement? _strdup(newElement) : "");
+}
+
+int c4_StringArray::Add(const char* newElement)
+{
+ int n = _ptrs.Add(0);
+ SetAt(n, newElement);
+ return n;
+}
+
+void c4_StringArray::InsertAt(int nIndex, const char* newElement, int nCount)
+{
+ _ptrs.InsertAt(nIndex, 0, nCount);
+
+ while (--nCount >= 0)
+ SetAt(nIndex++, newElement);
+}
+
+void c4_StringArray::RemoveAt(int nIndex, int nCount)
+{
+ for (int i = 0; i < nCount; ++i)
+ SetAt(nIndex + i, 0);
+
+ _ptrs.RemoveAt(nIndex, nCount);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // q4_UNIV
diff --git a/akregator/src/mk4storage/metakit/src/univ.h b/akregator/src/mk4storage/metakit/src/univ.h
new file mode 100644
index 00000000..5558e8a4
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/univ.h
@@ -0,0 +1,115 @@
+// univ.h --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Definition of the container classes
+ */
+
+#define q4_UNIV 1
+
+#include "mk4str.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+class c4_BaseArray
+{
+public:
+ c4_BaseArray ();
+ ~c4_BaseArray ();
+
+ int GetLength() const;
+ void SetLength(int nNewSize);
+
+ const void* GetData(int nIndex) const;
+ void* GetData(int nIndex);
+
+ void Grow(int nIndex);
+
+ void InsertAt(int nIndex, int nCount);
+ void RemoveAt(int nIndex, int nCount);
+
+private:
+ char* _data;
+ int _size;
+// char _buffer[4];
+};
+
+class c4_PtrArray
+{
+public:
+ c4_PtrArray ();
+ ~c4_PtrArray ();
+
+ int GetSize() const;
+ void SetSize(int nNewSize, int nGrowBy = -1);
+
+ void* GetAt(int nIndex) const;
+ void SetAt(int nIndex, const void* newElement);
+ void*& ElementAt(int nIndex);
+
+ int Add(void* newElement);
+
+ void InsertAt(int nIndex, void* newElement, int nCount = 1);
+ void RemoveAt(int nIndex, int nCount = 1);
+
+private:
+ static int Off(int n_);
+
+ c4_BaseArray _vector;
+};
+
+class c4_DWordArray
+{
+public:
+ c4_DWordArray ();
+ ~c4_DWordArray ();
+
+ int GetSize() const;
+ void SetSize(int nNewSize, int nGrowBy = -1);
+
+ t4_i32 GetAt(int nIndex) const;
+ void SetAt(int nIndex, t4_i32 newElement);
+ t4_i32& ElementAt(int nIndex);
+
+ int Add(t4_i32 newElement);
+
+ void InsertAt(int nIndex, t4_i32 newElement, int nCount = 1);
+ void RemoveAt(int nIndex, int nCount = 1);
+
+private:
+ static int Off(int n_);
+
+ c4_BaseArray _vector;
+};
+
+class c4_StringArray
+{
+public:
+ c4_StringArray ();
+ ~c4_StringArray ();
+
+ int GetSize() const;
+ void SetSize(int nNewSize, int nGrowBy = -1);
+
+ const char* GetAt(int nIndex) const;
+ void SetAt(int nIndex, const char* newElement);
+// c4_String& ElementAt(int nIndex);
+
+ int Add(const char* newElement);
+
+ void InsertAt(int nIndex, const char* newElement, int nCount = 1);
+ void RemoveAt(int nIndex, int nCount = 1);
+
+private:
+ c4_PtrArray _ptrs;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if q4_INLINE
+ #include "univ.inl"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
diff --git a/akregator/src/mk4storage/metakit/src/univ.inl b/akregator/src/mk4storage/metakit/src/univ.inl
new file mode 100644
index 00000000..45b086fb
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/univ.inl
@@ -0,0 +1,126 @@
+// univ.inl --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Inlined members of the container classes
+ */
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_BaseArray
+
+d4_inline int c4_BaseArray::GetLength() const
+{
+ return _size;
+}
+
+d4_inline const void* c4_BaseArray::GetData(int nIndex) const
+{
+ return _data + nIndex;
+}
+
+d4_inline void* c4_BaseArray::GetData(int nIndex)
+{
+ return _data + nIndex;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_PtrArray
+
+d4_inline c4_PtrArray::c4_PtrArray ()
+{
+}
+
+d4_inline c4_PtrArray::~c4_PtrArray ()
+{
+}
+
+d4_inline int c4_PtrArray::Off(int n_)
+{
+ return n_ * sizeof (void*);
+}
+
+d4_inline int c4_PtrArray::GetSize() const
+{
+ return _vector.GetLength() / sizeof (void*);
+}
+
+d4_inline void c4_PtrArray::SetSize(int nNewSize, int)
+{
+ _vector.SetLength(Off(nNewSize));
+}
+
+d4_inline void* c4_PtrArray::GetAt(int nIndex) const
+{
+ return *(void* const*) _vector.GetData(Off(nIndex));
+}
+
+d4_inline void c4_PtrArray::SetAt(int nIndex, const void* newElement)
+{
+ *(const void**) _vector.GetData(Off(nIndex)) = newElement;
+}
+
+d4_inline void*& c4_PtrArray::ElementAt(int nIndex)
+{
+ return *(void**) _vector.GetData(Off(nIndex));
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_DWordArray
+
+d4_inline c4_DWordArray::c4_DWordArray ()
+{
+}
+
+d4_inline c4_DWordArray::~c4_DWordArray ()
+{
+}
+
+d4_inline int c4_DWordArray::Off(int n_)
+{
+ return n_ * sizeof (t4_i32);
+}
+
+d4_inline int c4_DWordArray::GetSize() const
+{
+ return _vector.GetLength() / sizeof (t4_i32);
+}
+
+d4_inline void c4_DWordArray::SetSize(int nNewSize, int)
+{
+ _vector.SetLength(Off(nNewSize));
+}
+
+d4_inline t4_i32 c4_DWordArray::GetAt(int nIndex) const
+{
+ return *(const t4_i32*) _vector.GetData(Off(nIndex));
+}
+
+d4_inline void c4_DWordArray::SetAt(int nIndex, t4_i32 newElement)
+{
+ *(t4_i32*) _vector.GetData(Off(nIndex)) = newElement;
+}
+
+d4_inline t4_i32& c4_DWordArray::ElementAt(int nIndex)
+{
+ return *(t4_i32*) _vector.GetData(Off(nIndex));
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_StringArray
+
+d4_inline c4_StringArray::c4_StringArray ()
+{
+}
+
+d4_inline int c4_StringArray::GetSize() const
+{
+ return _ptrs.GetSize();
+}
+
+d4_inline const char* c4_StringArray::GetAt(int nIndex) const
+{
+ return (const char*) _ptrs.GetAt(nIndex);
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/view.cpp b/akregator/src/mk4storage/metakit/src/view.cpp
new file mode 100644
index 00000000..af2fc9fa
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/view.cpp
@@ -0,0 +1,1271 @@
+// view.cpp --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Implementation of main classes not involved in persistence
+ */
+
+#include "header.h"
+#include "derived.h"
+#include "custom.h"
+#include "store.h" // for RelocateRows
+#include "field.h" // for RelocateRows
+#include "persist.h"
+#include "remap.h"
+
+#if !q4_INLINE
+#include "mk4.inl"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_ThreadLock
+
+class c4_ThreadLock
+{
+public:
+ c4_ThreadLock ();
+
+ class Hold
+ {
+ public:
+ Hold ();
+ ~Hold ();
+ };
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if q4_MULTI
+
+#if q4_WIN32
+
+/*
+ * On Win32, use a critical section to protect the global symbol table.
+ * Also uses special thread-safe calls to inc/dec all reference counts.
+ *
+ * This implementation replaces the previous use of TLS, which cannot
+ * be used without special tricks in dynamically loaded DLL's, as is
+ * required for OCX/ActiveX use (which uses LoadLibrary).
+ *
+ * Note: Could have used MFC's CCriticalSection and CSingleLock classes,
+ * but the code below is so trivial that it hardly matters.
+ */
+
+#if q4_MSVC && !q4_STRICT
+#pragma warning(disable: 4201) // nonstandard extension used : ...
+#endif
+#include <windows.h>
+
+ static CRITICAL_SECTION gCritSect;
+
+ c4_ThreadLock::c4_ThreadLock ()
+ {
+ InitializeCriticalSection(&gCritSect);
+ }
+
+ c4_ThreadLock::Hold::Hold ()
+ {
+ EnterCriticalSection(&gCritSect);
+ }
+
+ c4_ThreadLock::Hold::~Hold ()
+ {
+ LeaveCriticalSection(&gCritSect);
+ }
+
+#else /* q4_WIN32 */
+
+#include <pthread.h>
+
+ static pthread_mutex_t gMutex;
+
+ d4_inline c4_ThreadLock::c4_ThreadLock ()
+ {
+ pthread_mutex_init(&gMutex, 0);
+ }
+
+ d4_inline c4_ThreadLock::Hold::Hold ()
+ {
+ d4_dbgdef(int r =)
+ pthread_mutex_lock(&gMutex);
+ d4_assert(r == 0);
+ }
+
+ d4_inline c4_ThreadLock::Hold::~Hold ()
+ {
+ d4_dbgdef(int r =)
+ pthread_mutex_unlock(&gMutex);
+ d4_assert(r == 0);
+ }
+
+#endif /* q4_WIN32 */
+
+#else /* q4_MULTI */
+
+// All other implementations revert to the simple "thread-less" case.
+
+ d4_inline c4_ThreadLock::c4_ThreadLock ()
+ {
+ }
+
+ d4_inline c4_ThreadLock::Hold::Hold ()
+ {
+ }
+
+ d4_inline c4_ThreadLock::Hold::~Hold ()
+ {
+ }
+
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if q4_LOGPROPMODS
+
+ static FILE* sPropModsFile = 0;
+ static int sPropModsProp = -1;
+
+FILE* f4_LogPropMods(FILE* fp_, int propId_)
+{
+ FILE* prevfp = sPropModsFile;
+ sPropModsFile = fp_;
+ sPropModsProp = propId_;
+ return prevfp;
+}
+
+void f4_DoLogProp(const c4_Handler* hp_, int id_, const char* fmt_, int arg_)
+{
+ if (sPropModsFile != 0 && (sPropModsProp < 0 || sPropModsProp == id_)) {
+ fprintf(sPropModsFile, "handler 0x%x id %d: ", hp_, id_);
+ fprintf(sPropModsFile, fmt_, arg_);
+ }
+}
+
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+/** @class c4_View
+ *
+ * A collection of data rows. This is the central public data structure of
+ * Metakit (often called "table", "array", or "relation" in other systems).
+ *
+ * Views are smart pointers to the actual collections, setting a view to a new
+ * value does not alter the collection to which this view pointed previously.
+ *
+ * The elements of views can be referred to by their 0-based index, which
+ * produces a row-reference of type c4_RowRef. These row references can
+ * be copied, used to get or set properties, or dereferenced (in which case
+ * an object of class c4_Row is returned). Taking the address of a row
+ * reference produces a c4_Cursor, which acts very much like a pointer.
+ *
+ * The following code creates a view with 1 row and 2 properties:
+ * @code
+ * c4_StringProp pName ("name");
+ * c4_IntProp pAge ("age");
+ *
+ * c4_Row data;
+ * pName (data) = "John Williams";
+ * pAge (data) = 43;
+ *
+ * c4_View myView;
+ * myView.Add(row);
+ * @endcode
+ */
+
+/// Construct a view based on a sequence
+c4_View::c4_View (c4_Sequence* seq_)
+ : _seq (seq_)
+{
+ if (!_seq)
+ _seq = d4_new c4_HandlerSeq (0);
+
+ _IncSeqRef();
+}
+
+/// Construct a view based on a custom viewer
+c4_View::c4_View (c4_CustomViewer* viewer_)
+ : _seq (0)
+{
+ d4_assert(viewer_);
+
+ _seq = d4_new c4_CustomSeq (viewer_);
+
+ _IncSeqRef();
+}
+
+/// Construct a view based on an input stream
+c4_View::c4_View (c4_Stream* stream_)
+ : _seq (c4_Persist::Load(stream_))
+{
+ if (_seq == 0)
+ _seq = d4_new c4_HandlerSeq (0);
+
+ _IncSeqRef();
+}
+
+/// Construct an empty view with one property
+c4_View::c4_View (const c4_Property& prop_)
+ : _seq (d4_new c4_HandlerSeq (0))
+{
+ _IncSeqRef();
+
+ _seq->PropIndex(prop_);
+}
+
+/// Copy constructor
+c4_View::c4_View (const c4_View& view_)
+ : _seq (view_._seq)
+{
+ _IncSeqRef();
+}
+
+/// Makes this view the same as another one.
+c4_View& c4_View::operator= (const c4_View& view_)
+{
+ if (_seq != view_._seq) {
+ _DecSeqRef();
+ _seq = view_._seq;
+ _IncSeqRef();
+ }
+ return *this;
+}
+
+/** Get a single data item in a generic way
+ *
+ * This can be used to access view data in a generalized way.
+ * Useful for c4_CustomViewers which are based on other views.
+ * @return true if the item is non-empty
+ */
+bool c4_View::GetItem(int row_, int col_, c4_Bytes& buf_) const
+{
+ const c4_Property& prop = NthProperty(col_);
+ return prop (GetAt(row_)).GetData(buf_);
+}
+
+/// Set a single data item in a generic way
+void c4_View::SetItem(int row_, int col_, const c4_Bytes& buf_) const
+{
+ const c4_Property& prop = NthProperty(col_);
+ prop (GetAt(row_)).SetData(buf_);
+}
+
+/// Set an entry, growing the view if needed
+void c4_View::SetAtGrow(int index_, const c4_RowRef& newElem_)
+{
+ if (index_ >= GetSize())
+ SetSize(index_ + 1);
+
+ _seq->SetAt(index_, &newElem_);
+}
+
+/** Add a new entry, same as "SetAtGrow(GetSize(), ...)"
+ * @return the index of the newly added row
+ */
+int c4_View::Add(const c4_RowRef& newElem_)
+{
+ int i = GetSize();
+ InsertAt(i, newElem_);
+ return i;
+}
+
+/** Construct a new view with a copy of the data
+ *
+ * The copy is a deep copy, because subviews are always copied in full.
+ */
+c4_View c4_View::Duplicate() const
+{
+ // insert all rows, sharing any subviews as needed
+ c4_View result = Clone();
+ result.InsertAt(0, _seq);
+ return result;
+}
+
+/** Constructs a new view with the same structure but no data
+ *
+ * Structural information can only be maintain for the top level,
+ * subviews will be included but without any properties themselves.
+ */
+c4_View c4_View::Clone() const
+{
+ c4_View view;
+
+ for (int i = 0; i < NumProperties(); ++i)
+ view._seq->PropIndex(NthProperty(i));
+
+ return view;
+}
+
+/** Adds a property column to a view if not already present
+ * @return 0-based column position of the property
+ */
+int c4_View::AddProperty(const c4_Property& prop_)
+{
+ return _seq->PropIndex(prop_);
+}
+
+/** Returns the N-th property (using zero-based indexing)
+ * @return reference to the specified property
+ */
+const c4_Property& c4_View::NthProperty(
+ int index_ ///< the zero-based property index
+ ) const
+{
+ return _seq->NthHandler(index_).Property();
+}
+
+/** Find the index of a property, given its name
+ * @return 0-based column index
+ * @retval -1 property not present in this view
+ */
+int c4_View::FindPropIndexByName(
+ const char* name_ ///< property name (case insensitive)
+ ) const
+{
+ // use a slow linear scan to find the untyped property by name
+ for (int i = 0; i < NumProperties(); ++i) {
+ c4_String s = NthProperty(i).Name();
+ if (s.CompareNoCase(name_) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+/** Defines a column for a property.
+ *
+ * The following code defines an empty view with three properties:
+ * @code
+ * c4_IntProp p1, p2, p3;
+ * c4_View myView = (p1, p2, p3);
+ * @endcode
+ * @return the new view object (without any data rows)
+ * @sa c4_Property
+ */
+c4_View c4_View::operator, (const c4_Property& prop_) const
+{
+ c4_View view = Clone();
+ view.AddProperty(prop_);
+ return view;
+}
+
+/// Insert copies of all rows of the specified view
+void c4_View::InsertAt(int index_, const c4_View& view_)
+{
+ int n = view_.GetSize();
+ if (n > 0) {
+ c4_Row empty;
+
+ InsertAt(index_, empty, n);
+
+ for (int i = 0; i < n; ++i)
+ SetAt(index_ + i, view_[i]);
+ }
+}
+
+bool c4_View::IsCompatibleWith(const c4_View& dest_) const
+{
+ // can't determine table without handlers (and can't be a table)
+ if (NumProperties() == 0 || dest_.NumProperties() == 0)
+ return false;
+
+ c4_Sequence* s1 = _seq;
+ c4_Sequence* s2 = dest_._seq;
+ c4_HandlerSeq* h1 = (c4_HandlerSeq*) s1->HandlerContext(0);
+ c4_HandlerSeq* h2 = (c4_HandlerSeq*) s2->HandlerContext(0);
+
+ // both must be real handler views, not derived ones
+ if (h1 != s1 || h2 != s2)
+ return false;
+
+ // both must not contain any temporary handlers
+ if (s1->NumHandlers() != h1->NumFields() ||
+ s2->NumHandlers() != h2->NumFields())
+ return false;
+
+ // both must be in the same storage
+ if (h1->Persist() == 0 || h1->Persist() != h2->Persist())
+ return false;
+
+ // both must have the same structure (is this expensive?)
+ c4_String d1 = h1->Definition().Description(true);
+ c4_String d2 = h1->Definition().Description(true);
+ return d1 == d2; // ignores all names
+}
+
+/** Move attached rows to somewhere else in same storage
+ *
+ * There is a lot of trickery going on here. The whole point of this
+ * code is that moving rows between (compatible!) subviews should not
+ * use copying when potentially large memo's and subviews are involved.
+ * In that case, the best solution is really to move pointers, not data.
+ */
+void c4_View::RelocateRows(int from_, int count_, c4_View& dest_, int pos_)
+{
+ if (count_ < 0)
+ count_ = GetSize() - from_;
+ if (pos_ < 0)
+ pos_ = dest_.GetSize();
+
+ d4_assert(0 <= from_ && from_ <= GetSize());
+ d4_assert(0 <= count_ && from_ + count_ <= GetSize());
+ d4_assert(0 <= pos_ && pos_ <= dest_.GetSize());
+
+ if (count_ > 0) {
+ // the destination must not be inside the source rows
+ d4_assert(&dest_ != this || from_ > pos_ || pos_ >= from_ + count_);
+
+ // this test is slow, so do it only as a debug check
+ d4_assert(IsCompatibleWith(dest_));
+
+ // make space, swap rows, drop originals
+ c4_Row empty;
+ dest_.InsertAt(pos_, empty, count_);
+
+ // careful if insert moves origin
+ if (&dest_ == this && pos_ <= from_)
+ from_ += count_;
+
+ for (int i = 0; i < count_; ++i)
+ ((c4_HandlerSeq*) _seq)->ExchangeEntries(from_ + i,
+ *(c4_HandlerSeq*) dest_._seq, pos_ + i);
+
+ RemoveAt(from_, count_);
+ }
+}
+
+/** Create view with all rows in natural (property-wise) order
+ *
+ * The result is virtual, it merely maintains a permutation to access the
+ * underlying view. This "derived" view uses change notification to track
+ * changes to the underlying view, but unfortunately there are some major
+ * limitations with this scheme - one of them being that deriving another
+ * view from this sorted one will not properly track changes.
+ */
+c4_View c4_View::Sort() const
+{
+ return f4_CreateSort(*_seq);
+}
+
+/** Create view sorted according to the specified properties
+ *
+ * The result is virtual, it merely maintains a permutation to access the
+ * underlying view. This "derived" view uses change notification to track
+ * changes to the underlying view, but unfortunately there are some major
+ * limitations with this scheme - one of them being that deriving another
+ * view from this sorted one will not properly track changes.
+ */
+c4_View c4_View::SortOn(const c4_View& up_) const
+{
+ c4_Sequence* seq = f4_CreateProject(*_seq, *up_._seq, true);
+
+ return f4_CreateSort(*seq);
+}
+
+/** Create sorted view, with some properties sorted in reverse
+ *
+ * The result is virtual, it merely maintains a permutation to access the
+ * underlying view. This "derived" view uses change notification to track
+ * changes to the underlying view, but unfortunately there are some major
+ * limitations with this scheme - one of them being that deriving another
+ * view from this sorted one will not properly track changes.
+ */
+c4_View c4_View::SortOnReverse(
+ const c4_View& up_, ///< the view which defines the sort order
+ const c4_View& down_ ///< subset of up_, defines reverse order
+ ) const
+{
+ c4_Sequence* seq = f4_CreateProject(*_seq, *up_._seq, true);
+
+ return f4_CreateSort(*seq, down_._seq);
+}
+
+/** Create view with rows matching the specified value
+ *
+ * The result is virtual, it merely maintains a permutation to access the
+ * underlying view. This "derived" view uses change notification to track
+ * changes to the underlying view, but this only works when based on views
+ * which properly generate change notifications (.e. raw views, other
+ * selections, and projections).
+ */
+c4_View c4_View::Select(const c4_RowRef& crit_) const
+{
+ return f4_CreateFilter(*_seq, &crit_, &crit_);
+}
+
+/** Create view with row values within the specified range
+ *
+ * The result is virtual, it merely maintains a permutation to access the
+ * underlying view. This "derived" view uses change notification to track
+ * changes to the underlying view, but this only works when based on views
+ * which properly generate change notifications (.e. raw views, other
+ * selections, and projections).
+ */
+c4_View c4_View::SelectRange(
+ const c4_RowRef& low_, ///< values of the lower bounds (inclusive)
+ const c4_RowRef& high_ ///< values of the upper bounds (inclusive)
+ ) const
+{
+ return f4_CreateFilter(*_seq, &low_, &high_);
+}
+
+/** Create view with the specified property arrangement
+ *
+ * The result is virtual, it merely maintains a permutation to access the
+ * underlying view. This "derived" view uses change notification to track
+ * changes to the underlying view, but this only works when based on views
+ * which properly generate change notifications (.e. raw views, selections,
+ * and other projections).
+ */
+c4_View c4_View::Project(const c4_View& in_) const
+{
+ return f4_CreateProject(*_seq, *in_._seq, false);
+}
+
+/** Create derived view with some properties omitted
+ *
+ * The result is virtual, it merely maintains a permutation to access the
+ * underlying view. This "derived" view uses change notification to track
+ * changes to the underlying view, but this only works when based on views
+ * which properly generate change notifications (.e. raw views, selections,
+ * and other projections).
+ */
+c4_View c4_View::ProjectWithout(const c4_View& out_) const
+{
+ return f4_CreateProject(*_seq, *_seq, false, out_._seq);
+}
+
+/** Create view which is a segment/slice (default is up to end)
+ *
+ * Returns a view which is a subset, either a contiguous range, or
+ * a "slice" with element taken from every step_ entries. If the
+ * step is negative, the same entries are returned, but in reverse
+ * order (start_ is still lower index, it'll then be returned last).
+ *
+ * This view operation is based on a custom viewer and is modifiable.
+ */
+c4_View c4_View::Slice(int first_, int limit_, int step_) const
+{
+ return f4_CustSlice(*_seq, first_, limit_, step_);
+}
+
+/** Create view which is the cartesian product with given view
+ *
+ * The cartesian product is defined as every combination of rows
+ * in both views. The number of entries is the product of the
+ * number of entries in the two views, properties which are present
+ * in both views will use the values defined in this view.
+ *
+ * This view operation is based on a read-only custom viewer.
+ */
+c4_View c4_View::Product(const c4_View& view_) const
+{
+ return f4_CustProduct(*_seq, view_);
+}
+
+/** Create view which remaps another given view
+ *
+ * Remapping constructs a view with the rows indicated by another
+ * view. The first property in the order_ view must be an int
+ * property with index values referring to this one. The size of
+ * the resulting view is determined by the order_ view and can
+ * differ, for example to act as a subset selection (if smaller).
+ *
+ * This view operation is based on a custom viewer and is modifiable.
+ */
+c4_View c4_View::RemapWith(const c4_View& view_) const
+{
+ return f4_CustRemapWith(*_seq, view_);
+}
+
+/** Create view which pairs each row with corresponding row
+ *
+ * This is like a row-by-row concatenation. Both views must have
+ * the same number of rows, the result has all properties from
+ * this view plus any other properties from the other view.
+ *
+ * This view operation is based on a custom viewer and is modifiable.
+ */
+c4_View c4_View::Pair(const c4_View& view_) const
+{
+ return f4_CustPair(*_seq, view_);
+}
+
+/** Create view with rows from another view appended
+ *
+ * Constructs a view which has all rows of this view, and all rows
+ * of the second view appended. The structure of the second view
+ * is assumed to be identical to this one. This operation is a bit
+ * similar to appending all rows from the second view, but it does
+ * not actually store the result anywhere, it just looks like it.
+ *
+ * This view operation is based on a custom viewer and is modifiable.
+ */
+c4_View c4_View::Concat(const c4_View& view_) const
+{
+ return f4_CustConcat(*_seq, view_);
+}
+
+/** Create view with one property renamed (must be of same type)
+ *
+ * This view operation is based on a custom viewer and is modifiable.
+ */
+c4_View c4_View::Rename(const c4_Property& old_, const c4_Property& new_) const
+{
+ return f4_CustRename(*_seq, old_, new_);
+}
+
+/** Create view with a subview, grouped by the specified properties
+ *
+ * This operation is similar to the SQL 'GROUP BY', but it takes
+ * advantage of the fact that Metakit supports nested views. The
+ * view returned from this member has one row per distinct group,
+ * with an extra view property holding the remaining properties.
+ * If there are N rows in the original view matching key X, then
+ * the result is a row for key X, with a subview of N rows. The
+ * properties of the subview are all the properties not in the key.
+ *
+ * This view operation is based on a read-only custom viewer.
+ */
+c4_View c4_View::GroupBy(
+ const c4_View& keys_, ///< properties in this view determine grouping
+ const c4_ViewProp& result_ ///< name of new subview defined in result
+ ) const
+{
+ return f4_CustGroupBy(*_seq, keys_, result_);
+}
+
+/** Create view with count of duplicates, when grouped by key
+ *
+ * This is similar to c4_View::GroupBy, but it determines only the
+ * number of rows in each group and does not create a nested view.
+ *
+ * This view operation is based on a read-only custom viewer.
+ */
+c4_View c4_View::Counts(
+ const c4_View& keys_, ///< properties in this view determine grouping
+ const c4_IntProp& result_ ///< new count property defined in result
+ ) const
+{
+ return f4_CustGroupBy(*_seq, keys_, result_); // third arg is c4_IntProp
+}
+
+/** Create view with all duplicate rows omitted
+ *
+ * This view operation is based on a read-only custom viewer.
+ */
+c4_View c4_View::Unique() const
+{
+ c4_IntProp count ("#N#");
+ return Counts(Clone(), count).ProjectWithout(count);
+}
+
+/** Create view which is the set union (assumes no duplicate rows)
+ *
+ * Calculates the set union. This will only work if both input
+ * views are sets, i.e. they have no duplicate rows in them.
+ *
+ * This view operation is based on a read-only custom viewer.
+ */
+c4_View c4_View::Union(const c4_View& view_) const
+{
+ return Concat(view_).Unique();
+}
+
+/** Create view with all rows also in the given view (no dups)
+ *
+ * Calculates the set intersection. This will only work if both
+ * input views are sets, i.e. they have no duplicate rows in them.
+ *
+ * This view operation is based on a read-only custom viewer.
+ */
+c4_View c4_View::Intersect(const c4_View& view_) const
+{
+ c4_View v = Concat(view_);
+
+ // assume neither view has any duplicates
+ c4_IntProp count ("#N#");
+ return v.Counts(Clone(), count).Select(count [2]).ProjectWithout(count);
+}
+
+/** Create view with all rows not in both views (no dups)
+ *
+ * Calculates the "XOR" of two sets. This will only work if both
+ * input views are sets, i.e. they have no duplicate rows in them.
+ *
+ * This view operation is based on a read-only custom viewer.
+ */
+c4_View c4_View::Different(const c4_View& view_) const
+{
+ c4_View v = Concat(view_);
+
+ // assume neither view has any duplicates
+ c4_IntProp count ("#N#");
+ return v.Counts(Clone(), count).Select(count [1]).ProjectWithout(count);
+}
+
+/** Create view with all rows not in the given view (no dups)
+ *
+ * Calculates set-difference of this view minus arg view. Result
+ * is a subset, unlike c4_View::Different. Will only work if both
+ * input views are sets, i.e. they have no duplicate rows in them.
+ *
+ * This view operation is based on a read-only custom viewer.
+ */
+c4_View c4_View::Minus(
+ const c4_View& view_ ///< the second view
+ ) const
+{
+ // inefficient: calculate difference, then keep only those in self
+ return Intersect(Different(view_));
+}
+
+/** Create view with a specific subview expanded, like a join
+ *
+ * This operation is the inverse of c4_View::GroupBy, expanding
+ * all rows in specified subview and returning a view which looks
+ * as if the rows in each subview were "expanded in place".
+ *
+ * This view operation is based on a read-only custom viewer.
+ */
+c4_View c4_View::JoinProp(
+ const c4_ViewProp& sub_, ///< name of the subview to expand
+ bool outer_ ///< true: keep rows with empty subviews
+ ) const
+{
+ return f4_CustJoinProp(*_seq, sub_, outer_);
+}
+
+/** Create view which is the relational join on the given keys
+ *
+ * This view operation is based on a read-only custom viewer.
+ */
+c4_View c4_View::Join(
+ const c4_View& keys_, ///< properties in this view determine the join
+ const c4_View& view_, ///< second view participating in the join
+ bool outer_ ///< true: keep rows with no match in second view
+ ) const
+{
+ // inefficient: calculate difference, then keep only those in self
+ return f4_CustJoin(*_seq, keys_, view_, outer_);
+}
+
+/** Create an identity view which only allows reading
+ *
+ * This view operation is based on a custom viewer.
+ */
+c4_View c4_View::ReadOnly() const
+{
+ return f4_CreateReadOnly(*_seq);
+}
+
+/** Create mapped view which adds a hash lookup layer
+ *
+ * This view creates and manages a special hash map view, to implement a
+ * fast find on the key. The key is defined to consist of the first
+ * numKeys_ properties of the underlying view.
+ *
+ * The map_ view must be empty the first time this hash view is used, so
+ * that Metakit can fill it based on whatever rows are already present in
+ * the underlying view. After that, neither the underlying view nor the
+ * map view may be modified other than through this hash mapping layer.
+ * The defined structure of the map view must be "_H:I,_R:I".
+ *
+ * This view is modifiable. Insertions and changes to key field properties
+ * can cause rows to be repositioned to maintain hash uniqueness. Careful:
+ * when a row is changed in such a way that its key is the same as in another
+ * row, that other row will be deleted from the view.
+ *
+ * Example of use:
+ * @code
+ * c4_View data = storage.GetAs("people[name:S,age:I]");
+ * c4_View datah = storage.GetAs("people_H[_H:I,_R:I]");
+ * c4_View hash = raw.Hash(datah, 1);
+ * ... hash.GetSize() ...
+ * hash.Add(...)
+ * @endcode
+ */
+c4_View c4_View::Hash(const c4_View& map_, int numKeys_) const
+{
+ return f4_CreateHash(*_seq, numKeys_, map_._seq);
+}
+
+/** Create mapped view which blocks its rows in two levels
+ *
+ * This view acts like a large flat view, even though the actual rows are
+ * stored in blocks, which are rebalanced automatically to maintain a good
+ * trade-off between block size and number of blocks.
+ *
+ * The underlying view must be defined with a single view property, with
+ * the structure of the subview being as needed. An example of a blocked
+ * view definition which will act like a single one containing 2 properties:
+ * @code
+ * c4_View raw = storage.GetAs("people[_B[name:S,age:I]]");
+ * c4_View flat = raw.Blocked();
+ * ... flat.GetSize() ...
+ * flat.InsertAt(...)
+ * @endcode
+ *
+ * This view operation is based on a custom viewer and is modifiable.
+ */
+c4_View c4_View::Blocked() const
+{
+ return f4_CreateBlocked(*_seq);
+}
+
+/** Create mapped view which keeps its rows ordered
+ *
+ * This is an identity view, which has as only use to inform Metakit that
+ * the underlying view can be considered to be sorted on its first numKeys_
+ * properties. The effect is that c4_View::Find will try to use binary
+ * search when the search includes key properties (results will be identical
+ * to unordered views, the find will just be more efficient).
+ *
+ * This view is modifiable. Insertions and changes to key field properties
+ * can cause rows to be repositioned to maintain the sort order. Careful:
+ * when a row is changed in such a way that its key is the same as in another
+ * row, that other row will be deleted from the view.
+ *
+ * This view can be combined with c4_View::Blocked, to create a 2-level
+ * btree structure.
+ */
+c4_View c4_View::Ordered(int numKeys_) const
+{
+ return f4_CreateOrdered(*_seq, numKeys_);
+}
+
+/** Create mapped view which maintains an index permutation
+ *
+ * This is an identity view which somewhat resembles the ordered view, it
+ * maintains a secondary "map" view to contain the permutation to act as
+ * an index. The indexed view presents the same order of rows as the
+ * underlying view, but the index map is set up in such a way that binary
+ * search is possible on the keys specified. When the "unique" parameter
+ * is true, insertions which would create a duplicate key are ignored.
+ *
+ * This view is modifiable. Careful: when a row is changed in such a way
+ * that its key is the same as in another row, that other row will be
+ * deleted from the view.
+ */
+c4_View c4_View::Indexed(const c4_View& map_, const c4_View& props_,
+ bool unique_) const
+{
+ return f4_CreateIndexed(*_seq, *map_._seq, props_, unique_);
+}
+
+/** Return the index of the specified row in this view (or -1)
+ *
+ * This function can be used to "unmap" an index of a derived view back
+ * to the original underlying view.
+ */
+int c4_View::GetIndexOf(const c4_RowRef& row_) const
+{
+ c4_Cursor cursor = &row_;
+
+ return cursor._seq->RemapIndex(cursor._index, _seq);
+}
+
+/// Restrict the search range for rows
+int c4_View::RestrictSearch(const c4_RowRef& c_, int& pos_, int& count_)
+{
+ return _seq->RestrictSearch(&c_, pos_, count_) ? 0 : ~0;
+}
+
+/** Find index of the the next entry matching the specified key.
+ *
+ * Defaults to linear search, but hash- and ordered-views will use a better
+ * algorithm if possible. Only the properties present in the search key
+ * are used to determine whether a row matches the key.
+ * @return position where match occurred
+ * @retval -1 if not found
+ */
+int c4_View::Find(
+ const c4_RowRef& crit_, ///< the value to look for
+ int start_ ///< the index to start with
+ ) const
+{
+ d4_assert(start_ >= 0);
+
+ c4_Row copy = crit_; // the lazy (and slow) solution: make a copy
+
+ int count = GetSize() - start_;
+ if (_seq->RestrictSearch(&copy, start_, count)) {
+ c4_View refView = copy.Container();
+ c4_Sequence* refSeq = refView._seq;
+ d4_assert(refSeq != 0);
+
+ c4_Bytes data;
+
+ for (int j = 0; j < count; ++j) {
+ int i;
+
+ for (i = 0; i < refSeq->NumHandlers(); ++i) {
+ c4_Handler& h = refSeq->NthHandler(i); // no context issues
+
+ if (!_seq->Get(start_ + j, h.PropId(), data))
+ h.ClearBytes(data);
+
+ if (h.Compare(0, data) != 0) // always row 0
+ break;
+ }
+
+ if (i == refSeq->NumHandlers())
+ return start_ + j;
+ }
+ }
+
+ return -1;
+}
+
+/** Search for a key, using the native sort order of the view
+ * @return position where found, or where it may be inserted,
+ * this position can also be just past the last row
+ */
+int c4_View::Search(const c4_RowRef& crit_) const
+{
+ int l = -1, u = GetSize();
+ while (l + 1 != u) {
+ const int m = (l + u) >> 1;
+ if (_seq->Compare(m, &crit_) < 0)
+ //if (crit_ > (*this)[m]) // Dec 2001: see comments below
+ l = m;
+ else
+ u = m;
+ }
+
+ return u;
+}
+
+/// Return number of matching keys, and pos of first one as arg
+int c4_View::Locate(const c4_RowRef& crit_, int* pos_) const
+{
+ // Dec 2001: fixed a problem with searching of partial rows.
+ //
+ // There is an *extremely* tricky issue in here, in that the
+ // comparison operator for rows is not symmetric. So in the
+ // general case, "a == b" is not euivalent to "b == a". This
+ // is without doubt a design mistake (and should have at least
+ // been named differently).
+ //
+ // The reason is that the number of properties in both rowrefs
+ // need not be the same. Only the properties of the leftmost
+ // rowref are compared against the other one. This also applies
+ // to the other comparisons, i.e. !=, <, >, <=, and >=.
+ //
+ // All Compare calls below have been changed to use comparisons
+ // in the proper order and now use "rowref <op> rowref" syntax.
+
+ c4_Cursor curr (*(c4_Sequence*) _seq, 0); // loses const
+
+ int l = -1, u = GetSize();
+ while (l + 1 != u) {
+ curr._index = (l + u) >> 1;
+ if (crit_ > *curr)
+ l = curr._index;
+ else
+ u = curr._index;
+ }
+
+ if (pos_ != 0)
+ *pos_ = u;
+
+ // only look for more if the search hit an exact match
+ curr._index = u;
+ if (u == GetSize() || crit_ != *curr)
+ return 0;
+
+ // as Jon Bentley wrote in DDJ Apr 2000, setting l2 to -1 is better than u
+ int l2 = -1, u2 = GetSize();
+ while (l2 + 1 != u2) {
+ curr._index = (l2 + u2) >> 1;
+ if (crit_ >= *curr)
+ l2 = curr._index;
+ else
+ u2 = curr._index;
+ }
+
+ return u2 - u;
+}
+
+/// Compare two views lexicographically (rows 0..N-1).
+int c4_View::Compare(const c4_View& view_) const
+{
+ if (_seq == view_._seq)
+ return 0;
+
+ int na = GetSize();
+ int nb = view_.GetSize();
+ int i;
+
+ for (i = 0; i < na && i < nb; ++i)
+ if (GetAt(i) != view_.GetAt(i))
+ return GetAt(i) < view_.GetAt(i) ? -1 : +1;
+
+ return na == nb ? 0 : i < na ? +1 : -1;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+/** @class c4_Cursor
+ *
+ * An iterator for collections of rows (views).
+ *
+ * Cursor objects can be used to point to specific entries in a view.
+ * A cursor acts very much like a pointer to a row in a view, and is
+ * returned when taking the address of a c4_RowRef. Dereferencing
+ * a cursor leads to the original row reference again. You can construct a
+ * cursor for a c4_Row, but since such rows are not part of a collection,
+ * incrementing or decrementing these cursors is meaningless (and wrong).
+ *
+ * The usual range of pointer operations can be applied to these objects:
+ * pre/post-increment and decrement, adding or subtracting integer offsets,
+ * as well as the full range of comparison operators. If two cursors
+ * point to entries in the same view, their difference can be calculated.
+ *
+ * As with regular pointers, care must be taken to avoid running off of
+ * either end of a view (the debug build includes assertions to check this).
+ */
+
+/** @class c4_RowRef
+ *
+ * Reference to a data row, can be used on either side of an assignment.
+ *
+ * Row references are created when dereferencing a c4_Cursor or when
+ * indexing an element of a c4_View. Assignment will change the
+ * corresponding item. Rows (objects of type c4_Row) are a special
+ * case of row references, consisting of a view with exactly one item.
+ *
+ * Internally, row references are very similar to cursors, in fact they are
+ * little more than a wrapper around them. The essential difference is one
+ * of semantics: comparing row references compares contents, copying row
+ * references copies the contents, whereas cursor comparison and copying
+ * deals with the pointer to the row, not its contents.
+ */
+
+/////////////////////////////////////////////////////////////////////////////
+// c4_Row
+
+c4_Row::c4_Row ()
+ : c4_RowRef (* Allocate())
+{
+}
+
+c4_Row::c4_Row (const c4_Row& row_)
+ : c4_RowRef (* Allocate())
+{
+ operator= (row_);
+}
+
+c4_Row::c4_Row (const c4_RowRef& rowRef_)
+ : c4_RowRef (* Allocate())
+{
+ operator= (rowRef_);
+}
+
+c4_Row::~c4_Row ()
+{
+ Release(_cursor);
+}
+
+c4_Row& c4_Row::operator= (const c4_Row& row_)
+{
+ return operator= ((const c4_RowRef&) row_);
+}
+
+/// Assignment from a reference to a row.
+c4_Row& c4_Row::operator= (const c4_RowRef& rowRef_)
+{
+ d4_assert(_cursor._seq != 0);
+
+ if (_cursor != &rowRef_) {
+ d4_assert(_cursor._index == 0);
+ _cursor._seq->SetAt(0, &rowRef_);
+ }
+
+ return *this;
+}
+
+/// Adds all properties and values into this row.
+void c4_Row::ConcatRow(const c4_RowRef& rowRef_)
+{
+ d4_assert(_cursor._seq != 0);
+
+ c4_Cursor cursor = &rowRef_; // trick to access private rowRef_._cursor
+ d4_assert(cursor._seq != 0);
+
+ c4_Sequence& rhSeq = * cursor._seq;
+
+ c4_Bytes data;
+
+ for (int i = 0; i < rhSeq.NumHandlers(); ++i) {
+ c4_Handler& h = rhSeq.NthHandler(i);
+
+ h.GetBytes(cursor._index, data);
+ _cursor._seq->Set(_cursor._index, h.Property(), data);
+ }
+}
+
+c4_Row operator+ (const c4_RowRef& a_, const c4_RowRef& b_)
+{
+ c4_Row row = a_;
+ row.ConcatRow(b_);
+ return row;
+}
+
+c4_Cursor c4_Row::Allocate()
+{
+ c4_Sequence* seq = d4_new c4_HandlerSeq (0);
+ seq->IncRef();
+
+ seq->Resize(1);
+
+ return c4_Cursor (*seq, 0);
+}
+
+void c4_Row::Release(c4_Cursor row_)
+{
+ d4_assert(row_._seq != 0);
+ d4_assert(row_._index == 0);
+
+ row_._seq->DecRef();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+/** @class c4_Property
+ *
+ * Base class for the basic data types.
+ *
+ * Property objects exist independently of view, row, and storage objects.
+ * They have a name and type, and can appear in any number of views.
+ * You will normally only use derived classes, to maintain strong typing.
+ */
+
+ // This is a workaround for the fact that the initialization order of
+ // static objects is not always adequate (implementation dependent).
+ // Extremely messy solution, to allow statically declared properties.
+ //
+ // These are the only static variables in the entire Metakit core lib.
+
+ static c4_ThreadLock* sThreadLock = 0;
+ static c4_StringArray* sPropNames = 0;
+ static c4_DWordArray* sPropCounts = 0;
+
+ /// Call this to get rid of some internal datastructues (on exit)
+ void c4_Property::CleanupInternalData()
+ {
+ delete sPropNames;
+ sPropNames = 0; // race
+
+ delete sPropCounts;
+ sPropCounts = 0; // race
+
+ delete sThreadLock;
+ sThreadLock = 0; // race
+ }
+
+c4_Property::c4_Property (char type_, const char* name_)
+ : _type (type_)
+{
+ if (sThreadLock == 0)
+ sThreadLock = d4_new c4_ThreadLock;
+
+ c4_ThreadLock::Hold lock; // grabs the lock until end of scope
+
+ if (sPropNames == 0)
+ sPropNames = d4_new c4_StringArray;
+
+ if (sPropCounts == 0)
+ sPropCounts = d4_new c4_DWordArray;
+
+ c4_String temp = name_;
+
+ _id = sPropNames->GetSize();
+ while (-- _id >= 0) {
+ const char* p = sPropNames->GetAt(_id);
+ // optimize for first char case-insensitive match
+ if (((*p ^ *name_) & ~0x20) == 0 && temp.CompareNoCase(p) == 0)
+ break;
+ }
+
+ if (_id < 0) {
+ int size = sPropCounts->GetSize();
+
+ for (_id = 0; _id < size; ++_id)
+ if (sPropCounts->GetAt(_id) == 0)
+ break;
+
+ if (_id >= size) {
+ sPropCounts->SetSize(_id + 1);
+ sPropNames->SetSize(_id + 1);
+ }
+
+ sPropCounts->SetAt(_id, 0);
+ sPropNames->SetAt(_id, name_);
+ }
+
+ Refs(+1);
+}
+
+c4_Property::c4_Property (const c4_Property& prop_)
+ : _id (prop_.GetId()), _type (prop_.Type())
+{
+ c4_ThreadLock::Hold lock;
+
+ d4_assert(sPropCounts != 0);
+ d4_assert(sPropCounts->GetAt(_id) > 0);
+
+ Refs(+1);
+}
+
+c4_Property::~c4_Property ()
+{
+ c4_ThreadLock::Hold lock;
+
+ Refs(-1);
+}
+
+void c4_Property::operator= (const c4_Property& prop_)
+{
+ c4_ThreadLock::Hold lock;
+
+ prop_.Refs(+1);
+ Refs(-1);
+
+ _id = prop_.GetId();
+ _type = prop_.Type();
+}
+
+ /// Return the name of this property
+const char* c4_Property::Name() const
+{
+ c4_ThreadLock::Hold lock;
+
+ d4_assert(sPropNames != 0);
+ return sPropNames->GetAt(_id);
+}
+
+/** Adjust the reference count
+ *
+ * This is part of the implementation and shouldn't normally be called.
+ * This code is only called with the lock held, and always thread-safe.
+ */
+void c4_Property::Refs(int diff_) const
+{
+ d4_assert(diff_ == -1 || diff_ == +1);
+
+ d4_assert(sPropCounts != 0);
+ sPropCounts->ElementAt(_id) += diff_;
+
+#if q4_CHECK
+ // get rid of the cache when the last property goes away
+ static t4_i32 sPropTotals;
+
+ sPropTotals += diff_;
+ if (sPropTotals == 0)
+ CleanupInternalData();
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/viewx.cpp b/akregator/src/mk4storage/metakit/src/viewx.cpp
new file mode 100644
index 00000000..db30d14e
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/viewx.cpp
@@ -0,0 +1,857 @@
+// viewx.cpp --
+// $Id$
+// This is part of Metakit, see http://www.equi4.com/metakit/
+
+/** @file
+ * Implements c4_Sequence, c4_Reference, and c4_...Ref
+ */
+
+#include "header.h"
+#include "handler.h"
+#include "store.h"
+#include "column.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_Sequence::c4_Sequence ()
+ : _refCount (0), _dependencies (0), _propertyLimit (0), _tempBuf (0)
+{
+}
+
+c4_Sequence::~c4_Sequence ()
+{
+ d4_assert(_refCount == 0);
+
+ d4_assert(!_dependencies); // there can be no dependencies left
+
+ ClearCache();
+
+ delete _tempBuf;
+}
+
+c4_Persist* c4_Sequence::Persist() const
+{
+ return 0;
+}
+
+ /// Increment the reference count of this sequence
+void c4_Sequence::IncRef()
+{
+ ++_refCount;
+
+ d4_assert(_refCount != 0);
+}
+
+ /// Decrement the reference count, delete objects when last
+void c4_Sequence::DecRef()
+{
+ d4_assert(_refCount != 0);
+
+ if (--_refCount == 0)
+ delete this;
+}
+
+ /// Return the current reference count
+int c4_Sequence::NumRefs() const
+{
+ return _refCount;
+}
+
+ /// Compare the specified row with another one
+int c4_Sequence::Compare(int index_, c4_Cursor cursor_) const
+{
+ d4_assert(cursor_._seq != 0);
+
+ c4_Bytes data;
+
+ for (int colNum = 0; colNum < NumHandlers(); ++colNum)
+ {
+ c4_Handler& h = NthHandler(colNum);
+
+ const c4_Sequence* hc = HandlerContext(colNum);
+ int i = RemapIndex(index_, hc);
+
+ if (!cursor_._seq->Get(cursor_._index, h.PropId(), data))
+ h.ClearBytes(data);
+
+ int f = h.Compare(i, data);
+ if (f != 0)
+ return f;
+ }
+
+ return 0;
+}
+
+ /// Restrict the search range for rows
+bool c4_Sequence::RestrictSearch(c4_Cursor, int&, int&)
+{
+ return true;
+}
+
+ /// Replace the contents of a specified row
+void c4_Sequence::SetAt(int index_, c4_Cursor newElem_)
+{
+ d4_assert(newElem_._seq != 0);
+
+ c4_Bytes data;
+
+ c4_Notifier change (this);
+ if (GetDependencies())
+ change.StartSetAt(index_, newElem_);
+
+ for (int i = 0; i < newElem_._seq->NumHandlers(); ++i)
+ {
+ c4_Handler& h = newElem_._seq->NthHandler(i);
+
+ // added 06-12-1999 to do index remapping for derived seq's
+ const c4_Sequence* hc = newElem_._seq->HandlerContext(i);
+ int ri = newElem_._seq->RemapIndex(newElem_._index, hc);
+
+ h.GetBytes(ri, data);
+
+// Set(index_, cursor._seq->NthProperty(i), data);
+ int colNum = PropIndex(h.Property());
+ d4_assert(colNum >= 0);
+
+ NthHandler(colNum).Set(index_, data);
+ }
+
+ // if number of props in dest is larger after adding, clear the rest
+ // this way, new props get copied and undefined props get cleared
+ if (newElem_._seq->NumHandlers() < NumHandlers())
+ {
+ for (int j = 0; j < NumHandlers(); ++j)
+ {
+ c4_Handler& h = NthHandler(j);
+
+ // if the property does not appear in the source
+ if (newElem_._seq->PropIndex(h.PropId()) < 0)
+ {
+ h.ClearBytes(data);
+ h.Set(index_, data);
+ }
+ }
+ }
+}
+
+ /// Remap the index to an underlying view
+int c4_Sequence::RemapIndex(int index_, const c4_Sequence* seq_) const
+{
+ return seq_ == this ? index_ : -1;
+}
+
+ /// Gives access to a general purpose temporary buffer
+c4_Bytes& c4_Sequence::Buffer()
+{
+ if (_tempBuf == 0)
+ _tempBuf = d4_new c4_Bytes;
+ return *_tempBuf;
+}
+
+ // 1.8.5: extra buffer to hold returned description strings
+const char* c4_Sequence::UseTempBuffer(const char* str_)
+{
+ return strcpy((char*) Buffer().SetBuffer(strlen(str_) + 1), str_);
+}
+
+ /// Change number of rows, either by inserting or removing them
+void c4_Sequence::Resize(int newSize_, int)
+{
+ if (NumHandlers() > 0)
+ {
+ int diff = newSize_ - NumRows();
+
+ if (diff > 0)
+ {
+ c4_Row empty; // make sure this doesn't recurse, see below
+ InsertAt(NumRows(), &empty, diff);
+ }
+ else if (diff < 0)
+ RemoveAt(newSize_, - diff);
+ }
+ else // need special case to avoid recursion for c4_Row allocations
+ SetNumRows(newSize_);
+}
+
+ /// Insert one or more rows into this sequence
+void c4_Sequence::InsertAt(int index_, c4_Cursor newElem_, int count_)
+{
+ d4_assert(newElem_._seq != 0);
+
+ c4_Notifier change (this);
+ if (GetDependencies())
+ change.StartInsertAt(index_, newElem_, count_);
+
+ SetNumRows(NumRows() + count_);
+
+ c4_Bytes data;
+
+ for (int i = 0; i < newElem_._seq->NumHandlers(); ++i)
+ {
+ c4_Handler& h = newElem_._seq->NthHandler(i);
+
+ // added 06-12-1999 to do index remapping for derived seq's
+ const c4_Sequence* hc = newElem_._seq->HandlerContext(i);
+ int ri = newElem_._seq->RemapIndex(newElem_._index, hc);
+
+ h.GetBytes(ri, data);
+
+ int colNum = PropIndex(h.Property());
+ d4_assert(colNum >= 0);
+
+ if (h.Property().Type() == 'V')
+ {
+ // special treatment for subviews, insert empty, then overwrite
+ // changed 19990904 - probably fixes a long-standing limitation
+ c4_Bytes temp;
+ h.ClearBytes(temp);
+
+ c4_Handler& h2 = NthHandler(colNum);
+ h2.Insert(index_, temp, count_);
+ for (int j = 0; j < count_; ++j)
+ h2.Set(index_ + j, data);
+ }
+ else
+ NthHandler(colNum).Insert(index_, data, count_);
+ }
+
+ // if number of props in dest is larger after adding, clear the rest
+ // this way, new props get copied and undefined props get cleared
+ if (newElem_._seq->NumHandlers() < NumHandlers())
+ {
+ for (int j = 0; j < NumHandlers(); ++j)
+ {
+ c4_Handler& h = NthHandler(j);
+
+ // if the property does not appear in the source
+ if (newElem_._seq->PropIndex(h.PropId()) < 0)
+ {
+ h.ClearBytes(data);
+ h.Insert(index_, data, count_);
+ }
+ }
+ }
+}
+
+ /// Remove one or more rows from this sequence
+void c4_Sequence::RemoveAt(int index_, int count_)
+{
+ c4_Notifier change (this);
+ if (GetDependencies())
+ change.StartRemoveAt(index_, count_);
+
+ SetNumRows(NumRows() - count_);
+
+ //! careful, this does no index remapping, wrong for derived seq's
+ for (int i = 0; i < NumHandlers(); ++i)
+ NthHandler(i).Remove(index_, count_);
+}
+
+ /// Move a row to another position
+void c4_Sequence::Move(int from_, int to_)
+{
+ c4_Notifier change (this);
+ if (GetDependencies())
+ change.StartMove(from_, to_);
+
+ //! careful, this does no index remapping, wrong for derived seq's
+ for (int i = 0; i < NumHandlers(); ++i)
+ NthHandler(i).Move(from_, to_);
+}
+
+ /// Return the id of the N-th property
+int c4_Sequence::NthPropId(int index_) const
+{
+ return NthHandler(index_).PropId();
+}
+
+void c4_Sequence::ClearCache()
+{
+ if (_propertyLimit > 0)
+ {
+ delete [] _propertyMap; // property indexes may change
+ _propertyLimit = 0;
+ }
+}
+
+ /// Find the index of a property by its id
+int c4_Sequence::PropIndex(int propId_)
+{
+//! CACHING NOTE: derived views will fail if underlying view is restructured
+// still, this cache is kept, since sort will fail anyway...
+// The only safe change in these cases is adding new properties at the end.
+
+ // use the map for the fastest result once known
+ if (propId_ < _propertyLimit && _propertyMap[propId_] >= 0)
+ return _propertyMap[propId_];
+
+ // locate the property using a linear search, return if not present
+ int n = NumHandlers();
+ do
+ if (--n < 0)
+ return -1;
+ while (NthPropId(n) != propId_);
+
+ // if the map is too small, resize it (with a little slack)
+ if (propId_ >= _propertyLimit)
+ {
+ int round = (propId_ + 8) & ~0x07;
+ short* vec = d4_new short [round];
+
+ for (int i = 0; i < round; ++i)
+ vec[i] = i < _propertyLimit ? _propertyMap[i] : -1;
+
+ if (_propertyLimit > 0)
+ delete [] _propertyMap;
+
+ _propertyMap = vec;
+ _propertyLimit = round;
+ }
+
+ // we have a map, adjust the entry and return
+ return _propertyMap[propId_] = n;
+}
+
+ /// Find the index of a property, or create a new entry
+int c4_Sequence::PropIndex(const c4_Property& prop_)
+{
+ int pos = PropIndex(prop_.GetId());
+ if (pos >= 0)
+ {
+ d4_assert(NthHandler(pos).Property().Type() == prop_.Type());
+ return pos;
+ }
+
+ c4_Handler* h = CreateHandler(prop_);
+ d4_assert(h != 0);
+
+ int i = AddHandler(h);
+ if (i >= 0 && NumRows() > 0)
+ {
+ c4_Bytes data;
+ h->ClearBytes(data);
+ h->Insert(0, data, NumRows());
+ }
+
+ return i;
+}
+
+const char* c4_Sequence::Description()
+{
+ return 0;
+}
+
+int c4_Sequence::ItemSize(int index_, int propId_)
+{
+ int colNum = PropIndex(propId_);
+ return colNum >= 0 ? NthHandler(colNum).ItemSize(index_) : -1;
+}
+
+bool c4_Sequence::Get(int index_, int propId_, c4_Bytes& buf_)
+{
+ int colNum = PropIndex(propId_);
+ if (colNum < 0)
+ return false;
+
+ NthHandler(colNum).GetBytes(index_, buf_);
+ return true;
+}
+
+void c4_Sequence::Set(int index_, const c4_Property& prop_, const c4_Bytes& buf_)
+{
+ int colNum = PropIndex(prop_);
+ d4_assert(colNum >= 0);
+
+ c4_Handler& h = NthHandler(colNum);
+
+ c4_Notifier change (this);
+ if (GetDependencies())
+ change.StartSet(index_, prop_.GetId(), buf_);
+
+ if (buf_.Size())
+ h.Set(index_, buf_);
+ else
+ {
+ c4_Bytes empty;
+ h.ClearBytes(empty);
+ h.Set(index_, empty);
+ }
+}
+
+ /// Register a sequence to receive change notifications
+void c4_Sequence::Attach(c4_Sequence* child_)
+{
+ IncRef();
+
+ if (!_dependencies)
+ _dependencies = d4_new c4_Dependencies;
+
+ _dependencies->Add(child_);
+}
+
+ /// Unregister a sequence which received change notifications
+void c4_Sequence::Detach(c4_Sequence* child_)
+{
+ d4_assert(_dependencies != 0);
+
+ if (!_dependencies->Remove(child_))
+ {
+ delete _dependencies;
+ _dependencies = 0;
+ }
+
+ DecRef();
+}
+
+ /// Called just before a change is made to the sequence
+c4_Notifier* c4_Sequence::PreChange(c4_Notifier&)
+{
+ d4_assert(0); // should not be called, because it should not attach
+ return 0;
+}
+
+ /// Called after changes have been made to the sequence
+void c4_Sequence::PostChange(c4_Notifier&)
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_Reference& c4_Reference::operator= (const c4_Reference& value_)
+{
+ c4_Bytes result;
+ value_.GetData(result);
+ SetData(result);
+
+ return *this;
+}
+
+bool operator== (const c4_Reference& a_, const c4_Reference& b_)
+{
+ c4_Bytes buf1;
+ bool f1 = a_.GetData(buf1);
+
+ c4_Bytes buf2;
+ bool f2 = b_.GetData(buf2);
+
+ // if absent, fill either with zero bytes to match length
+ if (!f1)
+ buf1.SetBufferClear(buf2.Size());
+ if (!f2)
+ buf2.SetBufferClear(buf1.Size());
+
+ return buf1 == buf2;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_IntRef::operator t4_i32 () const
+{
+ c4_Bytes result;
+ if (!GetData(result))
+ return 0;
+
+ d4_assert(result.Size() == sizeof (t4_i32));
+ return *(const t4_i32*) result.Contents();
+}
+
+c4_IntRef& c4_IntRef::operator= (t4_i32 value_)
+{
+ SetData(c4_Bytes (&value_, sizeof value_));
+ return *this;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+#if !q4_TINY
+/////////////////////////////////////////////////////////////////////////////
+
+c4_LongRef::operator t4_i64 () const
+{
+ c4_Bytes result;
+ if (!GetData(result))
+ {
+ static t4_i64 zero;
+ return zero;
+ }
+
+ d4_assert(result.Size() == sizeof (t4_i64));
+ return *(const t4_i64*) result.Contents();
+}
+
+c4_LongRef& c4_LongRef::operator= (t4_i64 value_)
+{
+ SetData(c4_Bytes (&value_, sizeof value_));
+ return *this;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_FloatRef::operator double () const
+{
+ c4_Bytes result;
+ if (!GetData(result))
+ return 0;
+
+ d4_assert(result.Size() == sizeof (float));
+ return *(const float*) result.Contents();
+}
+
+c4_FloatRef& c4_FloatRef::operator= (double value_)
+{
+ float v = (float) value_; // loses precision
+ SetData(c4_Bytes (&v, sizeof v));
+ return *this;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_DoubleRef::operator double () const
+{
+ c4_Bytes result;
+ if (!GetData(result))
+ return 0;
+
+ d4_assert(result.Size() == sizeof (double));
+ return *(const double*) result.Contents();
+}
+
+c4_DoubleRef& c4_DoubleRef::operator= (double value_)
+{
+ SetData(c4_Bytes (&value_, sizeof value_));
+ return *this;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // !q4_TINY
+/////////////////////////////////////////////////////////////////////////////
+
+c4_BytesRef::operator c4_Bytes () const
+{
+ c4_Bytes result;
+ GetData(result);
+
+ // the result must immediately be used, its lifetime may be limited
+ return result;
+}
+
+c4_BytesRef& c4_BytesRef::operator= (const c4_Bytes& value_)
+{
+ SetData(value_);
+ return *this;
+}
+
+c4_Bytes c4_BytesRef::Access(t4_i32 off_, int len_) const
+{
+ c4_Bytes& buffer = _cursor._seq->Buffer();
+
+ int colNum = _cursor._seq->PropIndex(_property.GetId());
+ if (colNum >= 0)
+ {
+ c4_Handler& h = _cursor._seq->NthHandler(colNum);
+ int sz = h.ItemSize(_cursor._index);
+ if (len_ == 0 || off_ + len_ > sz)
+ len_ = sz - off_;
+
+ c4_Column* col = h.GetNthMemoCol(_cursor._index, true);
+ if (col != 0)
+ {
+
+ if (len_ > 0) {
+ col->FetchBytes(off_, len_, buffer, true);
+ return buffer;
+ }
+ }
+ else // do it the hard way for custom/mapped views (2002-03-13)
+ {
+ c4_Bytes result;
+ GetData(result);
+ d4_assert(off_ + len_ <= result.Size());
+ return c4_Bytes (result.Contents() + off_, len_, true);
+ }
+ }
+
+ return c4_Bytes ();
+}
+
+bool c4_BytesRef::Modify(const c4_Bytes& buf_, t4_i32 off_, int diff_) const
+{
+ int colNum = _cursor._seq->PropIndex(_property.GetId());
+ if (colNum >= 0)
+ {
+ c4_Handler& h = _cursor._seq->NthHandler(colNum);
+ const int n = buf_.Size();
+ const t4_i32 limit = off_ + n; // past changed bytes
+ const t4_i32 overshoot = limit - h.ItemSize(_cursor._index);
+
+ if (diff_ < overshoot)
+ diff_ = overshoot;
+
+ c4_Column* col = h.GetNthMemoCol(_cursor._index, true);
+ if (col != 0)
+ {
+ if (diff_ < 0)
+ col->Shrink(limit, - diff_);
+ else if (diff_ > 0)
+ // insert bytes in the highest possible spot
+ // if a gap is created, it will contain garbage
+ col->Grow(overshoot > 0 ? col->ColSize() :
+ diff_ > n ? off_ : limit - diff_, diff_);
+
+ col->StoreBytes(off_, buf_);
+ }
+ else // do it the hard way for custom/mapped views (2002-03-13)
+ {
+ c4_Bytes orig;
+ GetData(orig);
+
+ c4_Bytes result;
+ t4_byte* ptr = result.SetBuffer(orig.Size() + diff_);
+
+ memcpy(ptr, orig.Contents(), off_);
+ memcpy(ptr + off_, buf_.Contents(), n);
+ memcpy(ptr + off_ + n, orig.Contents() + off_, orig.Size() - off_);
+
+ SetData(result);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_StringRef::operator const char* () const
+{
+ c4_Bytes result;
+ GetData(result);
+
+ return result.Size() > 0 ? (const char*) result.Contents() : "";
+}
+
+c4_StringRef& c4_StringRef::operator= (const char* value_)
+{
+ SetData(c4_Bytes (value_, strlen(value_) + 1));
+ return *this;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_ViewRef::operator c4_View () const
+{
+ c4_Bytes result;
+ if (!GetData(result))
+ return (c4_Sequence*) 0; // resolve ambiguity
+
+ d4_assert(result.Size() == sizeof (c4_Sequence*));
+ return *(c4_Sequence* const*) result.Contents();
+}
+
+c4_ViewRef& c4_ViewRef::operator= (const c4_View& value_)
+{
+ SetData(c4_Bytes (&value_._seq, sizeof value_._seq));
+ return *this;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_Stream::~c4_Stream ()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+c4_Strategy::c4_Strategy ()
+ : _bytesFlipped (false), _failure (0),
+ _mapStart (0), _dataSize (0), _baseOffset (0), _rootPos (-1), _rootLen (-1)
+{
+}
+
+c4_Strategy::~c4_Strategy ()
+{
+ d4_assert(_mapStart == 0);
+}
+
+ /// Read a number of bytes
+int c4_Strategy::DataRead(t4_i32, void*, int)
+{
+/*
+ if (_mapStart != 0 && pos_ + length_ <= _dataSize)
+ {
+ memcpy(buffer_, _mapStart + pos_, length_);
+ return length_;
+ }
+*/
+ ++_failure;
+ return -1;
+}
+
+ /// Write a number of bytes, return true if successful
+void c4_Strategy::DataWrite(t4_i32, const void*, int)
+{
+ ++_failure;
+}
+
+ /// Flush and truncate file
+void c4_Strategy::DataCommit(t4_i32)
+{
+}
+
+ /// Override to support memory-mapped files
+void c4_Strategy::ResetFileMapping()
+{
+}
+
+ /// Report total size of the datafile
+t4_i32 c4_Strategy::FileSize()
+{
+ return _dataSize;
+}
+
+ /// Return a value to use as fresh generation counter
+t4_i32 c4_Strategy::FreshGeneration()
+{
+ return 1;
+}
+
+ /// Define the base offset where data is stored
+void c4_Strategy::SetBase(t4_i32 base_)
+{
+ t4_i32 off = base_ - _baseOffset;
+ _baseOffset = base_;
+ _dataSize -= off;
+ if (_mapStart != 0)
+ _mapStart += off;
+}
+
+/*
+ end_ is file position to start from (0 defaults to FileSize())
+
+ result is the logical end of the datafile (or -1 if no data)
+
+ This code uses a tiny state machine so all the code to read and decode
+ file marks is in one place within the loop.
+*/
+
+ /// Scan datafile head/tail markers, return logical end of data
+t4_i32 c4_Strategy::EndOfData(t4_i32 end_)
+{
+ enum { kStateAtEnd, kStateCommit, kStateHead, kStateOld, kStateDone };
+
+ t4_i32 pos = (end_ >= 0 ? end_ : FileSize()) - _baseOffset;
+ t4_i32 last = pos;
+ t4_i32 rootPos = 0;
+ t4_i32 rootLen = -1; // impossible value, flags old-style header
+ t4_byte mark [8];
+
+ for (int state = kStateAtEnd; state != kStateDone; )
+ {
+ pos -= 8;
+ if (pos + _baseOffset < 0 && state != kStateOld)
+ {
+ // bad offset, try old-style header from start of file
+ pos = - _baseOffset;
+ state = kStateOld;
+ }
+
+ if (DataRead(pos, &mark, sizeof mark) != sizeof mark)
+ return -1;
+
+ t4_i32 count = 0;
+ for (int i = 1; i < 4; ++i)
+ count = (count << 8) + mark[i];
+
+ t4_i32 offset = 0;
+ for (int j = 4; j < 8; ++j)
+ offset = (offset << 8) + mark[j];
+
+ const bool isSkipTail = mark[0] == 0x80 && count == 0 && offset > 0;
+ const bool isCommitTail = mark[0] == 0x80 && count > 0 && offset > 0;
+ const bool isHeader = (mark[0] == 'J' || mark[0] == 'L') &&
+ (mark[0] ^ mark[1]) == ('J' ^ 'L') &&
+ mark[2] == 0x1A;
+ switch (state)
+ {
+ case kStateAtEnd: // no commit tail found yet
+
+ if (isSkipTail)
+ {
+ pos -= offset;
+ last = pos;
+ }
+ else if (isCommitTail)
+ {
+ rootPos = offset;
+ rootLen = count;
+ state = kStateCommit;
+ }
+ else
+ {
+ pos = 8;
+ state = kStateOld;
+ }
+ break;
+
+ case kStateCommit: // commit tail must be preceded by skip tail
+
+ if (!isSkipTail)
+ return -1;
+ pos -= offset - 8;
+ state = kStateHead;
+ break;
+
+ case kStateHead: // fetch the header
+
+ if (!isHeader)
+ {
+ pos = 8;
+ state = kStateOld;
+ }
+ else
+ {
+ state = kStateDone;
+ }
+ break;
+
+ case kStateOld: // old format, look for header in first 4 Kb
+
+ if (isHeader && mark[3] == 0x80)
+ {
+ d4_assert(rootPos == 0);
+ for (int k = 8; --k >= 4; ) // old header is little-endian
+ rootPos = (rootPos << 8) + mark[k];
+ state = kStateDone;
+ }
+ else
+ {
+ pos += 16;
+ if (pos > 4096)
+ return -1;
+ }
+ break;
+ }
+ }
+
+ last += _baseOffset; // all seeks were relative to current offset
+
+ if (end_ >= 0) // if end was specified, then adjust this strategy object
+ {
+ _baseOffset += pos;
+ d4_assert(_baseOffset >= 0);
+ if (_mapStart != 0)
+ {
+ _mapStart += pos;
+ _dataSize -= pos;
+ }
+
+ _rootPos = rootPos;
+ _rootLen = rootLen;
+ }
+
+ d4_assert(mark[0] == 'J' || mark[1] == 'J');
+ _bytesFlipped = (char) *(const short*) mark != 'J';
+
+ return last;
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/akregator/src/mk4storage/metakit/src/win.h b/akregator/src/mk4storage/metakit/src/win.h
new file mode 100644
index 00000000..d27eb244
--- /dev/null
+++ b/akregator/src/mk4storage/metakit/src/win.h
@@ -0,0 +1,33 @@
+// win.h --
+// $Id$
+// This is part of Metakit, the homepage is http://www.equi4.com/metakit/
+
+/** @file
+ * Configuration header for Windows builds
+ */
+
+#if defined (_MSDOS)
+#define q4_DOS 1
+#endif
+
+#if defined (_WINDOWS)
+#define q4_WIN 1
+#endif
+
+#if defined (_WIN32)
+#define q4_WIN32 1
+#endif
+
+#if defined (_WIN32_WCE) // check for Win CE
+#define q4_WINCE 1
+#define q4_WIN32 1
+#endif
+
+#if q4_WIN32 // WIN32 implies WIN
+#undef q4_WIN
+#define q4_WIN 1
+#endif
+
+#if q4_WIN // WIN implies not DOS, even for Win3
+#undef q4_DOS
+#endif