forked from mirrors/gecko-dev
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)
2072 lines
59 KiB
C++
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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|