Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _MORKSTORE_
#define _MORKSTORE_ 1
#ifndef _MORK_
# include "mork.h"
#endif
#ifndef _MORKOBJECT_
# include "morkObject.h"
#endif
#ifndef _MORKNODEMAP_
# include "morkNodeMap.h"
#endif
#ifndef _MORKPOOL_
# include "morkPool.h"
#endif
#ifndef _MORKZONE_
# include "morkZone.h"
#endif
#ifndef _MORKATOM_
# include "morkAtom.h"
#endif
#ifndef _MORKROWSPACE_
# include "morkRowSpace.h"
#endif
#ifndef _MORKATOMSPACE_
# include "morkAtomSpace.h"
#endif
// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
#define morkDerived_kPort /*i*/ 0x7054 /* ascii 'pT' */
#define morkDerived_kStore /*i*/ 0x7354 /* ascii 'sT' */
/*| kGroundColumnSpace: we use the 'column space' as the default scope
**| for grounding column name IDs, and this is also the default scope for
**| all other explicitly tokenized strings.
|*/
#define morkStore_kGroundColumnSpace 'c' /* for mStore_GroundColumnSpace*/
#define morkStore_kColumnSpaceScope ((mork_scope)'c') /*kGroundColumnSpace*/
#define morkStore_kValueSpaceScope ((mork_scope)'v')
#define morkStore_kStreamBufSize (8 * 1024) /* okay buffer size */
#define morkStore_kReservedColumnCount 0x20 /* for well-known columns */
#define morkStore_kNoneToken ((mork_token)'n')
#define morkStore_kFormColumn ((mork_column)'f')
#define morkStore_kAtomScopeColumn ((mork_column)'a')
#define morkStore_kRowScopeColumn ((mork_column)'r')
#define morkStore_kMetaScope ((mork_scope)'m')
#define morkStore_kKindColumn ((mork_column)'k')
#define morkStore_kStatusColumn ((mork_column)'s')
/*| morkStore:
|*/
class morkStore : public morkObject, public nsIMdbStore {
public: // state is public because the entire Mork system is private
NS_DECL_ISUPPORTS_INHERITED
morkEnv* mPort_Env; // non-refcounted env which created port
morkFactory* mPort_Factory; // weak ref to suite factory
nsIMdbHeap* mPort_Heap; // heap in which this port allocs objects
// { ===== begin morkNode interface =====
public: // morkNode virtual methods
void ClosePort(morkEnv* ev); // called by CloseMorkNode();
public: // dynamic type identification
mork_bool IsPort() const {
return IsNode() && mNode_Derived == morkDerived_kPort;
}
// } ===== end morkNode methods =====
public: // other port methods
// { ----- begin attribute methods -----
// NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly);
// same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port.
// } ----- end attribute methods -----
// { ----- begin factory methods -----
// NS_IMETHOD GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory);
// } ----- end factory methods -----
// { ----- begin ref counting for well-behaved cyclic graphs -----
NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev, // weak refs
mdb_count* outCount) override;
NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev, // strong refs
mdb_count* outCount) override;
NS_IMETHOD AddWeakRef(nsIMdbEnv* ev) override;
#ifndef _MSC_VER
// The first declaration of AddStrongRef is to suppress
// -Werror,-Woverloaded-virtual.
NS_IMETHOD_(mork_uses) AddStrongRef(morkEnv* ev) override;
#endif
NS_IMETHOD_(mork_uses) AddStrongRef(nsIMdbEnv* ev) override;
NS_IMETHOD CutWeakRef(nsIMdbEnv* ev) override;
#ifndef _MSC_VER
// The first declaration of CutStrongRef is to suppress
// -Werror,-Woverloaded-virtual.
NS_IMETHOD_(mork_uses) CutStrongRef(morkEnv* ev) override;
#endif
NS_IMETHOD CutStrongRef(nsIMdbEnv* ev) override;
NS_IMETHOD CloseMdbObject(
nsIMdbEnv* ev) override; // called at strong refs zero
NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen) override;
// } ----- end ref counting -----
// } ===== end nsIMdbObject methods =====
// { ===== begin nsIMdbPort methods =====
// { ----- begin attribute methods -----
NS_IMETHOD GetIsPortReadonly(nsIMdbEnv* ev, mdb_bool* outBool) override;
NS_IMETHOD GetIsStore(nsIMdbEnv* ev, mdb_bool* outBool) override;
NS_IMETHOD GetIsStoreAndDirty(nsIMdbEnv* ev, mdb_bool* outBool) override;
NS_IMETHOD GetUsagePolicy(nsIMdbEnv* ev,
mdbUsagePolicy* ioUsagePolicy) override;
NS_IMETHOD SetUsagePolicy(nsIMdbEnv* ev,
const mdbUsagePolicy* inUsagePolicy) override;
// } ----- end attribute methods -----
// { ----- begin memory policy methods -----
NS_IMETHOD IdleMemoryPurge( // do memory management already scheduled
nsIMdbEnv* ev, // context
mdb_size* outEstimatedBytesFreed)
override; // approximate bytes actually freed
NS_IMETHOD SessionMemoryPurge( // request specific footprint decrease
nsIMdbEnv* ev, // context
mdb_size inDesiredBytesFreed, // approximate number of bytes wanted
mdb_size* outEstimatedBytesFreed)
override; // approximate bytes actually freed
NS_IMETHOD PanicMemoryPurge( // desperately free all possible memory
nsIMdbEnv* ev, // context
mdb_size* outEstimatedBytesFreed)
override; // approximate bytes actually freed
// } ----- end memory policy methods -----
// { ----- begin filepath methods -----
NS_IMETHOD GetPortFilePath(
nsIMdbEnv* ev, // context
mdbYarn* outFilePath, // name of file holding port content
mdbYarn* outFormatVersion) override; // file format description
NS_IMETHOD GetPortFile(
nsIMdbEnv* ev, // context
nsIMdbFile** acqFile) override; // acquire file used by port or store
// } ----- end filepath methods -----
// { ----- begin export methods -----
NS_IMETHOD BestExportFormat( // determine preferred export format
nsIMdbEnv* ev, // context
mdbYarn* outFormatVersion) override; // file format description
NS_IMETHOD
CanExportToFormat( // can export content in given specific format?
nsIMdbEnv* ev, // context
const char* inFormatVersion, // file format description
mdb_bool* outCanExport) override; // whether ExportSource() might succeed
NS_IMETHOD ExportToFormat( // export content in given specific format
nsIMdbEnv* ev, // context
// const char* inFilePath, // the file to receive exported content
nsIMdbFile* ioFile, // destination abstract file interface
const char* inFormatVersion, // file format description
nsIMdbThumb** acqThumb) override; // acquire thumb for incremental export
// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
// then the export will be finished.
// } ----- end export methods -----
// { ----- begin token methods -----
NS_IMETHOD TokenToString( // return a string name for an integer token
nsIMdbEnv* ev, // context
mdb_token inToken, // token for inTokenName inside this port
mdbYarn* outTokenName) override; // the type of table to access
NS_IMETHOD StringToToken( // return an integer token for scope name
nsIMdbEnv* ev, // context
const char* inTokenName, // Latin1 string to tokenize if possible
mdb_token* outToken) override; // token for inTokenName inside this port
// String token zero is never used and never supported. If the port
// is a mutable store, then StringToToken() to create a new
// association of inTokenName with a new integer token if possible.
// But a readonly port will return zero for an unknown scope name.
NS_IMETHOD QueryToken( // like StringToToken(), but without adding
nsIMdbEnv* ev, // context
const char* inTokenName, // Latin1 string to tokenize if possible
mdb_token* outToken) override; // token for inTokenName inside this port
// QueryToken() will return a string token if one already exists,
// but unlike StringToToken(), will not assign a new token if not
// already in use.
// } ----- end token methods -----
// { ----- begin row methods -----
NS_IMETHOD HasRow( // contains a row with the specified oid?
nsIMdbEnv* ev, // context
const mdbOid* inOid, // hypothetical row oid
mdb_bool* outHasRow) override; // whether GetRow() might succeed
NS_IMETHOD GetRowRefCount( // get number of tables that contain a row
nsIMdbEnv* ev, // context
const mdbOid* inOid, // hypothetical row oid
mdb_count* outRefCount) override; // number of tables containing inRowKey
NS_IMETHOD GetRow( // access one row with specific oid
nsIMdbEnv* ev, // context
const mdbOid* inOid, // hypothetical row oid
nsIMdbRow** acqRow) override; // acquire specific row (or null)
NS_IMETHOD FindRow(
nsIMdbEnv* ev, // search for row with matching cell
mdb_scope inRowScope, // row scope for row ids
mdb_column inColumn, // the column to search (and maintain an index)
const mdbYarn* inTargetCellValue, // cell value for which to search
mdbOid* outRowOid, // out row oid on match (or {0,-1} for no match)
nsIMdbRow** acqRow)
override; // acquire matching row (or nil for no match)
// can be null if you only want the oid
// FindRow() searches for one row that has a cell in column inColumn with
// a contained value with the same form (i.e. charset) and is byte-wise
// identical to the blob described by yarn inTargetCellValue. Both content
// and form of the yarn must be an exact match to find a matching row.
//
// (In other words, both a yarn's blob bytes and form are significant. The
// form is not expected to vary in columns used for identity anyway. This
// is intended to make the cost of FindRow() cheaper for MDB implementors,
// since any cell value atomization performed internally must necessarily
// make yarn form significant in order to avoid data loss in atomization.)
//
// FindRow() can lazily create an index on attribute inColumn for all rows
// with that attribute in row space scope inRowScope, so that subsequent
// calls to FindRow() will perform faster. Such an index might or might
// not be persistent (but this seems desirable if it is cheap to do so).
// Note that lazy index creation in readonly DBs is not very feasible.
//
// This FindRow() interface assumes that attribute inColumn is effectively
// an alternative means of unique identification for a row in a rowspace,
// so correct behavior is only guaranteed when no duplicates for this col
// appear in the given set of rows. (If more than one row has the same cell
// value in this column, no more than one will be found; and cutting one of
// two duplicate rows can cause the index to assume no other such row lives
// in the row space, so future calls return nil for negative search results
// even though some duplicate row might still live within the rowspace.)
//
// In other words, the FindRow() implementation is allowed to assume simple
// hash tables mapping unique column keys to associated row values will be
// sufficient, where any duplication is not recorded because only one copy
// of a given key need be remembered. Implementors are not required to sort
// all rows by the specified column.
// } ----- end row methods -----
// { ----- begin table methods -----
NS_IMETHOD HasTable( // supports a table with the specified oid?
nsIMdbEnv* ev, // context
const mdbOid* inOid, // hypothetical table oid
mdb_bool* outHasTable) override; // whether GetTable() might succeed
NS_IMETHOD GetTable( // access one table with specific oid
nsIMdbEnv* ev, // context
const mdbOid* inOid, // hypothetical table oid
nsIMdbTable** acqTable) override; // acquire specific table (or null)
NS_IMETHOD HasTableKind( // supports a table of the specified type?
nsIMdbEnv* ev, // context
mdb_scope inRowScope, // rid scope for row ids
mdb_kind inTableKind, // the type of table to access
mdb_count* outTableCount, // current number of such tables
mdb_bool* outSupportsTable)
override; // whether GetTableKind() might succeed
NS_IMETHOD GetTableKind( // access one (random) table of specific type
nsIMdbEnv* ev, // context
mdb_scope inRowScope, // row scope for row ids
mdb_kind inTableKind, // the type of table to access
mdb_count* outTableCount, // current number of such tables
mdb_bool* outMustBeUnique, // whether port can hold only one of these
nsIMdbTable** acqTable) override; // acquire scoped collection of rows
NS_IMETHOD
GetPortTableCursor( // get cursor for all tables of specific type
nsIMdbEnv* ev, // context
mdb_scope inRowScope, // row scope for row ids
mdb_kind inTableKind, // the type of table to access
nsIMdbPortTableCursor** acqCursor)
override; // all such tables in the port
// } ----- end table methods -----
// { ----- begin commit methods -----
NS_IMETHOD ShouldCompress( // store wastes at least inPercentWaste?
nsIMdbEnv* ev, // context
mdb_percent inPercentWaste, // 0..100 percent file size waste threshold
mdb_percent* outActualWaste, // 0..100 percent of file actually wasted
mdb_bool* outShould)
override; // true when about inPercentWaste% is wasted
// ShouldCompress() returns true if the store can determine that the file
// will shrink by an estimated percentage of inPercentWaste% (or more) if
// CompressCommit() is called, because that percentage of the file seems
// to be recoverable free space. The granularity is only in terms of
// percentage points, and any value over 100 is considered equal to 100.
//
// If a store only has an approximate idea how much space might be saved
// during a compress, then a best guess should be made. For example, the
// Mork implementation might keep track of how much file space began with
// text content before the first updating transaction, and then consider
// all content following the start of the first transaction as potentially
// wasted space if it is all updates and not just new content. (This is
// a safe assumption in the sense that behavior will stabilize on a low
// estimate of wastage after a commit removes all transaction updates.)
//
// Some db formats might attempt to keep a very accurate reckoning of free
// space size, so a very accurate determination can be made. But other db
// formats might have difficulty determining size of free space, and might
// require some lengthy calculation to answer. This is the reason for
// passing in the percentage threshold of interest, so that such lengthy
// computations can terminate early as soon as at least inPercentWaste is
// found, so that the entire file need not be groveled when unnecessary.
// However, we hope implementations will always favor fast but imprecise
// heuristic answers instead of extremely slow but very precise answers.
//
// If the outActualWaste parameter is non-nil, it will be used to return
// the actual estimated space wasted as a percentage of file size. (This
// parameter is provided so callers need not call repeatedly with altered
// inPercentWaste values to isolate the actual wastage figure.) Note the
// actual wastage figure returned can exactly equal inPercentWaste even
// when this grossly underestimates the real figure involved, if the db
// finds it very expensive to determine the extent of wastage after it is
// known to at least exceed inPercentWaste. Note we expect that whenever
// outShould returns true, that outActualWaste returns >= inPercentWaste.
//
// The effect of different inPercentWaste values is not very uniform over
// the permitted range. For example, 50 represents 50% wastage, or a file
// that is about double what it should be ideally. But 99 represents 99%
// wastage, or a file that is about ninety-nine times as big as it should
// be ideally. In the smaller direction, 25 represents 25% wastage, or
// a file that is only 33% larger than it should be ideally.
//
// Callers can determine what policy they want to use for considering when
// a file holds too much wasted space, and express this as a percentage
// of total file size to pass as in the inPercentWaste parameter. A zero
// likely returns always trivially true, and 100 always trivially false.
// The great majority of callers are expected to use values from 25 to 75,
// since most plausible thresholds for compressing might fall between the
// extremes of 133% of ideal size and 400% of ideal size. (Presumably the
// larger a file gets, the more important the percentage waste involved, so
// a sliding scale for compress thresholds might use smaller numbers for
// much bigger file sizes.)
// } ----- end commit methods -----
// } ===== end nsIMdbPort methods =====
// { ===== begin nsIMdbStore methods =====
// { ----- begin table methods -----
NS_IMETHOD NewTable( // make one new table of specific type
nsIMdbEnv* ev, // context
mdb_scope inRowScope, // row scope for row ids
mdb_kind inTableKind, // the type of table to access
mdb_bool inMustBeUnique, // whether store can hold only one of these
const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
nsIMdbTable** acqTable) override; // acquire scoped collection of rows
NS_IMETHOD NewTableWithOid( // make one new table of specific type
nsIMdbEnv* ev, // context
const mdbOid* inOid, // caller assigned oid
mdb_kind inTableKind, // the type of table to access
mdb_bool inMustBeUnique, // whether store can hold only one of these
const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
nsIMdbTable** acqTable) override; // acquire scoped collection of rows
// } ----- end table methods -----
// { ----- begin row scope methods -----
NS_IMETHOD RowScopeHasAssignedIds(
nsIMdbEnv* ev,
mdb_scope inRowScope, // row scope for row ids
mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
mdb_bool* outStoreAssigned)
override; // nonzero if store db assigned specified
NS_IMETHOD SetCallerAssignedIds(
nsIMdbEnv* ev,
mdb_scope inRowScope, // row scope for row ids
mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
mdb_bool* outStoreAssigned)
override; // nonzero if store db assigned specified
NS_IMETHOD SetStoreAssignedIds(
nsIMdbEnv* ev,
mdb_scope inRowScope, // row scope for row ids
mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
mdb_bool* outStoreAssigned)
override; // nonzero if store db assigned specified
// } ----- end row scope methods -----
// { ----- begin row methods -----
NS_IMETHOD NewRowWithOid(nsIMdbEnv* ev, // new row w/ caller assigned oid
const mdbOid* inOid, // caller assigned oid
nsIMdbRow** acqRow) override; // create new row
NS_IMETHOD NewRow(nsIMdbEnv* ev, // new row with db assigned oid
mdb_scope inRowScope, // row scope for row ids
nsIMdbRow** acqRow) override; // create new row
// Note this row must be added to some table or cell child before the
// store is closed in order to make this row persist across sessions.
// } ----- end row methods -----
// { ----- begin import/export methods -----
NS_IMETHOD ImportContent( // import content from port
nsIMdbEnv* ev, // context
mdb_scope inRowScope, // scope for rows (or zero for all?)
nsIMdbPort* ioPort, // the port with content to add to store
nsIMdbThumb** acqThumb) override; // acquire thumb for incremental import
// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
// then the import will be finished.
NS_IMETHOD ImportFile( // import content from port
nsIMdbEnv* ev, // context
nsIMdbFile* ioFile, // the file with content to add to store
nsIMdbThumb** acqThumb) override; // acquire thumb for incremental import
// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
// then the import will be finished.
// } ----- end import/export methods -----
// { ----- begin hinting methods -----
NS_IMETHOD
ShareAtomColumnsHint( // advise re shared column content atomizing
nsIMdbEnv* ev, // context
mdb_scope inScopeHint, // zero, or suggested shared namespace
const mdbColumnSet* inColumnSet)
override; // cols desired tokenized together
NS_IMETHOD
AvoidAtomColumnsHint( // advise column with poor atomizing prospects
nsIMdbEnv* ev, // context
const mdbColumnSet* inColumnSet)
override; // cols with poor atomizing prospects
// } ----- end hinting methods -----
// { ----- begin commit methods -----
NS_IMETHOD LargeCommit( // save important changes if at all possible
nsIMdbEnv* ev, // context
nsIMdbThumb** acqThumb) override; // acquire thumb for incremental commit
// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
// then the commit will be finished. Note the store is effectively write
// locked until commit is finished or canceled through the thumb instance.
// Until the commit is done, the store will report it has readonly status.
NS_IMETHOD SessionCommit( // save all changes if large commits delayed
nsIMdbEnv* ev, // context
nsIMdbThumb** acqThumb) override; // acquire thumb for incremental commit
// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
// then the commit will be finished. Note the store is effectively write
// locked until commit is finished or canceled through the thumb instance.
// Until the commit is done, the store will report it has readonly status.
NS_IMETHOD
CompressCommit( // commit and make db physically smaller if possible
nsIMdbEnv* ev, // context
nsIMdbThumb** acqThumb) override; // acquire thumb for incremental commit
// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
// then the commit will be finished. Note the store is effectively write
// locked until commit is finished or canceled through the thumb instance.
// Until the commit is done, the store will report it has readonly status.
// } ----- end commit methods -----
// } ===== end nsIMdbStore methods =====
public: // typesafe refcounting inlines calling inherited morkNode methods
static void SlotWeakPort(morkPort* me, morkEnv* ev, morkPort** ioSlot) {
morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot);
}
static void SlotStrongPort(morkPort* me, morkEnv* ev, morkPort** ioSlot) {
morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot);
}
// public: // slots inherited from morkPort (meant to inform only)
// nsIMdbHeap* mNode_Heap;
// mork_able mNode_Mutable; // can this node be modified?
// mork_load mNode_Load; // is this node clean or dirty?
// mork_base mNode_Base; // must equal morkBase_kNode
// mork_derived mNode_Derived; // depends on specific node subclass
// mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
// mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
// mork_uses mNode_Uses; // refcount for strong refs
// mork_refs mNode_Refs; // refcount for strong refs + weak refs
// morkEnv* mPort_Env; // non-refcounted env which created port
// morkFactory* mPort_Factory; // weak ref to suite factory
// nsIMdbHeap* mPort_Heap; // heap in which this port allocs objects
public: // state is public because the entire Mork system is private
// mStore_OidAtomSpace might be unnecessary; I don't remember why I wanted it.
morkAtomSpace* mStore_OidAtomSpace; // ground atom space for oids
morkAtomSpace* mStore_GroundAtomSpace; // ground atom space for scopes
morkAtomSpace* mStore_GroundColumnSpace; // ground column space for scopes
nsIMdbFile* mStore_File; // the file containing Mork text
morkStream* mStore_InStream; // stream using file used by the builder
morkBuilder* mStore_Builder; // to parse Mork text and build structures
morkStream* mStore_OutStream; // stream using file used by the writer
morkRowSpaceMap mStore_RowSpaces; // maps mork_scope -> morkSpace
morkAtomSpaceMap mStore_AtomSpaces; // maps mork_scope -> morkSpace
morkZone mStore_Zone;
morkPool mStore_Pool;
// we alloc a max size book atom to reuse space for atom map key searches:
// morkMaxBookAtom mStore_BookAtom; // staging area for atom map searches
morkFarBookAtom mStore_FarBookAtom; // staging area for atom map searches
// GroupIdentity should be one more than largest seen in a parsed db file:
mork_gid mStore_CommitGroupIdentity; // transaction ID number
// group positions are used to help compute PercentOfStoreWasted():
mork_pos mStore_FirstCommitGroupPos; // start of first group
mork_pos mStore_SecondCommitGroupPos; // start of second group
// If the first commit group is very near the start of the file (say less
// than 512 bytes), then we might assume the file started nearly empty and
// that most of the first group is not wasted. In that case, the pos of
// the second commit group might make a better estimate of the start of
// transaction space that might represent wasted file space. That's why
// we support fields for both first and second commit group positions.
//
// We assume that a zero in either group pos means that the slot has not
// yet been given a valid value, since the file will always start with a
// tag, and a commit group cannot actually start at position zero.
//
// Either or both the first or second commit group positions might be
// supplied by either morkWriter (while committing) or morkBuilder (while
// parsing), since either reading or writing the file might encounter the
// first transaction groups which came into existence either in the past
// or in the very recent present.
mork_bool mStore_CanAutoAssignAtomIdentity;
mork_bool mStore_CanDirty; // changes imply the store becomes dirty?
mork_u1 mStore_CanWriteIncremental; // compress not required?
mork_u1 mStore_Pad; // for u4 alignment
// mStore_CanDirty should be FALSE when parsing a file while building the
// content going into the store, because such data structure modifications
// are actuallly in sync with the file. So content read from a file must
// be clean with respect to the file. After a file is finished parsing,
// the mStore_CanDirty slot should become TRUE, so that any additional
// changes at runtime cause structures to be marked dirty with respect to
// the file which must later be updated with changes during a commit.
//
// It might also make sense to set mStore_CanDirty to FALSE while a commit
// is in progress, lest some internal transformations make more content
// appear dirty when it should not. So anyone modifying content during a
// commit should think about the intended significance regarding dirty.
public: // more specific dirty methods for store:
void SetStoreDirty() { this->SetNodeDirty(); }
void SetStoreClean() { this->SetNodeClean(); }
mork_bool IsStoreClean() const { return this->IsNodeClean(); }
mork_bool IsStoreDirty() const { return this->IsNodeDirty(); }
public: // setting dirty based on CanDirty:
void MaybeDirtyStore() {
if (mStore_CanDirty) this->SetStoreDirty();
}
public: // space waste analysis
mork_percent PercentOfStoreWasted(morkEnv* ev);
public: // setting store and all subspaces canDirty:
void SetStoreAndAllSpacesCanDirty(morkEnv* ev, mork_bool inCanDirty);
public: // building an atom inside mStore_FarBookAtom from a char* string
morkFarBookAtom* StageAliasAsFarBookAtom(morkEnv* ev, const morkMid* inMid,
morkAtomSpace* ioSpace,
mork_cscode inForm);
morkFarBookAtom* StageYarnAsFarBookAtom(morkEnv* ev, const mdbYarn* inYarn,
morkAtomSpace* ioSpace);
morkFarBookAtom* StageStringAsFarBookAtom(morkEnv* ev, const char* inString,
mork_cscode inForm,
morkAtomSpace* ioSpace);
public: // determining whether incremental writing is a good use of time:
mork_bool DoPreferLargeOverCompressCommit(morkEnv* ev);
// true when mStore_CanWriteIncremental && store has file large enough
public: // lazy creation of members and nested row or atom spaces
morkAtomSpace* LazyGetOidAtomSpace(morkEnv* ev);
morkAtomSpace* LazyGetGroundAtomSpace(morkEnv* ev);
morkAtomSpace* LazyGetGroundColumnSpace(morkEnv* ev);
morkStream* LazyGetInStream(morkEnv* ev);
morkBuilder* LazyGetBuilder(morkEnv* ev);
void ForgetBuilder(morkEnv* ev);
morkStream* LazyGetOutStream(morkEnv* ev);
morkRowSpace* LazyGetRowSpace(morkEnv* ev, mdb_scope inRowScope);
morkAtomSpace* LazyGetAtomSpace(morkEnv* ev, mdb_scope inAtomScope);
// { ===== begin morkNode interface =====
public: // morkNode virtual methods
virtual void CloseMorkNode(
morkEnv* ev) override; // CloseStore() only if open
public: // morkStore construction & destruction
morkStore(morkEnv* ev, const morkUsage& inUsage,
nsIMdbHeap* ioNodeHeap, // the heap (if any) for this node instance
morkFactory* inFactory, // the factory for this
nsIMdbHeap* ioPortHeap // the heap to hold all content in the port
);
void CloseStore(morkEnv* ev); // called by CloseMorkNode();
private: // copying is not allowed
morkStore(const morkStore& other);
morkStore& operator=(const morkStore& other);
virtual ~morkStore(); // assert that CloseStore() executed earlier
public: // dynamic type identification
morkEnv* CanUseStore(nsIMdbEnv* mev, mork_bool inMutable,
nsresult* outErr) const;
mork_bool IsStore() const {
return IsNode() && mNode_Derived == morkDerived_kStore;
}
// } ===== end morkNode methods =====
public: // typing
static void NonStoreTypeError(morkEnv* ev);
static void NilStoreFileError(morkEnv* ev);
static void CannotAutoAssignAtomIdentityError(morkEnv* ev);
public: // store utilities
morkAtom* YarnToAtom(morkEnv* ev, const mdbYarn* inYarn,
bool createIfMissing = true);
morkAtom* AddAlias(morkEnv* ev, const morkMid& inMid, mork_cscode inForm);
public: // other store methods
void RenumberAllCollectableContent(morkEnv* ev);
nsIMdbStore* AcquireStoreHandle(morkEnv* ev); // mObject_Handle
morkPool* StorePool() { return &mStore_Pool; }
mork_bool OpenStoreFile(morkEnv* ev, // return value equals ev->Good()
mork_bool inFrozen,
// const char* inFilePath,
nsIMdbFile* ioFile, // db abstract file interface
const mdbOpenPolicy* inOpenPolicy);
mork_bool CreateStoreFile(morkEnv* ev, // return value equals ev->Good()
// const char* inFilePath,
nsIMdbFile* ioFile, // db abstract file interface
const mdbOpenPolicy* inOpenPolicy);
morkAtom* CopyAtom(morkEnv* ev, const morkAtom* inAtom);
// copy inAtom (from some other store) over to this store
mork_token CopyToken(morkEnv* ev, mdb_token inToken, morkStore* inStore);
// copy inToken from inStore over to this store
mork_token BufToToken(morkEnv* ev, const morkBuf* inBuf);
mork_token StringToToken(morkEnv* ev, const char* inTokenName);
mork_token QueryToken(morkEnv* ev, const char* inTokenName);
void TokenToString(morkEnv* ev, mdb_token inToken, mdbYarn* outTokenName);
mork_bool MidToOid(morkEnv* ev, const morkMid& inMid, mdbOid* outOid);
mork_bool OidToYarn(morkEnv* ev, const mdbOid& inOid, mdbYarn* outYarn);
mork_bool MidToYarn(morkEnv* ev, const morkMid& inMid, mdbYarn* outYarn);
morkBookAtom* MidToAtom(morkEnv* ev, const morkMid& inMid);
morkRow* MidToRow(morkEnv* ev, const morkMid& inMid);
morkTable* MidToTable(morkEnv* ev, const morkMid& inMid);
morkRow* OidToRow(morkEnv* ev, const mdbOid* inOid);
// OidToRow() finds old row with oid, or makes new one if not found.
morkTable* OidToTable(morkEnv* ev, const mdbOid* inOid,
const mdbOid* inOptionalMetaRowOid);
// OidToTable() finds old table with oid, or makes new one if not found.
static void SmallTokenToOneByteYarn(morkEnv* ev, mdb_token inToken,
mdbYarn* outYarn);
mork_bool HasTableKind(morkEnv* ev, mdb_scope inRowScope,
mdb_kind inTableKind, mdb_count* outTableCount);
morkTable* GetTableKind(morkEnv* ev, mdb_scope inRowScope,
mdb_kind inTableKind, mdb_count* outTableCount,
mdb_bool* outMustBeUnique);
morkRow* FindRow(morkEnv* ev, mdb_scope inScope, mdb_column inColumn,
const mdbYarn* inTargetCellValue);
morkRow* GetRow(morkEnv* ev, const mdbOid* inOid);
morkTable* GetTable(morkEnv* ev, const mdbOid* inOid);
morkTable* NewTable(morkEnv* ev, mdb_scope inRowScope, mdb_kind inTableKind,
mdb_bool inMustBeUnique,
const mdbOid* inOptionalMetaRowOid);
morkPortTableCursor* GetPortTableCursor(morkEnv* ev, mdb_scope inRowScope,
mdb_kind inTableKind);
morkRow* NewRowWithOid(morkEnv* ev, const mdbOid* inOid);
morkRow* NewRow(morkEnv* ev, mdb_scope inRowScope);
morkThumb* MakeCompressCommitThumb(morkEnv* ev, mork_bool inDoCollect);
public: // commit related methods
mork_bool MarkAllStoreContentDirty(morkEnv* ev);
// MarkAllStoreContentDirty() visits every object in the store and marks
// them dirty, including every table, row, cell, and atom. The return
// equals ev->Good(), to show whether any error happened. This method is
// intended for use in the beginning of a "compress commit" which writes
// all store content, whether dirty or not. We dirty everything first so
// that later iterations over content can mark things clean as they are
// written, and organize the process of serialization so that objects are
// written only at need (because of being dirty).
public: // typesafe refcounting inlines calling inherited morkNode methods
static void SlotWeakStore(morkStore* me, morkEnv* ev, morkStore** ioSlot) {
morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot);
}
static void SlotStrongStore(morkStore* me, morkEnv* ev, morkStore** ioSlot) {
morkStore* store = *ioSlot;
if (me != store) {
if (store) {
// what if this nulls out the ev and causes asserts?
// can we move this after the CutStrongRef()?
*ioSlot = 0;
store->Release();
}
if (me && me->AddRef()) *ioSlot = me;
}
}
};
// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
#endif /* _MORKSTORE_ */