diff options
Diffstat (limited to 'rdf/base/nsInMemoryDataSource.cpp')
-rw-r--r-- | rdf/base/nsInMemoryDataSource.cpp | 1964 |
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; +} + +//////////////////////////////////////////////////////////////////////// + |