summaryrefslogtreecommitdiffstats
path: root/rdf/base/nsInMemoryDataSource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'rdf/base/nsInMemoryDataSource.cpp')
-rw-r--r--rdf/base/nsInMemoryDataSource.cpp1964
1 files changed, 1964 insertions, 0 deletions
diff --git a/rdf/base/nsInMemoryDataSource.cpp b/rdf/base/nsInMemoryDataSource.cpp
new file mode 100644
index 000000000..9bdd6b4fb
--- /dev/null
+++ b/rdf/base/nsInMemoryDataSource.cpp
@@ -0,0 +1,1964 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ *
+ * This Original Code has been modified by IBM Corporation.
+ * Modifications made by IBM described herein are
+ * Copyright (c) International Business Machines
+ * Corporation, 2000
+ *
+ * Modifications to Mozilla code or documentation
+ * identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
+ * use in OS2
+ */
+
+/*
+
+ Implementation for an in-memory RDF data store.
+
+ TO DO
+
+ 1) Instrument this code to gather space and time performance
+ characteristics.
+
+ 2) Optimize lookups for datasources which have a small number
+ of properties + fanning out to a large number of targets.
+
+ 3) Complete implementation of thread-safety; specifically, make
+ assertions be reference counted objects (so that a cursor can
+ still refer to an assertion that gets removed from the graph).
+
+ */
+
+#include "nsAgg.h"
+#include "nsCOMPtr.h"
+#include "nscore.h"
+#include "nsArrayEnumerator.h"
+#include "nsIOutputStream.h"
+#include "nsIRDFDataSource.h"
+#include "nsIRDFLiteral.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFObserver.h"
+#include "nsIRDFInMemoryDataSource.h"
+#include "nsIRDFPropagatableDataSource.h"
+#include "nsIRDFPurgeableDataSource.h"
+#include "nsIRDFService.h"
+#include "nsIServiceManager.h"
+#include "nsCOMArray.h"
+#include "nsEnumeratorUtils.h"
+#include "nsTArray.h"
+#include "nsCRT.h"
+#include "nsRDFCID.h"
+#include "nsRDFBaseDataSources.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsXPIDLString.h"
+#include "rdfutil.h"
+#include "PLDHashTable.h"
+#include "plstr.h"
+#include "mozilla/Logging.h"
+#include "rdf.h"
+
+#include "rdfIDataSource.h"
+#include "rdfITripleVisitor.h"
+
+using mozilla::LogLevel;
+
+// This struct is used as the slot value in the forward and reverse
+// arcs hash tables.
+//
+// Assertion objects are reference counted, because each Assertion's
+// ownership is shared between the datasource and any enumerators that
+// are currently iterating over the datasource.
+//
+class Assertion
+{
+public:
+ Assertion(nsIRDFResource* aSource, // normal assertion
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue);
+ explicit Assertion(nsIRDFResource* aSource); // PLDHashTable assertion variant
+
+private:
+ ~Assertion();
+
+public:
+ void AddRef() {
+ if (mRefCnt == UINT16_MAX) {
+ NS_WARNING("refcount overflow, leaking Assertion");
+ return;
+ }
+ ++mRefCnt;
+ }
+
+ void Release() {
+ if (mRefCnt == UINT16_MAX) {
+ NS_WARNING("refcount overflow, leaking Assertion");
+ return;
+ }
+ if (--mRefCnt == 0)
+ delete this;
+ }
+
+ // For nsIRDFPurgeableDataSource
+ inline void Mark() { u.as.mMarked = true; }
+ inline bool IsMarked() { return u.as.mMarked; }
+ inline void Unmark() { u.as.mMarked = false; }
+
+ // public for now, because I'm too lazy to go thru and clean this up.
+
+ // These are shared between hash/as (see the union below)
+ nsIRDFResource* mSource;
+ Assertion* mNext;
+
+ union
+ {
+ struct hash
+ {
+ PLDHashTable* mPropertyHash;
+ } hash;
+ struct as
+ {
+ nsIRDFResource* mProperty;
+ nsIRDFNode* mTarget;
+ Assertion* mInvNext;
+ // make sure bool are final elements
+ bool mTruthValue;
+ bool mMarked;
+ } as;
+ } u;
+
+ // also shared between hash/as (see the union above)
+ // but placed after union definition to ensure that
+ // all 32-bit entries are long aligned
+ uint16_t mRefCnt;
+ bool mHashEntry;
+};
+
+
+struct Entry : PLDHashEntryHdr {
+ nsIRDFNode* mNode;
+ Assertion* mAssertions;
+};
+
+
+Assertion::Assertion(nsIRDFResource* aSource)
+ : mSource(aSource),
+ mNext(nullptr),
+ mRefCnt(0),
+ mHashEntry(true)
+{
+ MOZ_COUNT_CTOR(Assertion);
+
+ NS_ADDREF(mSource);
+
+ u.hash.mPropertyHash =
+ new PLDHashTable(PLDHashTable::StubOps(), sizeof(Entry));
+}
+
+Assertion::Assertion(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue)
+ : mSource(aSource),
+ mNext(nullptr),
+ mRefCnt(0),
+ mHashEntry(false)
+{
+ MOZ_COUNT_CTOR(Assertion);
+
+ u.as.mProperty = aProperty;
+ u.as.mTarget = aTarget;
+
+ NS_ADDREF(mSource);
+ NS_ADDREF(u.as.mProperty);
+ NS_ADDREF(u.as.mTarget);
+
+ u.as.mInvNext = nullptr;
+ u.as.mTruthValue = aTruthValue;
+ u.as.mMarked = false;
+}
+
+Assertion::~Assertion()
+{
+ if (mHashEntry && u.hash.mPropertyHash) {
+ for (auto i = u.hash.mPropertyHash->Iter(); !i.Done(); i.Next()) {
+ auto entry = static_cast<Entry*>(i.Get());
+ Assertion* as = entry->mAssertions;
+ while (as) {
+ Assertion* doomed = as;
+ as = as->mNext;
+
+ // Unlink, and release the datasource's reference.
+ doomed->mNext = doomed->u.as.mInvNext = nullptr;
+ doomed->Release();
+ }
+ }
+ delete u.hash.mPropertyHash;
+ u.hash.mPropertyHash = nullptr;
+ }
+
+ MOZ_COUNT_DTOR(Assertion);
+#ifdef DEBUG_REFS
+ --gInstanceCount;
+ fprintf(stdout, "%d - RDF: Assertion\n", gInstanceCount);
+#endif
+
+ NS_RELEASE(mSource);
+ if (!mHashEntry)
+ {
+ NS_RELEASE(u.as.mProperty);
+ NS_RELEASE(u.as.mTarget);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// InMemoryDataSource
+class InMemoryArcsEnumeratorImpl;
+class InMemoryAssertionEnumeratorImpl;
+class InMemoryResourceEnumeratorImpl;
+
+class InMemoryDataSource : public nsIRDFDataSource,
+ public nsIRDFInMemoryDataSource,
+ public nsIRDFPropagatableDataSource,
+ public nsIRDFPurgeableDataSource,
+ public rdfIDataSource
+{
+protected:
+ // These hash tables are keyed on pointers to nsIRDFResource
+ // objects (the nsIRDFService ensures that there is only ever one
+ // nsIRDFResource object per unique URI). The value of an entry is
+ // an Assertion struct, which is a linked list of (subject
+ // predicate object) triples.
+ PLDHashTable mForwardArcs;
+ PLDHashTable mReverseArcs;
+
+ nsCOMArray<nsIRDFObserver> mObservers;
+ uint32_t mNumObservers;
+
+ // VisitFoo needs to block writes, [Un]Assert only allowed
+ // during mReadCount == 0
+ uint32_t mReadCount;
+
+ friend class InMemoryArcsEnumeratorImpl;
+ friend class InMemoryAssertionEnumeratorImpl;
+ friend class InMemoryResourceEnumeratorImpl; // b/c it needs to enumerate mForwardArcs
+
+ // Thread-safe writer implementation methods.
+ nsresult
+ LockedAssert(nsIRDFResource* source,
+ nsIRDFResource* property,
+ nsIRDFNode* target,
+ bool tv);
+
+ nsresult
+ LockedUnassert(nsIRDFResource* source,
+ nsIRDFResource* property,
+ nsIRDFNode* target);
+
+ explicit InMemoryDataSource(nsISupports* aOuter);
+ virtual ~InMemoryDataSource();
+
+ friend nsresult
+ NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResult);
+
+public:
+ NS_DECL_CYCLE_COLLECTING_AGGREGATED
+ NS_DECL_AGGREGATED_CYCLE_COLLECTION_CLASS(InMemoryDataSource)
+
+ // nsIRDFDataSource methods
+ NS_DECL_NSIRDFDATASOURCE
+
+ // nsIRDFInMemoryDataSource methods
+ NS_DECL_NSIRDFINMEMORYDATASOURCE
+
+ // nsIRDFPropagatableDataSource methods
+ NS_DECL_NSIRDFPROPAGATABLEDATASOURCE
+
+ // nsIRDFPurgeableDataSource methods
+ NS_DECL_NSIRDFPURGEABLEDATASOURCE
+
+ // rdfIDataSource methods
+ NS_DECL_RDFIDATASOURCE
+
+protected:
+ struct SweepInfo {
+ Assertion* mUnassertList;
+ PLDHashTable* mReverseArcs;
+ };
+
+ static void
+ SweepForwardArcsEntries(PLDHashTable* aTable, SweepInfo* aArg);
+
+public:
+ // Implementation methods
+ Assertion*
+ GetForwardArcs(nsIRDFResource* u) {
+ PLDHashEntryHdr* hdr = mForwardArcs.Search(u);
+ return hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ }
+
+ Assertion*
+ GetReverseArcs(nsIRDFNode* v) {
+ PLDHashEntryHdr* hdr = mReverseArcs.Search(v);
+ return hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ }
+
+ void
+ SetForwardArcs(nsIRDFResource* u, Assertion* as) {
+ if (as) {
+ auto entry =
+ static_cast<Entry*>(mForwardArcs.Add(u, mozilla::fallible));
+ if (entry) {
+ entry->mNode = u;
+ entry->mAssertions = as;
+ }
+ }
+ else {
+ mForwardArcs.Remove(u);
+ }
+ }
+
+ void
+ SetReverseArcs(nsIRDFNode* v, Assertion* as) {
+ if (as) {
+ auto entry =
+ static_cast<Entry*>(mReverseArcs.Add(v, mozilla::fallible));
+ if (entry) {
+ entry->mNode = v;
+ entry->mAssertions = as;
+ }
+ }
+ else {
+ mReverseArcs.Remove(v);
+ }
+ }
+
+ void
+ LogOperation(const char* aOperation,
+ nsIRDFResource* asource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue = true);
+
+ bool mPropagateChanges;
+
+private:
+ static mozilla::LazyLogModule gLog;
+};
+
+mozilla::LazyLogModule InMemoryDataSource::gLog("InMemoryDataSource");
+
+//----------------------------------------------------------------------
+//
+// InMemoryAssertionEnumeratorImpl
+//
+
+/**
+ * InMemoryAssertionEnumeratorImpl
+ */
+class InMemoryAssertionEnumeratorImpl : public nsISimpleEnumerator
+{
+private:
+ InMemoryDataSource* mDataSource;
+ nsIRDFResource* mSource;
+ nsIRDFResource* mProperty;
+ nsIRDFNode* mTarget;
+ nsIRDFNode* mValue;
+ bool mTruthValue;
+ Assertion* mNextAssertion;
+
+ virtual ~InMemoryAssertionEnumeratorImpl();
+
+public:
+ InMemoryAssertionEnumeratorImpl(InMemoryDataSource* aDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue);
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator interface
+ NS_DECL_NSISIMPLEENUMERATOR
+};
+
+////////////////////////////////////////////////////////////////////////
+
+
+InMemoryAssertionEnumeratorImpl::InMemoryAssertionEnumeratorImpl(
+ InMemoryDataSource* aDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue)
+ : mDataSource(aDataSource),
+ mSource(aSource),
+ mProperty(aProperty),
+ mTarget(aTarget),
+ mValue(nullptr),
+ mTruthValue(aTruthValue),
+ mNextAssertion(nullptr)
+{
+ NS_ADDREF(mDataSource);
+ NS_IF_ADDREF(mSource);
+ NS_ADDREF(mProperty);
+ NS_IF_ADDREF(mTarget);
+
+ if (mSource) {
+ mNextAssertion = mDataSource->GetForwardArcs(mSource);
+
+ if (mNextAssertion && mNextAssertion->mHashEntry) {
+ // its our magical HASH_ENTRY forward hash for assertions
+ PLDHashEntryHdr* hdr =
+ mNextAssertion->u.hash.mPropertyHash->Search(aProperty);
+ mNextAssertion =
+ hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ }
+ }
+ else {
+ mNextAssertion = mDataSource->GetReverseArcs(mTarget);
+ }
+
+ // Add an owning reference from the enumerator
+ if (mNextAssertion)
+ mNextAssertion->AddRef();
+}
+
+InMemoryAssertionEnumeratorImpl::~InMemoryAssertionEnumeratorImpl()
+{
+#ifdef DEBUG_REFS
+ --gInstanceCount;
+ fprintf(stdout, "%d - RDF: InMemoryAssertionEnumeratorImpl\n", gInstanceCount);
+#endif
+
+ if (mNextAssertion)
+ mNextAssertion->Release();
+
+ NS_IF_RELEASE(mDataSource);
+ NS_IF_RELEASE(mSource);
+ NS_IF_RELEASE(mProperty);
+ NS_IF_RELEASE(mTarget);
+ NS_IF_RELEASE(mValue);
+}
+
+NS_IMPL_ADDREF(InMemoryAssertionEnumeratorImpl)
+NS_IMPL_RELEASE(InMemoryAssertionEnumeratorImpl)
+NS_IMPL_QUERY_INTERFACE(InMemoryAssertionEnumeratorImpl, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+InMemoryAssertionEnumeratorImpl::HasMoreElements(bool* aResult)
+{
+ if (mValue) {
+ *aResult = true;
+ return NS_OK;
+ }
+
+ while (mNextAssertion) {
+ bool foundIt = false;
+ if ((mProperty == mNextAssertion->u.as.mProperty) &&
+ (mTruthValue == mNextAssertion->u.as.mTruthValue)) {
+ if (mSource) {
+ mValue = mNextAssertion->u.as.mTarget;
+ NS_ADDREF(mValue);
+ }
+ else {
+ mValue = mNextAssertion->mSource;
+ NS_ADDREF(mValue);
+ }
+ foundIt = true;
+ }
+
+ // Remember the last assertion we were holding on to
+ Assertion* as = mNextAssertion;
+
+ // iterate
+ mNextAssertion = (mSource) ? mNextAssertion->mNext : mNextAssertion->u.as.mInvNext;
+
+ // grab an owning reference from the enumerator to the next assertion
+ if (mNextAssertion)
+ mNextAssertion->AddRef();
+
+ // ...and release the reference from the enumerator to the old one.
+ as->Release();
+
+ if (foundIt) {
+ *aResult = true;
+ return NS_OK;
+ }
+ }
+
+ *aResult = false;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+InMemoryAssertionEnumeratorImpl::GetNext(nsISupports** aResult)
+{
+ nsresult rv;
+
+ bool hasMore;
+ rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) return rv;
+
+ if (! hasMore)
+ return NS_ERROR_UNEXPECTED;
+
+ // Don't AddRef: we "transfer" ownership to the caller
+ *aResult = mValue;
+ mValue = nullptr;
+
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+
+/**
+ * This class is a little bit bizarre in that it implements both the
+ * <tt>nsIRDFArcsOutCursor</tt> and <tt>nsIRDFArcsInCursor</tt> interfaces.
+ * Because the structure of the in-memory graph is pretty flexible, it's
+ * fairly easy to parameterize this class. The only funky thing to watch
+ * out for is the multiple inheritance clashes.
+ */
+
+class InMemoryArcsEnumeratorImpl : public nsISimpleEnumerator
+{
+private:
+ InMemoryDataSource* mDataSource;
+ nsIRDFResource* mSource;
+ nsIRDFNode* mTarget;
+ AutoTArray<nsCOMPtr<nsIRDFResource>, 8> mAlreadyReturned;
+ nsIRDFResource* mCurrent;
+ Assertion* mAssertion;
+ nsCOMArray<nsIRDFNode>* mHashArcs;
+
+ virtual ~InMemoryArcsEnumeratorImpl();
+
+public:
+ InMemoryArcsEnumeratorImpl(InMemoryDataSource* aDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFNode* aTarget);
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator interface
+ NS_DECL_NSISIMPLEENUMERATOR
+};
+
+
+InMemoryArcsEnumeratorImpl::InMemoryArcsEnumeratorImpl(InMemoryDataSource* aDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFNode* aTarget)
+ : mDataSource(aDataSource),
+ mSource(aSource),
+ mTarget(aTarget),
+ mCurrent(nullptr),
+ mHashArcs(nullptr)
+{
+ NS_ADDREF(mDataSource);
+ NS_IF_ADDREF(mSource);
+ NS_IF_ADDREF(mTarget);
+
+ if (mSource) {
+ // cast okay because it's a closed system
+ mAssertion = mDataSource->GetForwardArcs(mSource);
+
+ if (mAssertion && mAssertion->mHashEntry) {
+ // its our magical HASH_ENTRY forward hash for assertions
+ mHashArcs = new nsCOMArray<nsIRDFNode>();
+ for (auto i = mAssertion->u.hash.mPropertyHash->Iter();
+ !i.Done();
+ i.Next()) {
+ auto entry = static_cast<Entry*>(i.Get());
+ mHashArcs->AppendElement(entry->mNode);
+ }
+ mAssertion = nullptr;
+ }
+ }
+ else {
+ mAssertion = mDataSource->GetReverseArcs(mTarget);
+ }
+}
+
+InMemoryArcsEnumeratorImpl::~InMemoryArcsEnumeratorImpl()
+{
+#ifdef DEBUG_REFS
+ --gInstanceCount;
+ fprintf(stdout, "%d - RDF: InMemoryArcsEnumeratorImpl\n", gInstanceCount);
+#endif
+
+ NS_RELEASE(mDataSource);
+ NS_IF_RELEASE(mSource);
+ NS_IF_RELEASE(mTarget);
+ NS_IF_RELEASE(mCurrent);
+ delete mHashArcs;
+}
+
+NS_IMPL_ADDREF(InMemoryArcsEnumeratorImpl)
+NS_IMPL_RELEASE(InMemoryArcsEnumeratorImpl)
+NS_IMPL_QUERY_INTERFACE(InMemoryArcsEnumeratorImpl, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+InMemoryArcsEnumeratorImpl::HasMoreElements(bool* aResult)
+{
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mCurrent) {
+ *aResult = true;
+ return NS_OK;
+ }
+
+ if (mHashArcs) {
+ if (!mHashArcs->IsEmpty()) {
+ const uint32_t last = mHashArcs->Length() - 1;
+ nsCOMPtr<nsIRDFResource> tmp(do_QueryInterface(mHashArcs->ObjectAt(last)));
+ tmp.forget(&mCurrent);
+ mHashArcs->RemoveElementAt(last);
+ *aResult = true;
+ return NS_OK;
+ }
+ }
+ else
+ while (mAssertion) {
+ nsIRDFResource* next = mAssertion->u.as.mProperty;
+
+ // "next" is the property arc we are tentatively going to return
+ // in a subsequent GetNext() call. It is important to do two
+ // things, however, before that can happen:
+ // 1) Make sure it's not an arc we've already returned.
+ // 2) Make sure that |mAssertion| is not left pointing to
+ // another assertion that has the same property as this one.
+ // The first is a practical concern; the second a defense against
+ // an obscure crash and other erratic behavior. To ensure the
+ // second condition, skip down the chain until we find the next
+ // assertion with a property that doesn't match the current one.
+ // (All these assertions would be skipped via mAlreadyReturned
+ // checks anyways; this is even a bit faster.)
+
+ do {
+ mAssertion = (mSource ? mAssertion->mNext :
+ mAssertion->u.as.mInvNext);
+ }
+ while (mAssertion && (next == mAssertion->u.as.mProperty));
+
+ bool alreadyReturned = false;
+ for (int32_t i = mAlreadyReturned.Length() - 1; i >= 0; --i) {
+ if (mAlreadyReturned[i] == next) {
+ alreadyReturned = true;
+ break;
+ }
+ }
+
+ if (! alreadyReturned) {
+ mCurrent = next;
+ NS_ADDREF(mCurrent);
+ *aResult = true;
+ return NS_OK;
+ }
+ }
+
+ *aResult = false;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+InMemoryArcsEnumeratorImpl::GetNext(nsISupports** aResult)
+{
+ nsresult rv;
+
+ bool hasMore;
+ rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) return rv;
+
+ if (! hasMore)
+ return NS_ERROR_UNEXPECTED;
+
+ // Add this to the set of things we've already returned so that we
+ // can ensure uniqueness
+ mAlreadyReturned.AppendElement(mCurrent);
+
+ // Don't AddRef: we "transfer" ownership to the caller
+ *aResult = mCurrent;
+ mCurrent = nullptr;
+
+ return NS_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// InMemoryDataSource
+
+nsresult
+NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResult)
+{
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+ *aResult = nullptr;
+
+ if (aOuter && !aIID.Equals(NS_GET_IID(nsISupports))) {
+ NS_ERROR("aggregation requires nsISupports");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ InMemoryDataSource* datasource = new InMemoryDataSource(aOuter);
+ NS_ADDREF(datasource);
+
+ datasource->fAggregated.AddRef();
+ nsresult rv = datasource->AggregatedQueryInterface(aIID, aResult); // This'll AddRef()
+ datasource->fAggregated.Release();
+
+ NS_RELEASE(datasource);
+ return rv;
+}
+
+
+InMemoryDataSource::InMemoryDataSource(nsISupports* aOuter)
+ : mForwardArcs(PLDHashTable::StubOps(), sizeof(Entry))
+ , mReverseArcs(PLDHashTable::StubOps(), sizeof(Entry))
+ , mNumObservers(0)
+ , mReadCount(0)
+{
+ NS_INIT_AGGREGATED(aOuter);
+
+ mPropagateChanges = true;
+ MOZ_COUNT_CTOR(InMemoryDataSource);
+}
+
+
+InMemoryDataSource::~InMemoryDataSource()
+{
+#ifdef DEBUG_REFS
+ --gInstanceCount;
+ fprintf(stdout, "%d - RDF: InMemoryDataSource\n", gInstanceCount);
+#endif
+
+ if (mForwardArcs.EntryCount() > 0) {
+ // This'll release all of the Assertion objects that are
+ // associated with this data source. We only need to do this
+ // for the forward arcs, because the reverse arcs table
+ // indexes the exact same set of resources.
+ for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<Entry*>(iter.Get());
+ Assertion* as = entry->mAssertions;
+ while (as) {
+ Assertion* doomed = as;
+ as = as->mNext;
+
+ // Unlink, and release the datasource's reference.
+ doomed->mNext = doomed->u.as.mInvNext = nullptr;
+ doomed->Release();
+ }
+ }
+ }
+
+ MOZ_LOG(gLog, LogLevel::Debug,
+ ("InMemoryDataSource(%p): destroyed.", this));
+
+ MOZ_COUNT_DTOR(InMemoryDataSource);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(InMemoryDataSource)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(InMemoryDataSource)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_AGGREGATED(InMemoryDataSource)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservers)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTING_AGGREGATED(InMemoryDataSource)
+NS_INTERFACE_MAP_BEGIN_AGGREGATED(InMemoryDataSource)
+ NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION_AGGREGATED(InMemoryDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFInMemoryDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFPropagatableDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFPurgeableDataSource)
+ NS_INTERFACE_MAP_ENTRY(rdfIDataSource)
+NS_INTERFACE_MAP_END
+
+////////////////////////////////////////////////////////////////////////
+
+
+void
+InMemoryDataSource::LogOperation(const char* aOperation,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue)
+{
+ if (! MOZ_LOG_TEST(gLog, LogLevel::Debug))
+ return;
+
+ nsXPIDLCString uri;
+ aSource->GetValue(getter_Copies(uri));
+ PR_LogPrint
+ ("InMemoryDataSource(%p): %s", this, aOperation);
+
+ PR_LogPrint
+ (" [(%p)%s]--", aSource, (const char*) uri);
+
+ aProperty->GetValue(getter_Copies(uri));
+
+ char tv = (aTruthValue ? '-' : '!');
+ PR_LogPrint
+ (" --%c[(%p)%s]--", tv, aProperty, (const char*) uri);
+
+ nsCOMPtr<nsIRDFResource> resource;
+ nsCOMPtr<nsIRDFLiteral> literal;
+
+ if ((resource = do_QueryInterface(aTarget)) != nullptr) {
+ resource->GetValue(getter_Copies(uri));
+ PR_LogPrint
+ (" -->[(%p)%s]", aTarget, (const char*) uri);
+ }
+ else if ((literal = do_QueryInterface(aTarget)) != nullptr) {
+ nsXPIDLString value;
+ literal->GetValue(getter_Copies(value));
+ nsAutoString valueStr(value);
+ char* valueCStr = ToNewCString(valueStr);
+
+ PR_LogPrint
+ (" -->(\"%s\")\n", valueCStr);
+
+ free(valueCStr);
+ }
+ else {
+ PR_LogPrint
+ (" -->(unknown-type)\n");
+ }
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::GetURI(char* *uri)
+{
+ NS_PRECONDITION(uri != nullptr, "null ptr");
+ if (! uri)
+ return NS_ERROR_NULL_POINTER;
+
+ *uri = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetSource(nsIRDFResource* property,
+ nsIRDFNode* target,
+ bool tv,
+ nsIRDFResource** source)
+{
+ NS_PRECONDITION(source != nullptr, "null ptr");
+ if (! source)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(property != nullptr, "null ptr");
+ if (! property)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(target != nullptr, "null ptr");
+ if (! target)
+ return NS_ERROR_NULL_POINTER;
+
+ for (Assertion* as = GetReverseArcs(target); as; as = as->u.as.mInvNext) {
+ if ((property == as->u.as.mProperty) && (tv == as->u.as.mTruthValue)) {
+ *source = as->mSource;
+ NS_ADDREF(*source);
+ return NS_OK;
+ }
+ }
+ *source = nullptr;
+ return NS_RDF_NO_VALUE;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetTarget(nsIRDFResource* source,
+ nsIRDFResource* property,
+ bool tv,
+ nsIRDFNode** target)
+{
+ NS_PRECONDITION(source != nullptr, "null ptr");
+ if (! source)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(property != nullptr, "null ptr");
+ if (! property)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(target != nullptr, "null ptr");
+ if (! target)
+ return NS_ERROR_NULL_POINTER;
+
+ Assertion *as = GetForwardArcs(source);
+ if (as && as->mHashEntry) {
+ PLDHashEntryHdr* hdr = as->u.hash.mPropertyHash->Search(property);
+ Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ while (val) {
+ if (tv == val->u.as.mTruthValue) {
+ *target = val->u.as.mTarget;
+ NS_IF_ADDREF(*target);
+ return NS_OK;
+ }
+ val = val->mNext;
+ }
+ }
+ else
+ for (; as != nullptr; as = as->mNext) {
+ if ((property == as->u.as.mProperty) && (tv == (as->u.as.mTruthValue))) {
+ *target = as->u.as.mTarget;
+ NS_ADDREF(*target);
+ return NS_OK;
+ }
+ }
+
+ // If we get here, then there was no target with for the specified
+ // property & truth value.
+ *target = nullptr;
+ return NS_RDF_NO_VALUE;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::HasAssertion(nsIRDFResource* source,
+ nsIRDFResource* property,
+ nsIRDFNode* target,
+ bool tv,
+ bool* hasAssertion)
+{
+ if (! source)
+ return NS_ERROR_NULL_POINTER;
+
+ if (! property)
+ return NS_ERROR_NULL_POINTER;
+
+ if (! target)
+ return NS_ERROR_NULL_POINTER;
+
+ Assertion *as = GetForwardArcs(source);
+ if (as && as->mHashEntry) {
+ PLDHashEntryHdr* hdr = as->u.hash.mPropertyHash->Search(property);
+ Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ while (val) {
+ if ((val->u.as.mTarget == target) && (tv == (val->u.as.mTruthValue))) {
+ *hasAssertion = true;
+ return NS_OK;
+ }
+ val = val->mNext;
+ }
+ }
+ else
+ for (; as != nullptr; as = as->mNext) {
+ // check target first as its most unique
+ if (target != as->u.as.mTarget)
+ continue;
+
+ if (property != as->u.as.mProperty)
+ continue;
+
+ if (tv != (as->u.as.mTruthValue))
+ continue;
+
+ // found it!
+ *hasAssertion = true;
+ return NS_OK;
+ }
+
+ // If we get here, we couldn't find the assertion
+ *hasAssertion = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetSources(nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue,
+ nsISimpleEnumerator** aResult)
+{
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ InMemoryAssertionEnumeratorImpl* result =
+ new InMemoryAssertionEnumeratorImpl(this, nullptr, aProperty,
+ aTarget, aTruthValue);
+
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(result);
+ *aResult = result;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetTargets(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ bool aTruthValue,
+ nsISimpleEnumerator** aResult)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aResult != nullptr, "null ptr");
+ if (! aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ InMemoryAssertionEnumeratorImpl* result =
+ new InMemoryAssertionEnumeratorImpl(this, aSource, aProperty,
+ nullptr, aTruthValue);
+
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(result);
+ *aResult = result;
+
+ return NS_OK;
+}
+
+
+nsresult
+InMemoryDataSource::LockedAssert(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue)
+{
+ LogOperation("ASSERT", aSource, aProperty, aTarget, aTruthValue);
+
+ Assertion* next = GetForwardArcs(aSource);
+ Assertion* prev = next;
+ Assertion* as = nullptr;
+
+ bool haveHash = (next) ? next->mHashEntry : false;
+ if (haveHash) {
+ PLDHashEntryHdr* hdr = next->u.hash.mPropertyHash->Search(aProperty);
+ Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ while (val) {
+ if (val->u.as.mTarget == aTarget) {
+ // Wow, we already had the assertion. Make sure that the
+ // truth values are correct and bail.
+ val->u.as.mTruthValue = aTruthValue;
+ return NS_OK;
+ }
+ val = val->mNext;
+ }
+ }
+ else
+ {
+ while (next) {
+ // check target first as its most unique
+ if (aTarget == next->u.as.mTarget) {
+ if (aProperty == next->u.as.mProperty) {
+ // Wow, we already had the assertion. Make sure that the
+ // truth values are correct and bail.
+ next->u.as.mTruthValue = aTruthValue;
+ return NS_OK;
+ }
+ }
+
+ prev = next;
+ next = next->mNext;
+ }
+ }
+
+ as = new Assertion(aSource, aProperty, aTarget, aTruthValue);
+ if (! as)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Add the datasource's owning reference.
+ as->AddRef();
+
+ if (haveHash)
+ {
+ PLDHashEntryHdr* hdr = next->u.hash.mPropertyHash->Search(aProperty);
+ Assertion *asRef =
+ hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ if (asRef)
+ {
+ as->mNext = asRef->mNext;
+ asRef->mNext = as;
+ }
+ else
+ {
+ hdr = next->u.hash.mPropertyHash->Add(aProperty, mozilla::fallible);
+ if (hdr)
+ {
+ Entry* entry = static_cast<Entry*>(hdr);
+ entry->mNode = aProperty;
+ entry->mAssertions = as;
+ }
+ }
+ }
+ else
+ {
+ // Link it in to the "forward arcs" table
+ if (!prev) {
+ SetForwardArcs(aSource, as);
+ } else {
+ prev->mNext = as;
+ }
+ }
+
+ // Link it in to the "reverse arcs" table
+
+ next = GetReverseArcs(aTarget);
+ as->u.as.mInvNext = next;
+ next = as;
+ SetReverseArcs(aTarget, next);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::Assert(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mReadCount) {
+ NS_WARNING("Writing to InMemoryDataSource during read\n");
+ return NS_RDF_ASSERTION_REJECTED;
+ }
+
+ nsresult rv;
+ rv = LockedAssert(aSource, aProperty, aTarget, aTruthValue);
+ if (NS_FAILED(rv)) return rv;
+
+ // notify observers
+ for (int32_t i = (int32_t)mNumObservers - 1; mPropagateChanges && i >= 0; --i) {
+ nsIRDFObserver* obs = mObservers[i];
+
+ // XXX this should never happen, but it does, and we can't figure out why.
+ NS_ASSERTION(obs, "observer array corrupted!");
+ if (! obs)
+ continue;
+
+ obs->OnAssert(this, aSource, aProperty, aTarget);
+ // XXX ignore return value?
+ }
+
+ return NS_RDF_ASSERTION_ACCEPTED;
+}
+
+
+nsresult
+InMemoryDataSource::LockedUnassert(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ LogOperation("UNASSERT", aSource, aProperty, aTarget);
+
+ Assertion* next = GetForwardArcs(aSource);
+ Assertion* prev = next;
+ Assertion* root = next;
+ Assertion* as = nullptr;
+
+ bool haveHash = (next) ? next->mHashEntry : false;
+ if (haveHash) {
+ PLDHashEntryHdr* hdr = next->u.hash.mPropertyHash->Search(aProperty);
+ prev = next = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ bool first = true;
+ while (next) {
+ if (aTarget == next->u.as.mTarget) {
+ break;
+ }
+ first = false;
+ prev = next;
+ next = next->mNext;
+ }
+ // We don't even have the assertion, so just bail.
+ if (!next)
+ return NS_OK;
+
+ as = next;
+
+ if (first) {
+ root->u.hash.mPropertyHash->RawRemove(hdr);
+
+ if (next && next->mNext) {
+ PLDHashEntryHdr* hdr =
+ root->u.hash.mPropertyHash->Add(aProperty,
+ mozilla::fallible);
+ if (hdr) {
+ Entry* entry = static_cast<Entry*>(hdr);
+ entry->mNode = aProperty;
+ entry->mAssertions = next->mNext;
+ }
+ }
+ else {
+ // If this second-level hash empties out, clean it up.
+ if (!root->u.hash.mPropertyHash->EntryCount()) {
+ root->Release();
+ SetForwardArcs(aSource, nullptr);
+ }
+ }
+ }
+ else {
+ prev->mNext = next->mNext;
+ }
+ }
+ else
+ {
+ while (next) {
+ // check target first as its most unique
+ if (aTarget == next->u.as.mTarget) {
+ if (aProperty == next->u.as.mProperty) {
+ if (prev == next) {
+ SetForwardArcs(aSource, next->mNext);
+ } else {
+ prev->mNext = next->mNext;
+ }
+ as = next;
+ break;
+ }
+ }
+
+ prev = next;
+ next = next->mNext;
+ }
+ }
+ // We don't even have the assertion, so just bail.
+ if (!as)
+ return NS_OK;
+
+#ifdef DEBUG
+ bool foundReverseArc = false;
+#endif
+
+ next = prev = GetReverseArcs(aTarget);
+ while (next) {
+ if (next == as) {
+ if (prev == next) {
+ SetReverseArcs(aTarget, next->u.as.mInvNext);
+ } else {
+ prev->u.as.mInvNext = next->u.as.mInvNext;
+ }
+#ifdef DEBUG
+ foundReverseArc = true;
+#endif
+ break;
+ }
+ prev = next;
+ next = next->u.as.mInvNext;
+ }
+
+#ifdef DEBUG
+ NS_ASSERTION(foundReverseArc, "in-memory db corrupted: unable to find reverse arc");
+#endif
+
+ // Unlink, and release the datasource's reference
+ as->mNext = as->u.as.mInvNext = nullptr;
+ as->Release();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::Unassert(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mReadCount) {
+ NS_WARNING("Writing to InMemoryDataSource during read\n");
+ return NS_RDF_ASSERTION_REJECTED;
+ }
+
+ nsresult rv;
+
+ rv = LockedUnassert(aSource, aProperty, aTarget);
+ if (NS_FAILED(rv)) return rv;
+
+ // Notify the world
+ for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+ nsIRDFObserver* obs = mObservers[i];
+
+ // XXX this should never happen, but it does, and we can't figure out why.
+ NS_ASSERTION(obs, "observer array corrupted!");
+ if (! obs)
+ continue;
+
+ obs->OnUnassert(this, aSource, aProperty, aTarget);
+ // XXX ignore return value?
+ }
+
+ return NS_RDF_ASSERTION_ACCEPTED;
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::Change(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aOldTarget,
+ nsIRDFNode* aNewTarget)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aOldTarget != nullptr, "null ptr");
+ if (! aOldTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aNewTarget != nullptr, "null ptr");
+ if (! aNewTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mReadCount) {
+ NS_WARNING("Writing to InMemoryDataSource during read\n");
+ return NS_RDF_ASSERTION_REJECTED;
+ }
+
+ nsresult rv;
+
+ // XXX We can implement LockedChange() if we decide that this
+ // is a performance bottleneck.
+
+ rv = LockedUnassert(aSource, aProperty, aOldTarget);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = LockedAssert(aSource, aProperty, aNewTarget, true);
+ if (NS_FAILED(rv)) return rv;
+
+ // Notify the world
+ for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+ nsIRDFObserver* obs = mObservers[i];
+
+ // XXX this should never happen, but it does, and we can't figure out why.
+ NS_ASSERTION(obs, "observer array corrupted!");
+ if (! obs)
+ continue;
+
+ obs->OnChange(this, aSource, aProperty, aOldTarget, aNewTarget);
+ // XXX ignore return value?
+ }
+
+ return NS_RDF_ASSERTION_ACCEPTED;
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::Move(nsIRDFResource* aOldSource,
+ nsIRDFResource* aNewSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ NS_PRECONDITION(aOldSource != nullptr, "null ptr");
+ if (! aOldSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aNewSource != nullptr, "null ptr");
+ if (! aNewSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mReadCount) {
+ NS_WARNING("Writing to InMemoryDataSource during read\n");
+ return NS_RDF_ASSERTION_REJECTED;
+ }
+
+ nsresult rv;
+
+ // XXX We can implement LockedMove() if we decide that this
+ // is a performance bottleneck.
+
+ rv = LockedUnassert(aOldSource, aProperty, aTarget);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = LockedAssert(aNewSource, aProperty, aTarget, true);
+ if (NS_FAILED(rv)) return rv;
+
+ // Notify the world
+ for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+ nsIRDFObserver* obs = mObservers[i];
+
+ // XXX this should never happen, but it does, and we can't figure out why.
+ NS_ASSERTION(obs, "observer array corrupted!");
+ if (! obs)
+ continue;
+
+ obs->OnMove(this, aOldSource, aNewSource, aProperty, aTarget);
+ // XXX ignore return value?
+ }
+
+ return NS_RDF_ASSERTION_ACCEPTED;
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::AddObserver(nsIRDFObserver* aObserver)
+{
+ NS_PRECONDITION(aObserver != nullptr, "null ptr");
+ if (! aObserver)
+ return NS_ERROR_NULL_POINTER;
+
+ mObservers.AppendObject(aObserver);
+ mNumObservers = mObservers.Count();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::RemoveObserver(nsIRDFObserver* aObserver)
+{
+ NS_PRECONDITION(aObserver != nullptr, "null ptr");
+ if (! aObserver)
+ return NS_ERROR_NULL_POINTER;
+
+ mObservers.RemoveObject(aObserver);
+ // note: use Count() instead of just decrementing
+ // in case aObserver wasn't in list, for example
+ mNumObservers = mObservers.Count();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *result)
+{
+ Assertion* ass = GetReverseArcs(aNode);
+ while (ass) {
+ nsIRDFResource* elbow = ass->u.as.mProperty;
+ if (elbow == aArc) {
+ *result = true;
+ return NS_OK;
+ }
+ ass = ass->u.as.mInvNext;
+ }
+ *result = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *result)
+{
+ Assertion* ass = GetForwardArcs(aSource);
+ if (ass && ass->mHashEntry) {
+ PLDHashEntryHdr* hdr = ass->u.hash.mPropertyHash->Search(aArc);
+ Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ if (val) {
+ *result = true;
+ return NS_OK;
+ }
+ ass = ass->mNext;
+ }
+ while (ass) {
+ nsIRDFResource* elbow = ass->u.as.mProperty;
+ if (elbow == aArc) {
+ *result = true;
+ return NS_OK;
+ }
+ ass = ass->mNext;
+ }
+ *result = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::ArcLabelsIn(nsIRDFNode* aTarget, nsISimpleEnumerator** aResult)
+{
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ InMemoryArcsEnumeratorImpl* result =
+ new InMemoryArcsEnumeratorImpl(this, nullptr, aTarget);
+
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(result);
+ *aResult = result;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::ArcLabelsOut(nsIRDFResource* aSource, nsISimpleEnumerator** aResult)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ InMemoryArcsEnumeratorImpl* result =
+ new InMemoryArcsEnumeratorImpl(this, aSource, nullptr);
+
+ if (! result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(result);
+ *aResult = result;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+InMemoryDataSource::GetAllResources(nsISimpleEnumerator** aResult)
+{
+ nsCOMArray<nsIRDFNode> nodes;
+ nodes.SetCapacity(mForwardArcs.EntryCount());
+
+ // Get all of our entries into an nsCOMArray
+ for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<Entry*>(iter.Get());
+ nodes.AppendObject(entry->mNode);
+ }
+ return NS_NewArrayEnumerator(aResult, nodes);
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::GetAllCmds(nsIRDFResource* source,
+ nsISimpleEnumerator/*<nsIRDFResource>*/** commands)
+{
+ return(NS_NewEmptyEnumerator(commands));
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::IsCommandEnabled(nsISupports* aSources,
+ nsIRDFResource* aCommand,
+ nsISupports* aArguments,
+ bool* aResult)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::DoCommand(nsISupports* aSources,
+ nsIRDFResource* aCommand,
+ nsISupports* aArguments)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::BeginUpdateBatch()
+{
+ for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+ nsIRDFObserver* obs = mObservers[i];
+ obs->OnBeginUpdateBatch(this);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::EndUpdateBatch()
+{
+ for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+ nsIRDFObserver* obs = mObservers[i];
+ obs->OnEndUpdateBatch(this);
+ }
+ return NS_OK;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFInMemoryDataSource methods
+
+NS_IMETHODIMP
+InMemoryDataSource::EnsureFastContainment(nsIRDFResource* aSource)
+{
+ Assertion *as = GetForwardArcs(aSource);
+ bool haveHash = (as) ? as->mHashEntry : false;
+
+ // if its already a hash, then nothing to do
+ if (haveHash) return(NS_OK);
+
+ // convert aSource in forward hash into a hash
+ Assertion *hashAssertion = new Assertion(aSource);
+ NS_ASSERTION(hashAssertion, "unable to create Assertion");
+ if (!hashAssertion) return(NS_ERROR_OUT_OF_MEMORY);
+
+ // Add the datasource's owning reference.
+ hashAssertion->AddRef();
+
+ Assertion *first = GetForwardArcs(aSource);
+ SetForwardArcs(aSource, hashAssertion);
+
+ // mutate references of existing forward assertions into this hash
+ PLDHashTable *table = hashAssertion->u.hash.mPropertyHash;
+ Assertion *nextRef;
+ while(first) {
+ nextRef = first->mNext;
+ nsIRDFResource *prop = first->u.as.mProperty;
+
+ PLDHashEntryHdr* hdr = table->Search(prop);
+ Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ if (val) {
+ first->mNext = val->mNext;
+ val->mNext = first;
+ }
+ else {
+ PLDHashEntryHdr* hdr = table->Add(prop, mozilla::fallible);
+ if (hdr) {
+ Entry* entry = static_cast<Entry*>(hdr);
+ entry->mNode = prop;
+ entry->mAssertions = first;
+ first->mNext = nullptr;
+ }
+ }
+ first = nextRef;
+ }
+ return(NS_OK);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFPropagatableDataSource methods
+NS_IMETHODIMP
+InMemoryDataSource::GetPropagateChanges(bool* aPropagateChanges)
+{
+ *aPropagateChanges = mPropagateChanges;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::SetPropagateChanges(bool aPropagateChanges)
+{
+ mPropagateChanges = aPropagateChanges;
+ return NS_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// nsIRDFPurgeableDataSource methods
+
+NS_IMETHODIMP
+InMemoryDataSource::Mark(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget,
+ bool aTruthValue,
+ bool* aDidMark)
+{
+ NS_PRECONDITION(aSource != nullptr, "null ptr");
+ if (! aSource)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aProperty != nullptr, "null ptr");
+ if (! aProperty)
+ return NS_ERROR_NULL_POINTER;
+
+ NS_PRECONDITION(aTarget != nullptr, "null ptr");
+ if (! aTarget)
+ return NS_ERROR_NULL_POINTER;
+
+ Assertion *as = GetForwardArcs(aSource);
+ if (as && as->mHashEntry) {
+ PLDHashEntryHdr* hdr = as->u.hash.mPropertyHash->Search(aProperty);
+ Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
+ while (val) {
+ if ((val->u.as.mTarget == aTarget) &&
+ (aTruthValue == (val->u.as.mTruthValue))) {
+
+ // found it! so mark it.
+ as->Mark();
+ *aDidMark = true;
+
+ LogOperation("MARK", aSource, aProperty, aTarget, aTruthValue);
+
+ return NS_OK;
+ }
+ val = val->mNext;
+ }
+ }
+ else for (; as != nullptr; as = as->mNext) {
+ // check target first as its most unique
+ if (aTarget != as->u.as.mTarget)
+ continue;
+
+ if (aProperty != as->u.as.mProperty)
+ continue;
+
+ if (aTruthValue != (as->u.as.mTruthValue))
+ continue;
+
+ // found it! so mark it.
+ as->Mark();
+ *aDidMark = true;
+
+ LogOperation("MARK", aSource, aProperty, aTarget, aTruthValue);
+
+ return NS_OK;
+ }
+
+ // If we get here, we couldn't find the assertion
+ *aDidMark = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::Sweep()
+{
+ SweepInfo info = { nullptr, &mReverseArcs };
+
+ // Remove all the assertions, but don't notify anyone.
+ SweepForwardArcsEntries(&mForwardArcs, &info);
+
+ // Now do the notification.
+ Assertion* as = info.mUnassertList;
+ while (as) {
+ LogOperation("SWEEP", as->mSource, as->u.as.mProperty, as->u.as.mTarget, as->u.as.mTruthValue);
+ if (!(as->mHashEntry))
+ {
+ for (int32_t i = int32_t(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
+ nsIRDFObserver* obs = mObservers[i];
+ // XXXbz other loops over mObservers null-check |obs| here!
+ obs->OnUnassert(this, as->mSource, as->u.as.mProperty, as->u.as.mTarget);
+ // XXX ignore return value?
+ }
+ }
+
+ Assertion* doomed = as;
+ as = as->mNext;
+
+ // Unlink, and release the datasource's reference
+ doomed->mNext = doomed->u.as.mInvNext = nullptr;
+ doomed->Release();
+ }
+
+ return NS_OK;
+}
+
+
+void
+InMemoryDataSource::SweepForwardArcsEntries(PLDHashTable* aTable,
+ SweepInfo* aInfo)
+{
+ for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<Entry*>(iter.Get());
+
+ Assertion* as = entry->mAssertions;
+ if (as && (as->mHashEntry)) {
+ // Stuff in sub-hashes must be swept recursively (max depth: 1)
+ SweepForwardArcsEntries(as->u.hash.mPropertyHash, aInfo);
+
+ // If the sub-hash is now empty, clean it up.
+ if (!as->u.hash.mPropertyHash->EntryCount()) {
+ as->Release();
+ iter.Remove();
+ }
+ continue;
+ }
+
+ Assertion* prev = nullptr;
+ while (as) {
+ if (as->IsMarked()) {
+ prev = as;
+ as->Unmark();
+ as = as->mNext;
+ }
+ else {
+ // remove from the list of assertions in the datasource
+ Assertion* next = as->mNext;
+ if (prev) {
+ prev->mNext = next;
+ }
+ else {
+ // it's the first one. update the hashtable entry.
+ entry->mAssertions = next;
+ }
+
+ // remove from the reverse arcs
+ PLDHashEntryHdr* hdr =
+ aInfo->mReverseArcs->Search(as->u.as.mTarget);
+ NS_ASSERTION(hdr, "no assertion in reverse arcs");
+
+ Entry* rentry = static_cast<Entry*>(hdr);
+ Assertion* ras = rentry->mAssertions;
+ Assertion* rprev = nullptr;
+ while (ras) {
+ if (ras == as) {
+ if (rprev) {
+ rprev->u.as.mInvNext = ras->u.as.mInvNext;
+ }
+ else {
+ // it's the first one. update the hashtable entry.
+ rentry->mAssertions = ras->u.as.mInvNext;
+ }
+ as->u.as.mInvNext = nullptr; // for my sanity.
+ break;
+ }
+ rprev = ras;
+ ras = ras->u.as.mInvNext;
+ }
+
+ // Wow, it was the _only_ one. Unhash it.
+ if (! rentry->mAssertions) {
+ aInfo->mReverseArcs->RawRemove(hdr);
+ }
+
+ // add to the list of assertions to unassert
+ as->mNext = aInfo->mUnassertList;
+ aInfo->mUnassertList = as;
+
+ // Advance to the next assertion
+ as = next;
+ }
+ }
+
+ // if no more assertions exist for this resource, then unhash it.
+ if (! entry->mAssertions) {
+ iter.Remove();
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// rdfIDataSource methods
+
+NS_IMETHODIMP
+InMemoryDataSource::VisitAllSubjects(rdfITripleVisitor *aVisitor)
+{
+ // Lock datasource against writes
+ ++mReadCount;
+
+ // Enumerate all of our entries.
+ nsresult rv = NS_OK;
+ for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<Entry*>(iter.Get());
+ nsresult rv2;
+ nsCOMPtr<nsIRDFNode> subject = do_QueryInterface(entry->mNode, &rv2);
+ if (NS_FAILED(rv2)) {
+ NS_WARNING("QI to nsIRDFNode failed");
+ continue;
+ }
+ rv = aVisitor->Visit(subject, nullptr, nullptr, true);
+ if (NS_FAILED(rv) || rv == NS_RDF_STOP_VISIT) {
+ break;
+ }
+ }
+
+ // Unlock datasource
+ --mReadCount;
+
+ return rv;
+}
+
+NS_IMETHODIMP
+InMemoryDataSource::VisitAllTriples(rdfITripleVisitor *aVisitor)
+{
+ // Lock datasource against writes
+ ++mReadCount;
+
+ // Enumerate all of our entries.
+ nsresult rv = NS_OK;
+ for (auto iter = mForwardArcs.Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<Entry*>(iter.Get());
+
+ nsresult rv2;
+ nsCOMPtr<nsIRDFNode> subject = do_QueryInterface(entry->mNode, &rv2);
+ if (NS_FAILED(rv2)) {
+ NS_WARNING("QI to nsIRDFNode failed");
+
+ } else if (entry->mAssertions->mHashEntry) {
+ for (auto iter = entry->mAssertions->u.hash.mPropertyHash->Iter();
+ !iter.Done();
+ iter.Next()) {
+ auto entry = static_cast<Entry*>(iter.Get());
+ Assertion* assertion = entry->mAssertions;
+ while (assertion) {
+ NS_ASSERTION(!assertion->mHashEntry, "shouldn't have to hashes");
+ rv = aVisitor->Visit(subject, assertion->u.as.mProperty,
+ assertion->u.as.mTarget,
+ assertion->u.as.mTruthValue);
+ if (NS_FAILED(rv)) {
+ goto end;
+ }
+ if (rv == NS_RDF_STOP_VISIT) {
+ goto inner_end;
+ }
+ assertion = assertion->mNext;
+ }
+ }
+
+ } else {
+ Assertion* assertion = entry->mAssertions;
+ while (assertion) {
+ NS_ASSERTION(!assertion->mHashEntry, "shouldn't have to hashes");
+ rv = aVisitor->Visit(subject, assertion->u.as.mProperty,
+ assertion->u.as.mTarget,
+ assertion->u.as.mTruthValue);
+ if (NS_FAILED(rv) || rv == NS_RDF_STOP_VISIT) {
+ goto end;
+ }
+ assertion = assertion->mNext;
+ }
+ }
+
+ inner_end:
+ (void) 0;
+ }
+
+ end:
+ // Unlock datasource
+ --mReadCount;
+
+ return rv;
+}
+
+////////////////////////////////////////////////////////////////////////
+