summaryrefslogtreecommitdiffstats
path: root/akregator/src/mk4storage/metakit/src/store.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'akregator/src/mk4storage/metakit/src/store.cpp')
-rw-r--r--akregator/src/mk4storage/metakit/src/store.cpp587
1 files changed, 587 insertions, 0 deletions
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;
+}
+
+/////////////////////////////////////////////////////////////////////////////