1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * Chris Waterson <waterson@netscape.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK *****
41 * This Original Code has been modified by IBM Corporation.
42 * Modifications made by IBM described herein are
43 * Copyright (c) International Business Machines
46 * Modifications to Mozilla code or documentation
47 * identified per MPL Section 3.3
49 * Date Modified by Description of modification
50 * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
56 Implementation for an in-memory RDF data store.
60 1) Instrument this code to gather space and time performance
63 2) Optimize lookups for datasources which have a small number
64 of properties + fanning out to a large number of targets.
66 3) Complete implementation of thread-safety; specifically, make
67 assertions be reference counted objects (so that a cursor can
68 still refer to an assertion that gets removed from the graph).
75 #include "nsIOutputStream.h"
76 #include "nsIRDFDataSource.h"
77 #include "nsIRDFLiteral.h"
78 #include "nsIRDFNode.h"
79 #include "nsIRDFObserver.h"
80 #include "nsIRDFInMemoryDataSource.h"
81 #include "nsIRDFPropagatableDataSource.h"
82 #include "nsIRDFPurgeableDataSource.h"
83 #include "nsIRDFService.h"
84 #include "nsIServiceManager.h"
85 #include "nsISupportsArray.h"
86 #include "nsCOMArray.h"
87 #include "nsEnumeratorUtils.h"
88 #include "nsVoidArray.h" // XXX introduces dependency on raptorbase
91 #include "nsRDFBaseDataSources.h"
93 #include "nsReadableUtils.h"
94 #include "nsXPIDLString.h"
95 #include "nsFixedSizeAllocator.h"
102 #include "rdfIDataSource.h"
103 #include "rdfITripleVisitor.h"
106 static PRLogModuleInfo
* gLog
= nsnull
;
110 // This struct is used as the slot value in the forward and reverse
113 // Assertion objects are reference counted, because each Assertion's
114 // ownership is shared between the datasource and any enumerators that
115 // are currently iterating over the datasource.
121 Create(nsFixedSizeAllocator
& aAllocator
,
122 nsIRDFResource
* aSource
,
123 nsIRDFResource
* aProperty
,
125 PRBool aTruthValue
) {
126 void* place
= aAllocator
.Alloc(sizeof(Assertion
));
128 ? ::new (place
) Assertion(aSource
, aProperty
, aTarget
, aTruthValue
)
131 Create(nsFixedSizeAllocator
& aAllocator
, nsIRDFResource
* aSource
) {
132 void* place
= aAllocator
.Alloc(sizeof(Assertion
));
134 ? ::new (place
) Assertion(aSource
)
138 Destroy(nsFixedSizeAllocator
& aAllocator
, Assertion
* aAssertion
) {
139 if (aAssertion
->mHashEntry
&& aAssertion
->u
.hash
.mPropertyHash
) {
140 PL_DHashTableEnumerate(aAssertion
->u
.hash
.mPropertyHash
,
141 DeletePropertyHashEntry
, &aAllocator
);
142 PL_DHashTableDestroy(aAssertion
->u
.hash
.mPropertyHash
);
143 aAssertion
->u
.hash
.mPropertyHash
= nsnull
;
145 aAssertion
->~Assertion();
146 aAllocator
.Free(aAssertion
, sizeof(*aAssertion
)); }
148 static PLDHashOperator
149 DeletePropertyHashEntry(PLDHashTable
* aTable
, PLDHashEntryHdr
* aHdr
,
150 PRUint32 aNumber
, void* aArg
);
152 Assertion(nsIRDFResource
* aSource
, // normal assertion
153 nsIRDFResource
* aProperty
,
156 Assertion(nsIRDFResource
* aSource
); // PLDHashTable assertion variant
161 if (mRefCnt
== PR_UINT16_MAX
) {
162 NS_WARNING("refcount overflow, leaking Assertion");
168 void Release(nsFixedSizeAllocator
& aAllocator
) {
169 if (mRefCnt
== PR_UINT16_MAX
) {
170 NS_WARNING("refcount overflow, leaking Assertion");
174 Destroy(aAllocator
, this);
177 // For nsIRDFPurgeableDataSource
178 inline void Mark() { u
.as
.mMarked
= PR_TRUE
; }
179 inline PRBool
IsMarked() { return u
.as
.mMarked
; }
180 inline void Unmark() { u
.as
.mMarked
= PR_FALSE
; }
182 // public for now, because I'm too lazy to go thru and clean this up.
184 // These are shared between hash/as (see the union below)
185 nsIRDFResource
* mSource
;
192 PLDHashTable
* mPropertyHash
;
196 nsIRDFResource
* mProperty
;
199 // make sure PRPackedBool are final elements
200 PRPackedBool mTruthValue
;
201 PRPackedBool mMarked
;
205 // also shared between hash/as (see the union above)
206 // but placed after union definition to ensure that
207 // all 32-bit entries are long aligned
209 PRPackedBool mHashEntry
;
212 // Hide so that only Create() and Destroy() can be used to
213 // allocate and deallocate from the heap
214 static void* operator new(size_t) CPP_THROW_NEW
{ return 0; }
215 static void operator delete(void*, size_t) {}
220 PLDHashEntryHdr mHdr
;
222 Assertion
* mAssertions
;
226 Assertion::Assertion(nsIRDFResource
* aSource
)
232 MOZ_COUNT_CTOR(RDF_Assertion
);
236 u
.hash
.mPropertyHash
= PL_NewDHashTable(PL_DHashGetStubOps(),
237 nsnull
, sizeof(Entry
), PL_DHASH_MIN_SIZE
);
240 Assertion::Assertion(nsIRDFResource
* aSource
,
241 nsIRDFResource
* aProperty
,
249 MOZ_COUNT_CTOR(RDF_Assertion
);
251 u
.as
.mProperty
= aProperty
;
252 u
.as
.mTarget
= aTarget
;
255 NS_ADDREF(u
.as
.mProperty
);
256 NS_ADDREF(u
.as
.mTarget
);
258 u
.as
.mInvNext
= nsnull
;
259 u
.as
.mTruthValue
= aTruthValue
;
260 u
.as
.mMarked
= PR_FALSE
;
263 Assertion::~Assertion()
265 MOZ_COUNT_DTOR(RDF_Assertion
);
268 fprintf(stdout
, "%d - RDF: Assertion\n", gInstanceCount
);
274 NS_RELEASE(u
.as
.mProperty
);
275 NS_RELEASE(u
.as
.mTarget
);
280 Assertion::DeletePropertyHashEntry(PLDHashTable
* aTable
, PLDHashEntryHdr
* aHdr
,
281 PRUint32 aNumber
, void* aArg
)
283 Entry
* entry
= reinterpret_cast<Entry
*>(aHdr
);
284 nsFixedSizeAllocator
* allocator
= static_cast<nsFixedSizeAllocator
*>(aArg
);
286 Assertion
* as
= entry
->mAssertions
;
288 Assertion
* doomed
= as
;
291 // Unlink, and release the datasource's reference.
292 doomed
->mNext
= doomed
->u
.as
.mInvNext
= nsnull
;
293 doomed
->Release(*allocator
);
295 return PL_DHASH_NEXT
;
300 ////////////////////////////////////////////////////////////////////////
301 // InMemoryDataSource
302 class InMemoryArcsEnumeratorImpl
;
303 class InMemoryAssertionEnumeratorImpl
;
304 class InMemoryResourceEnumeratorImpl
;
306 class InMemoryDataSource
: public nsIRDFDataSource
,
307 public nsIRDFInMemoryDataSource
,
308 public nsIRDFPropagatableDataSource
,
309 public nsIRDFPurgeableDataSource
,
310 public rdfIDataSource
313 nsFixedSizeAllocator mAllocator
;
315 // These hash tables are keyed on pointers to nsIRDFResource
316 // objects (the nsIRDFService ensures that there is only ever one
317 // nsIRDFResource object per unique URI). The value of an entry is
318 // an Assertion struct, which is a linked list of (subject
319 // predicate object) triples.
320 PLDHashTable mForwardArcs
;
321 PLDHashTable mReverseArcs
;
323 nsCOMArray
<nsIRDFObserver
> mObservers
;
324 PRUint32 mNumObservers
;
326 // VisitFoo needs to block writes, [Un]Assert only allowed
327 // during mReadCount == 0
330 static PLDHashOperator
331 DeleteForwardArcsEntry(PLDHashTable
* aTable
, PLDHashEntryHdr
* aHdr
,
332 PRUint32 aNumber
, void* aArg
);
334 static PLDHashOperator
335 ResourceEnumerator(PLDHashTable
* aTable
, PLDHashEntryHdr
* aHdr
,
336 PRUint32 aNumber
, void* aArg
);
338 friend class InMemoryArcsEnumeratorImpl
;
339 friend class InMemoryAssertionEnumeratorImpl
;
340 friend class InMemoryResourceEnumeratorImpl
; // b/c it needs to enumerate mForwardArcs
342 // Thread-safe writer implementation methods.
344 LockedAssert(nsIRDFResource
* source
,
345 nsIRDFResource
* property
,
350 LockedUnassert(nsIRDFResource
* source
,
351 nsIRDFResource
* property
,
354 InMemoryDataSource(nsISupports
* aOuter
);
355 virtual ~InMemoryDataSource();
359 NS_NewRDFInMemoryDataSource(nsISupports
* aOuter
, const nsIID
& aIID
, void** aResult
);
362 NS_DECL_CYCLE_COLLECTING_AGGREGATED
363 NS_DECL_AGGREGATED_CYCLE_COLLECTION_CLASS(InMemoryDataSource
)
365 // nsIRDFDataSource methods
366 NS_DECL_NSIRDFDATASOURCE
368 // nsIRDFInMemoryDataSource methods
369 NS_DECL_NSIRDFINMEMORYDATASOURCE
371 // nsIRDFPropagatableDataSource methods
372 NS_DECL_NSIRDFPROPAGATABLEDATASOURCE
374 // nsIRDFPurgeableDataSource methods
375 NS_DECL_NSIRDFPURGEABLEDATASOURCE
377 // rdfIDataSource methods
378 NS_DECL_RDFIDATASOURCE
381 static PLDHashOperator
382 SweepForwardArcsEntries(PLDHashTable
* aTable
, PLDHashEntryHdr
* aHdr
,
383 PRUint32 aNumber
, void* aArg
);
386 // Implementation methods
388 GetForwardArcs(nsIRDFResource
* u
) {
389 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(&mForwardArcs
, u
, PL_DHASH_LOOKUP
);
390 return PL_DHASH_ENTRY_IS_BUSY(hdr
)
391 ? reinterpret_cast<Entry
*>(hdr
)->mAssertions
395 GetReverseArcs(nsIRDFNode
* v
) {
396 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(&mReverseArcs
, v
, PL_DHASH_LOOKUP
);
397 return PL_DHASH_ENTRY_IS_BUSY(hdr
)
398 ? reinterpret_cast<Entry
*>(hdr
)->mAssertions
402 SetForwardArcs(nsIRDFResource
* u
, Assertion
* as
) {
403 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(&mForwardArcs
, u
,
404 as
? PL_DHASH_ADD
: PL_DHASH_REMOVE
);
406 Entry
* entry
= reinterpret_cast<Entry
*>(hdr
);
408 entry
->mAssertions
= as
;
412 SetReverseArcs(nsIRDFNode
* v
, Assertion
* as
) {
413 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(&mReverseArcs
, v
,
414 as
? PL_DHASH_ADD
: PL_DHASH_REMOVE
);
416 Entry
* entry
= reinterpret_cast<Entry
*>(hdr
);
418 entry
->mAssertions
= as
;
423 LogOperation(const char* aOperation
,
424 nsIRDFResource
* asource
,
425 nsIRDFResource
* aProperty
,
427 PRBool aTruthValue
= PR_TRUE
);
430 PRBool mPropagateChanges
;
433 //----------------------------------------------------------------------
435 // InMemoryAssertionEnumeratorImpl
439 * InMemoryAssertionEnumeratorImpl
441 class InMemoryAssertionEnumeratorImpl
: public nsISimpleEnumerator
444 InMemoryDataSource
* mDataSource
;
445 nsIRDFResource
* mSource
;
446 nsIRDFResource
* mProperty
;
451 Assertion
* mNextAssertion
;
452 nsCOMPtr
<nsISupportsArray
> mHashArcs
;
454 // Hide so that only Create() and Destroy() can be used to
455 // allocate and deallocate from the heap
456 static void* operator new(size_t) CPP_THROW_NEW
{ return 0; }
457 static void operator delete(void*, size_t) {}
459 InMemoryAssertionEnumeratorImpl(InMemoryDataSource
* aDataSource
,
460 nsIRDFResource
* aSource
,
461 nsIRDFResource
* aProperty
,
465 virtual ~InMemoryAssertionEnumeratorImpl();
468 static InMemoryAssertionEnumeratorImpl
*
469 Create(InMemoryDataSource
* aDataSource
,
470 nsIRDFResource
* aSource
,
471 nsIRDFResource
* aProperty
,
473 PRBool aTruthValue
) {
474 void* place
= aDataSource
->mAllocator
.Alloc(sizeof(InMemoryAssertionEnumeratorImpl
));
476 ? ::new (place
) InMemoryAssertionEnumeratorImpl(aDataSource
,
477 aSource
, aProperty
, aTarget
,
482 Destroy(InMemoryAssertionEnumeratorImpl
* aEnumerator
) {
483 // Keep the datasource alive for the duration of the stack
484 // frame so its allocator stays valid.
485 nsCOMPtr
<nsIRDFDataSource
> kungFuDeathGrip
= aEnumerator
->mDataSource
;
487 // Grab the pool from the datasource; since we keep the
488 // datasource alive, this has to be safe.
489 nsFixedSizeAllocator
& pool
= aEnumerator
->mDataSource
->mAllocator
;
490 aEnumerator
->~InMemoryAssertionEnumeratorImpl();
491 pool
.Free(aEnumerator
, sizeof(*aEnumerator
)); }
493 // nsISupports interface
496 // nsISimpleEnumerator interface
497 NS_DECL_NSISIMPLEENUMERATOR
500 ////////////////////////////////////////////////////////////////////////
503 InMemoryAssertionEnumeratorImpl::InMemoryAssertionEnumeratorImpl(
504 InMemoryDataSource
* aDataSource
,
505 nsIRDFResource
* aSource
,
506 nsIRDFResource
* aProperty
,
509 : mDataSource(aDataSource
),
511 mProperty(aProperty
),
515 mTruthValue(aTruthValue
),
516 mNextAssertion(nsnull
)
518 NS_ADDREF(mDataSource
);
519 NS_IF_ADDREF(mSource
);
520 NS_ADDREF(mProperty
);
521 NS_IF_ADDREF(mTarget
);
524 mNextAssertion
= mDataSource
->GetForwardArcs(mSource
);
526 if (mNextAssertion
&& mNextAssertion
->mHashEntry
) {
527 // its our magical HASH_ENTRY forward hash for assertions
528 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(mNextAssertion
->u
.hash
.mPropertyHash
,
529 aProperty
, PL_DHASH_LOOKUP
);
530 mNextAssertion
= PL_DHASH_ENTRY_IS_BUSY(hdr
)
531 ? reinterpret_cast<Entry
*>(hdr
)->mAssertions
536 mNextAssertion
= mDataSource
->GetReverseArcs(mTarget
);
539 // Add an owning reference from the enumerator
541 mNextAssertion
->AddRef();
544 InMemoryAssertionEnumeratorImpl::~InMemoryAssertionEnumeratorImpl()
548 fprintf(stdout
, "%d - RDF: InMemoryAssertionEnumeratorImpl\n", gInstanceCount
);
552 mNextAssertion
->Release(mDataSource
->mAllocator
);
554 NS_IF_RELEASE(mDataSource
);
555 NS_IF_RELEASE(mSource
);
556 NS_IF_RELEASE(mProperty
);
557 NS_IF_RELEASE(mTarget
);
558 NS_IF_RELEASE(mValue
);
561 NS_IMPL_ADDREF(InMemoryAssertionEnumeratorImpl
)
562 NS_IMPL_RELEASE_WITH_DESTROY(InMemoryAssertionEnumeratorImpl
, Destroy(this))
563 NS_IMPL_QUERY_INTERFACE1(InMemoryAssertionEnumeratorImpl
, nsISimpleEnumerator
)
566 InMemoryAssertionEnumeratorImpl::HasMoreElements(PRBool
* aResult
)
573 while (mNextAssertion
) {
574 PRBool foundIt
= PR_FALSE
;
575 if ((mProperty
== mNextAssertion
->u
.as
.mProperty
) &&
576 (mTruthValue
== mNextAssertion
->u
.as
.mTruthValue
)) {
578 mValue
= mNextAssertion
->u
.as
.mTarget
;
582 mValue
= mNextAssertion
->mSource
;
588 // Remember the last assertion we were holding on to
589 Assertion
* as
= mNextAssertion
;
592 mNextAssertion
= (mSource
) ? mNextAssertion
->mNext
: mNextAssertion
->u
.as
.mInvNext
;
594 // grab an owning reference from the enumerator to the next assertion
596 mNextAssertion
->AddRef();
598 // ...and release the reference from the enumerator to the old one.
599 as
->Release(mDataSource
->mAllocator
);
613 InMemoryAssertionEnumeratorImpl::GetNext(nsISupports
** aResult
)
618 rv
= HasMoreElements(&hasMore
);
619 if (NS_FAILED(rv
)) return rv
;
622 return NS_ERROR_UNEXPECTED
;
624 // Don't AddRef: we "transfer" ownership to the caller
631 ////////////////////////////////////////////////////////////////////////
635 * This class is a little bit bizarre in that it implements both the
636 * <tt>nsIRDFArcsOutCursor</tt> and <tt>nsIRDFArcsInCursor</tt> interfaces.
637 * Because the structure of the in-memory graph is pretty flexible, it's
638 * fairly easy to parameterize this class. The only funky thing to watch
639 * out for is the mutliple inheiritance clashes.
642 class InMemoryArcsEnumeratorImpl
: public nsISimpleEnumerator
645 // Hide so that only Create() and Destroy() can be used to
646 // allocate and deallocate from the heap
647 static void* operator new(size_t) CPP_THROW_NEW
{ return 0; }
648 static void operator delete(void*, size_t) {}
650 InMemoryDataSource
* mDataSource
;
651 nsIRDFResource
* mSource
;
653 nsAutoVoidArray mAlreadyReturned
;
654 nsIRDFResource
* mCurrent
;
655 Assertion
* mAssertion
;
656 nsCOMPtr
<nsISupportsArray
> mHashArcs
;
658 InMemoryArcsEnumeratorImpl(InMemoryDataSource
* aDataSource
,
659 nsIRDFResource
* aSource
,
660 nsIRDFNode
* aTarget
);
662 virtual ~InMemoryArcsEnumeratorImpl();
664 static PLDHashOperator
665 ArcEnumerator(PLDHashTable
* aTable
, PLDHashEntryHdr
* aHdr
,
666 PRUint32 aNumber
, void* aArg
);
669 // nsISupports interface
672 // nsISimpleEnumerator interface
673 NS_DECL_NSISIMPLEENUMERATOR
675 static InMemoryArcsEnumeratorImpl
*
676 Create(InMemoryDataSource
* aDataSource
,
677 nsIRDFResource
* aSource
,
678 nsIRDFNode
* aTarget
) {
679 void* place
= aDataSource
->mAllocator
.Alloc(sizeof(InMemoryArcsEnumeratorImpl
));
681 ? ::new (place
) InMemoryArcsEnumeratorImpl(aDataSource
, aSource
, aTarget
)
685 Destroy(InMemoryArcsEnumeratorImpl
* aEnumerator
) {
686 // Keep the datasource alive for the duration of the stack
687 // frame so its allocator stays valid.
688 nsCOMPtr
<nsIRDFDataSource
> kungFuDeathGrip
= aEnumerator
->mDataSource
;
690 // Grab the pool from the datasource; since we keep the
691 // datasource alive, this has to be safe.
692 nsFixedSizeAllocator
& pool
= aEnumerator
->mDataSource
->mAllocator
;
693 aEnumerator
->~InMemoryArcsEnumeratorImpl();
694 pool
.Free(aEnumerator
, sizeof(*aEnumerator
)); }
699 InMemoryArcsEnumeratorImpl::ArcEnumerator(PLDHashTable
* aTable
,
700 PLDHashEntryHdr
* aHdr
,
701 PRUint32 aNumber
, void* aArg
)
703 Entry
* entry
= reinterpret_cast<Entry
*>(aHdr
);
704 nsISupportsArray
* resources
= static_cast<nsISupportsArray
*>(aArg
);
706 resources
->AppendElement(entry
->mNode
);
707 return PL_DHASH_NEXT
;
711 InMemoryArcsEnumeratorImpl::InMemoryArcsEnumeratorImpl(InMemoryDataSource
* aDataSource
,
712 nsIRDFResource
* aSource
,
714 : mDataSource(aDataSource
),
719 NS_ADDREF(mDataSource
);
720 NS_IF_ADDREF(mSource
);
721 NS_IF_ADDREF(mTarget
);
724 // cast okay because it's a closed system
725 mAssertion
= mDataSource
->GetForwardArcs(mSource
);
727 if (mAssertion
&& mAssertion
->mHashEntry
) {
728 // its our magical HASH_ENTRY forward hash for assertions
729 nsresult rv
= NS_NewISupportsArray(getter_AddRefs(mHashArcs
));
730 if (NS_SUCCEEDED(rv
)) {
731 PL_DHashTableEnumerate(mAssertion
->u
.hash
.mPropertyHash
,
732 ArcEnumerator
, mHashArcs
.get());
738 mAssertion
= mDataSource
->GetReverseArcs(mTarget
);
742 InMemoryArcsEnumeratorImpl::~InMemoryArcsEnumeratorImpl()
746 fprintf(stdout
, "%d - RDF: InMemoryArcsEnumeratorImpl\n", gInstanceCount
);
749 NS_RELEASE(mDataSource
);
750 NS_IF_RELEASE(mSource
);
751 NS_IF_RELEASE(mTarget
);
752 NS_IF_RELEASE(mCurrent
);
754 for (PRInt32 i
= mAlreadyReturned
.Count() - 1; i
>= 0; --i
) {
755 nsIRDFResource
* resource
= (nsIRDFResource
*) mAlreadyReturned
[i
];
756 NS_RELEASE(resource
);
760 NS_IMPL_ADDREF(InMemoryArcsEnumeratorImpl
)
761 NS_IMPL_RELEASE_WITH_DESTROY(InMemoryArcsEnumeratorImpl
, Destroy(this))
762 NS_IMPL_QUERY_INTERFACE1(InMemoryArcsEnumeratorImpl
, nsISimpleEnumerator
)
765 InMemoryArcsEnumeratorImpl::HasMoreElements(PRBool
* aResult
)
767 NS_PRECONDITION(aResult
!= nsnull
, "null ptr");
769 return NS_ERROR_NULL_POINTER
;
779 if (NS_FAILED(rv
= mHashArcs
->Count(&itemCount
))) return(rv
);
782 mCurrent
= static_cast<nsIRDFResource
*>
783 (mHashArcs
->ElementAt(itemCount
));
784 mHashArcs
->RemoveElementAt(itemCount
);
791 nsIRDFResource
* next
= mAssertion
->u
.as
.mProperty
;
793 // "next" is the property arc we are tentatively going to return
794 // in a subsequent GetNext() call. It is important to do two
795 // things, however, before that can happen:
796 // 1) Make sure it's not an arc we've already returned.
797 // 2) Make sure that |mAssertion| is not left pointing to
798 // another assertion that has the same property as this one.
799 // The first is a practical concern; the second a defense against
800 // an obscure crash and other erratic behavior. To ensure the
801 // second condition, skip down the chain until we find the next
802 // assertion with a property that doesn't match the current one.
803 // (All these assertions would be skipped via mAlreadyReturned
804 // checks anyways; this is even a bit faster.)
807 mAssertion
= (mSource
? mAssertion
->mNext
:
808 mAssertion
->u
.as
.mInvNext
);
810 while (mAssertion
&& (next
== mAssertion
->u
.as
.mProperty
));
812 PRBool alreadyReturned
= PR_FALSE
;
813 for (PRInt32 i
= mAlreadyReturned
.Count() - 1; i
>= 0; --i
) {
814 if (mAlreadyReturned
[i
] == next
) {
815 alreadyReturned
= PR_TRUE
;
820 if (! alreadyReturned
) {
834 InMemoryArcsEnumeratorImpl::GetNext(nsISupports
** aResult
)
839 rv
= HasMoreElements(&hasMore
);
840 if (NS_FAILED(rv
)) return rv
;
843 return NS_ERROR_UNEXPECTED
;
845 // Add this to the set of things we've already returned so that we
846 // can ensure uniqueness
848 mAlreadyReturned
.AppendElement(mCurrent
);
850 // Don't AddRef: we "transfer" ownership to the caller
858 ////////////////////////////////////////////////////////////////////////
859 // InMemoryDataSource
862 NS_NewRDFInMemoryDataSource(nsISupports
* aOuter
, const nsIID
& aIID
, void** aResult
)
864 NS_PRECONDITION(aResult
!= nsnull
, "null ptr");
866 return NS_ERROR_NULL_POINTER
;
869 if (aOuter
&& !aIID
.Equals(NS_GET_IID(nsISupports
))) {
870 NS_ERROR("aggregation requires nsISupports");
871 return NS_ERROR_ILLEGAL_VALUE
;
874 InMemoryDataSource
* datasource
= new InMemoryDataSource(aOuter
);
876 return NS_ERROR_OUT_OF_MEMORY
;
877 NS_ADDREF(datasource
);
879 nsresult rv
= datasource
->Init();
880 if (NS_SUCCEEDED(rv
)) {
881 datasource
->fAggregated
.AddRef();
882 rv
= datasource
->AggregatedQueryInterface(aIID
, aResult
); // This'll AddRef()
883 datasource
->fAggregated
.Release();
886 NS_RELEASE(datasource
);
891 InMemoryDataSource::InMemoryDataSource(nsISupports
* aOuter
)
892 : mNumObservers(0), mReadCount(0)
894 NS_INIT_AGGREGATED(aOuter
);
896 static const size_t kBucketSizes
[] = {
899 sizeof(InMemoryArcsEnumeratorImpl
),
900 sizeof(InMemoryAssertionEnumeratorImpl
) };
902 static const PRInt32 kNumBuckets
= sizeof(kBucketSizes
) / sizeof(size_t);
904 // Per news://news.mozilla.org/39BEC105.5090206%40netscape.com
905 static const PRInt32 kInitialSize
= 1024;
907 mAllocator
.Init("nsInMemoryDataSource", kBucketSizes
, kNumBuckets
, kInitialSize
);
909 mForwardArcs
.ops
= nsnull
;
910 mReverseArcs
.ops
= nsnull
;
911 mPropagateChanges
= PR_TRUE
;
916 InMemoryDataSource::Init()
918 if (!PL_DHashTableInit(&mForwardArcs
,
919 PL_DHashGetStubOps(),
922 PL_DHASH_MIN_SIZE
)) {
923 mForwardArcs
.ops
= nsnull
;
924 return NS_ERROR_OUT_OF_MEMORY
;
926 if (!PL_DHashTableInit(&mReverseArcs
,
927 PL_DHashGetStubOps(),
930 PL_DHASH_MIN_SIZE
)) {
931 mReverseArcs
.ops
= nsnull
;
932 return NS_ERROR_OUT_OF_MEMORY
;
937 gLog
= PR_NewLogModule("InMemoryDataSource");
944 InMemoryDataSource::~InMemoryDataSource()
948 fprintf(stdout
, "%d - RDF: InMemoryDataSource\n", gInstanceCount
);
951 if (mForwardArcs
.ops
) {
952 // This'll release all of the Assertion objects that are
953 // associated with this data source. We only need to do this
954 // for the forward arcs, because the reverse arcs table
955 // indexes the exact same set of resources.
956 PL_DHashTableEnumerate(&mForwardArcs
, DeleteForwardArcsEntry
, &mAllocator
);
957 PL_DHashTableFinish(&mForwardArcs
);
959 if (mReverseArcs
.ops
)
960 PL_DHashTableFinish(&mReverseArcs
);
962 PR_LOG(gLog
, PR_LOG_NOTICE
,
963 ("InMemoryDataSource(%p): destroyed.", this));
968 InMemoryDataSource::DeleteForwardArcsEntry(PLDHashTable
* aTable
, PLDHashEntryHdr
* aHdr
,
969 PRUint32 aNumber
, void* aArg
)
971 Entry
* entry
= reinterpret_cast<Entry
*>(aHdr
);
972 nsFixedSizeAllocator
* allocator
= static_cast<nsFixedSizeAllocator
*>(aArg
);
974 Assertion
* as
= entry
->mAssertions
;
976 Assertion
* doomed
= as
;
979 // Unlink, and release the datasource's reference.
980 doomed
->mNext
= doomed
->u
.as
.mInvNext
= nsnull
;
981 doomed
->Release(*allocator
);
983 return PL_DHASH_NEXT
;
987 ////////////////////////////////////////////////////////////////////////
989 NS_IMPL_CYCLE_COLLECTION_CLASS(InMemoryDataSource
)
990 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(InMemoryDataSource
)
991 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mObservers
)
992 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
993 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_AGGREGATED(InMemoryDataSource
)
994 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mObservers
)
995 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
997 NS_IMPL_CYCLE_COLLECTING_AGGREGATED(InMemoryDataSource
)
998 NS_INTERFACE_MAP_BEGIN_AGGREGATED(InMemoryDataSource
)
999 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION_AGGREGATED(InMemoryDataSource
)
1000 NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource
)
1001 NS_INTERFACE_MAP_ENTRY(nsIRDFInMemoryDataSource
)
1002 NS_INTERFACE_MAP_ENTRY(nsIRDFPropagatableDataSource
)
1003 NS_INTERFACE_MAP_ENTRY(nsIRDFPurgeableDataSource
)
1004 NS_INTERFACE_MAP_ENTRY(rdfIDataSource
)
1005 NS_INTERFACE_MAP_END
1007 ////////////////////////////////////////////////////////////////////////
1012 InMemoryDataSource::LogOperation(const char* aOperation
,
1013 nsIRDFResource
* aSource
,
1014 nsIRDFResource
* aProperty
,
1015 nsIRDFNode
* aTarget
,
1018 if (! PR_LOG_TEST(gLog
, PR_LOG_NOTICE
))
1022 aSource
->GetValue(getter_Copies(uri
));
1024 ("InMemoryDataSource(%p): %s", this, aOperation
);
1027 (" [(%p)%s]--", aSource
, (const char*) uri
);
1029 aProperty
->GetValue(getter_Copies(uri
));
1031 char tv
= (aTruthValue
? '-' : '!');
1033 (" --%c[(%p)%s]--", tv
, aProperty
, (const char*) uri
);
1035 nsCOMPtr
<nsIRDFResource
> resource
;
1036 nsCOMPtr
<nsIRDFLiteral
> literal
;
1038 if ((resource
= do_QueryInterface(aTarget
)) != nsnull
) {
1039 resource
->GetValue(getter_Copies(uri
));
1041 (" -->[(%p)%s]", aTarget
, (const char*) uri
);
1043 else if ((literal
= do_QueryInterface(aTarget
)) != nsnull
) {
1044 nsXPIDLString value
;
1045 literal
->GetValue(getter_Copies(value
));
1046 nsAutoString
valueStr(value
);
1047 char* valueCStr
= ToNewCString(valueStr
);
1050 (" -->(\"%s\")\n", valueCStr
);
1056 (" -->(unknown-type)\n");
1063 InMemoryDataSource::GetURI(char* *uri
)
1065 NS_PRECONDITION(uri
!= nsnull
, "null ptr");
1067 return NS_ERROR_NULL_POINTER
;
1074 InMemoryDataSource::GetSource(nsIRDFResource
* property
,
1077 nsIRDFResource
** source
)
1079 NS_PRECONDITION(source
!= nsnull
, "null ptr");
1081 return NS_ERROR_NULL_POINTER
;
1083 NS_PRECONDITION(property
!= nsnull
, "null ptr");
1085 return NS_ERROR_NULL_POINTER
;
1087 NS_PRECONDITION(target
!= nsnull
, "null ptr");
1089 return NS_ERROR_NULL_POINTER
;
1091 for (Assertion
* as
= GetReverseArcs(target
); as
; as
= as
->u
.as
.mInvNext
) {
1092 if ((property
== as
->u
.as
.mProperty
) && (tv
== as
->u
.as
.mTruthValue
)) {
1093 *source
= as
->mSource
;
1099 return NS_RDF_NO_VALUE
;
1103 InMemoryDataSource::GetTarget(nsIRDFResource
* source
,
1104 nsIRDFResource
* property
,
1106 nsIRDFNode
** target
)
1108 NS_PRECONDITION(source
!= nsnull
, "null ptr");
1110 return NS_ERROR_NULL_POINTER
;
1112 NS_PRECONDITION(property
!= nsnull
, "null ptr");
1114 return NS_ERROR_NULL_POINTER
;
1116 NS_PRECONDITION(target
!= nsnull
, "null ptr");
1118 return NS_ERROR_NULL_POINTER
;
1120 Assertion
*as
= GetForwardArcs(source
);
1121 if (as
&& as
->mHashEntry
) {
1122 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(as
->u
.hash
.mPropertyHash
, property
, PL_DHASH_LOOKUP
);
1123 Assertion
* val
= PL_DHASH_ENTRY_IS_BUSY(hdr
)
1124 ? reinterpret_cast<Entry
*>(hdr
)->mAssertions
1127 if (tv
== val
->u
.as
.mTruthValue
) {
1128 *target
= val
->u
.as
.mTarget
;
1129 NS_IF_ADDREF(*target
);
1136 for (; as
!= nsnull
; as
= as
->mNext
) {
1137 if ((property
== as
->u
.as
.mProperty
) && (tv
== (as
->u
.as
.mTruthValue
))) {
1138 *target
= as
->u
.as
.mTarget
;
1144 // If we get here, then there was no target with for the specified
1145 // property & truth value.
1147 return NS_RDF_NO_VALUE
;
1151 InMemoryDataSource::HasAssertion(nsIRDFResource
* source
,
1152 nsIRDFResource
* property
,
1155 PRBool
* hasAssertion
)
1158 return NS_ERROR_NULL_POINTER
;
1161 return NS_ERROR_NULL_POINTER
;
1164 return NS_ERROR_NULL_POINTER
;
1166 Assertion
*as
= GetForwardArcs(source
);
1167 if (as
&& as
->mHashEntry
) {
1168 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(as
->u
.hash
.mPropertyHash
, property
, PL_DHASH_LOOKUP
);
1169 Assertion
* val
= PL_DHASH_ENTRY_IS_BUSY(hdr
)
1170 ? reinterpret_cast<Entry
*>(hdr
)->mAssertions
1173 if ((val
->u
.as
.mTarget
== target
) && (tv
== (val
->u
.as
.mTruthValue
))) {
1174 *hasAssertion
= PR_TRUE
;
1181 for (; as
!= nsnull
; as
= as
->mNext
) {
1182 // check target first as its most unique
1183 if (target
!= as
->u
.as
.mTarget
)
1186 if (property
!= as
->u
.as
.mProperty
)
1189 if (tv
!= (as
->u
.as
.mTruthValue
))
1193 *hasAssertion
= PR_TRUE
;
1197 // If we get here, we couldn't find the assertion
1198 *hasAssertion
= PR_FALSE
;
1203 InMemoryDataSource::GetSources(nsIRDFResource
* aProperty
,
1204 nsIRDFNode
* aTarget
,
1206 nsISimpleEnumerator
** aResult
)
1208 NS_PRECONDITION(aProperty
!= nsnull
, "null ptr");
1210 return NS_ERROR_NULL_POINTER
;
1212 NS_PRECONDITION(aTarget
!= nsnull
, "null ptr");
1214 return NS_ERROR_NULL_POINTER
;
1216 NS_PRECONDITION(aResult
!= nsnull
, "null ptr");
1218 return NS_ERROR_NULL_POINTER
;
1220 InMemoryAssertionEnumeratorImpl
* result
=
1221 InMemoryAssertionEnumeratorImpl::Create(this, nsnull
, aProperty
,
1222 aTarget
, aTruthValue
);
1225 return NS_ERROR_OUT_OF_MEMORY
;
1234 InMemoryDataSource::GetTargets(nsIRDFResource
* aSource
,
1235 nsIRDFResource
* aProperty
,
1237 nsISimpleEnumerator
** aResult
)
1239 NS_PRECONDITION(aSource
!= nsnull
, "null ptr");
1241 return NS_ERROR_NULL_POINTER
;
1243 NS_PRECONDITION(aProperty
!= nsnull
, "null ptr");
1245 return NS_ERROR_NULL_POINTER
;
1247 NS_PRECONDITION(aResult
!= nsnull
, "null ptr");
1249 return NS_ERROR_NULL_POINTER
;
1251 InMemoryAssertionEnumeratorImpl
* result
=
1252 InMemoryAssertionEnumeratorImpl::Create(this, aSource
, aProperty
,
1253 nsnull
, aTruthValue
);
1256 return NS_ERROR_OUT_OF_MEMORY
;
1266 InMemoryDataSource::LockedAssert(nsIRDFResource
* aSource
,
1267 nsIRDFResource
* aProperty
,
1268 nsIRDFNode
* aTarget
,
1272 LogOperation("ASSERT", aSource
, aProperty
, aTarget
, aTruthValue
);
1275 Assertion
* next
= GetForwardArcs(aSource
);
1276 Assertion
* prev
= next
;
1277 Assertion
* as
= nsnull
;
1279 PRBool haveHash
= (next
) ? next
->mHashEntry
: PR_FALSE
;
1281 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(next
->u
.hash
.mPropertyHash
, aProperty
, PL_DHASH_LOOKUP
);
1282 Assertion
* val
= PL_DHASH_ENTRY_IS_BUSY(hdr
)
1283 ? reinterpret_cast<Entry
*>(hdr
)->mAssertions
1286 if (val
->u
.as
.mTarget
== aTarget
) {
1287 // Wow, we already had the assertion. Make sure that the
1288 // truth values are correct and bail.
1289 val
->u
.as
.mTruthValue
= aTruthValue
;
1298 // check target first as its most unique
1299 if (aTarget
== next
->u
.as
.mTarget
) {
1300 if (aProperty
== next
->u
.as
.mProperty
) {
1301 // Wow, we already had the assertion. Make sure that the
1302 // truth values are correct and bail.
1303 next
->u
.as
.mTruthValue
= aTruthValue
;
1313 as
= Assertion::Create(mAllocator
, aSource
, aProperty
, aTarget
, aTruthValue
);
1315 return NS_ERROR_OUT_OF_MEMORY
;
1317 // Add the datasource's owning reference.
1322 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(next
->u
.hash
.mPropertyHash
,
1323 aProperty
, PL_DHASH_LOOKUP
);
1324 Assertion
*asRef
= PL_DHASH_ENTRY_IS_BUSY(hdr
)
1325 ? reinterpret_cast<Entry
*>(hdr
)->mAssertions
1329 as
->mNext
= asRef
->mNext
;
1334 hdr
= PL_DHashTableOperate(next
->u
.hash
.mPropertyHash
,
1335 aProperty
, PL_DHASH_ADD
);
1338 Entry
* entry
= reinterpret_cast<Entry
*>(hdr
);
1339 entry
->mNode
= aProperty
;
1340 entry
->mAssertions
= as
;
1346 // Link it in to the "forward arcs" table
1348 SetForwardArcs(aSource
, as
);
1354 // Link it in to the "reverse arcs" table
1356 next
= GetReverseArcs(aTarget
);
1357 as
->u
.as
.mInvNext
= next
;
1359 SetReverseArcs(aTarget
, next
);
1365 InMemoryDataSource::Assert(nsIRDFResource
* aSource
,
1366 nsIRDFResource
* aProperty
,
1367 nsIRDFNode
* aTarget
,
1370 NS_PRECONDITION(aSource
!= nsnull
, "null ptr");
1372 return NS_ERROR_NULL_POINTER
;
1374 NS_PRECONDITION(aProperty
!= nsnull
, "null ptr");
1376 return NS_ERROR_NULL_POINTER
;
1378 NS_PRECONDITION(aTarget
!= nsnull
, "null ptr");
1380 return NS_ERROR_NULL_POINTER
;
1383 NS_WARNING("Writing to InMemoryDataSource during read\n");
1384 return NS_RDF_ASSERTION_REJECTED
;
1388 rv
= LockedAssert(aSource
, aProperty
, aTarget
, aTruthValue
);
1389 if (NS_FAILED(rv
)) return rv
;
1392 for (PRInt32 i
= (PRInt32
)mNumObservers
- 1; mPropagateChanges
&& i
>= 0; --i
) {
1393 nsIRDFObserver
* obs
= mObservers
[i
];
1395 // XXX this should never happen, but it does, and we can't figure out why.
1396 NS_ASSERTION(obs
, "observer array corrupted!");
1400 obs
->OnAssert(this, aSource
, aProperty
, aTarget
);
1401 // XXX ignore return value?
1404 return NS_RDF_ASSERTION_ACCEPTED
;
1409 InMemoryDataSource::LockedUnassert(nsIRDFResource
* aSource
,
1410 nsIRDFResource
* aProperty
,
1411 nsIRDFNode
* aTarget
)
1414 LogOperation("UNASSERT", aSource
, aProperty
, aTarget
);
1417 Assertion
* next
= GetForwardArcs(aSource
);
1418 Assertion
* prev
= next
;
1419 Assertion
* root
= next
;
1420 Assertion
* as
= nsnull
;
1422 PRBool haveHash
= (next
) ? next
->mHashEntry
: PR_FALSE
;
1424 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(next
->u
.hash
.mPropertyHash
,
1425 aProperty
, PL_DHASH_LOOKUP
);
1426 prev
= next
= PL_DHASH_ENTRY_IS_BUSY(hdr
)
1427 ? reinterpret_cast<Entry
*>(hdr
)->mAssertions
1429 PRBool first
= PR_TRUE
;
1431 if (aTarget
== next
->u
.as
.mTarget
) {
1438 // We don't even have the assertion, so just bail.
1445 PL_DHashTableRawRemove(root
->u
.hash
.mPropertyHash
, hdr
);
1447 if (next
&& next
->mNext
) {
1448 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(root
->u
.hash
.mPropertyHash
,
1449 aProperty
, PL_DHASH_ADD
);
1451 Entry
* entry
= reinterpret_cast<Entry
*>(hdr
);
1452 entry
->mNode
= aProperty
;
1453 entry
->mAssertions
= next
->mNext
;
1457 // If this second-level hash empties out, clean it up.
1458 if (!root
->u
.hash
.mPropertyHash
->entryCount
) {
1459 Assertion::Destroy(mAllocator
, root
);
1460 SetForwardArcs(aSource
, nsnull
);
1465 prev
->mNext
= next
->mNext
;
1471 // check target first as its most unique
1472 if (aTarget
== next
->u
.as
.mTarget
) {
1473 if (aProperty
== next
->u
.as
.mProperty
) {
1475 SetForwardArcs(aSource
, next
->mNext
);
1477 prev
->mNext
= next
->mNext
;
1488 // We don't even have the assertion, so just bail.
1493 PRBool foundReverseArc
= PR_FALSE
;
1496 next
= prev
= GetReverseArcs(aTarget
);
1500 SetReverseArcs(aTarget
, next
->u
.as
.mInvNext
);
1502 prev
->u
.as
.mInvNext
= next
->u
.as
.mInvNext
;
1505 foundReverseArc
= PR_TRUE
;
1510 next
= next
->u
.as
.mInvNext
;
1514 NS_ASSERTION(foundReverseArc
, "in-memory db corrupted: unable to find reverse arc");
1517 // Unlink, and release the datasource's reference
1518 as
->mNext
= as
->u
.as
.mInvNext
= nsnull
;
1519 as
->Release(mAllocator
);
1525 InMemoryDataSource::Unassert(nsIRDFResource
* aSource
,
1526 nsIRDFResource
* aProperty
,
1527 nsIRDFNode
* aTarget
)
1529 NS_PRECONDITION(aSource
!= nsnull
, "null ptr");
1531 return NS_ERROR_NULL_POINTER
;
1533 NS_PRECONDITION(aProperty
!= nsnull
, "null ptr");
1535 return NS_ERROR_NULL_POINTER
;
1537 NS_PRECONDITION(aTarget
!= nsnull
, "null ptr");
1539 return NS_ERROR_NULL_POINTER
;
1542 NS_WARNING("Writing to InMemoryDataSource during read\n");
1543 return NS_RDF_ASSERTION_REJECTED
;
1548 rv
= LockedUnassert(aSource
, aProperty
, aTarget
);
1549 if (NS_FAILED(rv
)) return rv
;
1552 for (PRInt32 i
= PRInt32(mNumObservers
) - 1; mPropagateChanges
&& i
>= 0; --i
) {
1553 nsIRDFObserver
* obs
= mObservers
[i
];
1555 // XXX this should never happen, but it does, and we can't figure out why.
1556 NS_ASSERTION(obs
, "observer array corrupted!");
1560 obs
->OnUnassert(this, aSource
, aProperty
, aTarget
);
1561 // XXX ignore return value?
1564 return NS_RDF_ASSERTION_ACCEPTED
;
1569 InMemoryDataSource::Change(nsIRDFResource
* aSource
,
1570 nsIRDFResource
* aProperty
,
1571 nsIRDFNode
* aOldTarget
,
1572 nsIRDFNode
* aNewTarget
)
1574 NS_PRECONDITION(aSource
!= nsnull
, "null ptr");
1576 return NS_ERROR_NULL_POINTER
;
1578 NS_PRECONDITION(aProperty
!= nsnull
, "null ptr");
1580 return NS_ERROR_NULL_POINTER
;
1582 NS_PRECONDITION(aOldTarget
!= nsnull
, "null ptr");
1584 return NS_ERROR_NULL_POINTER
;
1586 NS_PRECONDITION(aNewTarget
!= nsnull
, "null ptr");
1588 return NS_ERROR_NULL_POINTER
;
1591 NS_WARNING("Writing to InMemoryDataSource during read\n");
1592 return NS_RDF_ASSERTION_REJECTED
;
1597 // XXX We can implement LockedChange() if we decide that this
1598 // is a performance bottleneck.
1600 rv
= LockedUnassert(aSource
, aProperty
, aOldTarget
);
1601 if (NS_FAILED(rv
)) return rv
;
1603 rv
= LockedAssert(aSource
, aProperty
, aNewTarget
, PR_TRUE
);
1604 if (NS_FAILED(rv
)) return rv
;
1607 for (PRInt32 i
= PRInt32(mNumObservers
) - 1; mPropagateChanges
&& i
>= 0; --i
) {
1608 nsIRDFObserver
* obs
= mObservers
[i
];
1610 // XXX this should never happen, but it does, and we can't figure out why.
1611 NS_ASSERTION(obs
, "observer array corrupted!");
1615 obs
->OnChange(this, aSource
, aProperty
, aOldTarget
, aNewTarget
);
1616 // XXX ignore return value?
1619 return NS_RDF_ASSERTION_ACCEPTED
;
1624 InMemoryDataSource::Move(nsIRDFResource
* aOldSource
,
1625 nsIRDFResource
* aNewSource
,
1626 nsIRDFResource
* aProperty
,
1627 nsIRDFNode
* aTarget
)
1629 NS_PRECONDITION(aOldSource
!= nsnull
, "null ptr");
1631 return NS_ERROR_NULL_POINTER
;
1633 NS_PRECONDITION(aNewSource
!= nsnull
, "null ptr");
1635 return NS_ERROR_NULL_POINTER
;
1637 NS_PRECONDITION(aProperty
!= nsnull
, "null ptr");
1639 return NS_ERROR_NULL_POINTER
;
1641 NS_PRECONDITION(aTarget
!= nsnull
, "null ptr");
1643 return NS_ERROR_NULL_POINTER
;
1646 NS_WARNING("Writing to InMemoryDataSource during read\n");
1647 return NS_RDF_ASSERTION_REJECTED
;
1652 // XXX We can implement LockedMove() if we decide that this
1653 // is a performance bottleneck.
1655 rv
= LockedUnassert(aOldSource
, aProperty
, aTarget
);
1656 if (NS_FAILED(rv
)) return rv
;
1658 rv
= LockedAssert(aNewSource
, aProperty
, aTarget
, PR_TRUE
);
1659 if (NS_FAILED(rv
)) return rv
;
1662 for (PRInt32 i
= PRInt32(mNumObservers
) - 1; mPropagateChanges
&& i
>= 0; --i
) {
1663 nsIRDFObserver
* obs
= mObservers
[i
];
1665 // XXX this should never happen, but it does, and we can't figure out why.
1666 NS_ASSERTION(obs
, "observer array corrupted!");
1670 obs
->OnMove(this, aOldSource
, aNewSource
, aProperty
, aTarget
);
1671 // XXX ignore return value?
1674 return NS_RDF_ASSERTION_ACCEPTED
;
1679 InMemoryDataSource::AddObserver(nsIRDFObserver
* aObserver
)
1681 NS_PRECONDITION(aObserver
!= nsnull
, "null ptr");
1683 return NS_ERROR_NULL_POINTER
;
1685 mObservers
.AppendObject(aObserver
);
1686 mNumObservers
= mObservers
.Count();
1692 InMemoryDataSource::RemoveObserver(nsIRDFObserver
* aObserver
)
1694 NS_PRECONDITION(aObserver
!= nsnull
, "null ptr");
1696 return NS_ERROR_NULL_POINTER
;
1698 mObservers
.RemoveObject(aObserver
);
1699 // note: use Count() instead of just decrementing
1700 // in case aObserver wasn't in list, for example
1701 mNumObservers
= mObservers
.Count();
1707 InMemoryDataSource::HasArcIn(nsIRDFNode
*aNode
, nsIRDFResource
*aArc
, PRBool
*result
)
1709 Assertion
* ass
= GetReverseArcs(aNode
);
1711 nsIRDFResource
* elbow
= ass
->u
.as
.mProperty
;
1712 if (elbow
== aArc
) {
1716 ass
= ass
->u
.as
.mInvNext
;
1723 InMemoryDataSource::HasArcOut(nsIRDFResource
*aSource
, nsIRDFResource
*aArc
, PRBool
*result
)
1725 Assertion
* ass
= GetForwardArcs(aSource
);
1726 if (ass
&& ass
->mHashEntry
) {
1727 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(ass
->u
.hash
.mPropertyHash
,
1728 aArc
, PL_DHASH_LOOKUP
);
1729 Assertion
* val
= PL_DHASH_ENTRY_IS_BUSY(hdr
)
1730 ? reinterpret_cast<Entry
*>(hdr
)->mAssertions
1739 nsIRDFResource
* elbow
= ass
->u
.as
.mProperty
;
1740 if (elbow
== aArc
) {
1751 InMemoryDataSource::ArcLabelsIn(nsIRDFNode
* aTarget
, nsISimpleEnumerator
** aResult
)
1753 NS_PRECONDITION(aTarget
!= nsnull
, "null ptr");
1755 return NS_ERROR_NULL_POINTER
;
1757 InMemoryArcsEnumeratorImpl
* result
=
1758 InMemoryArcsEnumeratorImpl::Create(this, nsnull
, aTarget
);
1761 return NS_ERROR_OUT_OF_MEMORY
;
1770 InMemoryDataSource::ArcLabelsOut(nsIRDFResource
* aSource
, nsISimpleEnumerator
** aResult
)
1772 NS_PRECONDITION(aSource
!= nsnull
, "null ptr");
1774 return NS_ERROR_NULL_POINTER
;
1776 InMemoryArcsEnumeratorImpl
* result
=
1777 InMemoryArcsEnumeratorImpl::Create(this, aSource
, nsnull
);
1780 return NS_ERROR_OUT_OF_MEMORY
;
1789 InMemoryDataSource::ResourceEnumerator(PLDHashTable
* aTable
,
1790 PLDHashEntryHdr
* aHdr
,
1791 PRUint32 aNumber
, void* aArg
)
1793 Entry
* entry
= reinterpret_cast<Entry
*>(aHdr
);
1794 nsISupportsArray
* resources
= static_cast<nsISupportsArray
*>(aArg
);
1796 resources
->AppendElement(entry
->mNode
);
1797 return PL_DHASH_NEXT
;
1802 InMemoryDataSource::GetAllResources(nsISimpleEnumerator
** aResult
)
1806 nsCOMPtr
<nsISupportsArray
> values
;
1807 rv
= NS_NewISupportsArray(getter_AddRefs(values
));
1808 if (NS_FAILED(rv
)) return rv
;
1810 // Enumerate all of our entries into an nsISupportsArray.
1811 PL_DHashTableEnumerate(&mForwardArcs
, ResourceEnumerator
, values
.get());
1813 return NS_NewArrayEnumerator(aResult
, values
);
1817 InMemoryDataSource::GetAllCmds(nsIRDFResource
* source
,
1818 nsISimpleEnumerator
/*<nsIRDFResource>*/** commands
)
1820 return(NS_NewEmptyEnumerator(commands
));
1824 InMemoryDataSource::IsCommandEnabled(nsISupportsArray
/*<nsIRDFResource>*/* aSources
,
1825 nsIRDFResource
* aCommand
,
1826 nsISupportsArray
/*<nsIRDFResource>*/* aArguments
,
1829 *aResult
= PR_FALSE
;
1834 InMemoryDataSource::DoCommand(nsISupportsArray
/*<nsIRDFResource>*/* aSources
,
1835 nsIRDFResource
* aCommand
,
1836 nsISupportsArray
/*<nsIRDFResource>*/* aArguments
)
1842 InMemoryDataSource::BeginUpdateBatch()
1844 for (PRInt32 i
= PRInt32(mNumObservers
) - 1; mPropagateChanges
&& i
>= 0; --i
) {
1845 nsIRDFObserver
* obs
= mObservers
[i
];
1846 obs
->OnBeginUpdateBatch(this);
1852 InMemoryDataSource::EndUpdateBatch()
1854 for (PRInt32 i
= PRInt32(mNumObservers
) - 1; mPropagateChanges
&& i
>= 0; --i
) {
1855 nsIRDFObserver
* obs
= mObservers
[i
];
1856 obs
->OnEndUpdateBatch(this);
1863 ////////////////////////////////////////////////////////////////////////
1864 // nsIRDFInMemoryDataSource methods
1867 InMemoryDataSource::EnsureFastContainment(nsIRDFResource
* aSource
)
1869 Assertion
*as
= GetForwardArcs(aSource
);
1870 PRBool haveHash
= (as
) ? as
->mHashEntry
: PR_FALSE
;
1872 // if its already a hash, then nothing to do
1873 if (haveHash
) return(NS_OK
);
1875 // convert aSource in forward hash into a hash
1876 Assertion
*hashAssertion
= Assertion::Create(mAllocator
, aSource
);
1877 NS_ASSERTION(hashAssertion
, "unable to Assertion::Create");
1878 if (!hashAssertion
) return(NS_ERROR_OUT_OF_MEMORY
);
1880 // Add the datasource's owning reference.
1881 hashAssertion
->AddRef();
1883 register Assertion
*first
= GetForwardArcs(aSource
);
1884 SetForwardArcs(aSource
, hashAssertion
);
1886 // mutate references of existing forward assertions into this hash
1887 PLDHashTable
*table
= hashAssertion
->u
.hash
.mPropertyHash
;
1890 nextRef
= first
->mNext
;
1891 nsIRDFResource
*prop
= first
->u
.as
.mProperty
;
1893 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(table
,
1894 prop
, PL_DHASH_LOOKUP
);
1895 Assertion
* val
= PL_DHASH_ENTRY_IS_BUSY(hdr
)
1896 ? reinterpret_cast<Entry
*>(hdr
)->mAssertions
1899 first
->mNext
= val
->mNext
;
1903 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(table
,
1904 prop
, PL_DHASH_ADD
);
1906 Entry
* entry
= reinterpret_cast<Entry
*>(hdr
);
1907 entry
->mNode
= prop
;
1908 entry
->mAssertions
= first
;
1909 first
->mNext
= nsnull
;
1918 ////////////////////////////////////////////////////////////////////////
1919 // nsIRDFPropagatableDataSource methods
1921 InMemoryDataSource::GetPropagateChanges(PRBool
* aPropagateChanges
)
1923 *aPropagateChanges
= mPropagateChanges
;
1928 InMemoryDataSource::SetPropagateChanges(PRBool aPropagateChanges
)
1930 mPropagateChanges
= aPropagateChanges
;
1935 ////////////////////////////////////////////////////////////////////////
1936 // nsIRDFPurgeableDataSource methods
1939 InMemoryDataSource::Mark(nsIRDFResource
* aSource
,
1940 nsIRDFResource
* aProperty
,
1941 nsIRDFNode
* aTarget
,
1945 NS_PRECONDITION(aSource
!= nsnull
, "null ptr");
1947 return NS_ERROR_NULL_POINTER
;
1949 NS_PRECONDITION(aProperty
!= nsnull
, "null ptr");
1951 return NS_ERROR_NULL_POINTER
;
1953 NS_PRECONDITION(aTarget
!= nsnull
, "null ptr");
1955 return NS_ERROR_NULL_POINTER
;
1957 Assertion
*as
= GetForwardArcs(aSource
);
1958 if (as
&& as
->mHashEntry
) {
1959 PLDHashEntryHdr
* hdr
= PL_DHashTableOperate(as
->u
.hash
.mPropertyHash
,
1960 aProperty
, PL_DHASH_LOOKUP
);
1961 Assertion
* val
= PL_DHASH_ENTRY_IS_BUSY(hdr
)
1962 ? reinterpret_cast<Entry
*>(hdr
)->mAssertions
1965 if ((val
->u
.as
.mTarget
== aTarget
) &&
1966 (aTruthValue
== (val
->u
.as
.mTruthValue
))) {
1968 // found it! so mark it.
1970 *aDidMark
= PR_TRUE
;
1973 LogOperation("MARK", aSource
, aProperty
, aTarget
, aTruthValue
);
1981 else for (; as
!= nsnull
; as
= as
->mNext
) {
1982 // check target first as its most unique
1983 if (aTarget
!= as
->u
.as
.mTarget
)
1986 if (aProperty
!= as
->u
.as
.mProperty
)
1989 if (aTruthValue
!= (as
->u
.as
.mTruthValue
))
1992 // found it! so mark it.
1994 *aDidMark
= PR_TRUE
;
1997 LogOperation("MARK", aSource
, aProperty
, aTarget
, aTruthValue
);
2003 // If we get here, we couldn't find the assertion
2004 *aDidMark
= PR_FALSE
;
2010 Assertion
* mUnassertList
;
2011 PLDHashTable
* mReverseArcs
;
2012 nsFixedSizeAllocator
* mAllocator
;
2016 InMemoryDataSource::Sweep()
2018 SweepInfo info
= { nsnull
, &mReverseArcs
, &mAllocator
};
2020 // Remove all the assertions, but don't notify anyone.
2021 PL_DHashTableEnumerate(&mForwardArcs
, SweepForwardArcsEntries
, &info
);
2023 // Now do the notification.
2024 Assertion
* as
= info
.mUnassertList
;
2027 LogOperation("SWEEP", as
->mSource
, as
->u
.as
.mProperty
, as
->u
.as
.mTarget
, as
->u
.as
.mTruthValue
);
2029 if (!(as
->mHashEntry
))
2031 for (PRInt32 i
= PRInt32(mNumObservers
) - 1; mPropagateChanges
&& i
>= 0; --i
) {
2032 nsIRDFObserver
* obs
= mObservers
[i
];
2033 // XXXbz other loops over mObservers null-check |obs| here!
2034 obs
->OnUnassert(this, as
->mSource
, as
->u
.as
.mProperty
, as
->u
.as
.mTarget
);
2035 // XXX ignore return value?
2039 Assertion
* doomed
= as
;
2042 // Unlink, and release the datasource's reference
2043 doomed
->mNext
= doomed
->u
.as
.mInvNext
= nsnull
;
2044 doomed
->Release(mAllocator
);
2052 InMemoryDataSource::SweepForwardArcsEntries(PLDHashTable
* aTable
,
2053 PLDHashEntryHdr
* aHdr
,
2054 PRUint32 aNumber
, void* aArg
)
2056 PLDHashOperator result
= PL_DHASH_NEXT
;
2057 Entry
* entry
= reinterpret_cast<Entry
*>(aHdr
);
2058 SweepInfo
* info
= static_cast<SweepInfo
*>(aArg
);
2060 Assertion
* as
= entry
->mAssertions
;
2061 if (as
&& (as
->mHashEntry
))
2063 // Stuff in sub-hashes must be swept recursively (max depth: 1)
2064 PL_DHashTableEnumerate(as
->u
.hash
.mPropertyHash
,
2065 SweepForwardArcsEntries
, info
);
2067 // If the sub-hash is now empty, clean it up.
2068 if (!as
->u
.hash
.mPropertyHash
->entryCount
) {
2069 Assertion::Destroy(*info
->mAllocator
, as
);
2070 result
= PL_DHASH_REMOVE
;
2076 Assertion
* prev
= nsnull
;
2078 if (as
->IsMarked()) {
2084 // remove from the list of assertions in the datasource
2085 Assertion
* next
= as
->mNext
;
2090 // it's the first one. update the hashtable entry.
2091 entry
->mAssertions
= next
;
2094 // remove from the reverse arcs
2095 PLDHashEntryHdr
* hdr
=
2096 PL_DHashTableOperate(info
->mReverseArcs
, as
->u
.as
.mTarget
, PL_DHASH_LOOKUP
);
2097 NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(hdr
), "no assertion in reverse arcs");
2099 Entry
* rentry
= reinterpret_cast<Entry
*>(hdr
);
2100 Assertion
* ras
= rentry
->mAssertions
;
2101 Assertion
* rprev
= nsnull
;
2105 rprev
->u
.as
.mInvNext
= ras
->u
.as
.mInvNext
;
2108 // it's the first one. update the hashtable entry.
2109 rentry
->mAssertions
= ras
->u
.as
.mInvNext
;
2111 as
->u
.as
.mInvNext
= nsnull
; // for my sanity.
2115 ras
= ras
->u
.as
.mInvNext
;
2118 // Wow, it was the _only_ one. Unhash it.
2119 if (! rentry
->mAssertions
)
2121 PL_DHashTableRawRemove(info
->mReverseArcs
, hdr
);
2124 // add to the list of assertions to unassert
2125 as
->mNext
= info
->mUnassertList
;
2126 info
->mUnassertList
= as
;
2128 // Advance to the next assertion
2133 // if no more assertions exist for this resource, then unhash it.
2134 if (! entry
->mAssertions
)
2135 result
= PL_DHASH_REMOVE
;
2140 ////////////////////////////////////////////////////////////////////////
2141 // rdfIDataSource methods
2143 class VisitorClosure
2146 VisitorClosure(rdfITripleVisitor
* aVisitor
) :
2150 rdfITripleVisitor
* mVisitor
;
2155 SubjectEnumerator(PLDHashTable
* aTable
, PLDHashEntryHdr
* aHdr
,
2156 PRUint32 aNumber
, void* aArg
) {
2157 Entry
* entry
= reinterpret_cast<Entry
*>(aHdr
);
2158 VisitorClosure
* closure
= static_cast<VisitorClosure
*>(aArg
);
2161 nsCOMPtr
<nsIRDFNode
> subject
= do_QueryInterface(entry
->mNode
, &rv
);
2162 NS_ENSURE_SUCCESS(rv
, PL_DHASH_NEXT
);
2164 closure
->mRv
= closure
->mVisitor
->Visit(subject
, nsnull
, nsnull
, PR_TRUE
);
2165 if (NS_FAILED(closure
->mRv
) || closure
->mRv
== NS_RDF_STOP_VISIT
)
2166 return PL_DHASH_STOP
;
2168 return PL_DHASH_NEXT
;
2172 InMemoryDataSource::VisitAllSubjects(rdfITripleVisitor
*aVisitor
)
2174 // Lock datasource against writes
2177 // Enumerate all of our entries into an nsISupportsArray.
2178 VisitorClosure
cls(aVisitor
);
2179 PL_DHashTableEnumerate(&mForwardArcs
, SubjectEnumerator
, &cls
);
2181 // Unlock datasource
2187 class TriplesInnerClosure
2190 TriplesInnerClosure(nsIRDFNode
* aSubject
, VisitorClosure
* aClosure
) :
2191 mSubject(aSubject
), mOuter(aClosure
) {}
2192 nsIRDFNode
* mSubject
;
2193 VisitorClosure
* mOuter
;
2197 TriplesInnerEnumerator(PLDHashTable
* aTable
, PLDHashEntryHdr
* aHdr
,
2198 PRUint32 aNumber
, void* aArg
) {
2199 Entry
* entry
= reinterpret_cast<Entry
*>(aHdr
);
2200 Assertion
* assertion
= entry
->mAssertions
;
2201 TriplesInnerClosure
* closure
=
2202 static_cast<TriplesInnerClosure
*>(aArg
);
2204 NS_ASSERTION(!assertion
->mHashEntry
, "shouldn't have to hashes");
2205 VisitorClosure
* cls
= closure
->mOuter
;
2206 cls
->mRv
= cls
->mVisitor
->Visit(closure
->mSubject
,
2207 assertion
->u
.as
.mProperty
,
2208 assertion
->u
.as
.mTarget
,
2209 assertion
->u
.as
.mTruthValue
);
2210 if (NS_FAILED(cls
->mRv
) || cls
->mRv
== NS_RDF_STOP_VISIT
) {
2211 return PL_DHASH_STOP
;
2213 assertion
= assertion
->mNext
;
2215 return PL_DHASH_NEXT
;
2218 TriplesEnumerator(PLDHashTable
* aTable
, PLDHashEntryHdr
* aHdr
,
2219 PRUint32 aNumber
, void* aArg
) {
2220 Entry
* entry
= reinterpret_cast<Entry
*>(aHdr
);
2221 VisitorClosure
* closure
= static_cast<VisitorClosure
*>(aArg
);
2224 nsCOMPtr
<nsIRDFNode
> subject
= do_QueryInterface(entry
->mNode
, &rv
);
2225 NS_ENSURE_SUCCESS(rv
, PL_DHASH_NEXT
);
2227 if (entry
->mAssertions
->mHashEntry
) {
2228 TriplesInnerClosure
cls(subject
, closure
);
2229 PL_DHashTableEnumerate(entry
->mAssertions
->u
.hash
.mPropertyHash
,
2230 TriplesInnerEnumerator
, &cls
);
2231 if (NS_FAILED(closure
->mRv
)) {
2232 return PL_DHASH_STOP
;
2234 return PL_DHASH_NEXT
;
2236 Assertion
* assertion
= entry
->mAssertions
;
2238 NS_ASSERTION(!assertion
->mHashEntry
, "shouldn't have to hashes");
2239 closure
->mRv
= closure
->mVisitor
->Visit(subject
,
2240 assertion
->u
.as
.mProperty
,
2241 assertion
->u
.as
.mTarget
,
2242 assertion
->u
.as
.mTruthValue
);
2243 if (NS_FAILED(closure
->mRv
) || closure
->mRv
== NS_RDF_STOP_VISIT
) {
2244 return PL_DHASH_STOP
;
2246 assertion
= assertion
->mNext
;
2248 return PL_DHASH_NEXT
;
2251 InMemoryDataSource::VisitAllTriples(rdfITripleVisitor
*aVisitor
)
2253 // Lock datasource against writes
2256 // Enumerate all of our entries into an nsISupportsArray.
2257 VisitorClosure
cls(aVisitor
);
2258 PL_DHashTableEnumerate(&mForwardArcs
, TriplesEnumerator
, &cls
);
2260 // Unlock datasource
2266 ////////////////////////////////////////////////////////////////////////