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 _MORKTABLE_
7 #define _MORKTABLE_ 1
8 
9 #ifndef _MORK_
10 #  include "mork.h"
11 #endif
12 
13 #ifndef _MORKNODE_
14 #  include "morkNode.h"
15 #endif
16 
17 #ifndef _MORKDEQUE_
18 #  include "morkDeque.h"
19 #endif
20 
21 #ifndef _MORKOBJECT_
22 #  include "morkObject.h"
23 #endif
24 
25 #ifndef _MORKARRAY_
26 #  include "morkArray.h"
27 #endif
28 
29 #ifndef _MORKROWMAP_
30 #  include "morkRowMap.h"
31 #endif
32 
33 #ifndef _MORKNODEMAP_
34 #  include "morkNodeMap.h"
35 #endif
36 
37 #ifndef _MORKPROBEMAP_
38 #  include "morkProbeMap.h"
39 #endif
40 
41 #ifndef _MORKBEAD_
42 #  include "morkBead.h"
43 #endif
44 
45 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
46 
47 class nsIMdbTable;
48 #define morkDerived_kTable /*i*/ 0x5462 /* ascii 'Tb' */
49 
50 /*| kStartRowArraySize: starting physical size of array for mTable_RowArray.
51 **| We want this number very small, so that a table containing exactly one
52 **| row member will not pay too significantly in space overhead.  But we want
53 **| a number bigger than one, so there is some space for growth.
54 |*/
55 #define morkTable_kStartRowArraySize 3 /* modest starting size for array */
56 
57 /*| kMakeRowMapThreshold: this is the number of rows in a table which causes
58 **| a hash table (mTable_RowMap) to be lazily created for faster member row
59 **| identification, during such operations as cuts and adds.  This number must
60 **| be small enough that linear searches are not bad for member counts less
61 **| than this; but this number must also be large enough that creating a hash
62 **| table does not increase the per-row space overhead by a big percentage.
63 **| For speed, numbers on the order of ten to twenty are all fine; for space,
64 **| I believe a number as small as ten will have too much space overhead.
65 |*/
66 #define morkTable_kMakeRowMapThreshold 17 /* when to build mTable_RowMap */
67 
68 #define morkTable_kStartRowMapSlotCount 13
69 #define morkTable_kMaxTableGcUses 0x0FF /* max for 8-bit unsigned int */
70 
71 #define morkTable_kUniqueBit ((mork_u1)(1 << 0))
72 #define morkTable_kVerboseBit ((mork_u1)(1 << 1))
73 #define morkTable_kNotedBit ((mork_u1)(1 << 2))   /* space has change notes */
74 #define morkTable_kRewriteBit ((mork_u1)(1 << 3)) /* must rewrite all rows */
75 #define morkTable_kNewMetaBit ((mork_u1)(1 << 4)) /* new table meta row */
76 
77 class morkTable : public morkObject, public morkLink, public nsIMdbTable {
78   // NOTE the morkLink base is for morkRowSpace::mRowSpace_TablesByPriority
79 
80   // public: // slots inherited from morkObject (meant to inform only)
81   // nsIMdbHeap*    mNode_Heap;
82 
83   // mork_base      mNode_Base;     // must equal morkBase_kNode
84   // mork_derived   mNode_Derived;  // depends on specific node subclass
85 
86   // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
87   // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
88   // mork_able      mNode_Mutable;  // can this node be modified?
89   // mork_load      mNode_Load;     // is this node clean or dirty?
90 
91   // mork_uses      mNode_Uses;     // refcount for strong refs
92   // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
93 
94   // mork_color   mBead_Color;   // ID for this bead
95   // morkHandle*  mObject_Handle;  // weak ref to handle for this object
96 
97  public:  // bead color setter & getter replace obsolete member mTable_Id:
98   NS_DECL_ISUPPORTS_INHERITED
TableId()99   mork_tid TableId() const { return mBead_Color; }
SetTableId(mork_tid inTid)100   void SetTableId(mork_tid inTid) { mBead_Color = inTid; }
101 
102   // we override these so we use xpcom ref-counting semantics.
103 #ifndef _MSC_VER
104   // The first declaration of AddStrongRef is to suppress
105   // -Werror,-Woverloaded-virtual.
106   virtual mork_refs AddStrongRef(nsIMdbEnv* ev) override;
107 #endif
108   virtual mork_refs AddStrongRef(morkEnv* ev) override;
109 #ifndef _MSC_VER
110   // The first declaration of CutStrongRef is to suppress
111   // -Werror,-Woverloaded-virtual.
112   virtual nsresult CutStrongRef(nsIMdbEnv* ev) override;
113 #endif
114   virtual mork_refs CutStrongRef(morkEnv* ev) override;
115 
116  public:
117   // { ===== begin nsIMdbCollection methods =====
118   // { ----- begin attribute methods -----
119   NS_IMETHOD GetSeed(nsIMdbEnv* ev,
120                      mdb_seed* outSeed) override;  // member change count
121   NS_IMETHOD GetCount(nsIMdbEnv* ev,
122                       mdb_count* outCount) override;  // member count
123 
124   NS_IMETHOD GetPort(nsIMdbEnv* ev,
125                      nsIMdbPort** acqPort) override;  // collection container
126   // } ----- end attribute methods -----
127 
128   // { ----- begin cursor methods -----
129   NS_IMETHOD GetCursor(     // make a cursor starting iter at inMemberPos
130       nsIMdbEnv* ev,        // context
131       mdb_pos inMemberPos,  // zero-based ordinal pos of member in collection
132       nsIMdbCursor** acqCursor) override;  // acquire new cursor instance
133   // } ----- end cursor methods -----
134 
135   // { ----- begin ID methods -----
136   NS_IMETHOD GetOid(nsIMdbEnv* ev,
137                     mdbOid* outOid) override;  // read object identity
138   NS_IMETHOD BecomeContent(nsIMdbEnv* ev,
139                            const mdbOid* inOid) override;  // exchange content
140   // } ----- end ID methods -----
141 
142   // { ----- begin activity dropping methods -----
143   NS_IMETHOD DropActivity(  // tell collection usage no longer expected
144       nsIMdbEnv* ev) override;
145   // } ----- end activity dropping methods -----
146 
147   // } ===== end nsIMdbCollection methods =====
148   NS_IMETHOD SetTablePriority(nsIMdbEnv* ev, mdb_priority inPrio) override;
149   NS_IMETHOD GetTablePriority(nsIMdbEnv* ev, mdb_priority* outPrio) override;
150 
151   NS_IMETHOD GetTableBeVerbose(nsIMdbEnv* ev, mdb_bool* outBeVerbose) override;
152   NS_IMETHOD SetTableBeVerbose(nsIMdbEnv* ev, mdb_bool inBeVerbose) override;
153 
154   NS_IMETHOD GetTableIsUnique(nsIMdbEnv* ev, mdb_bool* outIsUnique) override;
155 
156   NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) override;
157   NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) override;
158 
159   NS_IMETHOD GetMetaRow(
160       nsIMdbEnv* ev,                       // context
161       const mdbOid* inOptionalMetaRowOid,  // can be nil to avoid specifying
162       mdbOid* outOid,  // output meta row oid, can be nil to suppress output
163       nsIMdbRow** acqRow)
164       override;  // acquire table's unique singleton meta row
165   // The purpose of a meta row is to support the persistent recording of
166   // meta info about a table as cells put into the distinguished meta row.
167   // Each table has exactly one meta row, which is not considered a member
168   // of the collection of rows inside the table.  The only way to tell
169   // whether a row is a meta row is by the fact that it is returned by this
170   // GetMetaRow() method from some table. Otherwise nothing distinguishes
171   // a meta row from any other row.  A meta row can be used anyplace that
172   // any other row can be used, and can even be put into other tables (or
173   // the same table) as a table member, if this is useful for some reason.
174   // The first attempt to access a table's meta row using GetMetaRow() will
175   // cause the meta row to be created if it did not already exist.  When the
176   // meta row is created, it will have the row oid that was previously
177   // requested for this table's meta row; or if no oid was ever explicitly
178   // specified for this meta row, then a unique oid will be generated in
179   // the row scope named "m" (so obviously MDB clients should not
180   // manually allocate any row IDs from that special meta scope namespace).
181   // The meta row oid can be specified either when the table is created, or
182   // else the first time that GetMetaRow() is called, by passing a non-nil
183   // pointer to an oid for parameter inOptionalMetaRowOid.  The meta row's
184   // actual oid is returned in outOid (if this is a non-nil pointer), and
185   // it will be different from inOptionalMetaRowOid when the meta row was
186   // already given a different oid earlier.
187   // } ----- end meta attribute methods -----
188 
189   // { ----- begin cursor methods -----
190   NS_IMETHOD
191   GetTableRowCursor(     // make a cursor, starting iteration at inRowPos
192       nsIMdbEnv* ev,     // context
193       mdb_pos inRowPos,  // zero-based ordinal position of row in table
194       nsIMdbTableRowCursor** acqCursor)
195       override;  // acquire new cursor instance
196   // } ----- end row position methods -----
197 
198   // { ----- begin row position methods -----
199   NS_IMETHOD PosToOid(           // get row member for a table position
200       nsIMdbEnv* ev,             // context
201       mdb_pos inRowPos,          // zero-based ordinal position of row in table
202       mdbOid* outOid) override;  // row oid at the specified position
203 
204   NS_IMETHOD OidToPos(            // test for the table position of a row member
205       nsIMdbEnv* ev,              // context
206       const mdbOid* inOid,        // row to find in table
207       mdb_pos* outPos) override;  // zero-based ordinal position of row in table
208 
209   NS_IMETHOD PosToRow(   // test for the table position of a row member
210       nsIMdbEnv* ev,     // context
211       mdb_pos inRowPos,  // zero-based ordinal position of row in table
212       nsIMdbRow** acqRow) override;  // acquire row at table position inRowPos
213 
214   NS_IMETHOD RowToPos(            // test for the table position of a row member
215       nsIMdbEnv* ev,              // context
216       nsIMdbRow* ioRow,           // row to find in table
217       mdb_pos* outPos) override;  // zero-based ordinal position of row in table
218   // } ----- end row position methods -----
219 
220   // { ----- begin oid set methods -----
221   NS_IMETHOD AddOid(  // make sure the row with inOid is a table member
222       nsIMdbEnv* ev,  // context
223       const mdbOid* inOid) override;  // row to ensure membership in table
224 
225   NS_IMETHOD HasOid(        // test for the table position of a row member
226       nsIMdbEnv* ev,        // context
227       const mdbOid* inOid,  // row to find in table
228       mdb_bool* outHasOid) override;  // whether inOid is a member row
229 
230   NS_IMETHOD CutOid(  // make sure the row with inOid is not a member
231       nsIMdbEnv* ev,  // context
232       const mdbOid* inOid) override;  // row to remove from table
233   // } ----- end oid set methods -----
234 
235   // { ----- begin row set methods -----
236   NS_IMETHOD NewRow(  // create a new row instance in table
237       nsIMdbEnv* ev,  // context
238       mdbOid*
239           ioOid,  // please use minus one (unbound) rowId for db-assigned IDs
240       nsIMdbRow** acqRow) override;  // create new row
241 
242   NS_IMETHOD AddRow(  // make sure the row with inOid is a table member
243       nsIMdbEnv* ev,  // context
244       nsIMdbRow* ioRow) override;  // row to ensure membership in table
245 
246   NS_IMETHOD HasRow(     // test for the table position of a row member
247       nsIMdbEnv* ev,     // context
248       nsIMdbRow* ioRow,  // row to find in table
249       mdb_bool* outHasRow) override;  // whether row is a table member
250 
251   NS_IMETHOD CutRow(  // make sure the row with inOid is not a member
252       nsIMdbEnv* ev,  // context
253       nsIMdbRow* ioRow) override;  // row to remove from table
254 
255   NS_IMETHOD CutAllRows(        // remove all rows from the table
256       nsIMdbEnv* ev) override;  // context
257   // } ----- end row set methods -----
258 
259   // { ----- begin hinting methods -----
260   NS_IMETHOD SearchColumnsHint(  // advise re future expected search cols
261       nsIMdbEnv* ev,             // context
262       const mdbColumnSet* inColumnSet)
263       override;  // columns likely to be searched
264 
265   NS_IMETHOD SortColumnsHint(  // advise re future expected sort columns
266       nsIMdbEnv* ev,           // context
267       const mdbColumnSet* inColumnSet)
268       override;  // columns for likely sort requests
269 
270   NS_IMETHOD StartBatchChangeHint(    // advise before many adds and cuts
271       nsIMdbEnv* ev,                  // context
272       const void* inLabel) override;  // intend unique address to match end call
273   // If batch starts nest by virtue of nesting calls in the stack, then
274   // the address of a local variable makes a good batch start label that
275   // can be used at batch end time, and such addresses remain unique.
276 
277   NS_IMETHOD EndBatchChangeHint(      // advise before many adds and cuts
278       nsIMdbEnv* ev,                  // context
279       const void* inLabel) override;  // label matching start label
280   // Suppose a table is maintaining one or many sort orders for a table,
281   // so that every row added to the table must be inserted in each sort,
282   // and every row cut must be removed from each sort.  If a db client
283   // intends to make many such changes before needing any information
284   // about the order or positions of rows inside a table, then a client
285   // might tell the table to start batch changes in order to disable
286   // sorting of rows for the interim.  Presumably a table will then do
287   // a full sort of all rows at need when the batch changes end, or when
288   // a surprise request occurs for row position during batch changes.
289   // } ----- end hinting methods -----
290 
291   // { ----- begin searching methods -----
292   NS_IMETHOD FindRowMatches(  // search variable number of sorted cols
293       nsIMdbEnv* ev,          // context
294       const mdbYarn*
295           inPrefix,  // content to find as prefix in row's column cell
296       nsIMdbTableRowCursor** acqCursor) override;  // set of matching rows
297 
298   NS_IMETHOD GetSearchColumns(  // query columns used by FindRowMatches()
299       nsIMdbEnv* ev,            // context
300       mdb_count* outCount,      // context
301       mdbColumnSet* outColSet)
302       override;  // caller supplied space to put columns
303   // GetSearchColumns() returns the columns actually searched when the
304   // FindRowMatches() method is called.  No more than mColumnSet_Count
305   // slots of mColumnSet_Columns will be written, since mColumnSet_Count
306   // indicates how many slots are present in the column array.  The
307   // actual number of search column used by the table is returned in
308   // the outCount parameter; if this number exceeds mColumnSet_Count,
309   // then a caller needs a bigger array to read the entire column set.
310   // The minimum of mColumnSet_Count and outCount is the number slots
311   // in mColumnSet_Columns that were actually written by this method.
312   //
313   // Callers are expected to change this set of columns by calls to
314   // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both.
315   // } ----- end searching methods -----
316 
317   // { ----- begin sorting methods -----
318   // sorting: note all rows are assumed sorted by row ID as a secondary
319   // sort following the primary column sort, when table rows are sorted.
320 
321   NS_IMETHOD
322   CanSortColumn(            // query which column is currently used for sorting
323       nsIMdbEnv* ev,        // context
324       mdb_column inColumn,  // column to query sorting potential
325       mdb_bool* outCanSort) override;  // whether the column can be sorted
326 
327   NS_IMETHOD GetSorting(    // view same table in particular sorting
328       nsIMdbEnv* ev,        // context
329       mdb_column inColumn,  // requested new column for sorting table
330       nsIMdbSorting** acqSorting) override;  // acquire sorting for column
331 
332   NS_IMETHOD SetSearchSorting(  // use this sorting in FindRowMatches()
333       nsIMdbEnv* ev,            // context
334       mdb_column inColumn,      // often same as nsIMdbSorting::GetSortColumn()
335       nsIMdbSorting* ioSorting) override;  // requested sorting for some column
336   // SetSearchSorting() attempts to inform the table that ioSorting
337   // should be used during calls to FindRowMatches() for searching
338   // the column which is actually sorted by ioSorting.  This method
339   // is most useful in conjunction with nsIMdbSorting::SetCompare(),
340   // because otherwise a caller would not be able to override the
341   // comparison ordering method used during searches.  Note that some
342   // database implementations might be unable to use an arbitrarily
343   // specified sort order, either due to schema or runtime interface
344   // constraints, in which case ioSorting might not actually be used.
345   // Presumably ioSorting is an instance that was returned from some
346   // earlier call to nsIMdbTable::GetSorting().  A caller can also
347   // use nsIMdbTable::SearchColumnsHint() to specify desired change
348   // in which columns are sorted and searched by FindRowMatches().
349   //
350   // A caller can pass a nil pointer for ioSorting to request that
351   // column inColumn no longer be used at all by FindRowMatches().
352   // But when ioSorting is non-nil, then inColumn should match the
353   // column actually sorted by ioSorting; when these do not agree,
354   // implementations are instructed to give precedence to the column
355   // specified by ioSorting (so this means callers might just pass
356   // zero for inColumn when ioSorting is also provided, since then
357   // inColumn is both redundant and ignored).
358   // } ----- end sorting methods -----
359 
360   // { ----- begin moving methods -----
361   // moving a row does nothing unless a table is currently unsorted
362 
363   NS_IMETHOD MoveOid(         // change position of row in unsorted table
364       nsIMdbEnv* ev,          // context
365       const mdbOid* inOid,    // row oid to find in table
366       mdb_pos inHintFromPos,  // suggested hint regarding start position
367       mdb_pos inToPos,        // desired new position for row inRowId
368       mdb_pos* outActualPos) override;  // actual new position of row in table
369 
370   NS_IMETHOD MoveRow(         // change position of row in unsorted table
371       nsIMdbEnv* ev,          // context
372       nsIMdbRow* ioRow,       // row oid to find in table
373       mdb_pos inHintFromPos,  // suggested hint regarding start position
374       mdb_pos inToPos,        // desired new position for row inRowId
375       mdb_pos* outActualPos) override;  // actual new position of row in table
376   // } ----- end moving methods -----
377 
378   // { ----- begin index methods -----
379   NS_IMETHOD AddIndex(      // create a sorting index for column if possible
380       nsIMdbEnv* ev,        // context
381       mdb_column inColumn,  // the column to sort by index
382       nsIMdbThumb** acqThumb)
383       override;  // acquire thumb for incremental index building
384   // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
385   // then the index addition will be finished.
386 
387   NS_IMETHOD CutIndex(      // stop supporting a specific column index
388       nsIMdbEnv* ev,        // context
389       mdb_column inColumn,  // the column with index to be removed
390       nsIMdbThumb** acqThumb)
391       override;  // acquire thumb for incremental index destroy
392   // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
393   // then the index removal will be finished.
394 
395   NS_IMETHOD HasIndex(      // query for current presence of a column index
396       nsIMdbEnv* ev,        // context
397       mdb_column inColumn,  // the column to investigate
398       mdb_bool* outHasIndex)
399       override;  // whether column has index for this column
400 
401   NS_IMETHOD EnableIndexOnSort(       // create an index for col on first sort
402       nsIMdbEnv* ev,                  // context
403       mdb_column inColumn) override;  // the column to index if ever sorted
404 
405   NS_IMETHOD QueryIndexOnSort(  // check whether index on sort is enabled
406       nsIMdbEnv* ev,            // context
407       mdb_column inColumn,      // the column to investigate
408       mdb_bool* outIndexOnSort)
409       override;  // whether column has index-on-sort enabled
410 
411   NS_IMETHOD DisableIndexOnSort(      // prevent future index creation on sort
412       nsIMdbEnv* ev,                  // context
413       mdb_column inColumn) override;  // the column to index if ever sorted
414   // } ----- end index methods -----
415 
416   morkStore* mTable_Store;  // non-refcnted ptr to port
417 
418   // mTable_RowSpace->SpaceScope() is row scope
419   morkRowSpace* mTable_RowSpace;  // non-refcnted ptr to containing space
420 
421   morkRow* mTable_MetaRow;   // table's actual meta row
422   mdbOid mTable_MetaRowOid;  // oid for meta row
423 
424   morkRowMap* mTable_RowMap;  // (strong ref) hash table of all members
425   morkArray mTable_RowArray;  // array of morkRow pointers
426 
427   morkList mTable_ChangeList;   // list of table changes
428   mork_u2 mTable_ChangesCount;  // length of changes list
429   mork_u2 mTable_ChangesMax;    // max list length before rewrite
430 
431   // mork_tid        mTable_Id;
432   mork_kind mTable_Kind;
433 
434   mork_u1 mTable_Flags;           // bit flags
435   mork_priority mTable_Priority;  // 0..9, any other value equals 9
436   mork_u1 mTable_GcUses;          // persistent references from cells
437   mork_u1 mTable_Pad;             // for u4 alignment
438 
439  public:  // flags bit twiddling
SetTableUnique()440   void SetTableUnique() { mTable_Flags |= morkTable_kUniqueBit; }
SetTableVerbose()441   void SetTableVerbose() { mTable_Flags |= morkTable_kVerboseBit; }
SetTableNoted()442   void SetTableNoted() { mTable_Flags |= morkTable_kNotedBit; }
SetTableRewrite()443   void SetTableRewrite() { mTable_Flags |= morkTable_kRewriteBit; }
SetTableNewMeta()444   void SetTableNewMeta() { mTable_Flags |= morkTable_kNewMetaBit; }
445 
ClearTableUnique()446   void ClearTableUnique() { mTable_Flags &= (mork_u1)~morkTable_kUniqueBit; }
ClearTableVerbose()447   void ClearTableVerbose() { mTable_Flags &= (mork_u1)~morkTable_kVerboseBit; }
ClearTableNoted()448   void ClearTableNoted() { mTable_Flags &= (mork_u1)~morkTable_kNotedBit; }
ClearTableRewrite()449   void ClearTableRewrite() { mTable_Flags &= (mork_u1)~morkTable_kRewriteBit; }
ClearTableNewMeta()450   void ClearTableNewMeta() { mTable_Flags &= (mork_u1)~morkTable_kNewMetaBit; }
451 
IsTableUnique()452   mork_bool IsTableUnique() const {
453     return (mTable_Flags & morkTable_kUniqueBit) != 0;
454   }
455 
IsTableVerbose()456   mork_bool IsTableVerbose() const {
457     return (mTable_Flags & morkTable_kVerboseBit) != 0;
458   }
459 
IsTableNoted()460   mork_bool IsTableNoted() const {
461     return (mTable_Flags & morkTable_kNotedBit) != 0;
462   }
463 
IsTableRewrite()464   mork_bool IsTableRewrite() const {
465     return (mTable_Flags & morkTable_kRewriteBit) != 0;
466   }
467 
IsTableNewMeta()468   mork_bool IsTableNewMeta() const {
469     return (mTable_Flags & morkTable_kNewMetaBit) != 0;
470   }
471 
472  public
473      :  // table dirty handling more complex than morkNode::SetNodeDirty() etc.
SetTableDirty()474   void SetTableDirty() { this->SetNodeDirty(); }
475   void SetTableClean(morkEnv* ev);
476 
IsTableClean()477   mork_bool IsTableClean() const { return this->IsNodeClean(); }
IsTableDirty()478   mork_bool IsTableDirty() const { return this->IsNodeDirty(); }
479 
480  public:  // morkNode memory management operators
new(size_t inSize,nsIMdbHeap & ioHeap,morkEnv * ev)481   void* operator new(size_t inSize, nsIMdbHeap& ioHeap,
482                      morkEnv* ev) noexcept(true) {
483     return morkNode::MakeNew(inSize, ioHeap, ev);
484   }
485 
486   // { ===== begin morkNode interface =====
487  public:                                             // morkNode virtual methods
488   virtual void CloseMorkNode(morkEnv* ev) override;  // CloseTable() if open
489 
490  public:  // morkTable construction & destruction
491   morkTable(
492       morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap,
493       morkStore* ioStore, nsIMdbHeap* ioSlotHeap, morkRowSpace* ioRowSpace,
494       const mdbOid* inOptionalMetaRowOid,  // can be nil to avoid specifying
495       mork_tid inTableId, mork_kind inKind, mork_bool inMustBeUnique);
496   void CloseTable(morkEnv* ev);  // called by CloseMorkNode();
497 
498  private:  // copying is not allowed
499   morkTable(const morkTable& other);
500   morkTable& operator=(const morkTable& other);
501   virtual ~morkTable();  // assert that close executed earlier
502 
503  public:  // dynamic type identification
IsTable()504   mork_bool IsTable() const {
505     return IsNode() && mNode_Derived == morkDerived_kTable;
506   }
507   // } ===== end morkNode methods =====
508 
509  public:  // errors
510   static void NonTableTypeError(morkEnv* ev);
511   static void NonTableTypeWarning(morkEnv* ev);
512   static void NilRowSpaceError(morkEnv* ev);
513 
514  public:  // warnings
515   static void TableGcUsesUnderflowWarning(morkEnv* ev);
516 
517  public:  // noting table changes
HasChangeOverflow()518   mork_bool HasChangeOverflow() const {
519     return mTable_ChangesCount >= mTable_ChangesMax;
520   }
521 
522   void NoteTableSetAll(morkEnv* ev);
523   void NoteTableMoveRow(morkEnv* ev, morkRow* ioRow, mork_pos inPos);
524 
525   void note_row_change(morkEnv* ev, mork_change inChange, morkRow* ioRow);
526   void note_row_move(morkEnv* ev, morkRow* ioRow, mork_pos inNewPos);
527 
NoteTableAddRow(morkEnv * ev,morkRow * ioRow)528   void NoteTableAddRow(morkEnv* ev, morkRow* ioRow) {
529     this->note_row_change(ev, morkChange_kAdd, ioRow);
530   }
531 
NoteTableCutRow(morkEnv * ev,morkRow * ioRow)532   void NoteTableCutRow(morkEnv* ev, morkRow* ioRow) {
533     this->note_row_change(ev, morkChange_kCut, ioRow);
534   }
535 
536  protected:  // internal row map methods
537   morkRow* find_member_row(morkEnv* ev, morkRow* ioRow);
538   void build_row_map(morkEnv* ev);
539 
540  public:  // other table methods
541   mork_bool MaybeDirtySpaceStoreAndTable();
542 
543   morkRow* GetMetaRow(morkEnv* ev, const mdbOid* inOptionalMetaRowOid);
544 
545   mork_u2 AddTableGcUse(morkEnv* ev);
546   mork_u2 CutTableGcUse(morkEnv* ev);
547 
548   // void DirtyAllTableContent(morkEnv* ev);
549 
TableSeed()550   mork_seed TableSeed() const { return mTable_RowArray.mArray_Seed; }
551 
SafeRowAt(morkEnv * ev,mork_pos inPos)552   morkRow* SafeRowAt(morkEnv* ev, mork_pos inPos) {
553     return (morkRow*)mTable_RowArray.SafeAt(ev, inPos);
554   }
555 
556   nsIMdbTable* AcquireTableHandle(morkEnv* ev);  // mObject_Handle
557 
GetRowCount()558   mork_count GetRowCount() const { return mTable_RowArray.mArray_Fill; }
559 
IsTableUsed()560   mork_bool IsTableUsed() const {
561     return (mTable_GcUses != 0 || this->GetRowCount() != 0);
562   }
563 
564   void GetTableOid(morkEnv* ev, mdbOid* outOid);
565   mork_pos ArrayHasOid(morkEnv* ev, const mdbOid* inOid);
566   mork_bool MapHasOid(morkEnv* ev, const mdbOid* inOid);
567   mork_bool AddRow(morkEnv* ev, morkRow* ioRow);  // returns ev->Good()
568   mork_bool CutRow(morkEnv* ev, morkRow* ioRow);  // returns ev->Good()
569   mork_bool CutAllRows(morkEnv* ev);              // returns ev->Good()
570 
571   mork_pos MoveRow(
572       morkEnv* ev, morkRow* ioRow,  // change row position
573       mork_pos inHintFromPos,       // suggested hint regarding start position
574       mork_pos inToPos);            // desired new position for row ioRow
575   // MoveRow() returns the actual position of ioRow afterwards; this
576   // position is -1 if and only if ioRow was not found as a member.
577 
578   morkTableRowCursor* NewTableRowCursor(morkEnv* ev, mork_pos inRowPos);
579 
580  public:  // typesafe refcounting inlines calling inherited morkNode methods
SlotWeakTable(morkTable * me,morkEnv * ev,morkTable ** ioSlot)581   static void SlotWeakTable(morkTable* me, morkEnv* ev, morkTable** ioSlot) {
582     morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot);
583   }
584 
SlotStrongTable(morkTable * me,morkEnv * ev,morkTable ** ioSlot)585   static void SlotStrongTable(morkTable* me, morkEnv* ev, morkTable** ioSlot) {
586     morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot);
587   }
588 };
589 
590 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
591 
592 // use negative values for kCut and kAdd, to keep non-neg move pos distinct:
593 #define morkTableChange_kCut ((mork_pos)-1)  /* shows row was cut */
594 #define morkTableChange_kAdd ((mork_pos)-2)  /* shows row was added */
595 #define morkTableChange_kNone ((mork_pos)-3) /* unknown change */
596 
597 class morkTableChange : public morkNext {
598  public:  // state is public because the entire Mork system is private
599   morkRow* mTableChange_Row;  // the row in the change
600 
601   mork_pos mTableChange_Pos;  // kAdd, kCut, or non-neg for row move
602 
603  public:
604   morkTableChange(morkEnv* ev, mork_change inChange, morkRow* ioRow);
605   // use this constructor for inChange == morkChange_kAdd or morkChange_kCut
606 
607   morkTableChange(morkEnv* ev, morkRow* ioRow, mork_pos inPos);
608   // use this constructor when the row is moved
609 
610  public:
611   void UnknownChangeError(
612       morkEnv* ev) const;  // morkChange_kAdd or morkChange_kCut
613   void NegativeMovePosError(
614       morkEnv* ev) const;  // move must be non-neg position
615 
616  public:
IsAddRowTableChange()617   mork_bool IsAddRowTableChange() const {
618     return (mTableChange_Pos == morkTableChange_kAdd);
619   }
620 
IsCutRowTableChange()621   mork_bool IsCutRowTableChange() const {
622     return (mTableChange_Pos == morkTableChange_kCut);
623   }
624 
IsMoveRowTableChange()625   mork_bool IsMoveRowTableChange() const { return (mTableChange_Pos >= 0); }
626 
627  public:
GetMovePos()628   mork_pos GetMovePos() const { return mTableChange_Pos; }
629   // GetMovePos() assumes that IsMoveRowTableChange() is true.
630 };
631 
632 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
633 
634 #define morkDerived_kTableMap /*i*/ 0x744D /* ascii 'tM' */
635 
636 /*| morkTableMap: maps mork_token -> morkTable
637 |*/
638 #ifdef MORK_BEAD_OVER_NODE_MAPS
639 class morkTableMap : public morkBeadMap {
640 #else  /*MORK_BEAD_OVER_NODE_MAPS*/
641 class morkTableMap : public morkNodeMap {  // for mapping tokens to tables
642 #endif /*MORK_BEAD_OVER_NODE_MAPS*/
643 
644  public:
645   virtual ~morkTableMap();
646   morkTableMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
647                nsIMdbHeap* ioSlotHeap);
648 
649  public:  // other map methods
650 #ifdef MORK_BEAD_OVER_NODE_MAPS
AddTable(morkEnv * ev,morkTable * ioTable)651   mork_bool AddTable(morkEnv* ev, morkTable* ioTable) {
652     return this->AddBead(ev, ioTable);
653   }
654   // the AddTable() boolean return equals ev->Good().
655 
CutTable(morkEnv * ev,mork_tid inTid)656   mork_bool CutTable(morkEnv* ev, mork_tid inTid) {
657     return this->CutBead(ev, inTid);
658   }
659   // The CutTable() boolean return indicates whether removal happened.
660 
GetTable(morkEnv * ev,mork_tid inTid)661   morkTable* GetTable(morkEnv* ev, mork_tid inTid) {
662     return (morkTable*)this->GetBead(ev, inTid);
663   }
664   // Note the returned table does NOT have an increase in refcount for this.
665 
CutAllTables(morkEnv * ev)666   mork_num CutAllTables(morkEnv* ev) { return this->CutAllBeads(ev); }
667   // CutAllTables() releases all the referenced table values.
668 
669 #else  /*MORK_BEAD_OVER_NODE_MAPS*/
670   mork_bool AddTable(morkEnv* ev, morkTable* ioTable) {
671     return this->AddNode(ev, ioTable->TableId(), ioTable);
672   }
673   // the AddTable() boolean return equals ev->Good().
674 
675   mork_bool CutTable(morkEnv* ev, mork_tid inTid) {
676     return this->CutNode(ev, inTid);
677   }
678   // The CutTable() boolean return indicates whether removal happened.
679 
680   morkTable* GetTable(morkEnv* ev, mork_tid inTid) {
681     return (morkTable*)this->GetNode(ev, inTid);
682   }
683   // Note the returned table does NOT have an increase in refcount for this.
684 
685   mork_num CutAllTables(morkEnv* ev) { return this->CutAllNodes(ev); }
686   // CutAllTables() releases all the referenced table values.
687 #endif /*MORK_BEAD_OVER_NODE_MAPS*/
688 };
689 
690 #ifdef MORK_BEAD_OVER_NODE_MAPS
691 class morkTableMapIter : public morkBeadMapIter {
692 #else  /*MORK_BEAD_OVER_NODE_MAPS*/
693 class morkTableMapIter : public morkMapIter {  // typesafe wrapper class
694 #endif /*MORK_BEAD_OVER_NODE_MAPS*/
695 
696  public:
697 #ifdef MORK_BEAD_OVER_NODE_MAPS
morkTableMapIter(morkEnv * ev,morkTableMap * ioMap)698   morkTableMapIter(morkEnv* ev, morkTableMap* ioMap)
699       : morkBeadMapIter(ev, ioMap) {}
700 
morkTableMapIter()701   morkTableMapIter() : morkBeadMapIter() {}
InitTableMapIter(morkEnv * ev,morkTableMap * ioMap)702   void InitTableMapIter(morkEnv* ev, morkTableMap* ioMap) {
703     this->InitBeadMapIter(ev, ioMap);
704   }
705 
FirstTable(morkEnv * ev)706   morkTable* FirstTable(morkEnv* ev) { return (morkTable*)this->FirstBead(ev); }
707 
NextTable(morkEnv * ev)708   morkTable* NextTable(morkEnv* ev) { return (morkTable*)this->NextBead(ev); }
709 
HereTable(morkEnv * ev)710   morkTable* HereTable(morkEnv* ev) { return (morkTable*)this->HereBead(ev); }
711 
712 #else  /*MORK_BEAD_OVER_NODE_MAPS*/
713   morkTableMapIter(morkEnv* ev, morkTableMap* ioMap) : morkMapIter(ev, ioMap) {}
714 
715   morkTableMapIter() : morkMapIter() {}
716   void InitTableMapIter(morkEnv* ev, morkTableMap* ioMap) {
717     this->InitMapIter(ev, ioMap);
718   }
719 
720   mork_change* FirstTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable) {
721     return this->First(ev, outTid, outTable);
722   }
723 
724   mork_change* NextTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable) {
725     return this->Next(ev, outTid, outTable);
726   }
727 
728   mork_change* HereTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable) {
729     return this->Here(ev, outTid, outTable);
730   }
731 
732   // cutting while iterating hash map might dirty the parent table:
733   mork_change* CutHereTable(morkEnv* ev, mork_tid* outTid,
734                             morkTable** outTable) {
735     return this->CutHere(ev, outTid, outTable);
736   }
737 #endif /*MORK_BEAD_OVER_NODE_MAPS*/
738 };
739 
740 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
741 
742 #endif /* _MORKTABLE_ */
743