/* -*- 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 _MDB_ # include "mdb.h" #endif #ifndef _MORK_ # include "mork.h" #endif #ifndef _MORKNODE_ # include "morkNode.h" #endif #ifndef _MORKMAP_ # include "morkMap.h" #endif #ifndef _MORKENV_ # include "morkEnv.h" #endif #ifndef _MORKTABLE_ # include "morkTable.h" #endif #ifndef _MORKSTORE_ # include "morkStore.h" #endif #ifndef _MORKROWSPACE_ # include "morkRowSpace.h" #endif #ifndef _MORKARRAY_ # include "morkArray.h" #endif #ifndef _MORKROW_ # include "morkRow.h" #endif #ifndef _MORKTABLEROWCURSOR_ # include "morkTableRowCursor.h" #endif #ifndef _MORKROWOBJECT_ # include "morkRowObject.h" #endif // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 // ````` ````` ````` ````` ````` // { ===== begin morkNode interface ===== /*public virtual*/ void morkTable::CloseMorkNode( morkEnv* ev) /*i*/ // CloseTable() only if open { if (this->IsOpenNode()) { morkObject::CloseMorkNode(ev); // give base class a chance. this->MarkClosing(); this->CloseTable(ev); this->MarkShut(); } } /*public virtual*/ morkTable::~morkTable() /*i*/ // assert CloseTable() executed earlier { CloseMorkNode(mMorkEnv); MORK_ASSERT(this->IsShutNode()); MORK_ASSERT(mTable_Store == 0); MORK_ASSERT(mTable_RowSpace == 0); } /*public non-poly*/ morkTable::morkTable( morkEnv* ev, /*i*/ const morkUsage& inUsage, nsIMdbHeap* ioHeap, morkStore* ioStore, nsIMdbHeap* ioSlotHeap, morkRowSpace* ioRowSpace, const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying mork_tid inTid, mork_kind inKind, mork_bool inMustBeUnique) : morkObject(ev, inUsage, ioHeap, (mork_color)inTid, (morkHandle*)0), mTable_Store(0), mTable_RowSpace(0), mTable_MetaRow(0) , mTable_RowMap(0) // , mTable_RowMap(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap, // morkTable_kStartRowMapSlotCount) , mTable_RowArray(ev, morkUsage::kMember, (nsIMdbHeap*)0, morkTable_kStartRowArraySize, ioSlotHeap) , mTable_ChangeList(), mTable_ChangesCount(0), mTable_ChangesMax(3) // any very small number greater than zero , mTable_Kind(inKind) , mTable_Flags(0), mTable_Priority(morkPriority_kLo) // NOT high priority , mTable_GcUses(0), mTable_Pad(0) { this->mLink_Next = 0; this->mLink_Prev = 0; if (ev->Good()) { if (ioStore && ioSlotHeap && ioRowSpace) { if (inKind) { if (inMustBeUnique) this->SetTableUnique(); mTable_Store = ioStore; mTable_RowSpace = ioRowSpace; if (inOptionalMetaRowOid) mTable_MetaRowOid = *inOptionalMetaRowOid; else { mTable_MetaRowOid.mOid_Scope = 0; mTable_MetaRowOid.mOid_Id = morkRow_kMinusOneRid; } if (ev->Good()) { if (this->MaybeDirtySpaceStoreAndTable()) this->SetTableRewrite(); // everything is dirty mNode_Derived = morkDerived_kTable; } this->MaybeDirtySpaceStoreAndTable(); // new table might dirty store } else ioRowSpace->ZeroKindError(ev); } else ev->NilPointerError(); } } NS_IMPL_ISUPPORTS_INHERITED(morkTable, morkObject, nsIMdbTable) /*public non-poly*/ void morkTable::CloseTable( morkEnv* ev) /*i*/ // called by CloseMorkNode(); { if (this->IsNode()) { morkRowMap::SlotStrongRowMap((morkRowMap*)0, ev, &mTable_RowMap); // mTable_RowMap.CloseMorkNode(ev); mTable_RowArray.CloseMorkNode(ev); mTable_Store = 0; mTable_RowSpace = 0; this->MarkShut(); } else this->NonNodeError(ev); } // } ===== end morkNode methods ===== // ````` ````` ````` ````` ````` // { ===== begin nsIMdbCollection methods ===== // { ----- begin attribute methods ----- NS_IMETHODIMP morkTable::GetSeed(nsIMdbEnv* mev, mdb_seed* outSeed) // member change count { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { *outSeed = mTable_RowArray.mArray_Seed; outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::GetCount(nsIMdbEnv* mev, mdb_count* outCount) // member count { NS_ENSURE_ARG_POINTER(outCount); *outCount = mTable_RowArray.mArray_Fill; return NS_OK; } NS_IMETHODIMP morkTable::GetPort(nsIMdbEnv* mev, nsIMdbPort** acqPort) // collection container { (void)morkEnv::FromMdbEnv(mev); NS_ENSURE_ARG_POINTER(acqPort); *acqPort = mTable_Store; return NS_OK; } // } ----- end attribute methods ----- // { ----- begin cursor methods ----- NS_IMETHODIMP morkTable::GetCursor( // make a cursor starting iter at inMemberPos nsIMdbEnv* mev, // context mdb_pos inMemberPos, // zero-based ordinal pos of member in collection nsIMdbCursor** acqCursor) // acquire new cursor instance { return this->GetTableRowCursor(mev, inMemberPos, (nsIMdbTableRowCursor**)acqCursor); } // } ----- end cursor methods ----- // { ----- begin ID methods ----- NS_IMETHODIMP morkTable::GetOid(nsIMdbEnv* mev, mdbOid* outOid) // read object identity { morkEnv* ev = morkEnv::FromMdbEnv(mev); GetTableOid(ev, outOid); return NS_OK; } NS_IMETHODIMP morkTable::BecomeContent(nsIMdbEnv* mev, const mdbOid* inOid) // exchange content { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; // remember table->MaybeDirtySpaceStoreAndTable(); } // } ----- end ID methods ----- // { ----- begin activity dropping methods ----- NS_IMETHODIMP morkTable::DropActivity( // tell collection usage no longer expected nsIMdbEnv* mev) { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } // } ----- end activity dropping methods ----- // } ===== end nsIMdbCollection methods ===== // { ===== begin nsIMdbTable methods ===== // { ----- begin attribute methods ----- NS_IMETHODIMP morkTable::SetTablePriority(nsIMdbEnv* mev, mdb_priority inPrio) { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { if (inPrio > morkPriority_kMax) inPrio = morkPriority_kMax; mTable_Priority = inPrio; outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::GetTablePriority(nsIMdbEnv* mev, mdb_priority* outPrio) { nsresult outErr = NS_OK; mork_priority prio = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { prio = mTable_Priority; if (prio > morkPriority_kMax) { prio = morkPriority_kMax; mTable_Priority = prio; } outErr = ev->AsErr(); } if (outPrio) *outPrio = prio; return outErr; } NS_IMETHODIMP morkTable::GetTableBeVerbose(nsIMdbEnv* mev, mdb_bool* outBeVerbose) { NS_ENSURE_ARG_POINTER(outBeVerbose); *outBeVerbose = IsTableVerbose(); return NS_OK; } NS_IMETHODIMP morkTable::SetTableBeVerbose(nsIMdbEnv* mev, mdb_bool inBeVerbose) { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { if (inBeVerbose) SetTableVerbose(); else ClearTableVerbose(); outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::GetTableIsUnique(nsIMdbEnv* mev, mdb_bool* outIsUnique) { NS_ENSURE_ARG_POINTER(outIsUnique); *outIsUnique = IsTableUnique(); return NS_OK; } NS_IMETHODIMP morkTable::GetTableKind(nsIMdbEnv* mev, mdb_kind* outTableKind) { NS_ENSURE_ARG_POINTER(outTableKind); *outTableKind = mTable_Kind; return NS_OK; } NS_IMETHODIMP morkTable::GetRowScope(nsIMdbEnv* mev, mdb_scope* outRowScope) { nsresult outErr = NS_OK; mdb_scope rowScope = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { if (mTable_RowSpace) rowScope = mTable_RowSpace->SpaceScope(); else NilRowSpaceError(ev); outErr = ev->AsErr(); } if (outRowScope) *outRowScope = rowScope; return outErr; } NS_IMETHODIMP morkTable::GetMetaRow( nsIMdbEnv* mev, const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying mdbOid* outOid, // output meta row oid, can be nil to suppress output nsIMdbRow** acqRow) // acquire table's unique singleton meta row // The purpose of a meta row is to support the persistent recording of // meta info about a table as cells put into the distinguished meta row. // Each table has exactly one meta row, which is not considered a member // of the collection of rows inside the table. The only way to tell // whether a row is a meta row is by the fact that it is returned by this // GetMetaRow() method from some table. Otherwise nothing distinguishes // a meta row from any other row. A meta row can be used anyplace that // any other row can be used, and can even be put into other tables (or // the same table) as a table member, if this is useful for some reason. // The first attempt to access a table's meta row using GetMetaRow() will // cause the meta row to be created if it did not already exist. When the // meta row is created, it will have the row oid that was previously // requested for this table's meta row; or if no oid was ever explicitly // specified for this meta row, then a unique oid will be generated in // the row scope named "metaScope" (so obviously MDB clients should not // manually allocate any row IDs from that special meta scope namespace). // The meta row oid can be specified either when the table is created, or // else the first time that GetMetaRow() is called, by passing a non-nil // pointer to an oid for parameter inOptionalMetaRowOid. The meta row's // actual oid is returned in outOid (if this is a non-nil pointer), and // it will be different from inOptionalMetaRowOid when the meta row was // already given a different oid earlier. { nsresult outErr = NS_OK; nsIMdbRow* outRow = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { morkRow* row = GetMetaRow(ev, inOptionalMetaRowOid); if (row && ev->Good()) { if (outOid) *outOid = row->mRow_Oid; outRow = row->AcquireRowHandle(ev, mTable_Store); } outErr = ev->AsErr(); } if (acqRow) *acqRow = outRow; if (ev->Bad() && outOid) { outOid->mOid_Scope = 0; outOid->mOid_Id = morkRow_kMinusOneRid; } return outErr; } // } ----- end attribute methods ----- // { ----- begin cursor methods ----- NS_IMETHODIMP morkTable::GetTableRowCursor( // make a cursor, starting iteration at inRowPos nsIMdbEnv* mev, // context mdb_pos inRowPos, // zero-based ordinal position of row in table nsIMdbTableRowCursor** acqCursor) // acquire new cursor instance { nsresult outErr = NS_OK; nsIMdbTableRowCursor* outCursor = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { morkTableRowCursor* cursor = NewTableRowCursor(ev, inRowPos); if (cursor) { if (ev->Good()) { // cursor->mCursor_Seed = (mork_seed) inRowPos; outCursor = cursor; outCursor->AddRef(); } } outErr = ev->AsErr(); } if (acqCursor) *acqCursor = outCursor; return outErr; } // } ----- end row position methods ----- // { ----- begin row position methods ----- NS_IMETHODIMP morkTable::PosToOid( // get row member for a table position nsIMdbEnv* mev, // context mdb_pos inRowPos, // zero-based ordinal position of row in table mdbOid* outOid) // row oid at the specified position { nsresult outErr = NS_OK; mdbOid roid; roid.mOid_Scope = 0; roid.mOid_Id = (mork_id)-1; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { morkRow* row = SafeRowAt(ev, inRowPos); if (row) roid = row->mRow_Oid; outErr = ev->AsErr(); } if (outOid) *outOid = roid; return outErr; } NS_IMETHODIMP morkTable::OidToPos( // test for the table position of a row member nsIMdbEnv* mev, // context const mdbOid* inOid, // row to find in table mdb_pos* outPos) // zero-based ordinal position of row in table { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { mork_pos pos = ArrayHasOid(ev, inOid); if (outPos) *outPos = pos; outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::PosToRow( // get row member for a table position nsIMdbEnv* mev, // context mdb_pos inRowPos, // zero-based ordinal position of row in table nsIMdbRow** acqRow) // acquire row at table position inRowPos { nsresult outErr = NS_OK; nsIMdbRow* outRow = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { morkRow* row = SafeRowAt(ev, inRowPos); if (row && mTable_Store) outRow = row->AcquireRowHandle(ev, mTable_Store); outErr = ev->AsErr(); } if (acqRow) *acqRow = outRow; return outErr; } NS_IMETHODIMP morkTable::RowToPos( // test for the table position of a row member nsIMdbEnv* mev, // context nsIMdbRow* ioRow, // row to find in table mdb_pos* outPos) // zero-based ordinal position of row in table { nsresult outErr = NS_OK; mork_pos pos = -1; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { morkRowObject* row = (morkRowObject*)ioRow; pos = ArrayHasOid(ev, &row->mRowObject_Row->mRow_Oid); outErr = ev->AsErr(); } if (outPos) *outPos = pos; return outErr; } // Note that HasRow() performs the inverse oid->pos mapping // } ----- end row position methods ----- // { ----- begin oid set methods ----- NS_IMETHODIMP morkTable::AddOid( // make sure the row with inOid is a table member nsIMdbEnv* mev, // context const mdbOid* inOid) // row to ensure membership in table { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::HasOid( // test for the table position of a row member nsIMdbEnv* mev, // context const mdbOid* inOid, // row to find in table mdb_bool* outHasOid) // whether inOid is a member row { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { if (outHasOid) *outHasOid = MapHasOid(ev, inOid); outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::CutOid( // make sure the row with inOid is not a member nsIMdbEnv* mev, // context const mdbOid* inOid) // row to remove from table { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { if (inOid && mTable_Store) { morkRow* row = mTable_Store->GetRow(ev, inOid); if (row) CutRow(ev, row); } else ev->NilPointerError(); outErr = ev->AsErr(); } return outErr; } // } ----- end oid set methods ----- // { ----- begin row set methods ----- NS_IMETHODIMP morkTable::NewRow( // create a new row instance in table nsIMdbEnv* mev, // context mdbOid* ioOid, // please use zero (unbound) rowId for db-assigned IDs nsIMdbRow** acqRow) // create new row { nsresult outErr = NS_OK; nsIMdbRow* outRow = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { if (ioOid && mTable_Store) { morkRow* row = 0; if (ioOid->mOid_Id == morkRow_kMinusOneRid) row = mTable_Store->NewRow(ev, ioOid->mOid_Scope); else row = mTable_Store->NewRowWithOid(ev, ioOid); if (row && AddRow(ev, row)) outRow = row->AcquireRowHandle(ev, mTable_Store); } else ev->NilPointerError(); outErr = ev->AsErr(); } if (acqRow) *acqRow = outRow; return outErr; } NS_IMETHODIMP morkTable::AddRow( // make sure the row with inOid is a table member nsIMdbEnv* mev, // context nsIMdbRow* ioRow) // row to ensure membership in table { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { morkRowObject* rowObj = (morkRowObject*)ioRow; morkRow* row = rowObj->mRowObject_Row; AddRow(ev, row); outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::HasRow( // test for the table position of a row member nsIMdbEnv* mev, // context nsIMdbRow* ioRow, // row to find in table mdb_bool* outBool) // zero-based ordinal position of row in table { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { morkRowObject* rowObj = (morkRowObject*)ioRow; morkRow* row = rowObj->mRowObject_Row; if (outBool) *outBool = MapHasOid(ev, &row->mRow_Oid); outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::CutRow( // make sure the row with inOid is not a member nsIMdbEnv* mev, // context nsIMdbRow* ioRow) // row to remove from table { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { morkRowObject* rowObj = (morkRowObject*)ioRow; morkRow* row = rowObj->mRowObject_Row; CutRow(ev, row); outErr = ev->AsErr(); } return outErr; } NS_IMETHODIMP morkTable::CutAllRows( // remove all rows from the table nsIMdbEnv* mev) // context { nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { CutAllRows(ev); outErr = ev->AsErr(); } return outErr; } // } ----- end row set methods ----- // { ----- begin searching methods ----- NS_IMETHODIMP morkTable::FindRowMatches( // search variable number of sorted cols nsIMdbEnv* mev, // context const mdbYarn* inPrefix, // content to find as prefix in row's column cell nsIMdbTableRowCursor** acqCursor) // set of matching rows { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::GetSearchColumns( // query columns used by FindRowMatches() nsIMdbEnv* mev, // context mdb_count* outCount, // context mdbColumnSet* outColSet) // caller supplied space to put columns // GetSearchColumns() returns the columns actually searched when the // FindRowMatches() method is called. No more than mColumnSet_Count // slots of mColumnSet_Columns will be written, since mColumnSet_Count // indicates how many slots are present in the column array. The // actual number of search column used by the table is returned in // the outCount parameter; if this number exceeds mColumnSet_Count, // then a caller needs a bigger array to read the entire column set. // The minimum of mColumnSet_Count and outCount is the number slots // in mColumnSet_Columns that were actually written by this method. // // Callers are expected to change this set of columns by calls to // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both. { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } // } ----- end searching methods ----- // { ----- begin hinting methods ----- NS_IMETHODIMP morkTable::SearchColumnsHint( // advise re future expected search cols nsIMdbEnv* mev, // context const mdbColumnSet* inColumnSet) // columns likely to be searched { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::SortColumnsHint( // advise re future expected sort columns nsIMdbEnv* mev, // context const mdbColumnSet* inColumnSet) // columns for likely sort requests { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::StartBatchChangeHint( // advise before many adds and cuts nsIMdbEnv* mev, // context const void* inLabel) // intend unique address to match end call // If batch starts nest by virtue of nesting calls in the stack, then // the address of a local variable makes a good batch start label that // can be used at batch end time, and such addresses remain unique. { // we don't do anything here. return NS_OK; } NS_IMETHODIMP morkTable::EndBatchChangeHint( // advise before many adds and cuts nsIMdbEnv* mev, // context const void* inLabel) // label matching start label // Suppose a table is maintaining one or many sort orders for a table, // so that every row added to the table must be inserted in each sort, // and every row cut must be removed from each sort. If a db client // intends to make many such changes before needing any information // about the order or positions of rows inside a table, then a client // might tell the table to start batch changes in order to disable // sorting of rows for the interim. Presumably a table will then do // a full sort of all rows at need when the batch changes end, or when // a surprise request occurs for row position during batch changes. { // we don't do anything here. return NS_OK; } // } ----- end hinting methods ----- // { ----- begin sorting methods ----- // sorting: note all rows are assumed sorted by row ID as a secondary // sort following the primary column sort, when table rows are sorted. NS_IMETHODIMP morkTable::CanSortColumn( // query which column is currently used for sorting nsIMdbEnv* mev, // context mdb_column inColumn, // column to query sorting potential mdb_bool* outCanSort) // whether the column can be sorted { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::GetSorting( // view same table in particular sorting nsIMdbEnv* mev, // context mdb_column inColumn, // requested new column for sorting table nsIMdbSorting** acqSorting) // acquire sorting for column { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::SetSearchSorting( // use this sorting in FindRowMatches() nsIMdbEnv* mev, // context mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn() nsIMdbSorting* ioSorting) // requested sorting for some column // SetSearchSorting() attempts to inform the table that ioSorting // should be used during calls to FindRowMatches() for searching // the column which is actually sorted by ioSorting. This method // is most useful in conjunction with nsIMdbSorting::SetCompare(), // because otherwise a caller would not be able to override the // comparison ordering method used during searches. Note that some // database implementations might be unable to use an arbitrarily // specified sort order, either due to schema or runtime interface // constraints, in which case ioSorting might not actually be used. // Presumably ioSorting is an instance that was returned from some // earlier call to nsIMdbTable::GetSorting(). A caller can also // use nsIMdbTable::SearchColumnsHint() to specify desired change // in which columns are sorted and searched by FindRowMatches(). // // A caller can pass a nil pointer for ioSorting to request that // column inColumn no longer be used at all by FindRowMatches(). // But when ioSorting is non-nil, then inColumn should match the // column actually sorted by ioSorting; when these do not agree, // implementations are instructed to give precedence to the column // specified by ioSorting (so this means callers might just pass // zero for inColumn when ioSorting is also provided, since then // inColumn is both redundant and ignored). { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } // } ----- end sorting methods ----- // { ----- begin moving methods ----- // moving a row does nothing unless a table is currently unsorted NS_IMETHODIMP morkTable::MoveOid( // change position of row in unsorted table nsIMdbEnv* mev, // context const mdbOid* inOid, // row oid to find in table mdb_pos inHintFromPos, // suggested hint regarding start position mdb_pos inToPos, // desired new position for row inOid mdb_pos* outActualPos) // actual new position of row in table { nsresult outErr = NS_OK; mdb_pos actualPos = -1; // meaning it was never found in table morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { if (inOid && mTable_Store) { morkRow* row = mTable_Store->GetRow(ev, inOid); if (row) actualPos = MoveRow(ev, row, inHintFromPos, inToPos); } else ev->NilPointerError(); outErr = ev->AsErr(); } if (outActualPos) *outActualPos = actualPos; return outErr; } NS_IMETHODIMP morkTable::MoveRow( // change position of row in unsorted table nsIMdbEnv* mev, // context nsIMdbRow* ioRow, // row oid to find in table mdb_pos inHintFromPos, // suggested hint regarding start position mdb_pos inToPos, // desired new position for row ioRow mdb_pos* outActualPos) // actual new position of row in table { mdb_pos actualPos = -1; // meaning it was never found in table nsresult outErr = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { morkRowObject* rowObj = (morkRowObject*)ioRow; morkRow* row = rowObj->mRowObject_Row; actualPos = MoveRow(ev, row, inHintFromPos, inToPos); outErr = ev->AsErr(); } if (outActualPos) *outActualPos = actualPos; return outErr; } // } ----- end moving methods ----- // { ----- begin index methods ----- NS_IMETHODIMP morkTable::AddIndex( // create a sorting index for column if possible nsIMdbEnv* mev, // context mdb_column inColumn, // the column to sort by index nsIMdbThumb** acqThumb) // acquire thumb for incremental index building // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and // then the index addition will be finished. { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::CutIndex( // stop supporting a specific column index nsIMdbEnv* mev, // context mdb_column inColumn, // the column with index to be removed nsIMdbThumb** acqThumb) // acquire thumb for incremental index destroy // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and // then the index removal will be finished. { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::HasIndex( // query for current presence of a column index nsIMdbEnv* mev, // context mdb_column inColumn, // the column to investigate mdb_bool* outHasIndex) // whether column has index for this column { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::EnableIndexOnSort( // create an index for col on first sort nsIMdbEnv* mev, // context mdb_column inColumn) // the column to index if ever sorted { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::QueryIndexOnSort( // check whether index on sort is enabled nsIMdbEnv* mev, // context mdb_column inColumn, // the column to investigate mdb_bool* outIndexOnSort) // whether column has index-on-sort enabled { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP morkTable::DisableIndexOnSort( // prevent future index creation on sort nsIMdbEnv* mev, // context mdb_column inColumn) // the column to index if ever sorted { NS_ASSERTION(false, "not implemented"); return NS_ERROR_NOT_IMPLEMENTED; } // } ----- end index methods ----- // } ===== end nsIMdbTable methods ===== // we override these so that we'll use the xpcom add and release ref. #ifndef _MSC_VER mork_refs morkTable::AddStrongRef(nsIMdbEnv* ev) { return (mork_refs)AddRef(); } #endif mork_refs morkTable::AddStrongRef(morkEnv* ev) { return (mork_refs)AddRef(); } #ifndef _MSC_VER nsresult morkTable::CutStrongRef(nsIMdbEnv* ev) { return (nsresult)Release(); } #endif mork_refs morkTable::CutStrongRef(morkEnv* ev) { return (mork_refs)Release(); } mork_u2 morkTable::AddTableGcUse(morkEnv* ev) { MORK_USED_1(ev); if (mTable_GcUses < morkTable_kMaxTableGcUses) // not already maxed out? ++mTable_GcUses; return mTable_GcUses; } mork_u2 morkTable::CutTableGcUse(morkEnv* ev) { if (mTable_GcUses) // any outstanding uses to cut? { if (mTable_GcUses < morkTable_kMaxTableGcUses) // not frozen at max? --mTable_GcUses; } else this->TableGcUsesUnderflowWarning(ev); return mTable_GcUses; } // table dirty handling more complex than morkNode::SetNodeDirty() etc. void morkTable::SetTableClean(morkEnv* ev) { if (mTable_ChangeList.HasListMembers()) { nsIMdbHeap* heap = mTable_Store->mPort_Heap; mTable_ChangeList.CutAndZapAllListMembers(ev, heap); // forget changes } mTable_ChangesCount = 0; mTable_Flags = 0; this->SetNodeClean(); } // notifications regarding table changes: void morkTable::NoteTableMoveRow(morkEnv* ev, morkRow* ioRow, mork_pos inPos) { nsIMdbHeap* heap = mTable_Store->mPort_Heap; if (this->IsTableRewrite() || this->HasChangeOverflow()) this->NoteTableSetAll(ev); else { morkTableChange* tableChange = new (*heap, ev) morkTableChange(ev, ioRow, inPos); if (tableChange) { if (ev->Good()) { mTable_ChangeList.PushTail(tableChange); ++mTable_ChangesCount; } else { tableChange->ZapOldNext(ev, heap); this->SetTableRewrite(); // just plan to write all table rows } } } } void morkTable::note_row_move(morkEnv* ev, morkRow* ioRow, mork_pos inNewPos) { if (this->IsTableRewrite() || this->HasChangeOverflow()) this->NoteTableSetAll(ev); else { nsIMdbHeap* heap = mTable_Store->mPort_Heap; morkTableChange* tableChange = new (*heap, ev) morkTableChange(ev, ioRow, inNewPos); if (tableChange) { if (ev->Good()) { mTable_ChangeList.PushTail(tableChange); ++mTable_ChangesCount; } else { tableChange->ZapOldNext(ev, heap); this->NoteTableSetAll(ev); } } } } void morkTable::note_row_change(morkEnv* ev, mork_change inChange, morkRow* ioRow) { if (this->IsTableRewrite() || this->HasChangeOverflow()) this->NoteTableSetAll(ev); else { nsIMdbHeap* heap = mTable_Store->mPort_Heap; morkTableChange* tableChange = new (*heap, ev) morkTableChange(ev, inChange, ioRow); if (tableChange) { if (ev->Good()) { mTable_ChangeList.PushTail(tableChange); ++mTable_ChangesCount; } else { tableChange->ZapOldNext(ev, heap); this->NoteTableSetAll(ev); } } } } void morkTable::NoteTableSetAll(morkEnv* ev) { if (mTable_ChangeList.HasListMembers()) { nsIMdbHeap* heap = mTable_Store->mPort_Heap; mTable_ChangeList.CutAndZapAllListMembers(ev, heap); // forget changes } mTable_ChangesCount = 0; this->SetTableRewrite(); } /*static*/ void morkTable::TableGcUsesUnderflowWarning(morkEnv* ev) { ev->NewWarning("mTable_GcUses underflow"); } /*static*/ void morkTable::NonTableTypeError(morkEnv* ev) { ev->NewError("non morkTable"); } /*static*/ void morkTable::NonTableTypeWarning(morkEnv* ev) { ev->NewWarning("non morkTable"); } /*static*/ void morkTable::NilRowSpaceError(morkEnv* ev) { ev->NewError("nil mTable_RowSpace"); } mork_bool morkTable::MaybeDirtySpaceStoreAndTable() { morkRowSpace* rowSpace = mTable_RowSpace; if (rowSpace) { morkStore* store = rowSpace->mSpace_Store; if (store && store->mStore_CanDirty) { store->SetStoreDirty(); rowSpace->mSpace_CanDirty = morkBool_kTrue; } if (rowSpace->mSpace_CanDirty) // first time being dirtied? { if (this->IsTableClean()) { mork_count rowCount = this->GetRowCount(); mork_count oneThird = rowCount / 4; // one third of rows if (oneThird > 0x07FFF) // more than half max u2? oneThird = 0x07FFF; mTable_ChangesMax = (mork_u2)oneThird; } this->SetTableDirty(); rowSpace->SetRowSpaceDirty(); return morkBool_kTrue; } } return morkBool_kFalse; } morkRow* morkTable::GetMetaRow(morkEnv* ev, const mdbOid* inOptionalMetaRowOid) { morkRow* outRow = mTable_MetaRow; if (!outRow) { morkStore* store = mTable_Store; mdbOid* oid = &mTable_MetaRowOid; if (inOptionalMetaRowOid && !oid->mOid_Scope) *oid = *inOptionalMetaRowOid; if (oid->mOid_Scope) // oid already recorded in table? outRow = store->OidToRow(ev, oid); else { outRow = store->NewRow(ev, morkStore_kMetaScope); if (outRow) // need to record new oid in table? *oid = outRow->mRow_Oid; } mTable_MetaRow = outRow; if (outRow) // need to note another use of this row? { outRow->AddRowGcUse(ev); this->SetTableNewMeta(); if (this->IsTableClean()) // catch dirty status of meta row? this->MaybeDirtySpaceStoreAndTable(); } } return outRow; } void morkTable::GetTableOid(morkEnv* ev, mdbOid* outOid) { morkRowSpace* space = mTable_RowSpace; if (space) { outOid->mOid_Scope = space->SpaceScope(); outOid->mOid_Id = this->TableId(); } else this->NilRowSpaceError(ev); } nsIMdbTable* morkTable::AcquireTableHandle(morkEnv* ev) { AddRef(); return this; } mork_pos morkTable::ArrayHasOid(morkEnv* ev, const mdbOid* inOid) { MORK_USED_1(ev); mork_count count = mTable_RowArray.mArray_Fill; mork_pos pos = -1; while (++pos < (mork_pos)count) { morkRow* row = (morkRow*)mTable_RowArray.At(pos); MORK_ASSERT(row); if (row && row->EqualOid(inOid)) { return pos; } } return -1; } mork_bool morkTable::MapHasOid(morkEnv* ev, const mdbOid* inOid) { if (mTable_RowMap) return (mTable_RowMap->GetOid(ev, inOid) != 0); else return (ArrayHasOid(ev, inOid) >= 0); } void morkTable::build_row_map(morkEnv* ev) { morkRowMap* map = mTable_RowMap; if (!map) { mork_count count = mTable_RowArray.mArray_Fill + 3; nsIMdbHeap* heap = mTable_Store->mPort_Heap; map = new (*heap, ev) morkRowMap(ev, morkUsage::kHeap, heap, heap, count); if (map) { if (ev->Good()) { mTable_RowMap = map; // put strong ref here count = mTable_RowArray.mArray_Fill; mork_pos pos = -1; while (++pos < (mork_pos)count) { morkRow* row = (morkRow*)mTable_RowArray.At(pos); if (row && row->IsRow()) map->AddRow(ev, row); else row->NonRowTypeError(ev); } } else map->CutStrongRef(ev); } } } morkRow* morkTable::find_member_row(morkEnv* ev, morkRow* ioRow) { if (mTable_RowMap) return mTable_RowMap->GetRow(ev, ioRow); else { mork_count count = mTable_RowArray.mArray_Fill; mork_pos pos = -1; while (++pos < (mork_pos)count) { morkRow* row = (morkRow*)mTable_RowArray.At(pos); if (row == ioRow) return row; } } return (morkRow*)0; } mork_pos morkTable::MoveRow( morkEnv* ev, morkRow* ioRow, // change row position mork_pos inHintFromPos, // suggested hint regarding start position mork_pos inToPos) // desired new position for row ioRow // MoveRow() returns the actual position of ioRow afterwards; this // position is -1 if and only if ioRow was not found as a member. { mork_pos outPos = -1; // means ioRow was not a table member mork_bool canDirty = (this->IsTableClean()) ? this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue; morkRow** rows = (morkRow**)mTable_RowArray.mArray_Slots; mork_count count = mTable_RowArray.mArray_Fill; if (count && rows && ev->Good()) // any members at all? no errors? { mork_pos lastPos = count - 1; // index of last row slot if (inToPos > lastPos) // beyond last used array slot? inToPos = lastPos; // put row into last available slot else if (inToPos < 0) // before first usable slot? inToPos = 0; // put row in very first slow if (inHintFromPos > lastPos) // beyond last used array slot? inHintFromPos = lastPos; // seek row in last available slot else if (inHintFromPos < 0) // before first usable slot? inHintFromPos = 0; // seek row in very first slow morkRow** fromSlot = 0; // becomes nonzero of ioRow is ever found morkRow** rowsEnd = rows + count; // one past last used array slot if (inHintFromPos <= 0) // start of table? just scan for row? { morkRow** cursor = rows - 1; // before first array slot while (++cursor < rowsEnd) { if (*cursor == ioRow) { fromSlot = cursor; break; // end while loop } } } else // search near the start position and work outwards { morkRow** lo = rows + inHintFromPos; // lowest search point morkRow** hi = lo; // highest search point starts at lowest point // Seek ioRow in spiral widening search below and above inHintFromPos. // This is faster when inHintFromPos is at all accurate, but is slower // than a straightforward scan when inHintFromPos is nearly random. while (lo >= rows || hi < rowsEnd) // keep searching? { if (lo >= rows) // low direction search still feasible? { if (*lo == ioRow) // actually found the row? { fromSlot = lo; break; // end while loop } --lo; // advance further lower } if (hi < rowsEnd) // high direction search still feasible? { if (*hi == ioRow) // actually found the row? { fromSlot = hi; break; // end while loop } ++hi; // advance further higher } } } if (fromSlot) // ioRow was found as a table member? { outPos = fromSlot - rows; // actual position where row was found if (outPos != inToPos) // actually need to move this row? { morkRow** toSlot = rows + inToPos; // slot where row must go ++mTable_RowArray.mArray_Seed; // we modify the array now: if (fromSlot < toSlot) // row is moving upwards? { morkRow** up = fromSlot; // leading pointer going upward while (++up <= toSlot) // have not gone above destination? { *fromSlot = *up; // shift down one fromSlot = up; // shift trailing pointer up } } else // ( fromSlot > toSlot ) // row is moving downwards { morkRow** down = fromSlot; // leading pointer going downward while (--down >= toSlot) // have not gone below destination? { *fromSlot = *down; // shift up one fromSlot = down; // shift trailing pointer } } *toSlot = ioRow; outPos = inToPos; // okay, we actually moved the row here if (canDirty) this->note_row_move(ev, ioRow, inToPos); } } } return outPos; } mork_bool morkTable::AddRow(morkEnv* ev, morkRow* ioRow) { morkRow* row = this->find_member_row(ev, ioRow); if (!row && ev->Good()) { mork_bool canDirty = (this->IsTableClean()) ? this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue; mork_pos pos = mTable_RowArray.AppendSlot(ev, ioRow); if (ev->Good() && pos >= 0) { ioRow->AddRowGcUse(ev); if (mTable_RowMap) { if (mTable_RowMap->AddRow(ev, ioRow)) { // okay, anything else? } else mTable_RowArray.CutSlot(ev, pos); } else if (mTable_RowArray.mArray_Fill >= morkTable_kMakeRowMapThreshold) this->build_row_map(ev); if (canDirty && ev->Good()) this->NoteTableAddRow(ev, ioRow); } } return ev->Good(); } mork_bool morkTable::CutRow(morkEnv* ev, morkRow* ioRow) { morkRow* row = this->find_member_row(ev, ioRow); if (row) { mork_bool canDirty = (this->IsTableClean()) ? this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue; mork_count count = mTable_RowArray.mArray_Fill; morkRow** rowSlots = (morkRow**)mTable_RowArray.mArray_Slots; if (rowSlots) // array has vector as expected? { mork_pos pos = -1; morkRow** end = rowSlots + count; morkRow** slot = rowSlots - 1; // prepare for preincrement: while (++slot < end) // another slot to check? { if (*slot == row) // found the slot containing row? { pos = slot - rowSlots; // record absolute position break; // end while loop } } if (pos >= 0) // need to cut if from the array? mTable_RowArray.CutSlot(ev, pos); else ev->NewWarning("row not found in array"); } else mTable_RowArray.NilSlotsAddressError(ev); if (mTable_RowMap) mTable_RowMap->CutRow(ev, ioRow); if (canDirty) this->NoteTableCutRow(ev, ioRow); if (ioRow->CutRowGcUse(ev) == 0) ioRow->OnZeroRowGcUse(ev); } return ev->Good(); } mork_bool morkTable::CutAllRows(morkEnv* ev) { if (this->MaybeDirtySpaceStoreAndTable()) { this->SetTableRewrite(); // everything is dirty this->NoteTableSetAll(ev); } if (ev->Good()) { mTable_RowArray.CutAllSlots(ev); if (mTable_RowMap) { morkRowMapIter i(ev, mTable_RowMap); mork_change* c = 0; morkRow* r = 0; for (c = i.FirstRow(ev, &r); c; c = i.NextRow(ev, &r)) { if (r) { if (r->CutRowGcUse(ev) == 0) r->OnZeroRowGcUse(ev); i.CutHereRow(ev, (morkRow**)0); } else ev->NewWarning("nil row in table map"); } } } return ev->Good(); } morkTableRowCursor* morkTable::NewTableRowCursor(morkEnv* ev, mork_pos inRowPos) { morkTableRowCursor* outCursor = 0; if (ev->Good()) { nsIMdbHeap* heap = mTable_Store->mPort_Heap; morkTableRowCursor* cursor = new (*heap, ev) morkTableRowCursor(ev, morkUsage::kHeap, heap, this, inRowPos); if (cursor) { if (ev->Good()) outCursor = cursor; else cursor->CutStrongRef((nsIMdbEnv*)ev); } } return outCursor; } // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 morkTableChange::morkTableChange(morkEnv* ev, mork_change inChange, morkRow* ioRow) // use this constructor for inChange == morkChange_kAdd or morkChange_kCut : morkNext(), mTableChange_Row(ioRow), mTableChange_Pos(morkTableChange_kNone) { if (ioRow) { if (ioRow->IsRow()) { if (inChange == morkChange_kAdd) mTableChange_Pos = morkTableChange_kAdd; else if (inChange == morkChange_kCut) mTableChange_Pos = morkTableChange_kCut; else this->UnknownChangeError(ev); } else ioRow->NonRowTypeError(ev); } else ev->NilPointerError(); } morkTableChange::morkTableChange(morkEnv* ev, morkRow* ioRow, mork_pos inPos) // use this constructor when the row is moved : morkNext(), mTableChange_Row(ioRow), mTableChange_Pos(inPos) { if (ioRow) { if (ioRow->IsRow()) { if (inPos < 0) this->NegativeMovePosError(ev); } else ioRow->NonRowTypeError(ev); } else ev->NilPointerError(); } void morkTableChange::UnknownChangeError(morkEnv* ev) const // morkChange_kAdd or morkChange_kCut { ev->NewError("mTableChange_Pos neither kAdd nor kCut"); } void morkTableChange::NegativeMovePosError(morkEnv* ev) const // move must be non-neg position { ev->NewError("negative mTableChange_Pos for row move"); } // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 morkTableMap::~morkTableMap() {} morkTableMap::morkTableMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) #ifdef MORK_BEAD_OVER_NODE_MAPS : morkBeadMap(ev, inUsage, ioHeap, ioSlotHeap) #else /*MORK_BEAD_OVER_NODE_MAPS*/ : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap) #endif /*MORK_BEAD_OVER_NODE_MAPS*/ { if (ev->Good()) mNode_Derived = morkDerived_kTableMap; } // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789