1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
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/
10 : *
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
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
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.
22 : *
23 : * Contributor(s):
24 : * Chris Waterson <waterson@netscape.com>
25 : *
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.
37 : *
38 : * ***** END LICENSE BLOCK *****
39 : *
40 : *
41 : * This Original Code has been modified by IBM Corporation.
42 : * Modifications made by IBM described herein are
43 : * Copyright (c) International Business Machines
44 : * Corporation, 2000
45 : *
46 : * Modifications to Mozilla code or documentation
47 : * identified per MPL Section 3.3
48 : *
49 : * Date Modified by Description of modification
50 : * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
51 : * use in OS2
52 : */
53 :
54 : /*
55 :
56 : Implementation for an in-memory RDF data store.
57 :
58 : TO DO
59 :
60 : 1) Instrument this code to gather space and time performance
61 : characteristics.
62 :
63 : 2) Optimize lookups for datasources which have a small number
64 : of properties + fanning out to a large number of targets.
65 :
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).
69 :
70 : */
71 :
72 : #include "nsAgg.h"
73 : #include "nsCOMPtr.h"
74 : #include "nscore.h"
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 "nsTArray.h"
89 : #include "nsCRT.h"
90 : #include "nsRDFCID.h"
91 : #include "nsRDFBaseDataSources.h"
92 : #include "nsString.h"
93 : #include "nsReadableUtils.h"
94 : #include "nsXPIDLString.h"
95 : #include "nsFixedSizeAllocator.h"
96 : #include "rdfutil.h"
97 : #include "pldhash.h"
98 : #include "plstr.h"
99 : #include "prlog.h"
100 : #include "rdf.h"
101 :
102 : #include "rdfIDataSource.h"
103 : #include "rdfITripleVisitor.h"
104 :
105 : #ifdef PR_LOGGING
106 : static PRLogModuleInfo* gLog = nsnull;
107 : #endif
108 :
109 :
110 : // This struct is used as the slot value in the forward and reverse
111 : // arcs hash tables.
112 : //
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.
116 : //
117 : class Assertion
118 : {
119 : public:
120 : static Assertion*
121 36438 : Create(nsFixedSizeAllocator& aAllocator,
122 : nsIRDFResource* aSource,
123 : nsIRDFResource* aProperty,
124 : nsIRDFNode* aTarget,
125 : bool aTruthValue) {
126 36438 : void* place = aAllocator.Alloc(sizeof(Assertion));
127 : return place
128 36438 : ? ::new (place) Assertion(aSource, aProperty, aTarget, aTruthValue)
129 72876 : : nsnull; }
130 : static Assertion*
131 0 : Create(nsFixedSizeAllocator& aAllocator, nsIRDFResource* aSource) {
132 0 : void* place = aAllocator.Alloc(sizeof(Assertion));
133 : return place
134 0 : ? ::new (place) Assertion(aSource)
135 0 : : nsnull; }
136 :
137 : static void
138 36438 : Destroy(nsFixedSizeAllocator& aAllocator, Assertion* aAssertion) {
139 36438 : if (aAssertion->mHashEntry && aAssertion->u.hash.mPropertyHash) {
140 : PL_DHashTableEnumerate(aAssertion->u.hash.mPropertyHash,
141 0 : DeletePropertyHashEntry, &aAllocator);
142 0 : PL_DHashTableDestroy(aAssertion->u.hash.mPropertyHash);
143 0 : aAssertion->u.hash.mPropertyHash = nsnull;
144 : }
145 36438 : aAssertion->~Assertion();
146 36438 : aAllocator.Free(aAssertion, sizeof(*aAssertion)); }
147 :
148 : static PLDHashOperator
149 : DeletePropertyHashEntry(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
150 : PRUint32 aNumber, void* aArg);
151 :
152 : Assertion(nsIRDFResource* aSource, // normal assertion
153 : nsIRDFResource* aProperty,
154 : nsIRDFNode* aTarget,
155 : bool aTruthValue);
156 : Assertion(nsIRDFResource* aSource); // PLDHashTable assertion variant
157 :
158 : ~Assertion();
159 :
160 102310 : void AddRef() {
161 102310 : if (mRefCnt == PR_UINT16_MAX) {
162 0 : NS_WARNING("refcount overflow, leaking Assertion");
163 0 : return;
164 : }
165 102310 : ++mRefCnt;
166 : }
167 :
168 102310 : void Release(nsFixedSizeAllocator& aAllocator) {
169 102310 : if (mRefCnt == PR_UINT16_MAX) {
170 0 : NS_WARNING("refcount overflow, leaking Assertion");
171 0 : return;
172 : }
173 102310 : if (--mRefCnt == 0)
174 36438 : Destroy(aAllocator, this);
175 : }
176 :
177 : // For nsIRDFPurgeableDataSource
178 371 : inline void Mark() { u.as.mMarked = true; }
179 339 : inline bool IsMarked() { return u.as.mMarked; }
180 339 : inline void Unmark() { u.as.mMarked = false; }
181 :
182 : // public for now, because I'm too lazy to go thru and clean this up.
183 :
184 : // These are shared between hash/as (see the union below)
185 : nsIRDFResource* mSource;
186 : Assertion* mNext;
187 :
188 : union
189 : {
190 : struct hash
191 : {
192 : PLDHashTable* mPropertyHash;
193 : } hash;
194 : struct as
195 : {
196 : nsIRDFResource* mProperty;
197 : nsIRDFNode* mTarget;
198 : Assertion* mInvNext;
199 : // make sure bool are final elements
200 : bool mTruthValue;
201 : bool mMarked;
202 : } as;
203 : } u;
204 :
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
208 : PRUint16 mRefCnt;
209 : bool mHashEntry;
210 :
211 : private:
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) {}
216 : };
217 :
218 :
219 : struct Entry {
220 : PLDHashEntryHdr mHdr;
221 : nsIRDFNode* mNode;
222 : Assertion* mAssertions;
223 : };
224 :
225 :
226 0 : Assertion::Assertion(nsIRDFResource* aSource)
227 : : mSource(aSource),
228 : mNext(nsnull),
229 : mRefCnt(0),
230 0 : mHashEntry(true)
231 : {
232 0 : MOZ_COUNT_CTOR(RDF_Assertion);
233 :
234 0 : NS_ADDREF(mSource);
235 :
236 : u.hash.mPropertyHash = PL_NewDHashTable(PL_DHashGetStubOps(),
237 0 : nsnull, sizeof(Entry), PL_DHASH_MIN_SIZE);
238 0 : }
239 :
240 36438 : Assertion::Assertion(nsIRDFResource* aSource,
241 : nsIRDFResource* aProperty,
242 : nsIRDFNode* aTarget,
243 : bool aTruthValue)
244 : : mSource(aSource),
245 : mNext(nsnull),
246 : mRefCnt(0),
247 36438 : mHashEntry(false)
248 : {
249 36438 : MOZ_COUNT_CTOR(RDF_Assertion);
250 :
251 36438 : u.as.mProperty = aProperty;
252 36438 : u.as.mTarget = aTarget;
253 :
254 36438 : NS_ADDREF(mSource);
255 36438 : NS_ADDREF(u.as.mProperty);
256 36438 : NS_ADDREF(u.as.mTarget);
257 :
258 36438 : u.as.mInvNext = nsnull;
259 36438 : u.as.mTruthValue = aTruthValue;
260 36438 : u.as.mMarked = false;
261 36438 : }
262 :
263 36438 : Assertion::~Assertion()
264 : {
265 36438 : MOZ_COUNT_DTOR(RDF_Assertion);
266 : #ifdef DEBUG_REFS
267 : --gInstanceCount;
268 : fprintf(stdout, "%d - RDF: Assertion\n", gInstanceCount);
269 : #endif
270 :
271 36438 : NS_RELEASE(mSource);
272 36438 : if (!mHashEntry)
273 : {
274 36438 : NS_RELEASE(u.as.mProperty);
275 36438 : NS_RELEASE(u.as.mTarget);
276 : }
277 36438 : }
278 :
279 : PLDHashOperator
280 0 : Assertion::DeletePropertyHashEntry(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
281 : PRUint32 aNumber, void* aArg)
282 : {
283 0 : Entry* entry = reinterpret_cast<Entry*>(aHdr);
284 0 : nsFixedSizeAllocator* allocator = static_cast<nsFixedSizeAllocator*>(aArg);
285 :
286 0 : Assertion* as = entry->mAssertions;
287 0 : while (as) {
288 0 : Assertion* doomed = as;
289 0 : as = as->mNext;
290 :
291 : // Unlink, and release the datasource's reference.
292 0 : doomed->mNext = doomed->u.as.mInvNext = nsnull;
293 0 : doomed->Release(*allocator);
294 : }
295 0 : return PL_DHASH_NEXT;
296 : }
297 :
298 :
299 :
300 : ////////////////////////////////////////////////////////////////////////
301 : // InMemoryDataSource
302 : class InMemoryArcsEnumeratorImpl;
303 : class InMemoryAssertionEnumeratorImpl;
304 : class InMemoryResourceEnumeratorImpl;
305 :
306 : class InMemoryDataSource : public nsIRDFDataSource,
307 : public nsIRDFInMemoryDataSource,
308 : public nsIRDFPropagatableDataSource,
309 : public nsIRDFPurgeableDataSource,
310 : public rdfIDataSource
311 : {
312 : protected:
313 : nsFixedSizeAllocator mAllocator;
314 :
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;
322 :
323 : nsCOMArray<nsIRDFObserver> mObservers;
324 : PRUint32 mNumObservers;
325 :
326 : // VisitFoo needs to block writes, [Un]Assert only allowed
327 : // during mReadCount == 0
328 : PRUint32 mReadCount;
329 :
330 : static PLDHashOperator
331 : DeleteForwardArcsEntry(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
332 : PRUint32 aNumber, void* aArg);
333 :
334 : static PLDHashOperator
335 : ResourceEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
336 : PRUint32 aNumber, void* aArg);
337 :
338 : friend class InMemoryArcsEnumeratorImpl;
339 : friend class InMemoryAssertionEnumeratorImpl;
340 : friend class InMemoryResourceEnumeratorImpl; // b/c it needs to enumerate mForwardArcs
341 :
342 : // Thread-safe writer implementation methods.
343 : nsresult
344 : LockedAssert(nsIRDFResource* source,
345 : nsIRDFResource* property,
346 : nsIRDFNode* target,
347 : bool tv);
348 :
349 : nsresult
350 : LockedUnassert(nsIRDFResource* source,
351 : nsIRDFResource* property,
352 : nsIRDFNode* target);
353 :
354 : InMemoryDataSource(nsISupports* aOuter);
355 : virtual ~InMemoryDataSource();
356 : nsresult Init();
357 :
358 : friend nsresult
359 : NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResult);
360 :
361 : public:
362 4168 : NS_DECL_CYCLE_COLLECTING_AGGREGATED
363 128310 : NS_DECL_AGGREGATED_CYCLE_COLLECTION_CLASS(InMemoryDataSource)
364 :
365 : // nsIRDFDataSource methods
366 : NS_DECL_NSIRDFDATASOURCE
367 :
368 : // nsIRDFInMemoryDataSource methods
369 : NS_DECL_NSIRDFINMEMORYDATASOURCE
370 :
371 : // nsIRDFPropagatableDataSource methods
372 : NS_DECL_NSIRDFPROPAGATABLEDATASOURCE
373 :
374 : // nsIRDFPurgeableDataSource methods
375 : NS_DECL_NSIRDFPURGEABLEDATASOURCE
376 :
377 : // rdfIDataSource methods
378 : NS_DECL_RDFIDATASOURCE
379 :
380 : protected:
381 : static PLDHashOperator
382 : SweepForwardArcsEntries(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
383 : PRUint32 aNumber, void* aArg);
384 :
385 : public:
386 : // Implementation methods
387 : Assertion*
388 107422 : GetForwardArcs(nsIRDFResource* u) {
389 107422 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(&mForwardArcs, u, PL_DHASH_LOOKUP);
390 107422 : return PL_DHASH_ENTRY_IS_BUSY(hdr)
391 : ? reinterpret_cast<Entry*>(hdr)->mAssertions
392 107422 : : nsnull; }
393 :
394 : Assertion*
395 38553 : GetReverseArcs(nsIRDFNode* v) {
396 38553 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(&mReverseArcs, v, PL_DHASH_LOOKUP);
397 38553 : return PL_DHASH_ENTRY_IS_BUSY(hdr)
398 : ? reinterpret_cast<Entry*>(hdr)->mAssertions
399 38553 : : nsnull; }
400 :
401 : void
402 10915 : SetForwardArcs(nsIRDFResource* u, Assertion* as) {
403 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(&mForwardArcs, u,
404 10915 : as ? PL_DHASH_ADD : PL_DHASH_REMOVE);
405 10915 : if (as && hdr) {
406 10901 : Entry* entry = reinterpret_cast<Entry*>(hdr);
407 10901 : entry->mNode = u;
408 10901 : entry->mAssertions = as;
409 10915 : } }
410 :
411 : void
412 37622 : SetReverseArcs(nsIRDFNode* v, Assertion* as) {
413 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(&mReverseArcs, v,
414 37622 : as ? PL_DHASH_ADD : PL_DHASH_REMOVE);
415 37622 : if (as && hdr) {
416 37078 : Entry* entry = reinterpret_cast<Entry*>(hdr);
417 37078 : entry->mNode = v;
418 37078 : entry->mAssertions = as;
419 37622 : } }
420 :
421 : #ifdef PR_LOGGING
422 : void
423 : LogOperation(const char* aOperation,
424 : nsIRDFResource* asource,
425 : nsIRDFResource* aProperty,
426 : nsIRDFNode* aTarget,
427 : bool aTruthValue = true);
428 : #endif
429 :
430 : bool mPropagateChanges;
431 : };
432 :
433 : //----------------------------------------------------------------------
434 : //
435 : // InMemoryAssertionEnumeratorImpl
436 : //
437 :
438 : /**
439 : * InMemoryAssertionEnumeratorImpl
440 : */
441 : class InMemoryAssertionEnumeratorImpl : public nsISimpleEnumerator
442 : {
443 : private:
444 : InMemoryDataSource* mDataSource;
445 : nsIRDFResource* mSource;
446 : nsIRDFResource* mProperty;
447 : nsIRDFNode* mTarget;
448 : nsIRDFNode* mValue;
449 : PRInt32 mCount;
450 : bool mTruthValue;
451 : Assertion* mNextAssertion;
452 : nsCOMPtr<nsISupportsArray> mHashArcs;
453 :
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 0 : static void operator delete(void*, size_t) {}
458 :
459 : InMemoryAssertionEnumeratorImpl(InMemoryDataSource* aDataSource,
460 : nsIRDFResource* aSource,
461 : nsIRDFResource* aProperty,
462 : nsIRDFNode* aTarget,
463 : bool aTruthValue);
464 :
465 : virtual ~InMemoryAssertionEnumeratorImpl();
466 :
467 : public:
468 : static InMemoryAssertionEnumeratorImpl*
469 13720 : Create(InMemoryDataSource* aDataSource,
470 : nsIRDFResource* aSource,
471 : nsIRDFResource* aProperty,
472 : nsIRDFNode* aTarget,
473 : bool aTruthValue) {
474 13720 : void* place = aDataSource->mAllocator.Alloc(sizeof(InMemoryAssertionEnumeratorImpl));
475 : return place
476 : ? ::new (place) InMemoryAssertionEnumeratorImpl(aDataSource,
477 : aSource, aProperty, aTarget,
478 13720 : aTruthValue)
479 27440 : : nsnull; }
480 :
481 : static void
482 13720 : Destroy(InMemoryAssertionEnumeratorImpl* aEnumerator) {
483 : // Keep the datasource alive for the duration of the stack
484 : // frame so its allocator stays valid.
485 27440 : nsCOMPtr<nsIRDFDataSource> kungFuDeathGrip = aEnumerator->mDataSource;
486 :
487 : // Grab the pool from the datasource; since we keep the
488 : // datasource alive, this has to be safe.
489 13720 : nsFixedSizeAllocator& pool = aEnumerator->mDataSource->mAllocator;
490 13720 : aEnumerator->~InMemoryAssertionEnumeratorImpl();
491 27440 : pool.Free(aEnumerator, sizeof(*aEnumerator)); }
492 :
493 : // nsISupports interface
494 : NS_DECL_ISUPPORTS
495 :
496 : // nsISimpleEnumerator interface
497 : NS_DECL_NSISIMPLEENUMERATOR
498 : };
499 :
500 : ////////////////////////////////////////////////////////////////////////
501 :
502 :
503 13720 : InMemoryAssertionEnumeratorImpl::InMemoryAssertionEnumeratorImpl(
504 : InMemoryDataSource* aDataSource,
505 : nsIRDFResource* aSource,
506 : nsIRDFResource* aProperty,
507 : nsIRDFNode* aTarget,
508 : bool aTruthValue)
509 : : mDataSource(aDataSource),
510 : mSource(aSource),
511 : mProperty(aProperty),
512 : mTarget(aTarget),
513 : mValue(nsnull),
514 : mCount(0),
515 : mTruthValue(aTruthValue),
516 13720 : mNextAssertion(nsnull)
517 : {
518 13720 : NS_ADDREF(mDataSource);
519 13720 : NS_IF_ADDREF(mSource);
520 13720 : NS_ADDREF(mProperty);
521 13720 : NS_IF_ADDREF(mTarget);
522 :
523 13720 : if (mSource) {
524 13715 : mNextAssertion = mDataSource->GetForwardArcs(mSource);
525 :
526 13715 : if (mNextAssertion && mNextAssertion->mHashEntry) {
527 : // its our magical HASH_ENTRY forward hash for assertions
528 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(mNextAssertion->u.hash.mPropertyHash,
529 0 : aProperty, PL_DHASH_LOOKUP);
530 0 : mNextAssertion = PL_DHASH_ENTRY_IS_BUSY(hdr)
531 : ? reinterpret_cast<Entry*>(hdr)->mAssertions
532 0 : : nsnull;
533 : }
534 : }
535 : else {
536 5 : mNextAssertion = mDataSource->GetReverseArcs(mTarget);
537 : }
538 :
539 : // Add an owning reference from the enumerator
540 13720 : if (mNextAssertion)
541 13720 : mNextAssertion->AddRef();
542 13720 : }
543 :
544 27440 : InMemoryAssertionEnumeratorImpl::~InMemoryAssertionEnumeratorImpl()
545 : {
546 : #ifdef DEBUG_REFS
547 : --gInstanceCount;
548 : fprintf(stdout, "%d - RDF: InMemoryAssertionEnumeratorImpl\n", gInstanceCount);
549 : #endif
550 :
551 13720 : if (mNextAssertion)
552 0 : mNextAssertion->Release(mDataSource->mAllocator);
553 :
554 13720 : NS_IF_RELEASE(mDataSource);
555 13720 : NS_IF_RELEASE(mSource);
556 13720 : NS_IF_RELEASE(mProperty);
557 13720 : NS_IF_RELEASE(mTarget);
558 13720 : NS_IF_RELEASE(mValue);
559 13720 : }
560 :
561 38925 : NS_IMPL_ADDREF(InMemoryAssertionEnumeratorImpl)
562 38925 : NS_IMPL_RELEASE_WITH_DESTROY(InMemoryAssertionEnumeratorImpl, Destroy(this))
563 100631 : NS_IMPL_QUERY_INTERFACE1(InMemoryAssertionEnumeratorImpl, nsISimpleEnumerator)
564 :
565 : NS_IMETHODIMP
566 24036 : InMemoryAssertionEnumeratorImpl::HasMoreElements(bool* aResult)
567 : {
568 24036 : if (mValue) {
569 4855 : *aResult = true;
570 4855 : return NS_OK;
571 : }
572 :
573 99379 : while (mNextAssertion) {
574 65872 : bool foundIt = false;
575 65872 : if ((mProperty == mNextAssertion->u.as.mProperty) &&
576 : (mTruthValue == mNextAssertion->u.as.mTruthValue)) {
577 4855 : if (mSource) {
578 4850 : mValue = mNextAssertion->u.as.mTarget;
579 4850 : NS_ADDREF(mValue);
580 : }
581 : else {
582 5 : mValue = mNextAssertion->mSource;
583 5 : NS_ADDREF(mValue);
584 : }
585 4855 : foundIt = true;
586 : }
587 :
588 : // Remember the last assertion we were holding on to
589 65872 : Assertion* as = mNextAssertion;
590 :
591 : // iterate
592 65872 : mNextAssertion = (mSource) ? mNextAssertion->mNext : mNextAssertion->u.as.mInvNext;
593 :
594 : // grab an owning reference from the enumerator to the next assertion
595 65872 : if (mNextAssertion)
596 52152 : mNextAssertion->AddRef();
597 :
598 : // ...and release the reference from the enumerator to the old one.
599 65872 : as->Release(mDataSource->mAllocator);
600 :
601 65872 : if (foundIt) {
602 4855 : *aResult = true;
603 4855 : return NS_OK;
604 : }
605 : }
606 :
607 14326 : *aResult = false;
608 14326 : return NS_OK;
609 : }
610 :
611 :
612 : NS_IMETHODIMP
613 4855 : InMemoryAssertionEnumeratorImpl::GetNext(nsISupports** aResult)
614 : {
615 : nsresult rv;
616 :
617 : bool hasMore;
618 4855 : rv = HasMoreElements(&hasMore);
619 4855 : if (NS_FAILED(rv)) return rv;
620 :
621 4855 : if (! hasMore)
622 0 : return NS_ERROR_UNEXPECTED;
623 :
624 : // Don't AddRef: we "transfer" ownership to the caller
625 4855 : *aResult = mValue;
626 4855 : mValue = nsnull;
627 :
628 4855 : return NS_OK;
629 : }
630 :
631 : ////////////////////////////////////////////////////////////////////////
632 : //
633 :
634 : /**
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 multiple inheritance clashes.
640 : */
641 :
642 : class InMemoryArcsEnumeratorImpl : public nsISimpleEnumerator
643 : {
644 : private:
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 0 : static void operator delete(void*, size_t) {}
649 :
650 : InMemoryDataSource* mDataSource;
651 : nsIRDFResource* mSource;
652 : nsIRDFNode* mTarget;
653 : nsAutoTArray<nsCOMPtr<nsIRDFResource>, 8> mAlreadyReturned;
654 : nsIRDFResource* mCurrent;
655 : Assertion* mAssertion;
656 : nsCOMPtr<nsISupportsArray> mHashArcs;
657 :
658 : InMemoryArcsEnumeratorImpl(InMemoryDataSource* aDataSource,
659 : nsIRDFResource* aSource,
660 : nsIRDFNode* aTarget);
661 :
662 : virtual ~InMemoryArcsEnumeratorImpl();
663 :
664 : static PLDHashOperator
665 : ArcEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
666 : PRUint32 aNumber, void* aArg);
667 :
668 : public:
669 : // nsISupports interface
670 : NS_DECL_ISUPPORTS
671 :
672 : // nsISimpleEnumerator interface
673 : NS_DECL_NSISIMPLEENUMERATOR
674 :
675 : static InMemoryArcsEnumeratorImpl*
676 648 : Create(InMemoryDataSource* aDataSource,
677 : nsIRDFResource* aSource,
678 : nsIRDFNode* aTarget) {
679 648 : void* place = aDataSource->mAllocator.Alloc(sizeof(InMemoryArcsEnumeratorImpl));
680 : return place
681 648 : ? ::new (place) InMemoryArcsEnumeratorImpl(aDataSource, aSource, aTarget)
682 1296 : : nsnull; }
683 :
684 : static void
685 648 : Destroy(InMemoryArcsEnumeratorImpl* aEnumerator) {
686 : // Keep the datasource alive for the duration of the stack
687 : // frame so its allocator stays valid.
688 1296 : nsCOMPtr<nsIRDFDataSource> kungFuDeathGrip = aEnumerator->mDataSource;
689 :
690 : // Grab the pool from the datasource; since we keep the
691 : // datasource alive, this has to be safe.
692 648 : nsFixedSizeAllocator& pool = aEnumerator->mDataSource->mAllocator;
693 648 : aEnumerator->~InMemoryArcsEnumeratorImpl();
694 1296 : pool.Free(aEnumerator, sizeof(*aEnumerator)); }
695 : };
696 :
697 :
698 : PLDHashOperator
699 0 : InMemoryArcsEnumeratorImpl::ArcEnumerator(PLDHashTable* aTable,
700 : PLDHashEntryHdr* aHdr,
701 : PRUint32 aNumber, void* aArg)
702 : {
703 0 : Entry* entry = reinterpret_cast<Entry*>(aHdr);
704 0 : nsISupportsArray* resources = static_cast<nsISupportsArray*>(aArg);
705 :
706 0 : resources->AppendElement(entry->mNode);
707 0 : return PL_DHASH_NEXT;
708 : }
709 :
710 :
711 648 : InMemoryArcsEnumeratorImpl::InMemoryArcsEnumeratorImpl(InMemoryDataSource* aDataSource,
712 : nsIRDFResource* aSource,
713 : nsIRDFNode* aTarget)
714 : : mDataSource(aDataSource),
715 : mSource(aSource),
716 : mTarget(aTarget),
717 648 : mCurrent(nsnull)
718 : {
719 648 : NS_ADDREF(mDataSource);
720 648 : NS_IF_ADDREF(mSource);
721 648 : NS_IF_ADDREF(mTarget);
722 :
723 648 : if (mSource) {
724 : // cast okay because it's a closed system
725 629 : mAssertion = mDataSource->GetForwardArcs(mSource);
726 :
727 629 : if (mAssertion && mAssertion->mHashEntry) {
728 : // its our magical HASH_ENTRY forward hash for assertions
729 0 : nsresult rv = NS_NewISupportsArray(getter_AddRefs(mHashArcs));
730 0 : if (NS_SUCCEEDED(rv)) {
731 : PL_DHashTableEnumerate(mAssertion->u.hash.mPropertyHash,
732 0 : ArcEnumerator, mHashArcs.get());
733 : }
734 0 : mAssertion = nsnull;
735 : }
736 : }
737 : else {
738 19 : mAssertion = mDataSource->GetReverseArcs(mTarget);
739 : }
740 648 : }
741 :
742 1296 : InMemoryArcsEnumeratorImpl::~InMemoryArcsEnumeratorImpl()
743 : {
744 : #ifdef DEBUG_REFS
745 : --gInstanceCount;
746 : fprintf(stdout, "%d - RDF: InMemoryArcsEnumeratorImpl\n", gInstanceCount);
747 : #endif
748 :
749 648 : NS_RELEASE(mDataSource);
750 648 : NS_IF_RELEASE(mSource);
751 648 : NS_IF_RELEASE(mTarget);
752 648 : NS_IF_RELEASE(mCurrent);
753 648 : }
754 :
755 1370 : NS_IMPL_ADDREF(InMemoryArcsEnumeratorImpl)
756 1370 : NS_IMPL_RELEASE_WITH_DESTROY(InMemoryArcsEnumeratorImpl, Destroy(this))
757 1222 : NS_IMPL_QUERY_INTERFACE1(InMemoryArcsEnumeratorImpl, nsISimpleEnumerator)
758 :
759 : NS_IMETHODIMP
760 3359 : InMemoryArcsEnumeratorImpl::HasMoreElements(bool* aResult)
761 : {
762 3359 : NS_PRECONDITION(aResult != nsnull, "null ptr");
763 3359 : if (! aResult)
764 0 : return NS_ERROR_NULL_POINTER;
765 :
766 3359 : if (mCurrent) {
767 1358 : *aResult = true;
768 1358 : return NS_OK;
769 : }
770 :
771 2001 : if (mHashArcs) {
772 : PRUint32 itemCount;
773 : nsresult rv;
774 0 : if (NS_FAILED(rv = mHashArcs->Count(&itemCount))) return(rv);
775 0 : if (itemCount > 0) {
776 0 : --itemCount;
777 : mCurrent = static_cast<nsIRDFResource *>
778 0 : (mHashArcs->ElementAt(itemCount));
779 0 : mHashArcs->RemoveElementAt(itemCount);
780 0 : *aResult = true;
781 0 : return NS_OK;
782 : }
783 : }
784 : else
785 4002 : while (mAssertion) {
786 1358 : nsIRDFResource* next = mAssertion->u.as.mProperty;
787 :
788 : // "next" is the property arc we are tentatively going to return
789 : // in a subsequent GetNext() call. It is important to do two
790 : // things, however, before that can happen:
791 : // 1) Make sure it's not an arc we've already returned.
792 : // 2) Make sure that |mAssertion| is not left pointing to
793 : // another assertion that has the same property as this one.
794 : // The first is a practical concern; the second a defense against
795 : // an obscure crash and other erratic behavior. To ensure the
796 : // second condition, skip down the chain until we find the next
797 : // assertion with a property that doesn't match the current one.
798 : // (All these assertions would be skipped via mAlreadyReturned
799 : // checks anyways; this is even a bit faster.)
800 :
801 1390 : do {
802 : mAssertion = (mSource ? mAssertion->mNext :
803 1390 : mAssertion->u.as.mInvNext);
804 : }
805 : while (mAssertion && (next == mAssertion->u.as.mProperty));
806 :
807 1358 : bool alreadyReturned = false;
808 2429 : for (PRInt32 i = mAlreadyReturned.Length() - 1; i >= 0; --i) {
809 1071 : if (mAlreadyReturned[i] == next) {
810 0 : alreadyReturned = true;
811 0 : break;
812 : }
813 : }
814 :
815 1358 : if (! alreadyReturned) {
816 1358 : mCurrent = next;
817 1358 : NS_ADDREF(mCurrent);
818 1358 : *aResult = true;
819 1358 : return NS_OK;
820 : }
821 : }
822 :
823 643 : *aResult = false;
824 643 : return NS_OK;
825 : }
826 :
827 :
828 : NS_IMETHODIMP
829 1358 : InMemoryArcsEnumeratorImpl::GetNext(nsISupports** aResult)
830 : {
831 : nsresult rv;
832 :
833 : bool hasMore;
834 1358 : rv = HasMoreElements(&hasMore);
835 1358 : if (NS_FAILED(rv)) return rv;
836 :
837 1358 : if (! hasMore)
838 0 : return NS_ERROR_UNEXPECTED;
839 :
840 : // Add this to the set of things we've already returned so that we
841 : // can ensure uniqueness
842 1358 : mAlreadyReturned.AppendElement(mCurrent);
843 :
844 : // Don't AddRef: we "transfer" ownership to the caller
845 1358 : *aResult = mCurrent;
846 1358 : mCurrent = nsnull;
847 :
848 1358 : return NS_OK;
849 : }
850 :
851 :
852 : ////////////////////////////////////////////////////////////////////////
853 : // InMemoryDataSource
854 :
855 : nsresult
856 2072 : NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResult)
857 : {
858 2072 : NS_PRECONDITION(aResult != nsnull, "null ptr");
859 2072 : if (! aResult)
860 0 : return NS_ERROR_NULL_POINTER;
861 2072 : *aResult = nsnull;
862 :
863 2072 : if (aOuter && !aIID.Equals(NS_GET_IID(nsISupports))) {
864 0 : NS_ERROR("aggregation requires nsISupports");
865 0 : return NS_ERROR_ILLEGAL_VALUE;
866 : }
867 :
868 2072 : InMemoryDataSource* datasource = new InMemoryDataSource(aOuter);
869 2072 : if (! datasource)
870 0 : return NS_ERROR_OUT_OF_MEMORY;
871 2072 : NS_ADDREF(datasource);
872 :
873 2072 : nsresult rv = datasource->Init();
874 2072 : if (NS_SUCCEEDED(rv)) {
875 2072 : datasource->fAggregated.AddRef();
876 2072 : rv = datasource->AggregatedQueryInterface(aIID, aResult); // This'll AddRef()
877 2072 : datasource->fAggregated.Release();
878 : }
879 :
880 2072 : NS_RELEASE(datasource);
881 2072 : return rv;
882 : }
883 :
884 :
885 2072 : InMemoryDataSource::InMemoryDataSource(nsISupports* aOuter)
886 2072 : : mNumObservers(0), mReadCount(0)
887 : {
888 2072 : NS_INIT_AGGREGATED(aOuter);
889 :
890 : static const size_t kBucketSizes[] = {
891 : sizeof(Assertion),
892 : sizeof(Entry),
893 : sizeof(InMemoryArcsEnumeratorImpl),
894 : sizeof(InMemoryAssertionEnumeratorImpl) };
895 :
896 : static const PRInt32 kNumBuckets = sizeof(kBucketSizes) / sizeof(size_t);
897 :
898 : // Per news://news.mozilla.org/39BEC105.5090206%40netscape.com
899 : static const PRInt32 kInitialSize = 1024;
900 :
901 2072 : mAllocator.Init("nsInMemoryDataSource", kBucketSizes, kNumBuckets, kInitialSize);
902 :
903 2072 : mForwardArcs.ops = nsnull;
904 2072 : mReverseArcs.ops = nsnull;
905 2072 : mPropagateChanges = true;
906 2072 : }
907 :
908 :
909 : nsresult
910 2072 : InMemoryDataSource::Init()
911 : {
912 2072 : if (!PL_DHashTableInit(&mForwardArcs,
913 : PL_DHashGetStubOps(),
914 : nsnull,
915 : sizeof(Entry),
916 2072 : PL_DHASH_MIN_SIZE)) {
917 0 : mForwardArcs.ops = nsnull;
918 0 : return NS_ERROR_OUT_OF_MEMORY;
919 : }
920 2072 : if (!PL_DHashTableInit(&mReverseArcs,
921 : PL_DHashGetStubOps(),
922 : nsnull,
923 : sizeof(Entry),
924 2072 : PL_DHASH_MIN_SIZE)) {
925 0 : mReverseArcs.ops = nsnull;
926 0 : return NS_ERROR_OUT_OF_MEMORY;
927 : }
928 :
929 : #ifdef PR_LOGGING
930 2072 : if (! gLog)
931 172 : gLog = PR_NewLogModule("InMemoryDataSource");
932 : #endif
933 :
934 2072 : return NS_OK;
935 : }
936 :
937 :
938 6216 : InMemoryDataSource::~InMemoryDataSource()
939 : {
940 : #ifdef DEBUG_REFS
941 : --gInstanceCount;
942 : fprintf(stdout, "%d - RDF: InMemoryDataSource\n", gInstanceCount);
943 : #endif
944 :
945 2072 : if (mForwardArcs.ops) {
946 : // This'll release all of the Assertion objects that are
947 : // associated with this data source. We only need to do this
948 : // for the forward arcs, because the reverse arcs table
949 : // indexes the exact same set of resources.
950 2072 : PL_DHashTableEnumerate(&mForwardArcs, DeleteForwardArcsEntry, &mAllocator);
951 2072 : PL_DHashTableFinish(&mForwardArcs);
952 : }
953 2072 : if (mReverseArcs.ops)
954 2072 : PL_DHashTableFinish(&mReverseArcs);
955 :
956 2072 : PR_LOG(gLog, PR_LOG_NOTICE,
957 : ("InMemoryDataSource(%p): destroyed.", this));
958 :
959 8288 : }
960 :
961 : PLDHashOperator
962 10853 : InMemoryDataSource::DeleteForwardArcsEntry(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
963 : PRUint32 aNumber, void* aArg)
964 : {
965 10853 : Entry* entry = reinterpret_cast<Entry*>(aHdr);
966 10853 : nsFixedSizeAllocator* allocator = static_cast<nsFixedSizeAllocator*>(aArg);
967 :
968 10853 : Assertion* as = entry->mAssertions;
969 56060 : while (as) {
970 34354 : Assertion* doomed = as;
971 34354 : as = as->mNext;
972 :
973 : // Unlink, and release the datasource's reference.
974 34354 : doomed->mNext = doomed->u.as.mInvNext = nsnull;
975 34354 : doomed->Release(*allocator);
976 : }
977 10853 : return PL_DHASH_NEXT;
978 : }
979 :
980 :
981 : ////////////////////////////////////////////////////////////////////////
982 :
983 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(InMemoryDataSource)
984 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(InMemoryDataSource)
985 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mObservers)
986 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
987 6 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_AGGREGATED(InMemoryDataSource)
988 6 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mObservers)
989 6 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
990 :
991 440989 : NS_IMPL_CYCLE_COLLECTING_AGGREGATED(InMemoryDataSource)
992 116381 : NS_INTERFACE_MAP_BEGIN_AGGREGATED(InMemoryDataSource)
993 114319 : NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION_AGGREGATED(InMemoryDataSource)
994 90214 : NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
995 66690 : NS_INTERFACE_MAP_ENTRY(nsIRDFInMemoryDataSource)
996 66690 : NS_INTERFACE_MAP_ENTRY(nsIRDFPropagatableDataSource)
997 66690 : NS_INTERFACE_MAP_ENTRY(nsIRDFPurgeableDataSource)
998 66311 : NS_INTERFACE_MAP_ENTRY(rdfIDataSource)
999 66292 : NS_INTERFACE_MAP_END
1000 :
1001 : ////////////////////////////////////////////////////////////////////////
1002 :
1003 :
1004 : #ifdef PR_LOGGING
1005 : void
1006 38897 : InMemoryDataSource::LogOperation(const char* aOperation,
1007 : nsIRDFResource* aSource,
1008 : nsIRDFResource* aProperty,
1009 : nsIRDFNode* aTarget,
1010 : bool aTruthValue)
1011 : {
1012 38897 : if (! PR_LOG_TEST(gLog, PR_LOG_NOTICE))
1013 38897 : return;
1014 :
1015 0 : nsXPIDLCString uri;
1016 0 : aSource->GetValue(getter_Copies(uri));
1017 : PR_LogPrint
1018 0 : ("InMemoryDataSource(%p): %s", this, aOperation);
1019 :
1020 : PR_LogPrint
1021 0 : (" [(%p)%s]--", aSource, (const char*) uri);
1022 :
1023 0 : aProperty->GetValue(getter_Copies(uri));
1024 :
1025 0 : char tv = (aTruthValue ? '-' : '!');
1026 : PR_LogPrint
1027 0 : (" --%c[(%p)%s]--", tv, aProperty, (const char*) uri);
1028 :
1029 0 : nsCOMPtr<nsIRDFResource> resource;
1030 0 : nsCOMPtr<nsIRDFLiteral> literal;
1031 :
1032 0 : if ((resource = do_QueryInterface(aTarget)) != nsnull) {
1033 0 : resource->GetValue(getter_Copies(uri));
1034 : PR_LogPrint
1035 0 : (" -->[(%p)%s]", aTarget, (const char*) uri);
1036 : }
1037 0 : else if ((literal = do_QueryInterface(aTarget)) != nsnull) {
1038 0 : nsXPIDLString value;
1039 0 : literal->GetValue(getter_Copies(value));
1040 0 : nsAutoString valueStr(value);
1041 0 : char* valueCStr = ToNewCString(valueStr);
1042 :
1043 : PR_LogPrint
1044 0 : (" -->(\"%s\")\n", valueCStr);
1045 :
1046 0 : NS_Free(valueCStr);
1047 : }
1048 : else {
1049 : PR_LogPrint
1050 0 : (" -->(unknown-type)\n");
1051 : }
1052 : }
1053 : #endif
1054 :
1055 :
1056 : NS_IMETHODIMP
1057 0 : InMemoryDataSource::GetURI(char* *uri)
1058 : {
1059 0 : NS_PRECONDITION(uri != nsnull, "null ptr");
1060 0 : if (! uri)
1061 0 : return NS_ERROR_NULL_POINTER;
1062 :
1063 0 : *uri = nsnull;
1064 0 : return NS_OK;
1065 : }
1066 :
1067 : NS_IMETHODIMP
1068 1 : InMemoryDataSource::GetSource(nsIRDFResource* property,
1069 : nsIRDFNode* target,
1070 : bool tv,
1071 : nsIRDFResource** source)
1072 : {
1073 1 : NS_PRECONDITION(source != nsnull, "null ptr");
1074 1 : if (! source)
1075 0 : return NS_ERROR_NULL_POINTER;
1076 :
1077 1 : NS_PRECONDITION(property != nsnull, "null ptr");
1078 1 : if (! property)
1079 0 : return NS_ERROR_NULL_POINTER;
1080 :
1081 1 : NS_PRECONDITION(target != nsnull, "null ptr");
1082 1 : if (! target)
1083 0 : return NS_ERROR_NULL_POINTER;
1084 :
1085 1 : for (Assertion* as = GetReverseArcs(target); as; as = as->u.as.mInvNext) {
1086 1 : if ((property == as->u.as.mProperty) && (tv == as->u.as.mTruthValue)) {
1087 1 : *source = as->mSource;
1088 1 : NS_ADDREF(*source);
1089 1 : return NS_OK;
1090 : }
1091 : }
1092 0 : *source = nsnull;
1093 0 : return NS_RDF_NO_VALUE;
1094 : }
1095 :
1096 : NS_IMETHODIMP
1097 43680 : InMemoryDataSource::GetTarget(nsIRDFResource* source,
1098 : nsIRDFResource* property,
1099 : bool tv,
1100 : nsIRDFNode** target)
1101 : {
1102 43680 : NS_PRECONDITION(source != nsnull, "null ptr");
1103 43680 : if (! source)
1104 0 : return NS_ERROR_NULL_POINTER;
1105 :
1106 43680 : NS_PRECONDITION(property != nsnull, "null ptr");
1107 43680 : if (! property)
1108 0 : return NS_ERROR_NULL_POINTER;
1109 :
1110 43680 : NS_PRECONDITION(target != nsnull, "null ptr");
1111 43680 : if (! target)
1112 0 : return NS_ERROR_NULL_POINTER;
1113 :
1114 43680 : Assertion *as = GetForwardArcs(source);
1115 43680 : if (as && as->mHashEntry) {
1116 0 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(as->u.hash.mPropertyHash, property, PL_DHASH_LOOKUP);
1117 0 : Assertion* val = PL_DHASH_ENTRY_IS_BUSY(hdr)
1118 : ? reinterpret_cast<Entry*>(hdr)->mAssertions
1119 0 : : nsnull;
1120 0 : while (val) {
1121 0 : if (tv == val->u.as.mTruthValue) {
1122 0 : *target = val->u.as.mTarget;
1123 0 : NS_IF_ADDREF(*target);
1124 0 : return NS_OK;
1125 : }
1126 0 : val = val->mNext;
1127 0 : }
1128 : }
1129 : else
1130 200128 : for (; as != nsnull; as = as->mNext) {
1131 173649 : if ((property == as->u.as.mProperty) && (tv == (as->u.as.mTruthValue))) {
1132 17201 : *target = as->u.as.mTarget;
1133 17201 : NS_ADDREF(*target);
1134 17201 : return NS_OK;
1135 : }
1136 : }
1137 :
1138 : // If we get here, then there was no target with for the specified
1139 : // property & truth value.
1140 26479 : *target = nsnull;
1141 26479 : return NS_RDF_NO_VALUE;
1142 : }
1143 :
1144 : NS_IMETHODIMP
1145 9699 : InMemoryDataSource::HasAssertion(nsIRDFResource* source,
1146 : nsIRDFResource* property,
1147 : nsIRDFNode* target,
1148 : bool tv,
1149 : bool* hasAssertion)
1150 : {
1151 9699 : if (! source)
1152 0 : return NS_ERROR_NULL_POINTER;
1153 :
1154 9699 : if (! property)
1155 0 : return NS_ERROR_NULL_POINTER;
1156 :
1157 9699 : if (! target)
1158 0 : return NS_ERROR_NULL_POINTER;
1159 :
1160 9699 : Assertion *as = GetForwardArcs(source);
1161 9699 : if (as && as->mHashEntry) {
1162 0 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(as->u.hash.mPropertyHash, property, PL_DHASH_LOOKUP);
1163 0 : Assertion* val = PL_DHASH_ENTRY_IS_BUSY(hdr)
1164 : ? reinterpret_cast<Entry*>(hdr)->mAssertions
1165 0 : : nsnull;
1166 0 : while (val) {
1167 0 : if ((val->u.as.mTarget == target) && (tv == (val->u.as.mTruthValue))) {
1168 0 : *hasAssertion = true;
1169 0 : return NS_OK;
1170 : }
1171 0 : val = val->mNext;
1172 0 : }
1173 : }
1174 : else
1175 12282 : for (; as != nsnull; as = as->mNext) {
1176 : // check target first as its most unique
1177 5262 : if (target != as->u.as.mTarget)
1178 2583 : continue;
1179 :
1180 2679 : if (property != as->u.as.mProperty)
1181 0 : continue;
1182 :
1183 2679 : if (tv != (as->u.as.mTruthValue))
1184 0 : continue;
1185 :
1186 : // found it!
1187 2679 : *hasAssertion = true;
1188 2679 : return NS_OK;
1189 : }
1190 :
1191 : // If we get here, we couldn't find the assertion
1192 7020 : *hasAssertion = false;
1193 7020 : return NS_OK;
1194 : }
1195 :
1196 : NS_IMETHODIMP
1197 5 : InMemoryDataSource::GetSources(nsIRDFResource* aProperty,
1198 : nsIRDFNode* aTarget,
1199 : bool aTruthValue,
1200 : nsISimpleEnumerator** aResult)
1201 : {
1202 5 : NS_PRECONDITION(aProperty != nsnull, "null ptr");
1203 5 : if (! aProperty)
1204 0 : return NS_ERROR_NULL_POINTER;
1205 :
1206 5 : NS_PRECONDITION(aTarget != nsnull, "null ptr");
1207 5 : if (! aTarget)
1208 0 : return NS_ERROR_NULL_POINTER;
1209 :
1210 5 : NS_PRECONDITION(aResult != nsnull, "null ptr");
1211 5 : if (! aResult)
1212 0 : return NS_ERROR_NULL_POINTER;
1213 :
1214 : InMemoryAssertionEnumeratorImpl* result =
1215 : InMemoryAssertionEnumeratorImpl::Create(this, nsnull, aProperty,
1216 5 : aTarget, aTruthValue);
1217 :
1218 5 : if (! result)
1219 0 : return NS_ERROR_OUT_OF_MEMORY;
1220 :
1221 5 : NS_ADDREF(result);
1222 5 : *aResult = result;
1223 :
1224 5 : return NS_OK;
1225 : }
1226 :
1227 : NS_IMETHODIMP
1228 13715 : InMemoryDataSource::GetTargets(nsIRDFResource* aSource,
1229 : nsIRDFResource* aProperty,
1230 : bool aTruthValue,
1231 : nsISimpleEnumerator** aResult)
1232 : {
1233 13715 : NS_PRECONDITION(aSource != nsnull, "null ptr");
1234 13715 : if (! aSource)
1235 0 : return NS_ERROR_NULL_POINTER;
1236 :
1237 13715 : NS_PRECONDITION(aProperty != nsnull, "null ptr");
1238 13715 : if (! aProperty)
1239 0 : return NS_ERROR_NULL_POINTER;
1240 :
1241 13715 : NS_PRECONDITION(aResult != nsnull, "null ptr");
1242 13715 : if (! aResult)
1243 0 : return NS_ERROR_NULL_POINTER;
1244 :
1245 : InMemoryAssertionEnumeratorImpl* result =
1246 : InMemoryAssertionEnumeratorImpl::Create(this, aSource, aProperty,
1247 13715 : nsnull, aTruthValue);
1248 :
1249 13715 : if (! result)
1250 0 : return NS_ERROR_OUT_OF_MEMORY;
1251 :
1252 13715 : NS_ADDREF(result);
1253 13715 : *aResult = result;
1254 :
1255 13715 : return NS_OK;
1256 : }
1257 :
1258 :
1259 : nsresult
1260 36442 : InMemoryDataSource::LockedAssert(nsIRDFResource* aSource,
1261 : nsIRDFResource* aProperty,
1262 : nsIRDFNode* aTarget,
1263 : bool aTruthValue)
1264 : {
1265 : #ifdef PR_LOGGING
1266 36442 : LogOperation("ASSERT", aSource, aProperty, aTarget, aTruthValue);
1267 : #endif
1268 :
1269 36442 : Assertion* next = GetForwardArcs(aSource);
1270 36442 : Assertion* prev = next;
1271 36442 : Assertion* as = nsnull;
1272 :
1273 36442 : bool haveHash = (next) ? next->mHashEntry : false;
1274 36442 : if (haveHash) {
1275 0 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(next->u.hash.mPropertyHash, aProperty, PL_DHASH_LOOKUP);
1276 0 : Assertion* val = PL_DHASH_ENTRY_IS_BUSY(hdr)
1277 : ? reinterpret_cast<Entry*>(hdr)->mAssertions
1278 0 : : nsnull;
1279 0 : while (val) {
1280 0 : if (val->u.as.mTarget == aTarget) {
1281 : // Wow, we already had the assertion. Make sure that the
1282 : // truth values are correct and bail.
1283 0 : val->u.as.mTruthValue = aTruthValue;
1284 0 : return NS_OK;
1285 : }
1286 0 : val = val->mNext;
1287 : }
1288 : }
1289 : else
1290 : {
1291 125404 : while (next) {
1292 : // check target first as its most unique
1293 52524 : if (aTarget == next->u.as.mTarget) {
1294 2405 : if (aProperty == next->u.as.mProperty) {
1295 : // Wow, we already had the assertion. Make sure that the
1296 : // truth values are correct and bail.
1297 4 : next->u.as.mTruthValue = aTruthValue;
1298 4 : return NS_OK;
1299 : }
1300 : }
1301 :
1302 52520 : prev = next;
1303 52520 : next = next->mNext;
1304 : }
1305 : }
1306 :
1307 36438 : as = Assertion::Create(mAllocator, aSource, aProperty, aTarget, aTruthValue);
1308 36438 : if (! as)
1309 0 : return NS_ERROR_OUT_OF_MEMORY;
1310 :
1311 : // Add the datasource's owning reference.
1312 36438 : as->AddRef();
1313 :
1314 36438 : if (haveHash)
1315 : {
1316 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(next->u.hash.mPropertyHash,
1317 0 : aProperty, PL_DHASH_LOOKUP);
1318 0 : Assertion *asRef = PL_DHASH_ENTRY_IS_BUSY(hdr)
1319 : ? reinterpret_cast<Entry*>(hdr)->mAssertions
1320 0 : : nsnull;
1321 0 : if (asRef)
1322 : {
1323 0 : as->mNext = asRef->mNext;
1324 0 : asRef->mNext = as;
1325 : }
1326 : else
1327 : {
1328 : hdr = PL_DHashTableOperate(next->u.hash.mPropertyHash,
1329 0 : aProperty, PL_DHASH_ADD);
1330 0 : if (hdr)
1331 : {
1332 0 : Entry* entry = reinterpret_cast<Entry*>(hdr);
1333 0 : entry->mNode = aProperty;
1334 0 : entry->mAssertions = as;
1335 : }
1336 : }
1337 : }
1338 : else
1339 : {
1340 : // Link it in to the "forward arcs" table
1341 36438 : if (!prev) {
1342 10867 : SetForwardArcs(aSource, as);
1343 : } else {
1344 25571 : prev->mNext = as;
1345 : }
1346 : }
1347 :
1348 : // Link it in to the "reverse arcs" table
1349 :
1350 36438 : next = GetReverseArcs(aTarget);
1351 36438 : as->u.as.mInvNext = next;
1352 36438 : next = as;
1353 36438 : SetReverseArcs(aTarget, next);
1354 :
1355 36438 : return NS_OK;
1356 : }
1357 :
1358 : NS_IMETHODIMP
1359 36408 : InMemoryDataSource::Assert(nsIRDFResource* aSource,
1360 : nsIRDFResource* aProperty,
1361 : nsIRDFNode* aTarget,
1362 : bool aTruthValue)
1363 : {
1364 36408 : NS_PRECONDITION(aSource != nsnull, "null ptr");
1365 36408 : if (! aSource)
1366 0 : return NS_ERROR_NULL_POINTER;
1367 :
1368 36408 : NS_PRECONDITION(aProperty != nsnull, "null ptr");
1369 36408 : if (! aProperty)
1370 0 : return NS_ERROR_NULL_POINTER;
1371 :
1372 36408 : NS_PRECONDITION(aTarget != nsnull, "null ptr");
1373 36408 : if (! aTarget)
1374 0 : return NS_ERROR_NULL_POINTER;
1375 :
1376 36408 : if (mReadCount) {
1377 0 : NS_WARNING("Writing to InMemoryDataSource during read\n");
1378 0 : return NS_RDF_ASSERTION_REJECTED;
1379 : }
1380 :
1381 : nsresult rv;
1382 36408 : rv = LockedAssert(aSource, aProperty, aTarget, aTruthValue);
1383 36408 : if (NS_FAILED(rv)) return rv;
1384 :
1385 : // notify observers
1386 36408 : for (PRInt32 i = (PRInt32)mNumObservers - 1; mPropagateChanges && i >= 0; --i) {
1387 0 : nsIRDFObserver* obs = mObservers[i];
1388 :
1389 : // XXX this should never happen, but it does, and we can't figure out why.
1390 0 : NS_ASSERTION(obs, "observer array corrupted!");
1391 0 : if (! obs)
1392 0 : continue;
1393 :
1394 0 : obs->OnAssert(this, aSource, aProperty, aTarget);
1395 : // XXX ignore return value?
1396 : }
1397 :
1398 36408 : return NS_RDF_ASSERTION_ACCEPTED;
1399 : }
1400 :
1401 :
1402 : nsresult
1403 2084 : InMemoryDataSource::LockedUnassert(nsIRDFResource* aSource,
1404 : nsIRDFResource* aProperty,
1405 : nsIRDFNode* aTarget)
1406 : {
1407 : #ifdef PR_LOGGING
1408 2084 : LogOperation("UNASSERT", aSource, aProperty, aTarget);
1409 : #endif
1410 :
1411 2084 : Assertion* next = GetForwardArcs(aSource);
1412 2084 : Assertion* prev = next;
1413 2084 : Assertion* root = next;
1414 2084 : Assertion* as = nsnull;
1415 :
1416 2084 : bool haveHash = (next) ? next->mHashEntry : false;
1417 2084 : if (haveHash) {
1418 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(next->u.hash.mPropertyHash,
1419 0 : aProperty, PL_DHASH_LOOKUP);
1420 0 : prev = next = PL_DHASH_ENTRY_IS_BUSY(hdr)
1421 : ? reinterpret_cast<Entry*>(hdr)->mAssertions
1422 0 : : nsnull;
1423 0 : bool first = true;
1424 0 : while (next) {
1425 0 : if (aTarget == next->u.as.mTarget) {
1426 0 : break;
1427 : }
1428 0 : first = false;
1429 0 : prev = next;
1430 0 : next = next->mNext;
1431 : }
1432 : // We don't even have the assertion, so just bail.
1433 0 : if (!next)
1434 0 : return NS_OK;
1435 :
1436 0 : as = next;
1437 :
1438 0 : if (first) {
1439 0 : PL_DHashTableRawRemove(root->u.hash.mPropertyHash, hdr);
1440 :
1441 0 : if (next && next->mNext) {
1442 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(root->u.hash.mPropertyHash,
1443 0 : aProperty, PL_DHASH_ADD);
1444 0 : if (hdr) {
1445 0 : Entry* entry = reinterpret_cast<Entry*>(hdr);
1446 0 : entry->mNode = aProperty;
1447 0 : entry->mAssertions = next->mNext;
1448 0 : }
1449 : }
1450 : else {
1451 : // If this second-level hash empties out, clean it up.
1452 0 : if (!root->u.hash.mPropertyHash->entryCount) {
1453 0 : Assertion::Destroy(mAllocator, root);
1454 0 : SetForwardArcs(aSource, nsnull);
1455 : }
1456 : }
1457 : }
1458 : else {
1459 0 : prev->mNext = next->mNext;
1460 : }
1461 : }
1462 : else
1463 : {
1464 6775 : while (next) {
1465 : // check target first as its most unique
1466 4691 : if (aTarget == next->u.as.mTarget) {
1467 2084 : if (aProperty == next->u.as.mProperty) {
1468 2084 : if (prev == next) {
1469 48 : SetForwardArcs(aSource, next->mNext);
1470 : } else {
1471 2036 : prev->mNext = next->mNext;
1472 : }
1473 2084 : as = next;
1474 2084 : break;
1475 : }
1476 : }
1477 :
1478 2607 : prev = next;
1479 2607 : next = next->mNext;
1480 : }
1481 : }
1482 : // We don't even have the assertion, so just bail.
1483 2084 : if (!as)
1484 0 : return NS_OK;
1485 :
1486 : #ifdef DEBUG
1487 2084 : bool foundReverseArc = false;
1488 : #endif
1489 :
1490 2084 : next = prev = GetReverseArcs(aTarget);
1491 5598 : while (next) {
1492 3514 : if (next == as) {
1493 2084 : if (prev == next) {
1494 1184 : SetReverseArcs(aTarget, next->u.as.mInvNext);
1495 : } else {
1496 900 : prev->u.as.mInvNext = next->u.as.mInvNext;
1497 : }
1498 : #ifdef DEBUG
1499 2084 : foundReverseArc = true;
1500 : #endif
1501 2084 : break;
1502 : }
1503 1430 : prev = next;
1504 1430 : next = next->u.as.mInvNext;
1505 : }
1506 :
1507 : #ifdef DEBUG
1508 2084 : NS_ASSERTION(foundReverseArc, "in-memory db corrupted: unable to find reverse arc");
1509 : #endif
1510 :
1511 : // Unlink, and release the datasource's reference
1512 2084 : as->mNext = as->u.as.mInvNext = nsnull;
1513 2084 : as->Release(mAllocator);
1514 :
1515 2084 : return NS_OK;
1516 : }
1517 :
1518 : NS_IMETHODIMP
1519 2050 : InMemoryDataSource::Unassert(nsIRDFResource* aSource,
1520 : nsIRDFResource* aProperty,
1521 : nsIRDFNode* aTarget)
1522 : {
1523 2050 : NS_PRECONDITION(aSource != nsnull, "null ptr");
1524 2050 : if (! aSource)
1525 0 : return NS_ERROR_NULL_POINTER;
1526 :
1527 2050 : NS_PRECONDITION(aProperty != nsnull, "null ptr");
1528 2050 : if (! aProperty)
1529 0 : return NS_ERROR_NULL_POINTER;
1530 :
1531 2050 : NS_PRECONDITION(aTarget != nsnull, "null ptr");
1532 2050 : if (! aTarget)
1533 0 : return NS_ERROR_NULL_POINTER;
1534 :
1535 2050 : if (mReadCount) {
1536 0 : NS_WARNING("Writing to InMemoryDataSource during read\n");
1537 0 : return NS_RDF_ASSERTION_REJECTED;
1538 : }
1539 :
1540 : nsresult rv;
1541 :
1542 2050 : rv = LockedUnassert(aSource, aProperty, aTarget);
1543 2050 : if (NS_FAILED(rv)) return rv;
1544 :
1545 : // Notify the world
1546 2050 : for (PRInt32 i = PRInt32(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
1547 0 : nsIRDFObserver* obs = mObservers[i];
1548 :
1549 : // XXX this should never happen, but it does, and we can't figure out why.
1550 0 : NS_ASSERTION(obs, "observer array corrupted!");
1551 0 : if (! obs)
1552 0 : continue;
1553 :
1554 0 : obs->OnUnassert(this, aSource, aProperty, aTarget);
1555 : // XXX ignore return value?
1556 : }
1557 :
1558 2050 : return NS_RDF_ASSERTION_ACCEPTED;
1559 : }
1560 :
1561 :
1562 : NS_IMETHODIMP
1563 34 : InMemoryDataSource::Change(nsIRDFResource* aSource,
1564 : nsIRDFResource* aProperty,
1565 : nsIRDFNode* aOldTarget,
1566 : nsIRDFNode* aNewTarget)
1567 : {
1568 34 : NS_PRECONDITION(aSource != nsnull, "null ptr");
1569 34 : if (! aSource)
1570 0 : return NS_ERROR_NULL_POINTER;
1571 :
1572 34 : NS_PRECONDITION(aProperty != nsnull, "null ptr");
1573 34 : if (! aProperty)
1574 0 : return NS_ERROR_NULL_POINTER;
1575 :
1576 34 : NS_PRECONDITION(aOldTarget != nsnull, "null ptr");
1577 34 : if (! aOldTarget)
1578 0 : return NS_ERROR_NULL_POINTER;
1579 :
1580 34 : NS_PRECONDITION(aNewTarget != nsnull, "null ptr");
1581 34 : if (! aNewTarget)
1582 0 : return NS_ERROR_NULL_POINTER;
1583 :
1584 34 : if (mReadCount) {
1585 0 : NS_WARNING("Writing to InMemoryDataSource during read\n");
1586 0 : return NS_RDF_ASSERTION_REJECTED;
1587 : }
1588 :
1589 : nsresult rv;
1590 :
1591 : // XXX We can implement LockedChange() if we decide that this
1592 : // is a performance bottleneck.
1593 :
1594 34 : rv = LockedUnassert(aSource, aProperty, aOldTarget);
1595 34 : if (NS_FAILED(rv)) return rv;
1596 :
1597 34 : rv = LockedAssert(aSource, aProperty, aNewTarget, true);
1598 34 : if (NS_FAILED(rv)) return rv;
1599 :
1600 : // Notify the world
1601 34 : for (PRInt32 i = PRInt32(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
1602 0 : nsIRDFObserver* obs = mObservers[i];
1603 :
1604 : // XXX this should never happen, but it does, and we can't figure out why.
1605 0 : NS_ASSERTION(obs, "observer array corrupted!");
1606 0 : if (! obs)
1607 0 : continue;
1608 :
1609 0 : obs->OnChange(this, aSource, aProperty, aOldTarget, aNewTarget);
1610 : // XXX ignore return value?
1611 : }
1612 :
1613 34 : return NS_RDF_ASSERTION_ACCEPTED;
1614 : }
1615 :
1616 :
1617 : NS_IMETHODIMP
1618 0 : InMemoryDataSource::Move(nsIRDFResource* aOldSource,
1619 : nsIRDFResource* aNewSource,
1620 : nsIRDFResource* aProperty,
1621 : nsIRDFNode* aTarget)
1622 : {
1623 0 : NS_PRECONDITION(aOldSource != nsnull, "null ptr");
1624 0 : if (! aOldSource)
1625 0 : return NS_ERROR_NULL_POINTER;
1626 :
1627 0 : NS_PRECONDITION(aNewSource != nsnull, "null ptr");
1628 0 : if (! aNewSource)
1629 0 : return NS_ERROR_NULL_POINTER;
1630 :
1631 0 : NS_PRECONDITION(aProperty != nsnull, "null ptr");
1632 0 : if (! aProperty)
1633 0 : return NS_ERROR_NULL_POINTER;
1634 :
1635 0 : NS_PRECONDITION(aTarget != nsnull, "null ptr");
1636 0 : if (! aTarget)
1637 0 : return NS_ERROR_NULL_POINTER;
1638 :
1639 0 : if (mReadCount) {
1640 0 : NS_WARNING("Writing to InMemoryDataSource during read\n");
1641 0 : return NS_RDF_ASSERTION_REJECTED;
1642 : }
1643 :
1644 : nsresult rv;
1645 :
1646 : // XXX We can implement LockedMove() if we decide that this
1647 : // is a performance bottleneck.
1648 :
1649 0 : rv = LockedUnassert(aOldSource, aProperty, aTarget);
1650 0 : if (NS_FAILED(rv)) return rv;
1651 :
1652 0 : rv = LockedAssert(aNewSource, aProperty, aTarget, true);
1653 0 : if (NS_FAILED(rv)) return rv;
1654 :
1655 : // Notify the world
1656 0 : for (PRInt32 i = PRInt32(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
1657 0 : nsIRDFObserver* obs = mObservers[i];
1658 :
1659 : // XXX this should never happen, but it does, and we can't figure out why.
1660 0 : NS_ASSERTION(obs, "observer array corrupted!");
1661 0 : if (! obs)
1662 0 : continue;
1663 :
1664 0 : obs->OnMove(this, aOldSource, aNewSource, aProperty, aTarget);
1665 : // XXX ignore return value?
1666 : }
1667 :
1668 0 : return NS_RDF_ASSERTION_ACCEPTED;
1669 : }
1670 :
1671 :
1672 : NS_IMETHODIMP
1673 0 : InMemoryDataSource::AddObserver(nsIRDFObserver* aObserver)
1674 : {
1675 0 : NS_PRECONDITION(aObserver != nsnull, "null ptr");
1676 0 : if (! aObserver)
1677 0 : return NS_ERROR_NULL_POINTER;
1678 :
1679 0 : mObservers.AppendObject(aObserver);
1680 0 : mNumObservers = mObservers.Count();
1681 :
1682 0 : return NS_OK;
1683 : }
1684 :
1685 : NS_IMETHODIMP
1686 0 : InMemoryDataSource::RemoveObserver(nsIRDFObserver* aObserver)
1687 : {
1688 0 : NS_PRECONDITION(aObserver != nsnull, "null ptr");
1689 0 : if (! aObserver)
1690 0 : return NS_ERROR_NULL_POINTER;
1691 :
1692 0 : mObservers.RemoveObject(aObserver);
1693 : // note: use Count() instead of just decrementing
1694 : // in case aObserver wasn't in list, for example
1695 0 : mNumObservers = mObservers.Count();
1696 :
1697 0 : return NS_OK;
1698 : }
1699 :
1700 : NS_IMETHODIMP
1701 6 : InMemoryDataSource::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *result)
1702 : {
1703 6 : Assertion* ass = GetReverseArcs(aNode);
1704 12 : while (ass) {
1705 2 : nsIRDFResource* elbow = ass->u.as.mProperty;
1706 2 : if (elbow == aArc) {
1707 2 : *result = true;
1708 2 : return NS_OK;
1709 : }
1710 0 : ass = ass->u.as.mInvNext;
1711 : }
1712 4 : *result = false;
1713 4 : return NS_OK;
1714 : }
1715 :
1716 : NS_IMETHODIMP
1717 431 : InMemoryDataSource::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *result)
1718 : {
1719 431 : Assertion* ass = GetForwardArcs(aSource);
1720 431 : if (ass && ass->mHashEntry) {
1721 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(ass->u.hash.mPropertyHash,
1722 0 : aArc, PL_DHASH_LOOKUP);
1723 0 : Assertion* val = PL_DHASH_ENTRY_IS_BUSY(hdr)
1724 : ? reinterpret_cast<Entry*>(hdr)->mAssertions
1725 0 : : nsnull;
1726 0 : if (val) {
1727 0 : *result = true;
1728 0 : return NS_OK;
1729 : }
1730 0 : ass = ass->mNext;
1731 : }
1732 1350 : while (ass) {
1733 601 : nsIRDFResource* elbow = ass->u.as.mProperty;
1734 601 : if (elbow == aArc) {
1735 113 : *result = true;
1736 113 : return NS_OK;
1737 : }
1738 488 : ass = ass->mNext;
1739 : }
1740 318 : *result = false;
1741 318 : return NS_OK;
1742 : }
1743 :
1744 : NS_IMETHODIMP
1745 19 : InMemoryDataSource::ArcLabelsIn(nsIRDFNode* aTarget, nsISimpleEnumerator** aResult)
1746 : {
1747 19 : NS_PRECONDITION(aTarget != nsnull, "null ptr");
1748 19 : if (! aTarget)
1749 0 : return NS_ERROR_NULL_POINTER;
1750 :
1751 : InMemoryArcsEnumeratorImpl* result =
1752 19 : InMemoryArcsEnumeratorImpl::Create(this, nsnull, aTarget);
1753 :
1754 19 : if (! result)
1755 0 : return NS_ERROR_OUT_OF_MEMORY;
1756 :
1757 19 : NS_ADDREF(result);
1758 19 : *aResult = result;
1759 :
1760 19 : return NS_OK;
1761 : }
1762 :
1763 : NS_IMETHODIMP
1764 629 : InMemoryDataSource::ArcLabelsOut(nsIRDFResource* aSource, nsISimpleEnumerator** aResult)
1765 : {
1766 629 : NS_PRECONDITION(aSource != nsnull, "null ptr");
1767 629 : if (! aSource)
1768 0 : return NS_ERROR_NULL_POINTER;
1769 :
1770 : InMemoryArcsEnumeratorImpl* result =
1771 629 : InMemoryArcsEnumeratorImpl::Create(this, aSource, nsnull);
1772 :
1773 629 : if (! result)
1774 0 : return NS_ERROR_OUT_OF_MEMORY;
1775 :
1776 629 : NS_ADDREF(result);
1777 629 : *aResult = result;
1778 :
1779 629 : return NS_OK;
1780 : }
1781 :
1782 : PLDHashOperator
1783 349 : InMemoryDataSource::ResourceEnumerator(PLDHashTable* aTable,
1784 : PLDHashEntryHdr* aHdr,
1785 : PRUint32 aNumber, void* aArg)
1786 : {
1787 349 : Entry* entry = reinterpret_cast<Entry*>(aHdr);
1788 349 : nsISupportsArray* resources = static_cast<nsISupportsArray*>(aArg);
1789 :
1790 349 : resources->AppendElement(entry->mNode);
1791 349 : return PL_DHASH_NEXT;
1792 : }
1793 :
1794 :
1795 : NS_IMETHODIMP
1796 19 : InMemoryDataSource::GetAllResources(nsISimpleEnumerator** aResult)
1797 : {
1798 : nsresult rv;
1799 :
1800 38 : nsCOMPtr<nsISupportsArray> values;
1801 19 : rv = NS_NewISupportsArray(getter_AddRefs(values));
1802 19 : if (NS_FAILED(rv)) return rv;
1803 :
1804 : // Enumerate all of our entries into an nsISupportsArray.
1805 19 : PL_DHashTableEnumerate(&mForwardArcs, ResourceEnumerator, values.get());
1806 :
1807 19 : return NS_NewArrayEnumerator(aResult, values);
1808 : }
1809 :
1810 : NS_IMETHODIMP
1811 0 : InMemoryDataSource::GetAllCmds(nsIRDFResource* source,
1812 : nsISimpleEnumerator/*<nsIRDFResource>*/** commands)
1813 : {
1814 0 : return(NS_NewEmptyEnumerator(commands));
1815 : }
1816 :
1817 : NS_IMETHODIMP
1818 0 : InMemoryDataSource::IsCommandEnabled(nsISupportsArray/*<nsIRDFResource>*/* aSources,
1819 : nsIRDFResource* aCommand,
1820 : nsISupportsArray/*<nsIRDFResource>*/* aArguments,
1821 : bool* aResult)
1822 : {
1823 0 : *aResult = false;
1824 0 : return NS_OK;
1825 : }
1826 :
1827 : NS_IMETHODIMP
1828 0 : InMemoryDataSource::DoCommand(nsISupportsArray/*<nsIRDFResource>*/* aSources,
1829 : nsIRDFResource* aCommand,
1830 : nsISupportsArray/*<nsIRDFResource>*/* aArguments)
1831 : {
1832 0 : return NS_OK;
1833 : }
1834 :
1835 : NS_IMETHODIMP
1836 0 : InMemoryDataSource::BeginUpdateBatch()
1837 : {
1838 0 : for (PRInt32 i = PRInt32(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
1839 0 : nsIRDFObserver* obs = mObservers[i];
1840 0 : obs->OnBeginUpdateBatch(this);
1841 : }
1842 0 : return NS_OK;
1843 : }
1844 :
1845 : NS_IMETHODIMP
1846 0 : InMemoryDataSource::EndUpdateBatch()
1847 : {
1848 0 : for (PRInt32 i = PRInt32(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
1849 0 : nsIRDFObserver* obs = mObservers[i];
1850 0 : obs->OnEndUpdateBatch(this);
1851 : }
1852 0 : return NS_OK;
1853 : }
1854 :
1855 :
1856 :
1857 : ////////////////////////////////////////////////////////////////////////
1858 : // nsIRDFInMemoryDataSource methods
1859 :
1860 : NS_IMETHODIMP
1861 0 : InMemoryDataSource::EnsureFastContainment(nsIRDFResource* aSource)
1862 : {
1863 0 : Assertion *as = GetForwardArcs(aSource);
1864 0 : bool haveHash = (as) ? as->mHashEntry : false;
1865 :
1866 : // if its already a hash, then nothing to do
1867 0 : if (haveHash) return(NS_OK);
1868 :
1869 : // convert aSource in forward hash into a hash
1870 0 : Assertion *hashAssertion = Assertion::Create(mAllocator, aSource);
1871 0 : NS_ASSERTION(hashAssertion, "unable to Assertion::Create");
1872 0 : if (!hashAssertion) return(NS_ERROR_OUT_OF_MEMORY);
1873 :
1874 : // Add the datasource's owning reference.
1875 0 : hashAssertion->AddRef();
1876 :
1877 0 : register Assertion *first = GetForwardArcs(aSource);
1878 0 : SetForwardArcs(aSource, hashAssertion);
1879 :
1880 : // mutate references of existing forward assertions into this hash
1881 0 : PLDHashTable *table = hashAssertion->u.hash.mPropertyHash;
1882 : Assertion *nextRef;
1883 0 : while(first) {
1884 0 : nextRef = first->mNext;
1885 0 : nsIRDFResource *prop = first->u.as.mProperty;
1886 :
1887 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(table,
1888 0 : prop, PL_DHASH_LOOKUP);
1889 0 : Assertion* val = PL_DHASH_ENTRY_IS_BUSY(hdr)
1890 : ? reinterpret_cast<Entry*>(hdr)->mAssertions
1891 0 : : nsnull;
1892 0 : if (val) {
1893 0 : first->mNext = val->mNext;
1894 0 : val->mNext = first;
1895 : }
1896 : else {
1897 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(table,
1898 0 : prop, PL_DHASH_ADD);
1899 0 : if (hdr) {
1900 0 : Entry* entry = reinterpret_cast<Entry*>(hdr);
1901 0 : entry->mNode = prop;
1902 0 : entry->mAssertions = first;
1903 0 : first->mNext = nsnull;
1904 : }
1905 : }
1906 0 : first = nextRef;
1907 : }
1908 0 : return(NS_OK);
1909 : }
1910 :
1911 :
1912 : ////////////////////////////////////////////////////////////////////////
1913 : // nsIRDFPropagatableDataSource methods
1914 : NS_IMETHODIMP
1915 0 : InMemoryDataSource::GetPropagateChanges(bool* aPropagateChanges)
1916 : {
1917 0 : *aPropagateChanges = mPropagateChanges;
1918 0 : return NS_OK;
1919 : }
1920 :
1921 : NS_IMETHODIMP
1922 0 : InMemoryDataSource::SetPropagateChanges(bool aPropagateChanges)
1923 : {
1924 0 : mPropagateChanges = aPropagateChanges;
1925 0 : return NS_OK;
1926 : }
1927 :
1928 :
1929 : ////////////////////////////////////////////////////////////////////////
1930 : // nsIRDFPurgeableDataSource methods
1931 :
1932 : NS_IMETHODIMP
1933 742 : InMemoryDataSource::Mark(nsIRDFResource* aSource,
1934 : nsIRDFResource* aProperty,
1935 : nsIRDFNode* aTarget,
1936 : bool aTruthValue,
1937 : bool* aDidMark)
1938 : {
1939 742 : NS_PRECONDITION(aSource != nsnull, "null ptr");
1940 742 : if (! aSource)
1941 0 : return NS_ERROR_NULL_POINTER;
1942 :
1943 742 : NS_PRECONDITION(aProperty != nsnull, "null ptr");
1944 742 : if (! aProperty)
1945 0 : return NS_ERROR_NULL_POINTER;
1946 :
1947 742 : NS_PRECONDITION(aTarget != nsnull, "null ptr");
1948 742 : if (! aTarget)
1949 0 : return NS_ERROR_NULL_POINTER;
1950 :
1951 742 : Assertion *as = GetForwardArcs(aSource);
1952 742 : if (as && as->mHashEntry) {
1953 : PLDHashEntryHdr* hdr = PL_DHashTableOperate(as->u.hash.mPropertyHash,
1954 0 : aProperty, PL_DHASH_LOOKUP);
1955 0 : Assertion* val = PL_DHASH_ENTRY_IS_BUSY(hdr)
1956 : ? reinterpret_cast<Entry*>(hdr)->mAssertions
1957 0 : : nsnull;
1958 0 : while (val) {
1959 0 : if ((val->u.as.mTarget == aTarget) &&
1960 : (aTruthValue == (val->u.as.mTruthValue))) {
1961 :
1962 : // found it! so mark it.
1963 0 : as->Mark();
1964 0 : *aDidMark = true;
1965 :
1966 : #ifdef PR_LOGGING
1967 0 : LogOperation("MARK", aSource, aProperty, aTarget, aTruthValue);
1968 : #endif
1969 :
1970 0 : return NS_OK;
1971 : }
1972 0 : val = val->mNext;
1973 0 : }
1974 : }
1975 2526 : else for (; as != nsnull; as = as->mNext) {
1976 : // check target first as its most unique
1977 2155 : if (aTarget != as->u.as.mTarget)
1978 1760 : continue;
1979 :
1980 395 : if (aProperty != as->u.as.mProperty)
1981 24 : continue;
1982 :
1983 371 : if (aTruthValue != (as->u.as.mTruthValue))
1984 0 : continue;
1985 :
1986 : // found it! so mark it.
1987 371 : as->Mark();
1988 371 : *aDidMark = true;
1989 :
1990 : #ifdef PR_LOGGING
1991 371 : LogOperation("MARK", aSource, aProperty, aTarget, aTruthValue);
1992 : #endif
1993 :
1994 371 : return NS_OK;
1995 : }
1996 :
1997 : // If we get here, we couldn't find the assertion
1998 371 : *aDidMark = false;
1999 371 : return NS_OK;
2000 : }
2001 :
2002 :
2003 : struct SweepInfo {
2004 : Assertion* mUnassertList;
2005 : PLDHashTable* mReverseArcs;
2006 : nsFixedSizeAllocator* mAllocator;
2007 : };
2008 :
2009 : NS_IMETHODIMP
2010 8 : InMemoryDataSource::Sweep()
2011 : {
2012 8 : SweepInfo info = { nsnull, &mReverseArcs, &mAllocator};
2013 :
2014 : // Remove all the assertions, but don't notify anyone.
2015 8 : PL_DHashTableEnumerate(&mForwardArcs, SweepForwardArcsEntries, &info);
2016 :
2017 : // Now do the notification.
2018 8 : Assertion* as = info.mUnassertList;
2019 16 : while (as) {
2020 : #ifdef PR_LOGGING
2021 0 : LogOperation("SWEEP", as->mSource, as->u.as.mProperty, as->u.as.mTarget, as->u.as.mTruthValue);
2022 : #endif
2023 0 : if (!(as->mHashEntry))
2024 : {
2025 0 : for (PRInt32 i = PRInt32(mNumObservers) - 1; mPropagateChanges && i >= 0; --i) {
2026 0 : nsIRDFObserver* obs = mObservers[i];
2027 : // XXXbz other loops over mObservers null-check |obs| here!
2028 0 : obs->OnUnassert(this, as->mSource, as->u.as.mProperty, as->u.as.mTarget);
2029 : // XXX ignore return value?
2030 : }
2031 : }
2032 :
2033 0 : Assertion* doomed = as;
2034 0 : as = as->mNext;
2035 :
2036 : // Unlink, and release the datasource's reference
2037 0 : doomed->mNext = doomed->u.as.mInvNext = nsnull;
2038 0 : doomed->Release(mAllocator);
2039 : }
2040 :
2041 8 : return NS_OK;
2042 : }
2043 :
2044 :
2045 : PLDHashOperator
2046 75 : InMemoryDataSource::SweepForwardArcsEntries(PLDHashTable* aTable,
2047 : PLDHashEntryHdr* aHdr,
2048 : PRUint32 aNumber, void* aArg)
2049 : {
2050 75 : PLDHashOperator result = PL_DHASH_NEXT;
2051 75 : Entry* entry = reinterpret_cast<Entry*>(aHdr);
2052 75 : SweepInfo* info = static_cast<SweepInfo*>(aArg);
2053 :
2054 75 : Assertion* as = entry->mAssertions;
2055 75 : if (as && (as->mHashEntry))
2056 : {
2057 : // Stuff in sub-hashes must be swept recursively (max depth: 1)
2058 : PL_DHashTableEnumerate(as->u.hash.mPropertyHash,
2059 0 : SweepForwardArcsEntries, info);
2060 :
2061 : // If the sub-hash is now empty, clean it up.
2062 0 : if (!as->u.hash.mPropertyHash->entryCount) {
2063 0 : Assertion::Destroy(*info->mAllocator, as);
2064 0 : result = PL_DHASH_REMOVE;
2065 : }
2066 :
2067 0 : return result;
2068 : }
2069 :
2070 75 : Assertion* prev = nsnull;
2071 489 : while (as) {
2072 339 : if (as->IsMarked()) {
2073 339 : prev = as;
2074 339 : as->Unmark();
2075 339 : as = as->mNext;
2076 : }
2077 : else {
2078 : // remove from the list of assertions in the datasource
2079 0 : Assertion* next = as->mNext;
2080 0 : if (prev) {
2081 0 : prev->mNext = next;
2082 : }
2083 : else {
2084 : // it's the first one. update the hashtable entry.
2085 0 : entry->mAssertions = next;
2086 : }
2087 :
2088 : // remove from the reverse arcs
2089 : PLDHashEntryHdr* hdr =
2090 0 : PL_DHashTableOperate(info->mReverseArcs, as->u.as.mTarget, PL_DHASH_LOOKUP);
2091 0 : NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(hdr), "no assertion in reverse arcs");
2092 :
2093 0 : Entry* rentry = reinterpret_cast<Entry*>(hdr);
2094 0 : Assertion* ras = rentry->mAssertions;
2095 0 : Assertion* rprev = nsnull;
2096 0 : while (ras) {
2097 0 : if (ras == as) {
2098 0 : if (rprev) {
2099 0 : rprev->u.as.mInvNext = ras->u.as.mInvNext;
2100 : }
2101 : else {
2102 : // it's the first one. update the hashtable entry.
2103 0 : rentry->mAssertions = ras->u.as.mInvNext;
2104 : }
2105 0 : as->u.as.mInvNext = nsnull; // for my sanity.
2106 0 : break;
2107 : }
2108 0 : rprev = ras;
2109 0 : ras = ras->u.as.mInvNext;
2110 : }
2111 :
2112 : // Wow, it was the _only_ one. Unhash it.
2113 0 : if (! rentry->mAssertions)
2114 : {
2115 0 : PL_DHashTableRawRemove(info->mReverseArcs, hdr);
2116 : }
2117 :
2118 : // add to the list of assertions to unassert
2119 0 : as->mNext = info->mUnassertList;
2120 0 : info->mUnassertList = as;
2121 :
2122 : // Advance to the next assertion
2123 0 : as = next;
2124 : }
2125 : }
2126 :
2127 : // if no more assertions exist for this resource, then unhash it.
2128 75 : if (! entry->mAssertions)
2129 0 : result = PL_DHASH_REMOVE;
2130 :
2131 75 : return result;
2132 : }
2133 :
2134 : ////////////////////////////////////////////////////////////////////////
2135 : // rdfIDataSource methods
2136 :
2137 : class VisitorClosure
2138 : {
2139 : public:
2140 19 : VisitorClosure(rdfITripleVisitor* aVisitor) :
2141 : mVisitor(aVisitor),
2142 19 : mRv(NS_OK)
2143 19 : {}
2144 : rdfITripleVisitor* mVisitor;
2145 : nsresult mRv;
2146 : };
2147 :
2148 : PLDHashOperator
2149 0 : SubjectEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
2150 : PRUint32 aNumber, void* aArg) {
2151 0 : Entry* entry = reinterpret_cast<Entry*>(aHdr);
2152 0 : VisitorClosure* closure = static_cast<VisitorClosure*>(aArg);
2153 :
2154 : nsresult rv;
2155 0 : nsCOMPtr<nsIRDFNode> subject = do_QueryInterface(entry->mNode, &rv);
2156 0 : NS_ENSURE_SUCCESS(rv, PL_DHASH_NEXT);
2157 :
2158 0 : closure->mRv = closure->mVisitor->Visit(subject, nsnull, nsnull, true);
2159 0 : if (NS_FAILED(closure->mRv) || closure->mRv == NS_RDF_STOP_VISIT)
2160 0 : return PL_DHASH_STOP;
2161 :
2162 0 : return PL_DHASH_NEXT;
2163 : }
2164 :
2165 : NS_IMETHODIMP
2166 0 : InMemoryDataSource::VisitAllSubjects(rdfITripleVisitor *aVisitor)
2167 : {
2168 : // Lock datasource against writes
2169 0 : ++mReadCount;
2170 :
2171 : // Enumerate all of our entries into an nsISupportsArray.
2172 0 : VisitorClosure cls(aVisitor);
2173 0 : PL_DHashTableEnumerate(&mForwardArcs, SubjectEnumerator, &cls);
2174 :
2175 : // Unlock datasource
2176 0 : --mReadCount;
2177 :
2178 0 : return cls.mRv;
2179 : }
2180 :
2181 : class TriplesInnerClosure
2182 : {
2183 : public:
2184 0 : TriplesInnerClosure(nsIRDFNode* aSubject, VisitorClosure* aClosure) :
2185 0 : mSubject(aSubject), mOuter(aClosure) {}
2186 : nsIRDFNode* mSubject;
2187 : VisitorClosure* mOuter;
2188 : };
2189 :
2190 : PLDHashOperator
2191 0 : TriplesInnerEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
2192 : PRUint32 aNumber, void* aArg) {
2193 0 : Entry* entry = reinterpret_cast<Entry*>(aHdr);
2194 0 : Assertion* assertion = entry->mAssertions;
2195 : TriplesInnerClosure* closure =
2196 0 : static_cast<TriplesInnerClosure*>(aArg);
2197 0 : while (assertion) {
2198 0 : NS_ASSERTION(!assertion->mHashEntry, "shouldn't have to hashes");
2199 0 : VisitorClosure* cls = closure->mOuter;
2200 : cls->mRv = cls->mVisitor->Visit(closure->mSubject,
2201 : assertion->u.as.mProperty,
2202 : assertion->u.as.mTarget,
2203 0 : assertion->u.as.mTruthValue);
2204 0 : if (NS_FAILED(cls->mRv) || cls->mRv == NS_RDF_STOP_VISIT) {
2205 0 : return PL_DHASH_STOP;
2206 : }
2207 0 : assertion = assertion->mNext;
2208 : }
2209 0 : return PL_DHASH_NEXT;
2210 : }
2211 : PLDHashOperator
2212 349 : TriplesEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
2213 : PRUint32 aNumber, void* aArg) {
2214 349 : Entry* entry = reinterpret_cast<Entry*>(aHdr);
2215 349 : VisitorClosure* closure = static_cast<VisitorClosure*>(aArg);
2216 :
2217 : nsresult rv;
2218 698 : nsCOMPtr<nsIRDFNode> subject = do_QueryInterface(entry->mNode, &rv);
2219 349 : NS_ENSURE_SUCCESS(rv, PL_DHASH_NEXT);
2220 :
2221 349 : if (entry->mAssertions->mHashEntry) {
2222 0 : TriplesInnerClosure cls(subject, closure);
2223 : PL_DHashTableEnumerate(entry->mAssertions->u.hash.mPropertyHash,
2224 0 : TriplesInnerEnumerator, &cls);
2225 0 : if (NS_FAILED(closure->mRv)) {
2226 0 : return PL_DHASH_STOP;
2227 : }
2228 0 : return PL_DHASH_NEXT;
2229 : }
2230 349 : Assertion* assertion = entry->mAssertions;
2231 1481 : while (assertion) {
2232 783 : NS_ASSERTION(!assertion->mHashEntry, "shouldn't have to hashes");
2233 : closure->mRv = closure->mVisitor->Visit(subject,
2234 : assertion->u.as.mProperty,
2235 : assertion->u.as.mTarget,
2236 783 : assertion->u.as.mTruthValue);
2237 783 : if (NS_FAILED(closure->mRv) || closure->mRv == NS_RDF_STOP_VISIT) {
2238 0 : return PL_DHASH_STOP;
2239 : }
2240 783 : assertion = assertion->mNext;
2241 : }
2242 349 : return PL_DHASH_NEXT;
2243 : }
2244 : NS_IMETHODIMP
2245 19 : InMemoryDataSource::VisitAllTriples(rdfITripleVisitor *aVisitor)
2246 : {
2247 : // Lock datasource against writes
2248 19 : ++mReadCount;
2249 :
2250 : // Enumerate all of our entries into an nsISupportsArray.
2251 19 : VisitorClosure cls(aVisitor);
2252 19 : PL_DHashTableEnumerate(&mForwardArcs, TriplesEnumerator, &cls);
2253 :
2254 : // Unlock datasource
2255 19 : --mReadCount;
2256 :
2257 19 : return cls.mRv;
2258 4392 : }
2259 :
2260 : ////////////////////////////////////////////////////////////////////////
2261 :
|