1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef _MDB_
7 #  include "mdb.h"
8 #endif
9 
10 #ifndef _MORK_
11 #  include "mork.h"
12 #endif
13 
14 #ifndef _MORKNODE_
15 #  include "morkNode.h"
16 #endif
17 
18 #ifndef _MORKMAP_
19 #  include "morkMap.h"
20 #endif
21 
22 #ifndef _MORKSPACE_
23 #  include "morkSpace.h"
24 #endif
25 
26 #ifndef _MORKNODEMAP_
27 #  include "morkNodeMap.h"
28 #endif
29 
30 #ifndef _MORKROWMAP_
31 #  include "morkRowMap.h"
32 #endif
33 
34 #ifndef _MORKENV_
35 #  include "morkEnv.h"
36 #endif
37 
38 #ifndef _MORKROWSPACE_
39 #  include "morkRowSpace.h"
40 #endif
41 
42 #ifndef _MORKPOOL_
43 #  include "morkPool.h"
44 #endif
45 
46 #ifndef _MORKSTORE_
47 #  include "morkStore.h"
48 #endif
49 
50 #ifndef _MORKTABLE_
51 #  include "morkTable.h"
52 #endif
53 
54 #ifndef _MORKROW_
55 #  include "morkRow.h"
56 #endif
57 
58 #ifndef _MORKATOMMAP_
59 #  include "morkAtomMap.h"
60 #endif
61 
62 #ifndef _MORKROWOBJECT_
63 #  include "morkRowObject.h"
64 #endif
65 
66 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
67 
68 // ````` ````` ````` ````` `````
69 // { ===== begin morkNode interface =====
70 
CloseMorkNode(morkEnv * ev)71 /*public virtual*/ void morkRowSpace::CloseMorkNode(
72     morkEnv* ev)  // CloseRowSpace() only if open
73 {
74   if (this->IsOpenNode()) {
75     this->MarkClosing();
76     this->CloseRowSpace(ev);
77     this->MarkShut();
78   }
79 }
80 
81 /*public virtual*/
~morkRowSpace()82 morkRowSpace::~morkRowSpace()  // assert CloseRowSpace() executed earlier
83 {
84   MORK_ASSERT(this->IsShutNode());
85 }
86 
87 /*public non-poly*/
morkRowSpace(morkEnv * ev,const morkUsage & inUsage,mork_scope inScope,morkStore * ioStore,nsIMdbHeap * ioHeap,nsIMdbHeap * ioSlotHeap)88 morkRowSpace::morkRowSpace(morkEnv* ev, const morkUsage& inUsage,
89                            mork_scope inScope, morkStore* ioStore,
90                            nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
91     : morkSpace(ev, inUsage, inScope, ioStore, ioHeap, ioSlotHeap),
92       mRowSpace_SlotHeap(ioSlotHeap),
93       mRowSpace_Rows(ev, morkUsage::kMember, (nsIMdbHeap*)0, ioSlotHeap,
94                      morkRowSpace_kStartRowMapSlotCount),
95       mRowSpace_Tables(ev, morkUsage::kMember, (nsIMdbHeap*)0, ioSlotHeap),
96       mRowSpace_NextTableId(1),
97       mRowSpace_NextRowId(1)
98 
99       ,
100       mRowSpace_IndexCount(0) {
101   morkAtomRowMap** cache = mRowSpace_IndexCache;
102   morkAtomRowMap** cacheEnd = cache + morkRowSpace_kPrimeCacheSize;
103   while (cache < cacheEnd)
104     *cache++ = 0;  // put nil into every slot of cache table
105 
106   if (ev->Good()) {
107     if (ioSlotHeap) {
108       mNode_Derived = morkDerived_kRowSpace;
109 
110       // the morkSpace base constructor handles any dirty propagation
111     } else
112       ev->NilPointerError();
113   }
114 }
115 
CloseRowSpace(morkEnv * ev)116 /*public non-poly*/ void morkRowSpace::CloseRowSpace(
117     morkEnv* ev)  // called by CloseMorkNode();
118 {
119   if (this->IsNode()) {
120     morkAtomRowMap** cache = mRowSpace_IndexCache;
121     morkAtomRowMap** cacheEnd = cache + morkRowSpace_kPrimeCacheSize;
122     --cache;  // prepare for preincrement:
123     while (++cache < cacheEnd) {
124       if (*cache) morkAtomRowMap::SlotStrongAtomRowMap(0, ev, cache);
125     }
126 
127     mRowSpace_Tables.CloseMorkNode(ev);
128 
129     morkStore* store = mSpace_Store;
130     if (store) this->CutAllRows(ev, &store->mStore_Pool);
131 
132     mRowSpace_Rows.CloseMorkNode(ev);
133     this->CloseSpace(ev);
134   } else
135     this->NonNodeError(ev);
136 }
137 
138 // } ===== end morkNode methods =====
139 // ````` ````` ````` ````` `````
140 
NonRowSpaceTypeError(morkEnv * ev)141 /*static*/ void morkRowSpace::NonRowSpaceTypeError(morkEnv* ev) {
142   ev->NewError("non morkRowSpace");
143 }
144 
ZeroKindError(morkEnv * ev)145 /*static*/ void morkRowSpace::ZeroKindError(morkEnv* ev) {
146   ev->NewError("zero table kind");
147 }
148 
ZeroScopeError(morkEnv * ev)149 /*static*/ void morkRowSpace::ZeroScopeError(morkEnv* ev) {
150   ev->NewError("zero row scope");
151 }
152 
ZeroTidError(morkEnv * ev)153 /*static*/ void morkRowSpace::ZeroTidError(morkEnv* ev) {
154   ev->NewError("zero table ID");
155 }
156 
MinusOneRidError(morkEnv * ev)157 /*static*/ void morkRowSpace::MinusOneRidError(morkEnv* ev) {
158   ev->NewError("row ID is -1");
159 }
160 
161 ///*static*/ void
162 // morkRowSpace::ExpectAutoIdOnlyError(morkEnv* ev)
163 //{
164 //  ev->NewError("zero row ID");
165 //}
166 
167 ///*static*/ void
168 // morkRowSpace::ExpectAutoIdNeverError(morkEnv* ev)
169 //{
170 //}
171 
CutAllRows(morkEnv * ev,morkPool * ioPool)172 mork_num morkRowSpace::CutAllRows(morkEnv* ev, morkPool* ioPool) {
173   if (this->IsRowSpaceClean()) this->MaybeDirtyStoreAndSpace();
174 
175 #ifdef MORK_ENABLE_ZONE_ARENAS
176   MORK_USED_2(ev, ioPool);
177   return 0;
178 #else /*MORK_ENABLE_ZONE_ARENAS*/
179   mork_num outSlots = mRowSpace_Rows.MapFill();
180   morkZone* zone = &mSpace_Store->mStore_Zone;
181   morkRow* r = 0;  // old key row in the map
182   mork_change* c = 0;
183 
184 #  ifdef MORK_ENABLE_PROBE_MAPS
185   morkRowProbeMapIter i(ev, &mRowSpace_Rows);
186 #  else  /*MORK_ENABLE_PROBE_MAPS*/
187   morkRowMapIter i(ev, &mRowSpace_Rows);
188 #  endif /*MORK_ENABLE_PROBE_MAPS*/
189 
190   for (c = i.FirstRow(ev, &r); c && ev->Good(); c = i.NextRow(ev, &r)) {
191     if (r) {
192       if (r->IsRow()) {
193         if (r->mRow_Object) {
194           morkRowObject::SlotWeakRowObject((morkRowObject*)0, ev,
195                                            &r->mRow_Object);
196         }
197         ioPool->ZapRow(ev, r, zone);
198       } else
199         r->NonRowTypeWarning(ev);
200     } else
201       ev->NilPointerError();
202 
203 #  ifdef MORK_ENABLE_PROBE_MAPS
204       // cut nothing from the map
205 #  else  /*MORK_ENABLE_PROBE_MAPS*/
206     i.CutHereRow(ev, /*key*/ (morkRow**)0);
207 #  endif /*MORK_ENABLE_PROBE_MAPS*/
208   }
209 
210   return outSlots;
211 #endif   /*MORK_ENABLE_ZONE_ARENAS*/
212 }
213 
FindTableByKind(morkEnv * ev,mork_kind inTableKind)214 morkTable* morkRowSpace::FindTableByKind(morkEnv* ev, mork_kind inTableKind) {
215   if (inTableKind) {
216 #ifdef MORK_BEAD_OVER_NODE_MAPS
217 
218     morkTableMapIter i(ev, &mRowSpace_Tables);
219     morkTable* table = i.FirstTable(ev);
220     for (; table && ev->Good(); table = i.NextTable(ev))
221 #else  /*MORK_BEAD_OVER_NODE_MAPS*/
222     mork_tid* key = 0;     // nil pointer to suppress key access
223     morkTable* table = 0;  // old table in the map
224 
225     mork_change* c = 0;
226     morkTableMapIter i(ev, &mRowSpace_Tables);
227     for (c = i.FirstTable(ev, key, &table); c && ev->Good();
228          c = i.NextTable(ev, key, &table))
229 #endif /*MORK_BEAD_OVER_NODE_MAPS*/
230     {
231       if (table->mTable_Kind == inTableKind) return table;
232     }
233   } else
234     this->ZeroKindError(ev);
235 
236   return (morkTable*)0;
237 }
238 
NewTableWithTid(morkEnv * ev,mork_tid inTid,mork_kind inTableKind,const mdbOid * inOptionalMetaRowOid)239 morkTable* morkRowSpace::NewTableWithTid(
240     morkEnv* ev, mork_tid inTid, mork_kind inTableKind,
241     const mdbOid* inOptionalMetaRowOid)  // can be nil to avoid specifying
242 {
243   morkTable* outTable = 0;
244   morkStore* store = mSpace_Store;
245 
246   if (inTableKind && store) {
247     mdb_bool mustBeUnique = morkBool_kFalse;
248     nsIMdbHeap* heap = store->mPort_Heap;
249     morkTable* table = new (*heap, ev)
250         morkTable(ev, morkUsage::kHeap, heap, store, heap, this,
251                   inOptionalMetaRowOid, inTid, inTableKind, mustBeUnique);
252     if (table) {
253       if (mRowSpace_Tables.AddTable(ev, table)) {
254         outTable = table;
255         if (mRowSpace_NextTableId <= inTid) mRowSpace_NextTableId = inTid + 1;
256       }
257 
258       if (this->IsRowSpaceClean() && store->mStore_CanDirty)
259         this->MaybeDirtyStoreAndSpace();  // morkTable does already
260     }
261   } else if (store)
262     this->ZeroKindError(ev);
263   else
264     this->NilSpaceStoreError(ev);
265 
266   return outTable;
267 }
268 
NewTable(morkEnv * ev,mork_kind inTableKind,mdb_bool inMustBeUnique,const mdbOid * inOptionalMetaRowOid)269 morkTable* morkRowSpace::NewTable(
270     morkEnv* ev, mork_kind inTableKind, mdb_bool inMustBeUnique,
271     const mdbOid* inOptionalMetaRowOid)  // can be nil to avoid specifying
272 {
273   morkTable* outTable = 0;
274   morkStore* store = mSpace_Store;
275 
276   if (inTableKind && store) {
277     if (inMustBeUnique)  // need to look for existing table first?
278       outTable = this->FindTableByKind(ev, inTableKind);
279 
280     if (!outTable && ev->Good()) {
281       mork_tid id = this->MakeNewTableId(ev);
282       if (id) {
283         nsIMdbHeap* heap = mSpace_Store->mPort_Heap;
284         morkTable* table = new (*heap, ev)
285             morkTable(ev, morkUsage::kHeap, heap, mSpace_Store, heap, this,
286                       inOptionalMetaRowOid, id, inTableKind, inMustBeUnique);
287         if (table) {
288           if (mRowSpace_Tables.AddTable(ev, table))
289             outTable = table;
290           else
291             table->Release();
292 
293           if (this->IsRowSpaceClean() && store->mStore_CanDirty)
294             this->MaybeDirtyStoreAndSpace();  // morkTable does already
295         }
296       }
297     }
298   } else if (store)
299     this->ZeroKindError(ev);
300   else
301     this->NilSpaceStoreError(ev);
302 
303   return outTable;
304 }
305 
MakeNewTableId(morkEnv * ev)306 mork_tid morkRowSpace::MakeNewTableId(morkEnv* ev) {
307   mork_tid outTid = 0;
308   mork_tid id = mRowSpace_NextTableId;
309   mork_num count = 9;  // try up to eight times
310 
311   while (!outTid && --count)  // still trying to find an unused table ID?
312   {
313     if (!mRowSpace_Tables.GetTable(ev, id))
314       outTid = id;
315     else {
316       MORK_ASSERT(morkBool_kFalse);  // alert developer about ID problems
317       ++id;
318     }
319   }
320 
321   mRowSpace_NextTableId = id + 1;
322   return outTid;
323 }
324 
325 #define MAX_AUTO_ID (morkRow_kMinusOneRid - 2)
MakeNewRowId(morkEnv * ev)326 mork_rid morkRowSpace::MakeNewRowId(morkEnv* ev) {
327   mork_rid outRid = 0;
328   mork_rid id = mRowSpace_NextRowId;
329   mork_num count = 9;  // try up to eight times
330   mdbOid oid;
331   oid.mOid_Scope = this->SpaceScope();
332 
333   // still trying to find an unused row ID?
334   while (!outRid && --count && id <= MAX_AUTO_ID) {
335     oid.mOid_Id = id;
336     if (!mRowSpace_Rows.GetOid(ev, &oid))
337       outRid = id;
338     else {
339       MORK_ASSERT(morkBool_kFalse);  // alert developer about ID problems
340       ++id;
341     }
342   }
343 
344   if (id < MAX_AUTO_ID) mRowSpace_NextRowId = id + 1;
345   return outRid;
346 }
347 
make_index(morkEnv * ev,mork_column inCol)348 morkAtomRowMap* morkRowSpace::make_index(morkEnv* ev, mork_column inCol) {
349   morkAtomRowMap* outMap = 0;
350   nsIMdbHeap* heap = mRowSpace_SlotHeap;
351   if (heap)  // have expected heap for allocations?
352   {
353     morkAtomRowMap* map =
354         new (*heap, ev) morkAtomRowMap(ev, morkUsage::kHeap, heap, heap, inCol);
355 
356     if (map)  // able to create new map index?
357     {
358       if (ev->Good())  // no errors during construction?
359       {
360 #ifdef MORK_ENABLE_PROBE_MAPS
361         morkRowProbeMapIter i(ev, &mRowSpace_Rows);
362 #else  /*MORK_ENABLE_PROBE_MAPS*/
363         morkRowMapIter i(ev, &mRowSpace_Rows);
364 #endif /*MORK_ENABLE_PROBE_MAPS*/
365         mork_change* c = 0;
366         morkRow* row = 0;
367         mork_aid aidKey = 0;
368 
369         for (c = i.FirstRow(ev, &row); c && ev->Good();
370              c = i.NextRow(ev, &row))  // another row in space?
371         {
372           aidKey = row->GetCellAtomAid(ev, inCol);
373           if (aidKey)                      // row has indexed attribute?
374             map->AddAid(ev, aidKey, row);  // include in map
375         }
376       }
377       if (ev->Good())  // no errors constructing index?
378         outMap = map;  // return from function
379       else
380         map->CutStrongRef(ev);  // discard map on error
381     }
382   } else
383     ev->NilPointerError();
384 
385   return outMap;
386 }
387 
ForceMap(morkEnv * ev,mork_column inCol)388 morkAtomRowMap* morkRowSpace::ForceMap(morkEnv* ev, mork_column inCol) {
389   morkAtomRowMap* outMap = this->FindMap(ev, inCol);
390 
391   if (!outMap && ev->Good())  // no such existing index?
392   {
393     if (mRowSpace_IndexCount < morkRowSpace_kMaxIndexCount) {
394       morkAtomRowMap* map = this->make_index(ev, inCol);
395       if (map)  // created a new index for col?
396       {
397         mork_count wrap = 0;  // count times wrap-around occurs
398         morkAtomRowMap** slot = mRowSpace_IndexCache;  // table
399         morkAtomRowMap** end = slot + morkRowSpace_kPrimeCacheSize;
400         slot += (inCol % morkRowSpace_kPrimeCacheSize);  // hash
401         while (*slot)  // empty slot not yet found?
402         {
403           if (++slot >= end)  // wrap around?
404           {
405             slot = mRowSpace_IndexCache;  // back to table start
406             if (++wrap > 1)               // wrapped more than once?
407             {
408               ev->NewError("no free cache slots");  // disaster
409               break;                                // end while loop
410             }
411           }
412         }
413         if (ev->Good())  // everything went just fine?
414         {
415           ++mRowSpace_IndexCount;  // note another new map
416           *slot = map;             // install map in the hash table
417           outMap = map;            // return the new map from function
418         } else
419           map->CutStrongRef(ev);  // discard map on error
420       }
421     } else
422       ev->NewError("too many indexes");  // why so many indexes?
423   }
424   return outMap;
425 }
426 
FindMap(morkEnv * ev,mork_column inCol)427 morkAtomRowMap* morkRowSpace::FindMap(morkEnv* ev, mork_column inCol) {
428   if (mRowSpace_IndexCount && ev->Good()) {
429     mork_count wrap = 0;  // count times wrap-around occurs
430     morkAtomRowMap** slot = mRowSpace_IndexCache;  // table
431     morkAtomRowMap** end = slot + morkRowSpace_kPrimeCacheSize;
432     slot += (inCol % morkRowSpace_kPrimeCacheSize);  // hash
433     morkAtomRowMap* map = *slot;
434     while (map)  // another used slot to examine?
435     {
436       if (inCol == map->mAtomRowMap_IndexColumn)  // found col?
437         return map;
438       if (++slot >= end)  // wrap around?
439       {
440         slot = mRowSpace_IndexCache;
441         if (++wrap > 1)               // wrapped more than once?
442           return (morkAtomRowMap*)0;  // stop searching
443       }
444       map = *slot;
445     }
446   }
447   return (morkAtomRowMap*)0;
448 }
449 
FindRow(morkEnv * ev,mork_column inCol,const mdbYarn * inYarn)450 morkRow* morkRowSpace::FindRow(morkEnv* ev, mork_column inCol,
451                                const mdbYarn* inYarn) {
452   morkRow* outRow = 0;
453 
454   // if yarn hasn't been atomized, there can't be a corresponding row,
455   // so pass in false to not create the row - should help history bloat
456   morkAtom* atom = mSpace_Store->YarnToAtom(ev, inYarn, false);
457   if (atom)  // have or created an atom corresponding to input yarn?
458   {
459     mork_aid atomAid = atom->GetBookAtomAid();
460     if (atomAid)  // atom has an identity for use in hash table?
461     {
462       morkAtomRowMap* map = this->ForceMap(ev, inCol);
463       if (map)  // able to find or create index for col?
464       {
465         outRow = map->GetAid(ev, atomAid);  // search for row
466       }
467     }
468   }
469 
470   return outRow;
471 }
472 
NewRowWithOid(morkEnv * ev,const mdbOid * inOid)473 morkRow* morkRowSpace::NewRowWithOid(morkEnv* ev, const mdbOid* inOid) {
474   morkRow* outRow = mRowSpace_Rows.GetOid(ev, inOid);
475   MORK_ASSERT(outRow == 0);
476   if (!outRow && ev->Good()) {
477     morkStore* store = mSpace_Store;
478     if (store) {
479       morkPool* pool = this->GetSpaceStorePool();
480       morkRow* row = pool->NewRow(ev, &store->mStore_Zone);
481       if (row) {
482         row->InitRow(ev, inOid, this, /*length*/ 0, pool);
483 
484         if (ev->Good() && mRowSpace_Rows.AddRow(ev, row)) {
485           outRow = row;
486           mork_rid rid = inOid->mOid_Id;
487           if (mRowSpace_NextRowId <= rid) mRowSpace_NextRowId = rid + 1;
488         } else
489           pool->ZapRow(ev, row, &store->mStore_Zone);
490 
491         if (this->IsRowSpaceClean() && store->mStore_CanDirty)
492           this->MaybeDirtyStoreAndSpace();  // InitRow() does already
493       }
494     } else
495       this->NilSpaceStoreError(ev);
496   }
497   return outRow;
498 }
499 
NewRow(morkEnv * ev)500 morkRow* morkRowSpace::NewRow(morkEnv* ev) {
501   morkRow* outRow = 0;
502   if (ev->Good()) {
503     mork_rid id = this->MakeNewRowId(ev);
504     if (id) {
505       morkStore* store = mSpace_Store;
506       if (store) {
507         mdbOid oid;
508         oid.mOid_Scope = this->SpaceScope();
509         oid.mOid_Id = id;
510         morkPool* pool = this->GetSpaceStorePool();
511         morkRow* row = pool->NewRow(ev, &store->mStore_Zone);
512         if (row) {
513           row->InitRow(ev, &oid, this, /*length*/ 0, pool);
514 
515           if (ev->Good() && mRowSpace_Rows.AddRow(ev, row))
516             outRow = row;
517           else
518             pool->ZapRow(ev, row, &store->mStore_Zone);
519 
520           if (this->IsRowSpaceClean() && store->mStore_CanDirty)
521             this->MaybeDirtyStoreAndSpace();  // InitRow() does already
522         }
523       } else
524         this->NilSpaceStoreError(ev);
525     }
526   }
527   return outRow;
528 }
529 
530 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
531 
~morkRowSpaceMap()532 morkRowSpaceMap::~morkRowSpaceMap() {}
533 
morkRowSpaceMap(morkEnv * ev,const morkUsage & inUsage,nsIMdbHeap * ioHeap,nsIMdbHeap * ioSlotHeap)534 morkRowSpaceMap::morkRowSpaceMap(morkEnv* ev, const morkUsage& inUsage,
535                                  nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
536     : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap) {
537   if (ev->Good()) mNode_Derived = morkDerived_kRowSpaceMap;
538 }
539 
540 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
541