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 _MORKSTORE_
7 #define _MORKSTORE_ 1
8 
9 #ifndef _MORK_
10 #  include "mork.h"
11 #endif
12 
13 #ifndef _MORKOBJECT_
14 #  include "morkObject.h"
15 #endif
16 
17 #ifndef _MORKNODEMAP_
18 #  include "morkNodeMap.h"
19 #endif
20 
21 #ifndef _MORKPOOL_
22 #  include "morkPool.h"
23 #endif
24 
25 #ifndef _MORKZONE_
26 #  include "morkZone.h"
27 #endif
28 
29 #ifndef _MORKATOM_
30 #  include "morkAtom.h"
31 #endif
32 
33 #ifndef _MORKROWSPACE_
34 #  include "morkRowSpace.h"
35 #endif
36 
37 #ifndef _MORKATOMSPACE_
38 #  include "morkAtomSpace.h"
39 #endif
40 
41 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
42 
43 #define morkDerived_kPort /*i*/ 0x7054 /* ascii 'pT' */
44 
45 #define morkDerived_kStore /*i*/ 0x7354 /* ascii 'sT' */
46 
47 /*| kGroundColumnSpace: we use the 'column space' as the default scope
48 **| for grounding column name IDs, and this is also the default scope for
49 **| all other explicitly tokenized strings.
50 |*/
51 #define morkStore_kGroundColumnSpace 'c' /* for mStore_GroundColumnSpace*/
52 #define morkStore_kColumnSpaceScope ((mork_scope)'c') /*kGroundColumnSpace*/
53 #define morkStore_kValueSpaceScope ((mork_scope)'v')
54 #define morkStore_kStreamBufSize (8 * 1024) /* okay buffer size */
55 
56 #define morkStore_kReservedColumnCount 0x20 /* for well-known columns */
57 
58 #define morkStore_kNoneToken ((mork_token)'n')
59 #define morkStore_kFormColumn ((mork_column)'f')
60 #define morkStore_kAtomScopeColumn ((mork_column)'a')
61 #define morkStore_kRowScopeColumn ((mork_column)'r')
62 #define morkStore_kMetaScope ((mork_scope)'m')
63 #define morkStore_kKindColumn ((mork_column)'k')
64 #define morkStore_kStatusColumn ((mork_column)'s')
65 
66 /*| morkStore:
67 |*/
68 class morkStore : public morkObject, public nsIMdbStore {
69  public:  // state is public because the entire Mork system is private
70   NS_DECL_ISUPPORTS_INHERITED
71 
72   morkEnv* mPort_Env;          // non-refcounted env which created port
73   morkFactory* mPort_Factory;  // weak ref to suite factory
74   nsIMdbHeap* mPort_Heap;      // heap in which this port allocs objects
75 
76   // { ===== begin morkNode interface =====
77  public:                        // morkNode virtual methods
78   void ClosePort(morkEnv* ev);  // called by CloseMorkNode();
79 
80  public:  // dynamic type identification
IsPort()81   mork_bool IsPort() const {
82     return IsNode() && mNode_Derived == morkDerived_kPort;
83   }
84   // } ===== end morkNode methods =====
85 
86  public:  // other port methods
87   // { ----- begin attribute methods -----
88   //  NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly);
89   // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port.
90   // } ----- end attribute methods -----
91 
92   // { ----- begin factory methods -----
93   //  NS_IMETHOD GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory);
94   // } ----- end factory methods -----
95 
96   // { ----- begin ref counting for well-behaved cyclic graphs -----
97   NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev,  // weak refs
98                              mdb_count* outCount) override;
99   NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev,  // strong refs
100                                mdb_count* outCount) override;
101 
102   NS_IMETHOD AddWeakRef(nsIMdbEnv* ev) override;
103 #ifndef _MSC_VER
104   // The first declaration of AddStrongRef is to suppress
105   // -Werror,-Woverloaded-virtual.
106   NS_IMETHOD_(mork_uses) AddStrongRef(morkEnv* ev) override;
107 #endif
108   NS_IMETHOD_(mork_uses) AddStrongRef(nsIMdbEnv* ev) override;
109 
110   NS_IMETHOD CutWeakRef(nsIMdbEnv* ev) override;
111 #ifndef _MSC_VER
112   // The first declaration of CutStrongRef is to suppress
113   // -Werror,-Woverloaded-virtual.
114   NS_IMETHOD_(mork_uses) CutStrongRef(morkEnv* ev) override;
115 #endif
116   NS_IMETHOD CutStrongRef(nsIMdbEnv* ev) override;
117 
118   NS_IMETHOD CloseMdbObject(
119       nsIMdbEnv* ev) override;  // called at strong refs zero
120   NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen) override;
121   // } ----- end ref counting -----
122 
123   // } ===== end nsIMdbObject methods =====
124 
125   // { ===== begin nsIMdbPort methods =====
126 
127   // { ----- begin attribute methods -----
128   NS_IMETHOD GetIsPortReadonly(nsIMdbEnv* ev, mdb_bool* outBool) override;
129   NS_IMETHOD GetIsStore(nsIMdbEnv* ev, mdb_bool* outBool) override;
130   NS_IMETHOD GetIsStoreAndDirty(nsIMdbEnv* ev, mdb_bool* outBool) override;
131 
132   NS_IMETHOD GetUsagePolicy(nsIMdbEnv* ev,
133                             mdbUsagePolicy* ioUsagePolicy) override;
134 
135   NS_IMETHOD SetUsagePolicy(nsIMdbEnv* ev,
136                             const mdbUsagePolicy* inUsagePolicy) override;
137   // } ----- end attribute methods -----
138 
139   // { ----- begin memory policy methods -----
140   NS_IMETHOD IdleMemoryPurge(  // do memory management already scheduled
141       nsIMdbEnv* ev,           // context
142       mdb_size* outEstimatedBytesFreed)
143       override;  // approximate bytes actually freed
144 
145   NS_IMETHOD SessionMemoryPurge(     // request specific footprint decrease
146       nsIMdbEnv* ev,                 // context
147       mdb_size inDesiredBytesFreed,  // approximate number of bytes wanted
148       mdb_size* outEstimatedBytesFreed)
149       override;  // approximate bytes actually freed
150 
151   NS_IMETHOD PanicMemoryPurge(  // desperately free all possible memory
152       nsIMdbEnv* ev,            // context
153       mdb_size* outEstimatedBytesFreed)
154       override;  // approximate bytes actually freed
155   // } ----- end memory policy methods -----
156 
157   // { ----- begin filepath methods -----
158   NS_IMETHOD GetPortFilePath(
159       nsIMdbEnv* ev,                        // context
160       mdbYarn* outFilePath,                 // name of file holding port content
161       mdbYarn* outFormatVersion) override;  // file format description
162 
163   NS_IMETHOD GetPortFile(
164       nsIMdbEnv* ev,                   // context
165       nsIMdbFile** acqFile) override;  // acquire file used by port or store
166   // } ----- end filepath methods -----
167 
168   // { ----- begin export methods -----
169   NS_IMETHOD BestExportFormat(              // determine preferred export format
170       nsIMdbEnv* ev,                        // context
171       mdbYarn* outFormatVersion) override;  // file format description
172 
173   NS_IMETHOD
174   CanExportToFormat(  // can export content in given specific format?
175       nsIMdbEnv* ev,  // context
176       const char* inFormatVersion,       // file format description
177       mdb_bool* outCanExport) override;  // whether ExportSource() might succeed
178 
179   NS_IMETHOD ExportToFormat(  // export content in given specific format
180       nsIMdbEnv* ev,          // context
181       // const char* inFilePath, // the file to receive exported content
182       nsIMdbFile* ioFile,                // destination abstract file interface
183       const char* inFormatVersion,       // file format description
184       nsIMdbThumb** acqThumb) override;  // acquire thumb for incremental export
185   // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
186   // then the export will be finished.
187 
188   // } ----- end export methods -----
189 
190   // { ----- begin token methods -----
191   NS_IMETHOD TokenToString(  // return a string name for an integer token
192       nsIMdbEnv* ev,         // context
193       mdb_token inToken,     // token for inTokenName inside this port
194       mdbYarn* outTokenName) override;  // the type of table to access
195 
196   NS_IMETHOD StringToToken(           // return an integer token for scope name
197       nsIMdbEnv* ev,                  // context
198       const char* inTokenName,        // Latin1 string to tokenize if possible
199       mdb_token* outToken) override;  // token for inTokenName inside this port
200 
201   // String token zero is never used and never supported. If the port
202   // is a mutable store, then StringToToken() to create a new
203   // association of inTokenName with a new integer token if possible.
204   // But a readonly port will return zero for an unknown scope name.
205 
206   NS_IMETHOD QueryToken(        // like StringToToken(), but without adding
207       nsIMdbEnv* ev,            // context
208       const char* inTokenName,  // Latin1 string to tokenize if possible
209       mdb_token* outToken) override;  // token for inTokenName inside this port
210 
211   // QueryToken() will return a string token if one already exists,
212   // but unlike StringToToken(), will not assign a new token if not
213   // already in use.
214 
215   // } ----- end token methods -----
216 
217   // { ----- begin row methods -----
218   NS_IMETHOD HasRow(                  // contains a row with the specified oid?
219       nsIMdbEnv* ev,                  // context
220       const mdbOid* inOid,            // hypothetical row oid
221       mdb_bool* outHasRow) override;  // whether GetRow() might succeed
222 
223   NS_IMETHOD GetRowRefCount(  // get number of tables that contain a row
224       nsIMdbEnv* ev,          // context
225       const mdbOid* inOid,    // hypothetical row oid
226       mdb_count* outRefCount) override;  // number of tables containing inRowKey
227 
228   NS_IMETHOD GetRow(                 // access one row with specific oid
229       nsIMdbEnv* ev,                 // context
230       const mdbOid* inOid,           // hypothetical row oid
231       nsIMdbRow** acqRow) override;  // acquire specific row (or null)
232 
233   NS_IMETHOD FindRow(
234       nsIMdbEnv* ev,         // search for row with matching cell
235       mdb_scope inRowScope,  // row scope for row ids
236       mdb_column inColumn,   // the column to search (and maintain an index)
237       const mdbYarn* inTargetCellValue,  // cell value for which to search
238       mdbOid* outRowOid,  // out row oid on match (or {0,-1} for no match)
239       nsIMdbRow** acqRow)
240       override;  // acquire matching row (or nil for no match)
241                  // can be null if you only want the oid
242   // FindRow() searches for one row that has a cell in column inColumn with
243   // a contained value with the same form (i.e. charset) and is byte-wise
244   // identical to the blob described by yarn inTargetCellValue.  Both content
245   // and form of the yarn must be an exact match to find a matching row.
246   //
247   // (In other words, both a yarn's blob bytes and form are significant.  The
248   // form is not expected to vary in columns used for identity anyway.  This
249   // is intended to make the cost of FindRow() cheaper for MDB implementors,
250   // since any cell value atomization performed internally must necessarily
251   // make yarn form significant in order to avoid data loss in atomization.)
252   //
253   // FindRow() can lazily create an index on attribute inColumn for all rows
254   // with that attribute in row space scope inRowScope, so that subsequent
255   // calls to FindRow() will perform faster.  Such an index might or might
256   // not be persistent (but this seems desirable if it is cheap to do so).
257   // Note that lazy index creation in readonly DBs is not very feasible.
258   //
259   // This FindRow() interface assumes that attribute inColumn is effectively
260   // an alternative means of unique identification for a row in a rowspace,
261   // so correct behavior is only guaranteed when no duplicates for this col
262   // appear in the given set of rows.  (If more than one row has the same cell
263   // value in this column, no more than one will be found; and cutting one of
264   // two duplicate rows can cause the index to assume no other such row lives
265   // in the row space, so future calls return nil for negative search results
266   // even though some duplicate row might still live within the rowspace.)
267   //
268   // In other words, the FindRow() implementation is allowed to assume simple
269   // hash tables mapping unique column keys to associated row values will be
270   // sufficient, where any duplication is not recorded because only one copy
271   // of a given key need be remembered.  Implementors are not required to sort
272   // all rows by the specified column.
273   // } ----- end row methods -----
274 
275   // { ----- begin table methods -----
276   NS_IMETHOD HasTable(      // supports a table with the specified oid?
277       nsIMdbEnv* ev,        // context
278       const mdbOid* inOid,  // hypothetical table oid
279       mdb_bool* outHasTable) override;  // whether GetTable() might succeed
280 
281   NS_IMETHOD GetTable(                   // access one table with specific oid
282       nsIMdbEnv* ev,                     // context
283       const mdbOid* inOid,               // hypothetical table oid
284       nsIMdbTable** acqTable) override;  // acquire specific table (or null)
285 
286   NS_IMETHOD HasTableKind(       // supports a table of the specified type?
287       nsIMdbEnv* ev,             // context
288       mdb_scope inRowScope,      // rid scope for row ids
289       mdb_kind inTableKind,      // the type of table to access
290       mdb_count* outTableCount,  // current number of such tables
291       mdb_bool* outSupportsTable)
292       override;  // whether GetTableKind() might succeed
293 
294   NS_IMETHOD GetTableKind(        // access one (random) table of specific type
295       nsIMdbEnv* ev,              // context
296       mdb_scope inRowScope,       // row scope for row ids
297       mdb_kind inTableKind,       // the type of table to access
298       mdb_count* outTableCount,   // current number of such tables
299       mdb_bool* outMustBeUnique,  // whether port can hold only one of these
300       nsIMdbTable** acqTable) override;  // acquire scoped collection of rows
301 
302   NS_IMETHOD
303   GetPortTableCursor(        // get cursor for all tables of specific type
304       nsIMdbEnv* ev,         // context
305       mdb_scope inRowScope,  // row scope for row ids
306       mdb_kind inTableKind,  // the type of table to access
307       nsIMdbPortTableCursor** acqCursor)
308       override;  // all such tables in the port
309   // } ----- end table methods -----
310 
311   // { ----- begin commit methods -----
312 
313   NS_IMETHOD ShouldCompress(        // store wastes at least inPercentWaste?
314       nsIMdbEnv* ev,                // context
315       mdb_percent inPercentWaste,   // 0..100 percent file size waste threshold
316       mdb_percent* outActualWaste,  // 0..100 percent of file actually wasted
317       mdb_bool* outShould)
318       override;  // true when about inPercentWaste% is wasted
319   // ShouldCompress() returns true if the store can determine that the file
320   // will shrink by an estimated percentage of inPercentWaste% (or more) if
321   // CompressCommit() is called, because that percentage of the file seems
322   // to be recoverable free space.  The granularity is only in terms of
323   // percentage points, and any value over 100 is considered equal to 100.
324   //
325   // If a store only has an approximate idea how much space might be saved
326   // during a compress, then a best guess should be made.  For example, the
327   // Mork implementation might keep track of how much file space began with
328   // text content before the first updating transaction, and then consider
329   // all content following the start of the first transaction as potentially
330   // wasted space if it is all updates and not just new content.  (This is
331   // a safe assumption in the sense that behavior will stabilize on a low
332   // estimate of wastage after a commit removes all transaction updates.)
333   //
334   // Some db formats might attempt to keep a very accurate reckoning of free
335   // space size, so a very accurate determination can be made.  But other db
336   // formats might have difficulty determining size of free space, and might
337   // require some lengthy calculation to answer.  This is the reason for
338   // passing in the percentage threshold of interest, so that such lengthy
339   // computations can terminate early as soon as at least inPercentWaste is
340   // found, so that the entire file need not be groveled when unnecessary.
341   // However, we hope implementations will always favor fast but imprecise
342   // heuristic answers instead of extremely slow but very precise answers.
343   //
344   // If the outActualWaste parameter is non-nil, it will be used to return
345   // the actual estimated space wasted as a percentage of file size.  (This
346   // parameter is provided so callers need not call repeatedly with altered
347   // inPercentWaste values to isolate the actual wastage figure.)  Note the
348   // actual wastage figure returned can exactly equal inPercentWaste even
349   // when this grossly underestimates the real figure involved, if the db
350   // finds it very expensive to determine the extent of wastage after it is
351   // known to at least exceed inPercentWaste.  Note we expect that whenever
352   // outShould returns true, that outActualWaste returns >= inPercentWaste.
353   //
354   // The effect of different inPercentWaste values is not very uniform over
355   // the permitted range.  For example, 50 represents 50% wastage, or a file
356   // that is about double what it should be ideally.  But 99 represents 99%
357   // wastage, or a file that is about ninety-nine times as big as it should
358   // be ideally.  In the smaller direction, 25 represents 25% wastage, or
359   // a file that is only 33% larger than it should be ideally.
360   //
361   // Callers can determine what policy they want to use for considering when
362   // a file holds too much wasted space, and express this as a percentage
363   // of total file size to pass as in the inPercentWaste parameter.  A zero
364   // likely returns always trivially true, and 100 always trivially false.
365   // The great majority of callers are expected to use values from 25 to 75,
366   // since most plausible thresholds for compressing might fall between the
367   // extremes of 133% of ideal size and 400% of ideal size.  (Presumably the
368   // larger a file gets, the more important the percentage waste involved, so
369   // a sliding scale for compress thresholds might use smaller numbers for
370   // much bigger file sizes.)
371 
372   // } ----- end commit methods -----
373 
374   // } ===== end nsIMdbPort methods =====
375 
376   // { ===== begin nsIMdbStore methods =====
377 
378   // { ----- begin table methods -----
379   NS_IMETHOD NewTable(          // make one new table of specific type
380       nsIMdbEnv* ev,            // context
381       mdb_scope inRowScope,     // row scope for row ids
382       mdb_kind inTableKind,     // the type of table to access
383       mdb_bool inMustBeUnique,  // whether store can hold only one of these
384       const mdbOid* inOptionalMetaRowOid,  // can be nil to avoid specifying
385       nsIMdbTable** acqTable) override;    // acquire scoped collection of rows
386 
387   NS_IMETHOD NewTableWithOid(   // make one new table of specific type
388       nsIMdbEnv* ev,            // context
389       const mdbOid* inOid,      // caller assigned oid
390       mdb_kind inTableKind,     // the type of table to access
391       mdb_bool inMustBeUnique,  // whether store can hold only one of these
392       const mdbOid* inOptionalMetaRowOid,  // can be nil to avoid specifying
393       nsIMdbTable** acqTable) override;    // acquire scoped collection of rows
394   // } ----- end table methods -----
395 
396   // { ----- begin row scope methods -----
397   NS_IMETHOD RowScopeHasAssignedIds(
398       nsIMdbEnv* ev,
399       mdb_scope inRowScope,         // row scope for row ids
400       mdb_bool* outCallerAssigned,  // nonzero if caller assigned specified
401       mdb_bool* outStoreAssigned)
402       override;  // nonzero if store db assigned specified
403 
404   NS_IMETHOD SetCallerAssignedIds(
405       nsIMdbEnv* ev,
406       mdb_scope inRowScope,         // row scope for row ids
407       mdb_bool* outCallerAssigned,  // nonzero if caller assigned specified
408       mdb_bool* outStoreAssigned)
409       override;  // nonzero if store db assigned specified
410 
411   NS_IMETHOD SetStoreAssignedIds(
412       nsIMdbEnv* ev,
413       mdb_scope inRowScope,         // row scope for row ids
414       mdb_bool* outCallerAssigned,  // nonzero if caller assigned specified
415       mdb_bool* outStoreAssigned)
416       override;  // nonzero if store db assigned specified
417   // } ----- end row scope methods -----
418 
419   // { ----- begin row methods -----
420   NS_IMETHOD NewRowWithOid(nsIMdbEnv* ev,  // new row w/ caller assigned oid
421                            const mdbOid* inOid,           // caller assigned oid
422                            nsIMdbRow** acqRow) override;  // create new row
423 
424   NS_IMETHOD NewRow(nsIMdbEnv* ev,         // new row with db assigned oid
425                     mdb_scope inRowScope,  // row scope for row ids
426                     nsIMdbRow** acqRow) override;  // create new row
427   // Note this row must be added to some table or cell child before the
428   // store is closed in order to make this row persist across sessions.
429 
430   // } ----- end row methods -----
431 
432   // { ----- begin import/export methods -----
433   NS_IMETHOD ImportContent(  // import content from port
434       nsIMdbEnv* ev,         // context
435       mdb_scope inRowScope,  // scope for rows (or zero for all?)
436       nsIMdbPort* ioPort,    // the port with content to add to store
437       nsIMdbThumb** acqThumb) override;  // acquire thumb for incremental import
438   // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
439   // then the import will be finished.
440 
441   NS_IMETHOD ImportFile(   // import content from port
442       nsIMdbEnv* ev,       // context
443       nsIMdbFile* ioFile,  // the file with content to add to store
444       nsIMdbThumb** acqThumb) override;  // acquire thumb for incremental import
445   // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
446   // then the import will be finished.
447   // } ----- end import/export methods -----
448 
449   // { ----- begin hinting methods -----
450   NS_IMETHOD
451   ShareAtomColumnsHint(       // advise re shared column content atomizing
452       nsIMdbEnv* ev,          // context
453       mdb_scope inScopeHint,  // zero, or suggested shared namespace
454       const mdbColumnSet* inColumnSet)
455       override;  // cols desired tokenized together
456 
457   NS_IMETHOD
458   AvoidAtomColumnsHint(  // advise column with poor atomizing prospects
459       nsIMdbEnv* ev,     // context
460       const mdbColumnSet* inColumnSet)
461       override;  // cols with poor atomizing prospects
462   // } ----- end hinting methods -----
463 
464   // { ----- begin commit methods -----
465   NS_IMETHOD LargeCommit(  // save important changes if at all possible
466       nsIMdbEnv* ev,       // context
467       nsIMdbThumb** acqThumb) override;  // acquire thumb for incremental commit
468   // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
469   // then the commit will be finished.  Note the store is effectively write
470   // locked until commit is finished or canceled through the thumb instance.
471   // Until the commit is done, the store will report it has readonly status.
472 
473   NS_IMETHOD SessionCommit(  // save all changes if large commits delayed
474       nsIMdbEnv* ev,         // context
475       nsIMdbThumb** acqThumb) override;  // acquire thumb for incremental commit
476   // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
477   // then the commit will be finished.  Note the store is effectively write
478   // locked until commit is finished or canceled through the thumb instance.
479   // Until the commit is done, the store will report it has readonly status.
480 
481   NS_IMETHOD
482   CompressCommit(     // commit and make db physically smaller if possible
483       nsIMdbEnv* ev,  // context
484       nsIMdbThumb** acqThumb) override;  // acquire thumb for incremental commit
485   // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
486   // then the commit will be finished.  Note the store is effectively write
487   // locked until commit is finished or canceled through the thumb instance.
488   // Until the commit is done, the store will report it has readonly status.
489 
490   // } ----- end commit methods -----
491 
492   // } ===== end nsIMdbStore methods =====
493 
494  public:  // typesafe refcounting inlines calling inherited morkNode methods
SlotWeakPort(morkPort * me,morkEnv * ev,morkPort ** ioSlot)495   static void SlotWeakPort(morkPort* me, morkEnv* ev, morkPort** ioSlot) {
496     morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot);
497   }
498 
SlotStrongPort(morkPort * me,morkEnv * ev,morkPort ** ioSlot)499   static void SlotStrongPort(morkPort* me, morkEnv* ev, morkPort** ioSlot) {
500     morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot);
501   }
502   // public: // slots inherited from morkPort (meant to inform only)
503   // nsIMdbHeap*     mNode_Heap;
504   // mork_able    mNode_Mutable; // can this node be modified?
505   // mork_load    mNode_Load;    // is this node clean or dirty?
506   // mork_base    mNode_Base;    // must equal morkBase_kNode
507   // mork_derived mNode_Derived; // depends on specific node subclass
508   // mork_access  mNode_Access;  // kOpen, kClosing, kShut, or kDead
509   // mork_usage   mNode_Usage;   // kHeap, kStack, kMember, kGlobal, kNone
510   // mork_uses    mNode_Uses;    // refcount for strong refs
511   // mork_refs    mNode_Refs;    // refcount for strong refs + weak refs
512 
513   // morkEnv*        mPort_Env;      // non-refcounted env which created port
514   // morkFactory*    mPort_Factory;  // weak ref to suite factory
515   // nsIMdbHeap*     mPort_Heap;     // heap in which this port allocs objects
516 
517  public:  // state is public because the entire Mork system is private
518   // mStore_OidAtomSpace might be unnecessary; I don't remember why I wanted it.
519   morkAtomSpace* mStore_OidAtomSpace;       // ground atom space for oids
520   morkAtomSpace* mStore_GroundAtomSpace;    // ground atom space for scopes
521   morkAtomSpace* mStore_GroundColumnSpace;  // ground column space for scopes
522 
523   nsIMdbFile* mStore_File;      // the file containing Mork text
524   morkStream* mStore_InStream;  // stream using file used by the builder
525   morkBuilder* mStore_Builder;  // to parse Mork text and build structures
526 
527   morkStream* mStore_OutStream;  // stream using file used by the writer
528 
529   morkRowSpaceMap mStore_RowSpaces;    // maps mork_scope -> morkSpace
530   morkAtomSpaceMap mStore_AtomSpaces;  // maps mork_scope -> morkSpace
531 
532   morkZone mStore_Zone;
533 
534   morkPool mStore_Pool;
535 
536   // we alloc a max size book atom to reuse space for atom map key searches:
537   // morkMaxBookAtom  mStore_BookAtom; // staging area for atom map searches
538 
539   morkFarBookAtom mStore_FarBookAtom;  // staging area for atom map searches
540 
541   // GroupIdentity should be one more than largest seen in a parsed db file:
542   mork_gid mStore_CommitGroupIdentity;  // transaction ID number
543 
544   // group positions are used to help compute PercentOfStoreWasted():
545   mork_pos mStore_FirstCommitGroupPos;   // start of first group
546   mork_pos mStore_SecondCommitGroupPos;  // start of second group
547   // If the first commit group is very near the start of the file (say less
548   // than 512 bytes), then we might assume the file started nearly empty and
549   // that most of the first group is not wasted.  In that case, the pos of
550   // the second commit group might make a better estimate of the start of
551   // transaction space that might represent wasted file space.  That's why
552   // we support fields for both first and second commit group positions.
553   //
554   // We assume that a zero in either group pos means that the slot has not
555   // yet been given a valid value, since the file will always start with a
556   // tag, and a commit group cannot actually start at position zero.
557   //
558   // Either or both the first or second commit group positions might be
559   // supplied by either morkWriter (while committing) or morkBuilder (while
560   // parsing), since either reading or writing the file might encounter the
561   // first transaction groups which came into existence either in the past
562   // or in the very recent present.
563 
564   mork_bool mStore_CanAutoAssignAtomIdentity;
565   mork_bool mStore_CanDirty;           // changes imply the store becomes dirty?
566   mork_u1 mStore_CanWriteIncremental;  // compress not required?
567   mork_u1 mStore_Pad;                  // for u4 alignment
568 
569   // mStore_CanDirty should be FALSE when parsing a file while building the
570   // content going into the store, because such data structure modifications
571   // are actuallly in sync with the file.  So content read from a file must
572   // be clean with respect to the file.  After a file is finished parsing,
573   // the mStore_CanDirty slot should become TRUE, so that any additional
574   // changes at runtime cause structures to be marked dirty with respect to
575   // the file which must later be updated with changes during a commit.
576   //
577   // It might also make sense to set mStore_CanDirty to FALSE while a commit
578   // is in progress, lest some internal transformations make more content
579   // appear dirty when it should not.  So anyone modifying content during a
580   // commit should think about the intended significance regarding dirty.
581 
582  public:  // more specific dirty methods for store:
SetStoreDirty()583   void SetStoreDirty() { this->SetNodeDirty(); }
SetStoreClean()584   void SetStoreClean() { this->SetNodeClean(); }
585 
IsStoreClean()586   mork_bool IsStoreClean() const { return this->IsNodeClean(); }
IsStoreDirty()587   mork_bool IsStoreDirty() const { return this->IsNodeDirty(); }
588 
589  public:  // setting dirty based on CanDirty:
MaybeDirtyStore()590   void MaybeDirtyStore() {
591     if (mStore_CanDirty) this->SetStoreDirty();
592   }
593 
594  public:  // space waste analysis
595   mork_percent PercentOfStoreWasted(morkEnv* ev);
596 
597  public:  // setting store and all subspaces canDirty:
598   void SetStoreAndAllSpacesCanDirty(morkEnv* ev, mork_bool inCanDirty);
599 
600  public:  // building an atom inside mStore_FarBookAtom from a char* string
601   morkFarBookAtom* StageAliasAsFarBookAtom(morkEnv* ev, const morkMid* inMid,
602                                            morkAtomSpace* ioSpace,
603                                            mork_cscode inForm);
604 
605   morkFarBookAtom* StageYarnAsFarBookAtom(morkEnv* ev, const mdbYarn* inYarn,
606                                           morkAtomSpace* ioSpace);
607 
608   morkFarBookAtom* StageStringAsFarBookAtom(morkEnv* ev, const char* inString,
609                                             mork_cscode inForm,
610                                             morkAtomSpace* ioSpace);
611 
612  public:  // determining whether incremental writing is a good use of time:
613   mork_bool DoPreferLargeOverCompressCommit(morkEnv* ev);
614   // true when mStore_CanWriteIncremental && store has file large enough
615 
616  public:  // lazy creation of members and nested row or atom spaces
617   morkAtomSpace* LazyGetOidAtomSpace(morkEnv* ev);
618   morkAtomSpace* LazyGetGroundAtomSpace(morkEnv* ev);
619   morkAtomSpace* LazyGetGroundColumnSpace(morkEnv* ev);
620 
621   morkStream* LazyGetInStream(morkEnv* ev);
622   morkBuilder* LazyGetBuilder(morkEnv* ev);
623   void ForgetBuilder(morkEnv* ev);
624 
625   morkStream* LazyGetOutStream(morkEnv* ev);
626 
627   morkRowSpace* LazyGetRowSpace(morkEnv* ev, mdb_scope inRowScope);
628   morkAtomSpace* LazyGetAtomSpace(morkEnv* ev, mdb_scope inAtomScope);
629 
630   // { ===== begin morkNode interface =====
631  public:  // morkNode virtual methods
632   virtual void CloseMorkNode(
633       morkEnv* ev) override;  // CloseStore() only if open
634 
635  public:  // morkStore construction & destruction
636   morkStore(morkEnv* ev, const morkUsage& inUsage,
637             nsIMdbHeap* ioNodeHeap,  // the heap (if any) for this node instance
638             morkFactory* inFactory,  // the factory for this
639             nsIMdbHeap* ioPortHeap   // the heap to hold all content in the port
640   );
641   void CloseStore(morkEnv* ev);  // called by CloseMorkNode();
642 
643  private:  // copying is not allowed
644   morkStore(const morkStore& other);
645   morkStore& operator=(const morkStore& other);
646   virtual ~morkStore();  // assert that CloseStore() executed earlier
647 
648  public:  // dynamic type identification
649   morkEnv* CanUseStore(nsIMdbEnv* mev, mork_bool inMutable,
650                        nsresult* outErr) const;
IsStore()651   mork_bool IsStore() const {
652     return IsNode() && mNode_Derived == morkDerived_kStore;
653   }
654   // } ===== end morkNode methods =====
655 
656  public:  // typing
657   static void NonStoreTypeError(morkEnv* ev);
658   static void NilStoreFileError(morkEnv* ev);
659   static void CannotAutoAssignAtomIdentityError(morkEnv* ev);
660 
661  public:  //  store utilities
662   morkAtom* YarnToAtom(morkEnv* ev, const mdbYarn* inYarn,
663                        bool createIfMissing = true);
664   morkAtom* AddAlias(morkEnv* ev, const morkMid& inMid, mork_cscode inForm);
665 
666  public:  // other store methods
667   void RenumberAllCollectableContent(morkEnv* ev);
668 
669   nsIMdbStore* AcquireStoreHandle(morkEnv* ev);  // mObject_Handle
670 
StorePool()671   morkPool* StorePool() { return &mStore_Pool; }
672 
673   mork_bool OpenStoreFile(morkEnv* ev,  // return value equals ev->Good()
674                           mork_bool inFrozen,
675                           // const char* inFilePath,
676                           nsIMdbFile* ioFile,  // db abstract file interface
677                           const mdbOpenPolicy* inOpenPolicy);
678 
679   mork_bool CreateStoreFile(morkEnv* ev,  // return value equals ev->Good()
680                                           // const char* inFilePath,
681                             nsIMdbFile* ioFile,  // db abstract file interface
682                             const mdbOpenPolicy* inOpenPolicy);
683 
684   morkAtom* CopyAtom(morkEnv* ev, const morkAtom* inAtom);
685   // copy inAtom (from some other store) over to this store
686 
687   mork_token CopyToken(morkEnv* ev, mdb_token inToken, morkStore* inStore);
688   // copy inToken from inStore over to this store
689 
690   mork_token BufToToken(morkEnv* ev, const morkBuf* inBuf);
691   mork_token StringToToken(morkEnv* ev, const char* inTokenName);
692   mork_token QueryToken(morkEnv* ev, const char* inTokenName);
693   void TokenToString(morkEnv* ev, mdb_token inToken, mdbYarn* outTokenName);
694 
695   mork_bool MidToOid(morkEnv* ev, const morkMid& inMid, mdbOid* outOid);
696   mork_bool OidToYarn(morkEnv* ev, const mdbOid& inOid, mdbYarn* outYarn);
697   mork_bool MidToYarn(morkEnv* ev, const morkMid& inMid, mdbYarn* outYarn);
698 
699   morkBookAtom* MidToAtom(morkEnv* ev, const morkMid& inMid);
700   morkRow* MidToRow(morkEnv* ev, const morkMid& inMid);
701   morkTable* MidToTable(morkEnv* ev, const morkMid& inMid);
702 
703   morkRow* OidToRow(morkEnv* ev, const mdbOid* inOid);
704   // OidToRow() finds old row with oid, or makes new one if not found.
705 
706   morkTable* OidToTable(morkEnv* ev, const mdbOid* inOid,
707                         const mdbOid* inOptionalMetaRowOid);
708   // OidToTable() finds old table with oid, or makes new one if not found.
709 
710   static void SmallTokenToOneByteYarn(morkEnv* ev, mdb_token inToken,
711                                       mdbYarn* outYarn);
712 
713   mork_bool HasTableKind(morkEnv* ev, mdb_scope inRowScope,
714                          mdb_kind inTableKind, mdb_count* outTableCount);
715 
716   morkTable* GetTableKind(morkEnv* ev, mdb_scope inRowScope,
717                           mdb_kind inTableKind, mdb_count* outTableCount,
718                           mdb_bool* outMustBeUnique);
719 
720   morkRow* FindRow(morkEnv* ev, mdb_scope inScope, mdb_column inColumn,
721                    const mdbYarn* inTargetCellValue);
722 
723   morkRow* GetRow(morkEnv* ev, const mdbOid* inOid);
724   morkTable* GetTable(morkEnv* ev, const mdbOid* inOid);
725 
726   morkTable* NewTable(morkEnv* ev, mdb_scope inRowScope, mdb_kind inTableKind,
727                       mdb_bool inMustBeUnique,
728                       const mdbOid* inOptionalMetaRowOid);
729 
730   morkPortTableCursor* GetPortTableCursor(morkEnv* ev, mdb_scope inRowScope,
731                                           mdb_kind inTableKind);
732 
733   morkRow* NewRowWithOid(morkEnv* ev, const mdbOid* inOid);
734   morkRow* NewRow(morkEnv* ev, mdb_scope inRowScope);
735 
736   morkThumb* MakeCompressCommitThumb(morkEnv* ev, mork_bool inDoCollect);
737 
738  public:  // commit related methods
739   mork_bool MarkAllStoreContentDirty(morkEnv* ev);
740   // MarkAllStoreContentDirty() visits every object in the store and marks
741   // them dirty, including every table, row, cell, and atom.  The return
742   // equals ev->Good(), to show whether any error happened.  This method is
743   // intended for use in the beginning of a "compress commit" which writes
744   // all store content, whether dirty or not.  We dirty everything first so
745   // that later iterations over content can mark things clean as they are
746   // written, and organize the process of serialization so that objects are
747   // written only at need (because of being dirty).
748 
749  public:  // typesafe refcounting inlines calling inherited morkNode methods
SlotWeakStore(morkStore * me,morkEnv * ev,morkStore ** ioSlot)750   static void SlotWeakStore(morkStore* me, morkEnv* ev, morkStore** ioSlot) {
751     morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot);
752   }
753 
SlotStrongStore(morkStore * me,morkEnv * ev,morkStore ** ioSlot)754   static void SlotStrongStore(morkStore* me, morkEnv* ev, morkStore** ioSlot) {
755     morkStore* store = *ioSlot;
756     if (me != store) {
757       if (store) {
758         // what if this nulls out the ev and causes asserts?
759         // can we move this after the CutStrongRef()?
760         *ioSlot = 0;
761         store->Release();
762       }
763       if (me && me->AddRef()) *ioSlot = me;
764     }
765   }
766 };
767 
768 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
769 
770 #endif /* _MORKSTORE_ */
771