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