fune/rdf/base/nsInMemoryDataSource.cpp
Wes Kocher 4e9f80ed2e Backed out 14 changesets (bug 1165515) for b2g mochitest-6 permafail CLOSED TREE
Backed out changeset 9b97e2aa2ed9 (bug 1165515)
Backed out changeset 150606c022a2 (bug 1165515)
Backed out changeset 4e875a488349 (bug 1165515)
Backed out changeset 467e7feeb546 (bug 1165515)
Backed out changeset d6b6cc373197 (bug 1165515)
Backed out changeset 0615265b593c (bug 1165515)
Backed out changeset fafd1dce9f08 (bug 1165515)
Backed out changeset d1df869245f9 (bug 1165515)
Backed out changeset 6876a7c63611 (bug 1165515)
Backed out changeset b7841c94a9a3 (bug 1165515)
Backed out changeset e5e3617f7c73 (bug 1165515)
Backed out changeset 39be3db95978 (bug 1165515)
Backed out changeset 0ec74176f8de (bug 1165515)
Backed out changeset 5b928dd10d71 (bug 1165515)
2015-06-01 17:57:58 -07:00

2072 lines
59 KiB
C++

/* -*- 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 "nsISupportsArray.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 "pldhash.h"
#include "plstr.h"
#include "mozilla/Logging.h"
#include "rdf.h"
#include "rdfIDataSource.h"
#include "rdfITripleVisitor.h"
// 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:
static PLDHashOperator
DeletePropertyHashEntry(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
uint32_t aNumber, void* aArg);
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
{
PLDHashTable2* 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 PLDHashTable2(PL_DHashGetStubOps(), 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) {
PL_DHashTableEnumerate(u.hash.mPropertyHash, DeletePropertyHashEntry,
nullptr);
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);
}
}
PLDHashOperator
Assertion::DeletePropertyHashEntry(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
uint32_t aNumber, void* aArg)
{
Entry* entry = static_cast<Entry*>(aHdr);
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();
}
return PL_DHASH_NEXT;
}
////////////////////////////////////////////////////////////////////////
// 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.
PLDHashTable2 mForwardArcs;
PLDHashTable2 mReverseArcs;
nsCOMArray<nsIRDFObserver> mObservers;
uint32_t mNumObservers;
// VisitFoo needs to block writes, [Un]Assert only allowed
// during mReadCount == 0
uint32_t mReadCount;
static PLDHashOperator
DeleteForwardArcsEntry(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
uint32_t aNumber, void* aArg);
static PLDHashOperator
ResourceEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
uint32_t aNumber, void* aArg);
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:
static PLDHashOperator
SweepForwardArcsEntries(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
uint32_t aNumber, void* aArg);
public:
// Implementation methods
Assertion*
GetForwardArcs(nsIRDFResource* u) {
PLDHashEntryHdr* hdr = PL_DHashTableSearch(&mForwardArcs, u);
return hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
}
Assertion*
GetReverseArcs(nsIRDFNode* v) {
PLDHashEntryHdr* hdr = PL_DHashTableSearch(&mReverseArcs, v);
return hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
}
void
SetForwardArcs(nsIRDFResource* u, Assertion* as) {
if (as) {
Entry* entry = static_cast<Entry*>
(PL_DHashTableAdd(&mForwardArcs, u, mozilla::fallible));
if (entry) {
entry->mNode = u;
entry->mAssertions = as;
}
}
else {
PL_DHashTableRemove(&mForwardArcs, u);
}
}
void
SetReverseArcs(nsIRDFNode* v, Assertion* as) {
if (as) {
Entry* entry = static_cast<Entry*>
(PL_DHashTableAdd(&mReverseArcs, v, mozilla::fallible));
if (entry) {
entry->mNode = v;
entry->mAssertions = as;
}
}
else {
PL_DHashTableRemove(&mReverseArcs, v);
}
}
void
LogOperation(const char* aOperation,
nsIRDFResource* asource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
bool aTruthValue = true);
bool mPropagateChanges;
private:
static PRLogModuleInfo* gLog;
};
PRLogModuleInfo* InMemoryDataSource::gLog;
//----------------------------------------------------------------------
//
// InMemoryAssertionEnumeratorImpl
//
/**
* InMemoryAssertionEnumeratorImpl
*/
class InMemoryAssertionEnumeratorImpl : public nsISimpleEnumerator
{
private:
InMemoryDataSource* mDataSource;
nsIRDFResource* mSource;
nsIRDFResource* mProperty;
nsIRDFNode* mTarget;
nsIRDFNode* mValue;
bool mTruthValue;
Assertion* mNextAssertion;
nsCOMPtr<nsISupportsArray> mHashArcs;
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 =
PL_DHashTableSearch(mNextAssertion->u.hash.mPropertyHash,
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;
nsAutoTArray<nsCOMPtr<nsIRDFResource>, 8> mAlreadyReturned;
nsIRDFResource* mCurrent;
Assertion* mAssertion;
nsCOMPtr<nsISupportsArray> mHashArcs;
static PLDHashOperator
ArcEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
uint32_t aNumber, void* aArg);
virtual ~InMemoryArcsEnumeratorImpl();
public:
InMemoryArcsEnumeratorImpl(InMemoryDataSource* aDataSource,
nsIRDFResource* aSource,
nsIRDFNode* aTarget);
// nsISupports interface
NS_DECL_ISUPPORTS
// nsISimpleEnumerator interface
NS_DECL_NSISIMPLEENUMERATOR
};
PLDHashOperator
InMemoryArcsEnumeratorImpl::ArcEnumerator(PLDHashTable* aTable,
PLDHashEntryHdr* aHdr,
uint32_t aNumber, void* aArg)
{
Entry* entry = static_cast<Entry*>(aHdr);
nsISupportsArray* resources = static_cast<nsISupportsArray*>(aArg);
resources->AppendElement(entry->mNode);
return PL_DHASH_NEXT;
}
InMemoryArcsEnumeratorImpl::InMemoryArcsEnumeratorImpl(InMemoryDataSource* aDataSource,
nsIRDFResource* aSource,
nsIRDFNode* aTarget)
: mDataSource(aDataSource),
mSource(aSource),
mTarget(aTarget),
mCurrent(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
nsresult rv = NS_NewISupportsArray(getter_AddRefs(mHashArcs));
if (NS_SUCCEEDED(rv)) {
PL_DHashTableEnumerate(mAssertion->u.hash.mPropertyHash,
ArcEnumerator, mHashArcs.get());
}
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);
}
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) {
uint32_t itemCount;
nsresult rv;
if (NS_FAILED(rv = mHashArcs->Count(&itemCount))) return(rv);
if (itemCount > 0) {
--itemCount;
nsCOMPtr<nsIRDFResource> tmp = do_QueryElementAt(mHashArcs, itemCount);
tmp.forget(&mCurrent);
mHashArcs->RemoveElementAt(itemCount);
*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(PL_DHashGetStubOps(), sizeof(Entry))
, mReverseArcs(PL_DHashGetStubOps(), sizeof(Entry))
, mNumObservers(0)
, mReadCount(0)
{
NS_INIT_AGGREGATED(aOuter);
mPropagateChanges = true;
MOZ_COUNT_CTOR(InMemoryDataSource);
if (! gLog)
gLog = PR_NewLogModule("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.
PL_DHashTableEnumerate(&mForwardArcs, DeleteForwardArcsEntry, nullptr);
}
MOZ_LOG(gLog, PR_LOG_NOTICE,
("InMemoryDataSource(%p): destroyed.", this));
MOZ_COUNT_DTOR(InMemoryDataSource);
}
PLDHashOperator
InMemoryDataSource::DeleteForwardArcsEntry(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
uint32_t aNumber, void* aArg)
{
Entry* entry = static_cast<Entry*>(aHdr);
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();
}
return PL_DHASH_NEXT;
}
////////////////////////////////////////////////////////////////////////
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 (! PR_LOG_TEST(gLog, PR_LOG_NOTICE))
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 =
PL_DHashTableSearch(as->u.hash.mPropertyHash, 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 =
PL_DHashTableSearch(as->u.hash.mPropertyHash, 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 =
PL_DHashTableSearch(next->u.hash.mPropertyHash, 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 =
PL_DHashTableSearch(next->u.hash.mPropertyHash, aProperty);
Assertion *asRef =
hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
if (asRef)
{
as->mNext = asRef->mNext;
asRef->mNext = as;
}
else
{
hdr = PL_DHashTableAdd(next->u.hash.mPropertyHash, 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 =
PL_DHashTableSearch(next->u.hash.mPropertyHash, 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) {
PL_DHashTableRawRemove(root->u.hash.mPropertyHash, hdr);
if (next && next->mNext) {
PLDHashEntryHdr* hdr =
PL_DHashTableAdd(root->u.hash.mPropertyHash, 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 =
PL_DHashTableSearch(ass->u.hash.mPropertyHash, 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;
}
PLDHashOperator
InMemoryDataSource::ResourceEnumerator(PLDHashTable* aTable,
PLDHashEntryHdr* aHdr,
uint32_t aNumber, void* aArg)
{
Entry* entry = static_cast<Entry*>(aHdr);
static_cast<nsCOMArray<nsIRDFNode>*>(aArg)->AppendObject(entry->mNode);
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
InMemoryDataSource::GetAllResources(nsISimpleEnumerator** aResult)
{
nsCOMArray<nsIRDFNode> nodes;
nodes.SetCapacity(mForwardArcs.EntryCount());
// Enumerate all of our entries into an nsCOMArray
PL_DHashTableEnumerate(&mForwardArcs, ResourceEnumerator, &nodes);
return NS_NewArrayEnumerator(aResult, nodes);
}
NS_IMETHODIMP
InMemoryDataSource::GetAllCmds(nsIRDFResource* source,
nsISimpleEnumerator/*<nsIRDFResource>*/** commands)
{
return(NS_NewEmptyEnumerator(commands));
}
NS_IMETHODIMP
InMemoryDataSource::IsCommandEnabled(nsISupportsArray/*<nsIRDFResource>*/* aSources,
nsIRDFResource* aCommand,
nsISupportsArray/*<nsIRDFResource>*/* aArguments,
bool* aResult)
{
*aResult = false;
return NS_OK;
}
NS_IMETHODIMP
InMemoryDataSource::DoCommand(nsISupportsArray/*<nsIRDFResource>*/* aSources,
nsIRDFResource* aCommand,
nsISupportsArray/*<nsIRDFResource>*/* aArguments)
{
return NS_OK;
}
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 = PL_DHashTableSearch(table, prop);
Assertion* val = hdr ? static_cast<Entry*>(hdr)->mAssertions : nullptr;
if (val) {
first->mNext = val->mNext;
val->mNext = first;
}
else {
PLDHashEntryHdr* hdr = PL_DHashTableAdd(table, 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 =
PL_DHashTableSearch(as->u.hash.mPropertyHash, 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;
}
struct SweepInfo {
Assertion* mUnassertList;
PLDHashTable* mReverseArcs;
};
NS_IMETHODIMP
InMemoryDataSource::Sweep()
{
SweepInfo info = { nullptr, &mReverseArcs };
// Remove all the assertions, but don't notify anyone.
PL_DHashTableEnumerate(&mForwardArcs, SweepForwardArcsEntries, &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;
}
PLDHashOperator
InMemoryDataSource::SweepForwardArcsEntries(PLDHashTable* aTable,
PLDHashEntryHdr* aHdr,
uint32_t aNumber, void* aArg)
{
PLDHashOperator result = PL_DHASH_NEXT;
Entry* entry = static_cast<Entry*>(aHdr);
SweepInfo* info = static_cast<SweepInfo*>(aArg);
Assertion* as = entry->mAssertions;
if (as && (as->mHashEntry))
{
// Stuff in sub-hashes must be swept recursively (max depth: 1)
PL_DHashTableEnumerate(as->u.hash.mPropertyHash,
SweepForwardArcsEntries, info);
// If the sub-hash is now empty, clean it up.
if (!as->u.hash.mPropertyHash->EntryCount()) {
as->Release();
result = PL_DHASH_REMOVE;
}
return result;
}
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 =
PL_DHashTableSearch(info->mReverseArcs, 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)
{
PL_DHashTableRawRemove(info->mReverseArcs, hdr);
}
// add to the list of assertions to unassert
as->mNext = info->mUnassertList;
info->mUnassertList = as;
// Advance to the next assertion
as = next;
}
}
// if no more assertions exist for this resource, then unhash it.
if (! entry->mAssertions)
result = PL_DHASH_REMOVE;
return result;
}
////////////////////////////////////////////////////////////////////////
// rdfIDataSource methods
class VisitorClosure
{
public:
explicit VisitorClosure(rdfITripleVisitor* aVisitor) :
mVisitor(aVisitor),
mRv(NS_OK)
{}
rdfITripleVisitor* mVisitor;
nsresult mRv;
};
PLDHashOperator
SubjectEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
uint32_t aNumber, void* aArg) {
Entry* entry = static_cast<Entry*>(aHdr);
VisitorClosure* closure = static_cast<VisitorClosure*>(aArg);
nsresult rv;
nsCOMPtr<nsIRDFNode> subject = do_QueryInterface(entry->mNode, &rv);
NS_ENSURE_SUCCESS(rv, PL_DHASH_NEXT);
closure->mRv = closure->mVisitor->Visit(subject, nullptr, nullptr, true);
if (NS_FAILED(closure->mRv) || closure->mRv == NS_RDF_STOP_VISIT)
return PL_DHASH_STOP;
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
InMemoryDataSource::VisitAllSubjects(rdfITripleVisitor *aVisitor)
{
// Lock datasource against writes
++mReadCount;
// Enumerate all of our entries into an nsISupportsArray.
VisitorClosure cls(aVisitor);
PL_DHashTableEnumerate(&mForwardArcs, SubjectEnumerator, &cls);
// Unlock datasource
--mReadCount;
return cls.mRv;
}
class TriplesInnerClosure
{
public:
TriplesInnerClosure(nsIRDFNode* aSubject, VisitorClosure* aClosure) :
mSubject(aSubject), mOuter(aClosure) {}
nsIRDFNode* mSubject;
VisitorClosure* mOuter;
};
PLDHashOperator
TriplesInnerEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
uint32_t aNumber, void* aArg) {
Entry* entry = static_cast<Entry*>(aHdr);
Assertion* assertion = entry->mAssertions;
TriplesInnerClosure* closure =
static_cast<TriplesInnerClosure*>(aArg);
while (assertion) {
NS_ASSERTION(!assertion->mHashEntry, "shouldn't have to hashes");
VisitorClosure* cls = closure->mOuter;
cls->mRv = cls->mVisitor->Visit(closure->mSubject,
assertion->u.as.mProperty,
assertion->u.as.mTarget,
assertion->u.as.mTruthValue);
if (NS_FAILED(cls->mRv) || cls->mRv == NS_RDF_STOP_VISIT) {
return PL_DHASH_STOP;
}
assertion = assertion->mNext;
}
return PL_DHASH_NEXT;
}
PLDHashOperator
TriplesEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
uint32_t aNumber, void* aArg) {
Entry* entry = static_cast<Entry*>(aHdr);
VisitorClosure* closure = static_cast<VisitorClosure*>(aArg);
nsresult rv;
nsCOMPtr<nsIRDFNode> subject = do_QueryInterface(entry->mNode, &rv);
NS_ENSURE_SUCCESS(rv, PL_DHASH_NEXT);
if (entry->mAssertions->mHashEntry) {
TriplesInnerClosure cls(subject, closure);
PL_DHashTableEnumerate(entry->mAssertions->u.hash.mPropertyHash,
TriplesInnerEnumerator, &cls);
if (NS_FAILED(closure->mRv)) {
return PL_DHASH_STOP;
}
return PL_DHASH_NEXT;
}
Assertion* assertion = entry->mAssertions;
while (assertion) {
NS_ASSERTION(!assertion->mHashEntry, "shouldn't have to hashes");
closure->mRv = closure->mVisitor->Visit(subject,
assertion->u.as.mProperty,
assertion->u.as.mTarget,
assertion->u.as.mTruthValue);
if (NS_FAILED(closure->mRv) || closure->mRv == NS_RDF_STOP_VISIT) {
return PL_DHASH_STOP;
}
assertion = assertion->mNext;
}
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
InMemoryDataSource::VisitAllTriples(rdfITripleVisitor *aVisitor)
{
// Lock datasource against writes
++mReadCount;
// Enumerate all of our entries into an nsISupportsArray.
VisitorClosure cls(aVisitor);
PL_DHashTableEnumerate(&mForwardArcs, TriplesEnumerator, &cls);
// Unlock datasource
--mReadCount;
return cls.mRv;
}
////////////////////////////////////////////////////////////////////////