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