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 _MORKENV_
23 #  include "morkEnv.h"
24 #endif
25 
26 #ifndef _MORKTABLE_
27 #  include "morkTable.h"
28 #endif
29 
30 #ifndef _MORKSTORE_
31 #  include "morkStore.h"
32 #endif
33 
34 #ifndef _MORKROWSPACE_
35 #  include "morkRowSpace.h"
36 #endif
37 
38 #ifndef _MORKARRAY_
39 #  include "morkArray.h"
40 #endif
41 
42 #ifndef _MORKROW_
43 #  include "morkRow.h"
44 #endif
45 
46 #ifndef _MORKTABLEROWCURSOR_
47 #  include "morkTableRowCursor.h"
48 #endif
49 
50 #ifndef _MORKROWOBJECT_
51 #  include "morkRowObject.h"
52 #endif
53 
54 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
55 
56 // ````` ````` ````` ````` `````
57 // { ===== begin morkNode interface =====
58 
CloseMorkNode(morkEnv * ev)59 /*public virtual*/ void morkTable::CloseMorkNode(
60     morkEnv* ev) /*i*/  // CloseTable() only if open
61 {
62   if (this->IsOpenNode()) {
63     morkObject::CloseMorkNode(ev);  // give base class a chance.
64     this->MarkClosing();
65     this->CloseTable(ev);
66     this->MarkShut();
67   }
68 }
69 
70 /*public virtual*/
~morkTable()71 morkTable::~morkTable() /*i*/  // assert CloseTable() executed earlier
72 {
73   CloseMorkNode(mMorkEnv);
74   MORK_ASSERT(this->IsShutNode());
75   MORK_ASSERT(mTable_Store == 0);
76   MORK_ASSERT(mTable_RowSpace == 0);
77 }
78 
79 /*public non-poly*/
morkTable(morkEnv * ev,const morkUsage & inUsage,nsIMdbHeap * ioHeap,morkStore * ioStore,nsIMdbHeap * ioSlotHeap,morkRowSpace * ioRowSpace,const mdbOid * inOptionalMetaRowOid,mork_tid inTid,mork_kind inKind,mork_bool inMustBeUnique)80 morkTable::morkTable(
81     morkEnv* ev, /*i*/
82     const morkUsage& inUsage, nsIMdbHeap* ioHeap, morkStore* ioStore,
83     nsIMdbHeap* ioSlotHeap, morkRowSpace* ioRowSpace,
84     const mdbOid* inOptionalMetaRowOid,  // can be nil to avoid specifying
85     mork_tid inTid, mork_kind inKind, mork_bool inMustBeUnique)
86     : morkObject(ev, inUsage, ioHeap, (mork_color)inTid, (morkHandle*)0),
87       mTable_Store(0),
88       mTable_RowSpace(0),
89       mTable_MetaRow(0)
90 
91       ,
92       mTable_RowMap(0)
93       // , mTable_RowMap(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap,
94       //   morkTable_kStartRowMapSlotCount)
95       ,
96       mTable_RowArray(ev, morkUsage::kMember, (nsIMdbHeap*)0,
97                       morkTable_kStartRowArraySize, ioSlotHeap)
98 
99       ,
100       mTable_ChangeList(),
101       mTable_ChangesCount(0),
102       mTable_ChangesMax(3)  // any very small number greater than zero
103 
104       ,
105       mTable_Kind(inKind)
106 
107       ,
108       mTable_Flags(0),
109       mTable_Priority(morkPriority_kLo)  // NOT high priority
110       ,
111       mTable_GcUses(0),
112       mTable_Pad(0) {
113   this->mLink_Next = 0;
114   this->mLink_Prev = 0;
115 
116   if (ev->Good()) {
117     if (ioStore && ioSlotHeap && ioRowSpace) {
118       if (inKind) {
119         if (inMustBeUnique) this->SetTableUnique();
120         mTable_Store = ioStore;
121         mTable_RowSpace = ioRowSpace;
122         if (inOptionalMetaRowOid)
123           mTable_MetaRowOid = *inOptionalMetaRowOid;
124         else {
125           mTable_MetaRowOid.mOid_Scope = 0;
126           mTable_MetaRowOid.mOid_Id = morkRow_kMinusOneRid;
127         }
128         if (ev->Good()) {
129           if (this->MaybeDirtySpaceStoreAndTable())
130             this->SetTableRewrite();  // everything is dirty
131 
132           mNode_Derived = morkDerived_kTable;
133         }
134         this->MaybeDirtySpaceStoreAndTable();  // new table might dirty store
135       } else
136         ioRowSpace->ZeroKindError(ev);
137     } else
138       ev->NilPointerError();
139   }
140 }
141 
NS_IMPL_ISUPPORTS_INHERITED(morkTable,morkObject,nsIMdbTable)142 NS_IMPL_ISUPPORTS_INHERITED(morkTable, morkObject, nsIMdbTable)
143 
144 /*public non-poly*/ void morkTable::CloseTable(
145     morkEnv* ev) /*i*/  // called by CloseMorkNode();
146 {
147   if (this->IsNode()) {
148     morkRowMap::SlotStrongRowMap((morkRowMap*)0, ev, &mTable_RowMap);
149     // mTable_RowMap.CloseMorkNode(ev);
150     mTable_RowArray.CloseMorkNode(ev);
151     mTable_Store = 0;
152     mTable_RowSpace = 0;
153     this->MarkShut();
154   } else
155     this->NonNodeError(ev);
156 }
157 
158 // } ===== end morkNode methods =====
159 // ````` ````` ````` ````` `````
160 
161 // { ===== begin nsIMdbCollection methods =====
162 
163 // { ----- begin attribute methods -----
164 NS_IMETHODIMP
GetSeed(nsIMdbEnv * mev,mdb_seed * outSeed)165 morkTable::GetSeed(nsIMdbEnv* mev,
166                    mdb_seed* outSeed)  // member change count
167 {
168   nsresult outErr = NS_OK;
169   morkEnv* ev = morkEnv::FromMdbEnv(mev);
170   if (ev) {
171     *outSeed = mTable_RowArray.mArray_Seed;
172     outErr = ev->AsErr();
173   }
174   return outErr;
175 }
176 
177 NS_IMETHODIMP
GetCount(nsIMdbEnv * mev,mdb_count * outCount)178 morkTable::GetCount(nsIMdbEnv* mev,
179                     mdb_count* outCount)  // member count
180 {
181   NS_ENSURE_ARG_POINTER(outCount);
182   *outCount = mTable_RowArray.mArray_Fill;
183   return NS_OK;
184 }
185 
186 NS_IMETHODIMP
GetPort(nsIMdbEnv * mev,nsIMdbPort ** acqPort)187 morkTable::GetPort(nsIMdbEnv* mev,
188                    nsIMdbPort** acqPort)  // collection container
189 {
190   (void)morkEnv::FromMdbEnv(mev);
191   NS_ENSURE_ARG_POINTER(acqPort);
192   *acqPort = mTable_Store;
193   return NS_OK;
194 }
195 // } ----- end attribute methods -----
196 
197 // { ----- begin cursor methods -----
198 NS_IMETHODIMP
GetCursor(nsIMdbEnv * mev,mdb_pos inMemberPos,nsIMdbCursor ** acqCursor)199 morkTable::GetCursor(          // make a cursor starting iter at inMemberPos
200     nsIMdbEnv* mev,            // context
201     mdb_pos inMemberPos,       // zero-based ordinal pos of member in collection
202     nsIMdbCursor** acqCursor)  // acquire new cursor instance
203 {
204   return this->GetTableRowCursor(mev, inMemberPos,
205                                  (nsIMdbTableRowCursor**)acqCursor);
206 }
207 // } ----- end cursor methods -----
208 
209 // { ----- begin ID methods -----
210 NS_IMETHODIMP
GetOid(nsIMdbEnv * mev,mdbOid * outOid)211 morkTable::GetOid(nsIMdbEnv* mev,
212                   mdbOid* outOid)  // read object identity
213 {
214   morkEnv* ev = morkEnv::FromMdbEnv(mev);
215   GetTableOid(ev, outOid);
216   return NS_OK;
217 }
218 
219 NS_IMETHODIMP
BecomeContent(nsIMdbEnv * mev,const mdbOid * inOid)220 morkTable::BecomeContent(nsIMdbEnv* mev,
221                          const mdbOid* inOid)  // exchange content
222 {
223   NS_ASSERTION(false, "not implemented");
224   return NS_ERROR_NOT_IMPLEMENTED;
225   // remember table->MaybeDirtySpaceStoreAndTable();
226 }
227 
228 // } ----- end ID methods -----
229 
230 // { ----- begin activity dropping methods -----
231 NS_IMETHODIMP
DropActivity(nsIMdbEnv * mev)232 morkTable::DropActivity(  // tell collection usage no longer expected
233     nsIMdbEnv* mev) {
234   NS_ASSERTION(false, "not implemented");
235   return NS_ERROR_NOT_IMPLEMENTED;
236 }
237 
238 // } ----- end activity dropping methods -----
239 
240 // } ===== end nsIMdbCollection methods =====
241 
242 // { ===== begin nsIMdbTable methods =====
243 
244 // { ----- begin attribute methods -----
245 
246 NS_IMETHODIMP
SetTablePriority(nsIMdbEnv * mev,mdb_priority inPrio)247 morkTable::SetTablePriority(nsIMdbEnv* mev, mdb_priority inPrio) {
248   nsresult outErr = NS_OK;
249   morkEnv* ev = morkEnv::FromMdbEnv(mev);
250   if (ev) {
251     if (inPrio > morkPriority_kMax) inPrio = morkPriority_kMax;
252 
253     mTable_Priority = inPrio;
254     outErr = ev->AsErr();
255   }
256   return outErr;
257 }
258 
259 NS_IMETHODIMP
GetTablePriority(nsIMdbEnv * mev,mdb_priority * outPrio)260 morkTable::GetTablePriority(nsIMdbEnv* mev, mdb_priority* outPrio) {
261   nsresult outErr = NS_OK;
262   mork_priority prio = 0;
263   morkEnv* ev = morkEnv::FromMdbEnv(mev);
264   if (ev) {
265     prio = mTable_Priority;
266     if (prio > morkPriority_kMax) {
267       prio = morkPriority_kMax;
268       mTable_Priority = prio;
269     }
270     outErr = ev->AsErr();
271   }
272   if (outPrio) *outPrio = prio;
273   return outErr;
274 }
275 
276 NS_IMETHODIMP
GetTableBeVerbose(nsIMdbEnv * mev,mdb_bool * outBeVerbose)277 morkTable::GetTableBeVerbose(nsIMdbEnv* mev, mdb_bool* outBeVerbose) {
278   NS_ENSURE_ARG_POINTER(outBeVerbose);
279   *outBeVerbose = IsTableVerbose();
280   return NS_OK;
281 }
282 
283 NS_IMETHODIMP
SetTableBeVerbose(nsIMdbEnv * mev,mdb_bool inBeVerbose)284 morkTable::SetTableBeVerbose(nsIMdbEnv* mev, mdb_bool inBeVerbose) {
285   nsresult outErr = NS_OK;
286   morkEnv* ev = morkEnv::FromMdbEnv(mev);
287   if (ev) {
288     if (inBeVerbose)
289       SetTableVerbose();
290     else
291       ClearTableVerbose();
292 
293     outErr = ev->AsErr();
294   }
295   return outErr;
296 }
297 
298 NS_IMETHODIMP
GetTableIsUnique(nsIMdbEnv * mev,mdb_bool * outIsUnique)299 morkTable::GetTableIsUnique(nsIMdbEnv* mev, mdb_bool* outIsUnique) {
300   NS_ENSURE_ARG_POINTER(outIsUnique);
301   *outIsUnique = IsTableUnique();
302   return NS_OK;
303 }
304 
305 NS_IMETHODIMP
GetTableKind(nsIMdbEnv * mev,mdb_kind * outTableKind)306 morkTable::GetTableKind(nsIMdbEnv* mev, mdb_kind* outTableKind) {
307   NS_ENSURE_ARG_POINTER(outTableKind);
308   *outTableKind = mTable_Kind;
309   return NS_OK;
310 }
311 
312 NS_IMETHODIMP
GetRowScope(nsIMdbEnv * mev,mdb_scope * outRowScope)313 morkTable::GetRowScope(nsIMdbEnv* mev, mdb_scope* outRowScope) {
314   nsresult outErr = NS_OK;
315   mdb_scope rowScope = 0;
316   morkEnv* ev = morkEnv::FromMdbEnv(mev);
317   if (ev) {
318     if (mTable_RowSpace)
319       rowScope = mTable_RowSpace->SpaceScope();
320     else
321       NilRowSpaceError(ev);
322 
323     outErr = ev->AsErr();
324   }
325   if (outRowScope) *outRowScope = rowScope;
326   return outErr;
327 }
328 
329 NS_IMETHODIMP
GetMetaRow(nsIMdbEnv * mev,const mdbOid * inOptionalMetaRowOid,mdbOid * outOid,nsIMdbRow ** acqRow)330 morkTable::GetMetaRow(
331     nsIMdbEnv* mev,
332     const mdbOid* inOptionalMetaRowOid,  // can be nil to avoid specifying
333     mdbOid* outOid,      // output meta row oid, can be nil to suppress output
334     nsIMdbRow** acqRow)  // acquire table's unique singleton meta row
335 // The purpose of a meta row is to support the persistent recording of
336 // meta info about a table as cells put into the distinguished meta row.
337 // Each table has exactly one meta row, which is not considered a member
338 // of the collection of rows inside the table.  The only way to tell
339 // whether a row is a meta row is by the fact that it is returned by this
340 // GetMetaRow() method from some table. Otherwise nothing distinguishes
341 // a meta row from any other row.  A meta row can be used anyplace that
342 // any other row can be used, and can even be put into other tables (or
343 // the same table) as a table member, if this is useful for some reason.
344 // The first attempt to access a table's meta row using GetMetaRow() will
345 // cause the meta row to be created if it did not already exist.  When the
346 // meta row is created, it will have the row oid that was previously
347 // requested for this table's meta row; or if no oid was ever explicitly
348 // specified for this meta row, then a unique oid will be generated in
349 // the row scope named "metaScope" (so obviously MDB clients should not
350 // manually allocate any row IDs from that special meta scope namespace).
351 // The meta row oid can be specified either when the table is created, or
352 // else the first time that GetMetaRow() is called, by passing a non-nil
353 // pointer to an oid for parameter inOptionalMetaRowOid.  The meta row's
354 // actual oid is returned in outOid (if this is a non-nil pointer), and
355 // it will be different from inOptionalMetaRowOid when the meta row was
356 // already given a different oid earlier.
357 {
358   nsresult outErr = NS_OK;
359   nsIMdbRow* outRow = 0;
360   morkEnv* ev = morkEnv::FromMdbEnv(mev);
361   if (ev) {
362     morkRow* row = GetMetaRow(ev, inOptionalMetaRowOid);
363     if (row && ev->Good()) {
364       if (outOid) *outOid = row->mRow_Oid;
365 
366       outRow = row->AcquireRowHandle(ev, mTable_Store);
367     }
368     outErr = ev->AsErr();
369   }
370   if (acqRow) *acqRow = outRow;
371 
372   if (ev->Bad() && outOid) {
373     outOid->mOid_Scope = 0;
374     outOid->mOid_Id = morkRow_kMinusOneRid;
375   }
376   return outErr;
377 }
378 
379 // } ----- end attribute methods -----
380 
381 // { ----- begin cursor methods -----
382 NS_IMETHODIMP
GetTableRowCursor(nsIMdbEnv * mev,mdb_pos inRowPos,nsIMdbTableRowCursor ** acqCursor)383 morkTable::GetTableRowCursor(  // make a cursor, starting iteration at inRowPos
384     nsIMdbEnv* mev,            // context
385     mdb_pos inRowPos,          // zero-based ordinal position of row in table
386     nsIMdbTableRowCursor** acqCursor)  // acquire new cursor instance
387 {
388   nsresult outErr = NS_OK;
389   nsIMdbTableRowCursor* outCursor = 0;
390   morkEnv* ev = morkEnv::FromMdbEnv(mev);
391   if (ev) {
392     morkTableRowCursor* cursor = NewTableRowCursor(ev, inRowPos);
393     if (cursor) {
394       if (ev->Good()) {
395         // cursor->mCursor_Seed = (mork_seed) inRowPos;
396         outCursor = cursor;
397         outCursor->AddRef();
398       }
399     }
400 
401     outErr = ev->AsErr();
402   }
403   if (acqCursor) *acqCursor = outCursor;
404   return outErr;
405 }
406 // } ----- end row position methods -----
407 
408 // { ----- begin row position methods -----
409 NS_IMETHODIMP
PosToOid(nsIMdbEnv * mev,mdb_pos inRowPos,mdbOid * outOid)410 morkTable::PosToOid(   // get row member for a table position
411     nsIMdbEnv* mev,    // context
412     mdb_pos inRowPos,  // zero-based ordinal position of row in table
413     mdbOid* outOid)    // row oid at the specified position
414 {
415   nsresult outErr = NS_OK;
416   mdbOid roid;
417   roid.mOid_Scope = 0;
418   roid.mOid_Id = (mork_id)-1;
419 
420   morkEnv* ev = morkEnv::FromMdbEnv(mev);
421   if (ev) {
422     morkRow* row = SafeRowAt(ev, inRowPos);
423     if (row) roid = row->mRow_Oid;
424 
425     outErr = ev->AsErr();
426   }
427   if (outOid) *outOid = roid;
428   return outErr;
429 }
430 
431 NS_IMETHODIMP
OidToPos(nsIMdbEnv * mev,const mdbOid * inOid,mdb_pos * outPos)432 morkTable::OidToPos(      // test for the table position of a row member
433     nsIMdbEnv* mev,       // context
434     const mdbOid* inOid,  // row to find in table
435     mdb_pos* outPos)      // zero-based ordinal position of row in table
436 {
437   nsresult outErr = NS_OK;
438   morkEnv* ev = morkEnv::FromMdbEnv(mev);
439   if (ev) {
440     mork_pos pos = ArrayHasOid(ev, inOid);
441     if (outPos) *outPos = pos;
442     outErr = ev->AsErr();
443   }
444   return outErr;
445 }
446 
447 NS_IMETHODIMP
PosToRow(nsIMdbEnv * mev,mdb_pos inRowPos,nsIMdbRow ** acqRow)448 morkTable::PosToRow(     // get row member for a table position
449     nsIMdbEnv* mev,      // context
450     mdb_pos inRowPos,    // zero-based ordinal position of row in table
451     nsIMdbRow** acqRow)  // acquire row at table position inRowPos
452 {
453   nsresult outErr = NS_OK;
454   nsIMdbRow* outRow = 0;
455   morkEnv* ev = morkEnv::FromMdbEnv(mev);
456   if (ev) {
457     morkRow* row = SafeRowAt(ev, inRowPos);
458     if (row && mTable_Store) outRow = row->AcquireRowHandle(ev, mTable_Store);
459 
460     outErr = ev->AsErr();
461   }
462   if (acqRow) *acqRow = outRow;
463   return outErr;
464 }
465 
466 NS_IMETHODIMP
RowToPos(nsIMdbEnv * mev,nsIMdbRow * ioRow,mdb_pos * outPos)467 morkTable::RowToPos(   // test for the table position of a row member
468     nsIMdbEnv* mev,    // context
469     nsIMdbRow* ioRow,  // row to find in table
470     mdb_pos* outPos)   // zero-based ordinal position of row in table
471 {
472   nsresult outErr = NS_OK;
473   mork_pos pos = -1;
474   morkEnv* ev = morkEnv::FromMdbEnv(mev);
475   if (ev) {
476     morkRowObject* row = (morkRowObject*)ioRow;
477     pos = ArrayHasOid(ev, &row->mRowObject_Row->mRow_Oid);
478     outErr = ev->AsErr();
479   }
480   if (outPos) *outPos = pos;
481   return outErr;
482 }
483 
484 // Note that HasRow() performs the inverse oid->pos mapping
485 // } ----- end row position methods -----
486 
487 // { ----- begin oid set methods -----
488 NS_IMETHODIMP
AddOid(nsIMdbEnv * mev,const mdbOid * inOid)489 morkTable::AddOid(        // make sure the row with inOid is a table member
490     nsIMdbEnv* mev,       // context
491     const mdbOid* inOid)  // row to ensure membership in table
492 {
493   NS_ASSERTION(false, "not implemented");
494   return NS_ERROR_NOT_IMPLEMENTED;
495 }
496 
497 NS_IMETHODIMP
HasOid(nsIMdbEnv * mev,const mdbOid * inOid,mdb_bool * outHasOid)498 morkTable::HasOid(        // test for the table position of a row member
499     nsIMdbEnv* mev,       // context
500     const mdbOid* inOid,  // row to find in table
501     mdb_bool* outHasOid)  // whether inOid is a member row
502 {
503   nsresult outErr = NS_OK;
504   morkEnv* ev = morkEnv::FromMdbEnv(mev);
505   if (ev) {
506     if (outHasOid) *outHasOid = MapHasOid(ev, inOid);
507     outErr = ev->AsErr();
508   }
509   return outErr;
510 }
511 
512 NS_IMETHODIMP
CutOid(nsIMdbEnv * mev,const mdbOid * inOid)513 morkTable::CutOid(        // make sure the row with inOid is not a member
514     nsIMdbEnv* mev,       // context
515     const mdbOid* inOid)  // row to remove from table
516 {
517   nsresult outErr = NS_OK;
518   morkEnv* ev = morkEnv::FromMdbEnv(mev);
519   if (ev) {
520     if (inOid && mTable_Store) {
521       morkRow* row = mTable_Store->GetRow(ev, inOid);
522       if (row) CutRow(ev, row);
523     } else
524       ev->NilPointerError();
525 
526     outErr = ev->AsErr();
527   }
528   return outErr;
529 }
530 // } ----- end oid set methods -----
531 
532 // { ----- begin row set methods -----
533 NS_IMETHODIMP
NewRow(nsIMdbEnv * mev,mdbOid * ioOid,nsIMdbRow ** acqRow)534 morkTable::NewRow(       // create a new row instance in table
535     nsIMdbEnv* mev,      // context
536     mdbOid* ioOid,       // please use zero (unbound) rowId for db-assigned IDs
537     nsIMdbRow** acqRow)  // create new row
538 {
539   nsresult outErr = NS_OK;
540   nsIMdbRow* outRow = 0;
541   morkEnv* ev = morkEnv::FromMdbEnv(mev);
542   if (ev) {
543     if (ioOid && mTable_Store) {
544       morkRow* row = 0;
545       if (ioOid->mOid_Id == morkRow_kMinusOneRid)
546         row = mTable_Store->NewRow(ev, ioOid->mOid_Scope);
547       else
548         row = mTable_Store->NewRowWithOid(ev, ioOid);
549 
550       if (row && AddRow(ev, row))
551         outRow = row->AcquireRowHandle(ev, mTable_Store);
552     } else
553       ev->NilPointerError();
554 
555     outErr = ev->AsErr();
556   }
557   if (acqRow) *acqRow = outRow;
558   return outErr;
559 }
560 
561 NS_IMETHODIMP
AddRow(nsIMdbEnv * mev,nsIMdbRow * ioRow)562 morkTable::AddRow(     // make sure the row with inOid is a table member
563     nsIMdbEnv* mev,    // context
564     nsIMdbRow* ioRow)  // row to ensure membership in table
565 {
566   nsresult outErr = NS_OK;
567   morkEnv* ev = morkEnv::FromMdbEnv(mev);
568   if (ev) {
569     morkRowObject* rowObj = (morkRowObject*)ioRow;
570     morkRow* row = rowObj->mRowObject_Row;
571     AddRow(ev, row);
572     outErr = ev->AsErr();
573   }
574   return outErr;
575 }
576 
577 NS_IMETHODIMP
HasRow(nsIMdbEnv * mev,nsIMdbRow * ioRow,mdb_bool * outBool)578 morkTable::HasRow(      // test for the table position of a row member
579     nsIMdbEnv* mev,     // context
580     nsIMdbRow* ioRow,   // row to find in table
581     mdb_bool* outBool)  // zero-based ordinal position of row in table
582 {
583   nsresult outErr = NS_OK;
584   morkEnv* ev = morkEnv::FromMdbEnv(mev);
585   if (ev) {
586     morkRowObject* rowObj = (morkRowObject*)ioRow;
587     morkRow* row = rowObj->mRowObject_Row;
588     if (outBool) *outBool = MapHasOid(ev, &row->mRow_Oid);
589     outErr = ev->AsErr();
590   }
591   return outErr;
592 }
593 
594 NS_IMETHODIMP
CutRow(nsIMdbEnv * mev,nsIMdbRow * ioRow)595 morkTable::CutRow(     // make sure the row with inOid is not a member
596     nsIMdbEnv* mev,    // context
597     nsIMdbRow* ioRow)  // row to remove from table
598 {
599   nsresult outErr = NS_OK;
600   morkEnv* ev = morkEnv::FromMdbEnv(mev);
601   if (ev) {
602     morkRowObject* rowObj = (morkRowObject*)ioRow;
603     morkRow* row = rowObj->mRowObject_Row;
604     CutRow(ev, row);
605     outErr = ev->AsErr();
606   }
607   return outErr;
608 }
609 
610 NS_IMETHODIMP
CutAllRows(nsIMdbEnv * mev)611 morkTable::CutAllRows(  // remove all rows from the table
612     nsIMdbEnv* mev)     // context
613 {
614   nsresult outErr = NS_OK;
615   morkEnv* ev = morkEnv::FromMdbEnv(mev);
616   if (ev) {
617     CutAllRows(ev);
618     outErr = ev->AsErr();
619   }
620   return outErr;
621 }
622 // } ----- end row set methods -----
623 
624 // { ----- begin searching methods -----
625 NS_IMETHODIMP
FindRowMatches(nsIMdbEnv * mev,const mdbYarn * inPrefix,nsIMdbTableRowCursor ** acqCursor)626 morkTable::FindRowMatches(    // search variable number of sorted cols
627     nsIMdbEnv* mev,           // context
628     const mdbYarn* inPrefix,  // content to find as prefix in row's column cell
629     nsIMdbTableRowCursor** acqCursor)  // set of matching rows
630 {
631   NS_ASSERTION(false, "not implemented");
632   return NS_ERROR_NOT_IMPLEMENTED;
633 }
634 
635 NS_IMETHODIMP
GetSearchColumns(nsIMdbEnv * mev,mdb_count * outCount,mdbColumnSet * outColSet)636 morkTable::GetSearchColumns(  // query columns used by FindRowMatches()
637     nsIMdbEnv* mev,           // context
638     mdb_count* outCount,      // context
639     mdbColumnSet* outColSet)  // caller supplied space to put columns
640 // GetSearchColumns() returns the columns actually searched when the
641 // FindRowMatches() method is called.  No more than mColumnSet_Count
642 // slots of mColumnSet_Columns will be written, since mColumnSet_Count
643 // indicates how many slots are present in the column array.  The
644 // actual number of search column used by the table is returned in
645 // the outCount parameter; if this number exceeds mColumnSet_Count,
646 // then a caller needs a bigger array to read the entire column set.
647 // The minimum of mColumnSet_Count and outCount is the number slots
648 // in mColumnSet_Columns that were actually written by this method.
649 //
650 // Callers are expected to change this set of columns by calls to
651 // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both.
652 {
653   NS_ASSERTION(false, "not implemented");
654   return NS_ERROR_NOT_IMPLEMENTED;
655 }
656 // } ----- end searching methods -----
657 
658 // { ----- begin hinting methods -----
659 NS_IMETHODIMP
SearchColumnsHint(nsIMdbEnv * mev,const mdbColumnSet * inColumnSet)660 morkTable::SearchColumnsHint(         // advise re future expected search cols
661     nsIMdbEnv* mev,                   // context
662     const mdbColumnSet* inColumnSet)  // columns likely to be searched
663 {
664   NS_ASSERTION(false, "not implemented");
665   return NS_ERROR_NOT_IMPLEMENTED;
666 }
667 
668 NS_IMETHODIMP
SortColumnsHint(nsIMdbEnv * mev,const mdbColumnSet * inColumnSet)669 morkTable::SortColumnsHint(           // advise re future expected sort columns
670     nsIMdbEnv* mev,                   // context
671     const mdbColumnSet* inColumnSet)  // columns for likely sort requests
672 {
673   NS_ASSERTION(false, "not implemented");
674   return NS_ERROR_NOT_IMPLEMENTED;
675 }
676 
677 NS_IMETHODIMP
StartBatchChangeHint(nsIMdbEnv * mev,const void * inLabel)678 morkTable::StartBatchChangeHint(  // advise before many adds and cuts
679     nsIMdbEnv* mev,               // context
680     const void* inLabel)          // intend unique address to match end call
681 // If batch starts nest by virtue of nesting calls in the stack, then
682 // the address of a local variable makes a good batch start label that
683 // can be used at batch end time, and such addresses remain unique.
684 {
685   // we don't do anything here.
686   return NS_OK;
687 }
688 
689 NS_IMETHODIMP
EndBatchChangeHint(nsIMdbEnv * mev,const void * inLabel)690 morkTable::EndBatchChangeHint(  // advise before many adds and cuts
691     nsIMdbEnv* mev,             // context
692     const void* inLabel)        // label matching start label
693 // Suppose a table is maintaining one or many sort orders for a table,
694 // so that every row added to the table must be inserted in each sort,
695 // and every row cut must be removed from each sort.  If a db client
696 // intends to make many such changes before needing any information
697 // about the order or positions of rows inside a table, then a client
698 // might tell the table to start batch changes in order to disable
699 // sorting of rows for the interim.  Presumably a table will then do
700 // a full sort of all rows at need when the batch changes end, or when
701 // a surprise request occurs for row position during batch changes.
702 {
703   // we don't do anything here.
704   return NS_OK;
705 }
706 // } ----- end hinting methods -----
707 
708 // { ----- begin sorting methods -----
709 // sorting: note all rows are assumed sorted by row ID as a secondary
710 // sort following the primary column sort, when table rows are sorted.
711 
712 NS_IMETHODIMP
CanSortColumn(nsIMdbEnv * mev,mdb_column inColumn,mdb_bool * outCanSort)713 morkTable::CanSortColumn(  // query which column is currently used for sorting
714     nsIMdbEnv* mev,        // context
715     mdb_column inColumn,   // column to query sorting potential
716     mdb_bool* outCanSort)  // whether the column can be sorted
717 {
718   NS_ASSERTION(false, "not implemented");
719   return NS_ERROR_NOT_IMPLEMENTED;
720 }
721 
722 NS_IMETHODIMP
GetSorting(nsIMdbEnv * mev,mdb_column inColumn,nsIMdbSorting ** acqSorting)723 morkTable::GetSorting(           // view same table in particular sorting
724     nsIMdbEnv* mev,              // context
725     mdb_column inColumn,         // requested new column for sorting table
726     nsIMdbSorting** acqSorting)  // acquire sorting for column
727 {
728   NS_ASSERTION(false, "not implemented");
729   return NS_ERROR_NOT_IMPLEMENTED;
730 }
731 
732 NS_IMETHODIMP
SetSearchSorting(nsIMdbEnv * mev,mdb_column inColumn,nsIMdbSorting * ioSorting)733 morkTable::SetSearchSorting(   // use this sorting in FindRowMatches()
734     nsIMdbEnv* mev,            // context
735     mdb_column inColumn,       // often same as nsIMdbSorting::GetSortColumn()
736     nsIMdbSorting* ioSorting)  // requested sorting for some column
737 // SetSearchSorting() attempts to inform the table that ioSorting
738 // should be used during calls to FindRowMatches() for searching
739 // the column which is actually sorted by ioSorting.  This method
740 // is most useful in conjunction with nsIMdbSorting::SetCompare(),
741 // because otherwise a caller would not be able to override the
742 // comparison ordering method used during searches.  Note that some
743 // database implementations might be unable to use an arbitrarily
744 // specified sort order, either due to schema or runtime interface
745 // constraints, in which case ioSorting might not actually be used.
746 // Presumably ioSorting is an instance that was returned from some
747 // earlier call to nsIMdbTable::GetSorting().  A caller can also
748 // use nsIMdbTable::SearchColumnsHint() to specify desired change
749 // in which columns are sorted and searched by FindRowMatches().
750 //
751 // A caller can pass a nil pointer for ioSorting to request that
752 // column inColumn no longer be used at all by FindRowMatches().
753 // But when ioSorting is non-nil, then inColumn should match the
754 // column actually sorted by ioSorting; when these do not agree,
755 // implementations are instructed to give precedence to the column
756 // specified by ioSorting (so this means callers might just pass
757 // zero for inColumn when ioSorting is also provided, since then
758 // inColumn is both redundant and ignored).
759 {
760   NS_ASSERTION(false, "not implemented");
761   return NS_ERROR_NOT_IMPLEMENTED;
762 }
763 
764 // } ----- end sorting methods -----
765 
766 // { ----- begin moving methods -----
767 // moving a row does nothing unless a table is currently unsorted
768 
769 NS_IMETHODIMP
MoveOid(nsIMdbEnv * mev,const mdbOid * inOid,mdb_pos inHintFromPos,mdb_pos inToPos,mdb_pos * outActualPos)770 morkTable::MoveOid(         // change position of row in unsorted table
771     nsIMdbEnv* mev,         // context
772     const mdbOid* inOid,    // row oid to find in table
773     mdb_pos inHintFromPos,  // suggested hint regarding start position
774     mdb_pos inToPos,        // desired new position for row inOid
775     mdb_pos* outActualPos)  // actual new position of row in table
776 {
777   nsresult outErr = NS_OK;
778   mdb_pos actualPos = -1;  // meaning it was never found in table
779   morkEnv* ev = morkEnv::FromMdbEnv(mev);
780   if (ev) {
781     if (inOid && mTable_Store) {
782       morkRow* row = mTable_Store->GetRow(ev, inOid);
783       if (row) actualPos = MoveRow(ev, row, inHintFromPos, inToPos);
784     } else
785       ev->NilPointerError();
786 
787     outErr = ev->AsErr();
788   }
789   if (outActualPos) *outActualPos = actualPos;
790   return outErr;
791 }
792 
793 NS_IMETHODIMP
MoveRow(nsIMdbEnv * mev,nsIMdbRow * ioRow,mdb_pos inHintFromPos,mdb_pos inToPos,mdb_pos * outActualPos)794 morkTable::MoveRow(         // change position of row in unsorted table
795     nsIMdbEnv* mev,         // context
796     nsIMdbRow* ioRow,       // row oid to find in table
797     mdb_pos inHintFromPos,  // suggested hint regarding start position
798     mdb_pos inToPos,        // desired new position for row ioRow
799     mdb_pos* outActualPos)  // actual new position of row in table
800 {
801   mdb_pos actualPos = -1;  // meaning it was never found in table
802   nsresult outErr = NS_OK;
803   morkEnv* ev = morkEnv::FromMdbEnv(mev);
804   if (ev) {
805     morkRowObject* rowObj = (morkRowObject*)ioRow;
806     morkRow* row = rowObj->mRowObject_Row;
807     actualPos = MoveRow(ev, row, inHintFromPos, inToPos);
808     outErr = ev->AsErr();
809   }
810   if (outActualPos) *outActualPos = actualPos;
811   return outErr;
812 }
813 // } ----- end moving methods -----
814 
815 // { ----- begin index methods -----
816 NS_IMETHODIMP
AddIndex(nsIMdbEnv * mev,mdb_column inColumn,nsIMdbThumb ** acqThumb)817 morkTable::AddIndex(         // create a sorting index for column if possible
818     nsIMdbEnv* mev,          // context
819     mdb_column inColumn,     // the column to sort by index
820     nsIMdbThumb** acqThumb)  // acquire thumb for incremental index building
821 // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
822 // then the index addition will be finished.
823 {
824   NS_ASSERTION(false, "not implemented");
825   return NS_ERROR_NOT_IMPLEMENTED;
826 }
827 
828 NS_IMETHODIMP
CutIndex(nsIMdbEnv * mev,mdb_column inColumn,nsIMdbThumb ** acqThumb)829 morkTable::CutIndex(         // stop supporting a specific column index
830     nsIMdbEnv* mev,          // context
831     mdb_column inColumn,     // the column with index to be removed
832     nsIMdbThumb** acqThumb)  // acquire thumb for incremental index destroy
833 // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
834 // then the index removal will be finished.
835 {
836   NS_ASSERTION(false, "not implemented");
837   return NS_ERROR_NOT_IMPLEMENTED;
838 }
839 
840 NS_IMETHODIMP
HasIndex(nsIMdbEnv * mev,mdb_column inColumn,mdb_bool * outHasIndex)841 morkTable::HasIndex(        // query for current presence of a column index
842     nsIMdbEnv* mev,         // context
843     mdb_column inColumn,    // the column to investigate
844     mdb_bool* outHasIndex)  // whether column has index for this column
845 {
846   NS_ASSERTION(false, "not implemented");
847   return NS_ERROR_NOT_IMPLEMENTED;
848 }
849 
850 NS_IMETHODIMP
EnableIndexOnSort(nsIMdbEnv * mev,mdb_column inColumn)851 morkTable::EnableIndexOnSort(  // create an index for col on first sort
852     nsIMdbEnv* mev,            // context
853     mdb_column inColumn)       // the column to index if ever sorted
854 {
855   NS_ASSERTION(false, "not implemented");
856   return NS_ERROR_NOT_IMPLEMENTED;
857 }
858 
859 NS_IMETHODIMP
QueryIndexOnSort(nsIMdbEnv * mev,mdb_column inColumn,mdb_bool * outIndexOnSort)860 morkTable::QueryIndexOnSort(   // check whether index on sort is enabled
861     nsIMdbEnv* mev,            // context
862     mdb_column inColumn,       // the column to investigate
863     mdb_bool* outIndexOnSort)  // whether column has index-on-sort enabled
864 {
865   NS_ASSERTION(false, "not implemented");
866   return NS_ERROR_NOT_IMPLEMENTED;
867 }
868 
869 NS_IMETHODIMP
DisableIndexOnSort(nsIMdbEnv * mev,mdb_column inColumn)870 morkTable::DisableIndexOnSort(  // prevent future index creation on sort
871     nsIMdbEnv* mev,             // context
872     mdb_column inColumn)        // the column to index if ever sorted
873 {
874   NS_ASSERTION(false, "not implemented");
875   return NS_ERROR_NOT_IMPLEMENTED;
876 }
877 // } ----- end index methods -----
878 
879 // } ===== end nsIMdbTable methods =====
880 
881 // we override these so that we'll use the xpcom add and release ref.
882 #ifndef _MSC_VER
AddStrongRef(nsIMdbEnv * ev)883 mork_refs morkTable::AddStrongRef(nsIMdbEnv* ev) { return (mork_refs)AddRef(); }
884 #endif
885 
AddStrongRef(morkEnv * ev)886 mork_refs morkTable::AddStrongRef(morkEnv* ev) { return (mork_refs)AddRef(); }
887 
888 #ifndef _MSC_VER
CutStrongRef(nsIMdbEnv * ev)889 nsresult morkTable::CutStrongRef(nsIMdbEnv* ev) { return (nsresult)Release(); }
890 #endif
891 
CutStrongRef(morkEnv * ev)892 mork_refs morkTable::CutStrongRef(morkEnv* ev) { return (mork_refs)Release(); }
893 
AddTableGcUse(morkEnv * ev)894 mork_u2 morkTable::AddTableGcUse(morkEnv* ev) {
895   MORK_USED_1(ev);
896   if (mTable_GcUses < morkTable_kMaxTableGcUses)  // not already maxed out?
897     ++mTable_GcUses;
898 
899   return mTable_GcUses;
900 }
901 
CutTableGcUse(morkEnv * ev)902 mork_u2 morkTable::CutTableGcUse(morkEnv* ev) {
903   if (mTable_GcUses)  // any outstanding uses to cut?
904   {
905     if (mTable_GcUses < morkTable_kMaxTableGcUses)  // not frozen at max?
906       --mTable_GcUses;
907   } else
908     this->TableGcUsesUnderflowWarning(ev);
909 
910   return mTable_GcUses;
911 }
912 
913 // table dirty handling more complex than morkNode::SetNodeDirty() etc.
914 
SetTableClean(morkEnv * ev)915 void morkTable::SetTableClean(morkEnv* ev) {
916   if (mTable_ChangeList.HasListMembers()) {
917     nsIMdbHeap* heap = mTable_Store->mPort_Heap;
918     mTable_ChangeList.CutAndZapAllListMembers(ev, heap);  // forget changes
919   }
920   mTable_ChangesCount = 0;
921 
922   mTable_Flags = 0;
923   this->SetNodeClean();
924 }
925 
926 // notifications regarding table changes:
927 
NoteTableMoveRow(morkEnv * ev,morkRow * ioRow,mork_pos inPos)928 void morkTable::NoteTableMoveRow(morkEnv* ev, morkRow* ioRow, mork_pos inPos) {
929   nsIMdbHeap* heap = mTable_Store->mPort_Heap;
930   if (this->IsTableRewrite() || this->HasChangeOverflow())
931     this->NoteTableSetAll(ev);
932   else {
933     morkTableChange* tableChange =
934         new (*heap, ev) morkTableChange(ev, ioRow, inPos);
935     if (tableChange) {
936       if (ev->Good()) {
937         mTable_ChangeList.PushTail(tableChange);
938         ++mTable_ChangesCount;
939       } else {
940         tableChange->ZapOldNext(ev, heap);
941         this->SetTableRewrite();  // just plan to write all table rows
942       }
943     }
944   }
945 }
946 
note_row_move(morkEnv * ev,morkRow * ioRow,mork_pos inNewPos)947 void morkTable::note_row_move(morkEnv* ev, morkRow* ioRow, mork_pos inNewPos) {
948   if (this->IsTableRewrite() || this->HasChangeOverflow())
949     this->NoteTableSetAll(ev);
950   else {
951     nsIMdbHeap* heap = mTable_Store->mPort_Heap;
952     morkTableChange* tableChange =
953         new (*heap, ev) morkTableChange(ev, ioRow, inNewPos);
954     if (tableChange) {
955       if (ev->Good()) {
956         mTable_ChangeList.PushTail(tableChange);
957         ++mTable_ChangesCount;
958       } else {
959         tableChange->ZapOldNext(ev, heap);
960         this->NoteTableSetAll(ev);
961       }
962     }
963   }
964 }
965 
note_row_change(morkEnv * ev,mork_change inChange,morkRow * ioRow)966 void morkTable::note_row_change(morkEnv* ev, mork_change inChange,
967                                 morkRow* ioRow) {
968   if (this->IsTableRewrite() || this->HasChangeOverflow())
969     this->NoteTableSetAll(ev);
970   else {
971     nsIMdbHeap* heap = mTable_Store->mPort_Heap;
972     morkTableChange* tableChange =
973         new (*heap, ev) morkTableChange(ev, inChange, ioRow);
974     if (tableChange) {
975       if (ev->Good()) {
976         mTable_ChangeList.PushTail(tableChange);
977         ++mTable_ChangesCount;
978       } else {
979         tableChange->ZapOldNext(ev, heap);
980         this->NoteTableSetAll(ev);
981       }
982     }
983   }
984 }
985 
NoteTableSetAll(morkEnv * ev)986 void morkTable::NoteTableSetAll(morkEnv* ev) {
987   if (mTable_ChangeList.HasListMembers()) {
988     nsIMdbHeap* heap = mTable_Store->mPort_Heap;
989     mTable_ChangeList.CutAndZapAllListMembers(ev, heap);  // forget changes
990   }
991   mTable_ChangesCount = 0;
992   this->SetTableRewrite();
993 }
994 
TableGcUsesUnderflowWarning(morkEnv * ev)995 /*static*/ void morkTable::TableGcUsesUnderflowWarning(morkEnv* ev) {
996   ev->NewWarning("mTable_GcUses underflow");
997 }
998 
NonTableTypeError(morkEnv * ev)999 /*static*/ void morkTable::NonTableTypeError(morkEnv* ev) {
1000   ev->NewError("non morkTable");
1001 }
1002 
NonTableTypeWarning(morkEnv * ev)1003 /*static*/ void morkTable::NonTableTypeWarning(morkEnv* ev) {
1004   ev->NewWarning("non morkTable");
1005 }
1006 
NilRowSpaceError(morkEnv * ev)1007 /*static*/ void morkTable::NilRowSpaceError(morkEnv* ev) {
1008   ev->NewError("nil mTable_RowSpace");
1009 }
1010 
MaybeDirtySpaceStoreAndTable()1011 mork_bool morkTable::MaybeDirtySpaceStoreAndTable() {
1012   morkRowSpace* rowSpace = mTable_RowSpace;
1013   if (rowSpace) {
1014     morkStore* store = rowSpace->mSpace_Store;
1015     if (store && store->mStore_CanDirty) {
1016       store->SetStoreDirty();
1017       rowSpace->mSpace_CanDirty = morkBool_kTrue;
1018     }
1019 
1020     if (rowSpace->mSpace_CanDirty)  // first time being dirtied?
1021     {
1022       if (this->IsTableClean()) {
1023         mork_count rowCount = this->GetRowCount();
1024         mork_count oneThird = rowCount / 4;  // one third of rows
1025         if (oneThird > 0x07FFF)              // more than half max u2?
1026           oneThird = 0x07FFF;
1027 
1028         mTable_ChangesMax = (mork_u2)oneThird;
1029       }
1030       this->SetTableDirty();
1031       rowSpace->SetRowSpaceDirty();
1032 
1033       return morkBool_kTrue;
1034     }
1035   }
1036   return morkBool_kFalse;
1037 }
1038 
GetMetaRow(morkEnv * ev,const mdbOid * inOptionalMetaRowOid)1039 morkRow* morkTable::GetMetaRow(morkEnv* ev,
1040                                const mdbOid* inOptionalMetaRowOid) {
1041   morkRow* outRow = mTable_MetaRow;
1042   if (!outRow) {
1043     morkStore* store = mTable_Store;
1044     mdbOid* oid = &mTable_MetaRowOid;
1045     if (inOptionalMetaRowOid && !oid->mOid_Scope) *oid = *inOptionalMetaRowOid;
1046 
1047     if (oid->mOid_Scope)  // oid already recorded in table?
1048       outRow = store->OidToRow(ev, oid);
1049     else {
1050       outRow = store->NewRow(ev, morkStore_kMetaScope);
1051       if (outRow)  // need to record new oid in table?
1052         *oid = outRow->mRow_Oid;
1053     }
1054     mTable_MetaRow = outRow;
1055     if (outRow)  // need to note another use of this row?
1056     {
1057       outRow->AddRowGcUse(ev);
1058 
1059       this->SetTableNewMeta();
1060       if (this->IsTableClean())  // catch dirty status of meta row?
1061         this->MaybeDirtySpaceStoreAndTable();
1062     }
1063   }
1064 
1065   return outRow;
1066 }
1067 
GetTableOid(morkEnv * ev,mdbOid * outOid)1068 void morkTable::GetTableOid(morkEnv* ev, mdbOid* outOid) {
1069   morkRowSpace* space = mTable_RowSpace;
1070   if (space) {
1071     outOid->mOid_Scope = space->SpaceScope();
1072     outOid->mOid_Id = this->TableId();
1073   } else
1074     this->NilRowSpaceError(ev);
1075 }
1076 
AcquireTableHandle(morkEnv * ev)1077 nsIMdbTable* morkTable::AcquireTableHandle(morkEnv* ev) {
1078   AddRef();
1079   return this;
1080 }
1081 
ArrayHasOid(morkEnv * ev,const mdbOid * inOid)1082 mork_pos morkTable::ArrayHasOid(morkEnv* ev, const mdbOid* inOid) {
1083   MORK_USED_1(ev);
1084   mork_count count = mTable_RowArray.mArray_Fill;
1085   mork_pos pos = -1;
1086   while (++pos < (mork_pos)count) {
1087     morkRow* row = (morkRow*)mTable_RowArray.At(pos);
1088     MORK_ASSERT(row);
1089     if (row && row->EqualOid(inOid)) {
1090       return pos;
1091     }
1092   }
1093   return -1;
1094 }
1095 
MapHasOid(morkEnv * ev,const mdbOid * inOid)1096 mork_bool morkTable::MapHasOid(morkEnv* ev, const mdbOid* inOid) {
1097   if (mTable_RowMap)
1098     return (mTable_RowMap->GetOid(ev, inOid) != 0);
1099   else
1100     return (ArrayHasOid(ev, inOid) >= 0);
1101 }
1102 
build_row_map(morkEnv * ev)1103 void morkTable::build_row_map(morkEnv* ev) {
1104   morkRowMap* map = mTable_RowMap;
1105   if (!map) {
1106     mork_count count = mTable_RowArray.mArray_Fill + 3;
1107     nsIMdbHeap* heap = mTable_Store->mPort_Heap;
1108     map = new (*heap, ev) morkRowMap(ev, morkUsage::kHeap, heap, heap, count);
1109     if (map) {
1110       if (ev->Good()) {
1111         mTable_RowMap = map;  // put strong ref here
1112         count = mTable_RowArray.mArray_Fill;
1113         mork_pos pos = -1;
1114         while (++pos < (mork_pos)count) {
1115           morkRow* row = (morkRow*)mTable_RowArray.At(pos);
1116           if (row && row->IsRow())
1117             map->AddRow(ev, row);
1118           else
1119             row->NonRowTypeError(ev);
1120         }
1121       } else
1122         map->CutStrongRef(ev);
1123     }
1124   }
1125 }
1126 
find_member_row(morkEnv * ev,morkRow * ioRow)1127 morkRow* morkTable::find_member_row(morkEnv* ev, morkRow* ioRow) {
1128   if (mTable_RowMap)
1129     return mTable_RowMap->GetRow(ev, ioRow);
1130   else {
1131     mork_count count = mTable_RowArray.mArray_Fill;
1132     mork_pos pos = -1;
1133     while (++pos < (mork_pos)count) {
1134       morkRow* row = (morkRow*)mTable_RowArray.At(pos);
1135       if (row == ioRow) return row;
1136     }
1137   }
1138   return (morkRow*)0;
1139 }
1140 
MoveRow(morkEnv * ev,morkRow * ioRow,mork_pos inHintFromPos,mork_pos inToPos)1141 mork_pos morkTable::MoveRow(
1142     morkEnv* ev, morkRow* ioRow,  // change row position
1143     mork_pos inHintFromPos,       // suggested hint regarding start position
1144     mork_pos inToPos)             // desired new position for row ioRow
1145 // MoveRow() returns the actual position of ioRow afterwards; this
1146 // position is -1 if and only if ioRow was not found as a member.
1147 {
1148   mork_pos outPos = -1;  // means ioRow was not a table member
1149   mork_bool canDirty = (this->IsTableClean())
1150                            ? this->MaybeDirtySpaceStoreAndTable()
1151                            : morkBool_kTrue;
1152 
1153   morkRow** rows = (morkRow**)mTable_RowArray.mArray_Slots;
1154   mork_count count = mTable_RowArray.mArray_Fill;
1155   if (count && rows && ev->Good())  // any members at all? no errors?
1156   {
1157     mork_pos lastPos = count - 1;  // index of last row slot
1158 
1159     if (inToPos > lastPos)  // beyond last used array slot?
1160       inToPos = lastPos;    // put row into last available slot
1161     else if (inToPos < 0)   // before first usable slot?
1162       inToPos = 0;          // put row in very first slow
1163 
1164     if (inHintFromPos > lastPos)  // beyond last used array slot?
1165       inHintFromPos = lastPos;    // seek row in last available slot
1166     else if (inHintFromPos < 0)   // before first usable slot?
1167       inHintFromPos = 0;          // seek row in very first slow
1168 
1169     morkRow** fromSlot = 0;            // becomes nonzero of ioRow is ever found
1170     morkRow** rowsEnd = rows + count;  // one past last used array slot
1171 
1172     if (inHintFromPos <= 0)  // start of table? just scan for row?
1173     {
1174       morkRow** cursor = rows - 1;  // before first array slot
1175       while (++cursor < rowsEnd) {
1176         if (*cursor == ioRow) {
1177           fromSlot = cursor;
1178           break;  // end while loop
1179         }
1180       }
1181     } else  // search near the start position and work outwards
1182     {
1183       morkRow** lo = rows + inHintFromPos;  // lowest search point
1184       morkRow** hi = lo;  // highest search point starts at lowest point
1185 
1186       // Seek ioRow in spiral widening search below and above inHintFromPos.
1187       // This is faster when inHintFromPos is at all accurate, but is slower
1188       // than a straightforward scan when inHintFromPos is nearly random.
1189 
1190       while (lo >= rows || hi < rowsEnd)  // keep searching?
1191       {
1192         if (lo >= rows)  // low direction search still feasible?
1193         {
1194           if (*lo == ioRow)  // actually found the row?
1195           {
1196             fromSlot = lo;
1197             break;  // end while loop
1198           }
1199           --lo;  // advance further lower
1200         }
1201         if (hi < rowsEnd)  // high direction search still feasible?
1202         {
1203           if (*hi == ioRow)  // actually found the row?
1204           {
1205             fromSlot = hi;
1206             break;  // end while loop
1207           }
1208           ++hi;  // advance further higher
1209         }
1210       }
1211     }
1212 
1213     if (fromSlot)  // ioRow was found as a table member?
1214     {
1215       outPos = fromSlot - rows;  // actual position where row was found
1216       if (outPos != inToPos)     // actually need to move this row?
1217       {
1218         morkRow** toSlot = rows + inToPos;  // slot where row must go
1219 
1220         ++mTable_RowArray.mArray_Seed;  // we modify the array now:
1221 
1222         if (fromSlot < toSlot)  // row is moving upwards?
1223         {
1224           morkRow** up = fromSlot;  // leading pointer going upward
1225           while (++up <= toSlot)    // have not gone above destination?
1226           {
1227             *fromSlot = *up;  // shift down one
1228             fromSlot = up;    // shift trailing pointer up
1229           }
1230         } else  // ( fromSlot > toSlot ) // row is moving downwards
1231         {
1232           morkRow** down = fromSlot;  // leading pointer going downward
1233           while (--down >= toSlot)    // have not gone below destination?
1234           {
1235             *fromSlot = *down;  // shift up one
1236             fromSlot = down;    // shift trailing pointer
1237           }
1238         }
1239         *toSlot = ioRow;
1240         outPos = inToPos;  // okay, we actually moved the row here
1241 
1242         if (canDirty) this->note_row_move(ev, ioRow, inToPos);
1243       }
1244     }
1245   }
1246   return outPos;
1247 }
1248 
AddRow(morkEnv * ev,morkRow * ioRow)1249 mork_bool morkTable::AddRow(morkEnv* ev, morkRow* ioRow) {
1250   morkRow* row = this->find_member_row(ev, ioRow);
1251   if (!row && ev->Good()) {
1252     mork_bool canDirty = (this->IsTableClean())
1253                              ? this->MaybeDirtySpaceStoreAndTable()
1254                              : morkBool_kTrue;
1255 
1256     mork_pos pos = mTable_RowArray.AppendSlot(ev, ioRow);
1257     if (ev->Good() && pos >= 0) {
1258       ioRow->AddRowGcUse(ev);
1259       if (mTable_RowMap) {
1260         if (mTable_RowMap->AddRow(ev, ioRow)) {
1261           // okay, anything else?
1262         } else
1263           mTable_RowArray.CutSlot(ev, pos);
1264       } else if (mTable_RowArray.mArray_Fill >= morkTable_kMakeRowMapThreshold)
1265         this->build_row_map(ev);
1266 
1267       if (canDirty && ev->Good()) this->NoteTableAddRow(ev, ioRow);
1268     }
1269   }
1270   return ev->Good();
1271 }
1272 
CutRow(morkEnv * ev,morkRow * ioRow)1273 mork_bool morkTable::CutRow(morkEnv* ev, morkRow* ioRow) {
1274   morkRow* row = this->find_member_row(ev, ioRow);
1275   if (row) {
1276     mork_bool canDirty = (this->IsTableClean())
1277                              ? this->MaybeDirtySpaceStoreAndTable()
1278                              : morkBool_kTrue;
1279 
1280     mork_count count = mTable_RowArray.mArray_Fill;
1281     morkRow** rowSlots = (morkRow**)mTable_RowArray.mArray_Slots;
1282     if (rowSlots)  // array has vector as expected?
1283     {
1284       mork_pos pos = -1;
1285       morkRow** end = rowSlots + count;
1286       morkRow** slot = rowSlots - 1;  // prepare for preincrement:
1287       while (++slot < end)            // another slot to check?
1288       {
1289         if (*slot == row)  // found the slot containing row?
1290         {
1291           pos = slot - rowSlots;  // record absolute position
1292           break;                  // end while loop
1293         }
1294       }
1295       if (pos >= 0)  // need to cut if from the array?
1296         mTable_RowArray.CutSlot(ev, pos);
1297       else
1298         ev->NewWarning("row not found in array");
1299     } else
1300       mTable_RowArray.NilSlotsAddressError(ev);
1301 
1302     if (mTable_RowMap) mTable_RowMap->CutRow(ev, ioRow);
1303 
1304     if (canDirty) this->NoteTableCutRow(ev, ioRow);
1305 
1306     if (ioRow->CutRowGcUse(ev) == 0) ioRow->OnZeroRowGcUse(ev);
1307   }
1308   return ev->Good();
1309 }
1310 
CutAllRows(morkEnv * ev)1311 mork_bool morkTable::CutAllRows(morkEnv* ev) {
1312   if (this->MaybeDirtySpaceStoreAndTable()) {
1313     this->SetTableRewrite();  // everything is dirty
1314     this->NoteTableSetAll(ev);
1315   }
1316 
1317   if (ev->Good()) {
1318     mTable_RowArray.CutAllSlots(ev);
1319     if (mTable_RowMap) {
1320       morkRowMapIter i(ev, mTable_RowMap);
1321       mork_change* c = 0;
1322       morkRow* r = 0;
1323 
1324       for (c = i.FirstRow(ev, &r); c; c = i.NextRow(ev, &r)) {
1325         if (r) {
1326           if (r->CutRowGcUse(ev) == 0) r->OnZeroRowGcUse(ev);
1327 
1328           i.CutHereRow(ev, (morkRow**)0);
1329         } else
1330           ev->NewWarning("nil row in table map");
1331       }
1332     }
1333   }
1334   return ev->Good();
1335 }
1336 
NewTableRowCursor(morkEnv * ev,mork_pos inRowPos)1337 morkTableRowCursor* morkTable::NewTableRowCursor(morkEnv* ev,
1338                                                  mork_pos inRowPos) {
1339   morkTableRowCursor* outCursor = 0;
1340   if (ev->Good()) {
1341     nsIMdbHeap* heap = mTable_Store->mPort_Heap;
1342     morkTableRowCursor* cursor = new (*heap, ev)
1343         morkTableRowCursor(ev, morkUsage::kHeap, heap, this, inRowPos);
1344     if (cursor) {
1345       if (ev->Good())
1346         outCursor = cursor;
1347       else
1348         cursor->CutStrongRef((nsIMdbEnv*)ev);
1349     }
1350   }
1351   return outCursor;
1352 }
1353 
1354 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
1355 
morkTableChange(morkEnv * ev,mork_change inChange,morkRow * ioRow)1356 morkTableChange::morkTableChange(morkEnv* ev, mork_change inChange,
1357                                  morkRow* ioRow)
1358     // use this constructor for inChange == morkChange_kAdd or morkChange_kCut
1359     : morkNext(),
1360       mTableChange_Row(ioRow),
1361       mTableChange_Pos(morkTableChange_kNone) {
1362   if (ioRow) {
1363     if (ioRow->IsRow()) {
1364       if (inChange == morkChange_kAdd)
1365         mTableChange_Pos = morkTableChange_kAdd;
1366       else if (inChange == morkChange_kCut)
1367         mTableChange_Pos = morkTableChange_kCut;
1368       else
1369         this->UnknownChangeError(ev);
1370     } else
1371       ioRow->NonRowTypeError(ev);
1372   } else
1373     ev->NilPointerError();
1374 }
1375 
morkTableChange(morkEnv * ev,morkRow * ioRow,mork_pos inPos)1376 morkTableChange::morkTableChange(morkEnv* ev, morkRow* ioRow, mork_pos inPos)
1377     // use this constructor when the row is moved
1378     : morkNext(), mTableChange_Row(ioRow), mTableChange_Pos(inPos) {
1379   if (ioRow) {
1380     if (ioRow->IsRow()) {
1381       if (inPos < 0) this->NegativeMovePosError(ev);
1382     } else
1383       ioRow->NonRowTypeError(ev);
1384   } else
1385     ev->NilPointerError();
1386 }
1387 
UnknownChangeError(morkEnv * ev) const1388 void morkTableChange::UnknownChangeError(morkEnv* ev) const
1389 // morkChange_kAdd or morkChange_kCut
1390 {
1391   ev->NewError("mTableChange_Pos neither kAdd nor kCut");
1392 }
1393 
NegativeMovePosError(morkEnv * ev) const1394 void morkTableChange::NegativeMovePosError(morkEnv* ev) const
1395 // move must be non-neg position
1396 {
1397   ev->NewError("negative mTableChange_Pos for row move");
1398 }
1399 
1400 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
1401 
~morkTableMap()1402 morkTableMap::~morkTableMap() {}
1403 
morkTableMap(morkEnv * ev,const morkUsage & inUsage,nsIMdbHeap * ioHeap,nsIMdbHeap * ioSlotHeap)1404 morkTableMap::morkTableMap(morkEnv* ev, const morkUsage& inUsage,
1405                            nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
1406 #ifdef MORK_BEAD_OVER_NODE_MAPS
1407     : morkBeadMap(ev, inUsage, ioHeap, ioSlotHeap)
1408 #else  /*MORK_BEAD_OVER_NODE_MAPS*/
1409     : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap)
1410 #endif /*MORK_BEAD_OVER_NODE_MAPS*/
1411 {
1412   if (ev->Good()) mNode_Derived = morkDerived_kTableMap;
1413 }
1414 
1415 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
1416