diff options
Diffstat (limited to 'comm/mailnews/db/mork')
96 files changed, 35472 insertions, 0 deletions
diff --git a/comm/mailnews/db/mork/components.conf b/comm/mailnews/db/mork/components.conf new file mode 100644 index 0000000000..edf2f0d382 --- /dev/null +++ b/comm/mailnews/db/mork/components.conf @@ -0,0 +1,12 @@ +# 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/. + +Classes = [ + { + "cid": "{36d90300-27f5-11d3-8d74-00805f8a6617}", + "contract_ids": ["@mozilla.org/db/mork;1"], + "type": "nsMorkFactoryService", + "headers": ["/comm/mailnews/db/mork/nsMorkFactory.h"], + }, +] diff --git a/comm/mailnews/db/mork/mdb.h b/comm/mailnews/db/mork/mdb.h new file mode 100644 index 0000000000..2f0f0d80e1 --- /dev/null +++ b/comm/mailnews/db/mork/mdb.h @@ -0,0 +1,2550 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Blake Ross (blake@blakeross.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MDB_ +#define _MDB_ 1 + +#include "mozilla/Path.h" +#include "nscore.h" +#include "nsISupports.h" +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// { %%%%% begin scalar typedefs %%%%% +typedef unsigned char mdb_u1; // make sure this is one byte +typedef unsigned short mdb_u2; // make sure this is two bytes +typedef short mdb_i2; // make sure this is two bytes +typedef uint32_t mdb_u4; // make sure this is four bytes +typedef int32_t mdb_i4; // make sure this is four bytes +typedef PRWord mdb_ip; // make sure sizeof(mdb_ip) == sizeof(void*) + +typedef mdb_u1 mdb_bool; // unsigned byte with zero=false, nonzero=true + +/* canonical boolean constants provided only for code clarity: */ +#define mdbBool_kTrue ((mdb_bool)1) /* actually any nonzero means true */ +#define mdbBool_kFalse ((mdb_bool)0) /* only zero means false */ + +typedef mdb_u4 mdb_id; // unsigned object identity in a scope +typedef mdb_id mdb_rid; // unsigned row identity inside scope +typedef mdb_id mdb_tid; // unsigned table identity inside scope +typedef mdb_u4 mdb_token; // unsigned token for atomized string +typedef mdb_token mdb_scope; // token used to id scope for rows +typedef mdb_token mdb_kind; // token used to id kind for tables +typedef mdb_token mdb_column; // token used to id columns for rows +typedef mdb_token mdb_cscode; // token used to id charset names +typedef mdb_u4 mdb_seed; // unsigned collection change counter +typedef mdb_u4 mdb_count; // unsigned collection member count +typedef mdb_u4 mdb_size; // unsigned physical media size +typedef mdb_u4 mdb_fill; // unsigned logical content size +typedef mdb_u4 mdb_more; // more available bytes for larger buffer + +typedef mdb_u2 mork_uses; // 2-byte strong uses count +typedef mdb_u2 mork_refs; // 2-byte actual reference count + +#define mdbId_kNone ((mdb_id)-1) /* never a valid Mork object ID */ + +typedef mdb_u4 mdb_percent; // 0..100, with values >100 same as 100 + +typedef mdb_u1 mdb_priority; // 0..9, for a total of ten different values + +// sequence position is signed; negative is useful to mean "before first": +typedef mdb_i4 mdb_pos; // signed zero-based ordinal collection position + +#define mdbPos_kBeforeFirst ((mdb_pos)-1) /* any negative is before zero */ + +// order is also signed, so we can use three states for comparison order: +typedef mdb_i4 mdb_order; // neg:lessthan, zero:equalto, pos:greaterthan + +typedef mdb_order (*mdbAny_Order)(const void* inA, const void* inB, + const void* inClosure); + +// } %%%%% end scalar typedefs %%%%% + +// { %%%%% begin C structs %%%%% + +#ifndef mdbScopeStringSet_typedef +typedef struct mdbScopeStringSet mdbScopeStringSet; +# define mdbScopeStringSet_typedef 1 +#endif + +/*| mdbScopeStringSet: a set of null-terminated C strings that enumerate some +**| names of row scopes, so that row scopes intended for use by an application +**| can be declared by an app when trying to open or create a database file. +**| (We use strings and not tokens because we cannot know the tokens for any +**| particular db without having first opened the db.) The goal is to inform +**| a db runtime that scopes not appearing in this list can be given relatively +**| short shrift in runtime representation, with the expectation that other +**| scopes will not actually be used. However, a db should still be prepared +**| to handle accessing row scopes not in this list, rather than raising errors. +**| But it could be quite expensive to access a row scope not on the list. +**| Note a zero count for the string set means no such string set is being +**| specified, and that a db should handle all row scopes efficiently. +**| (It does NOT mean an app plans to use no content whatsoever.) +|*/ +#ifndef mdbScopeStringSet_struct +# define mdbScopeStringSet_struct 1 +struct mdbScopeStringSet { // vector of scopes for use in db opening policy + // when mScopeStringSet_Count is zero, this means no scope constraints + mdb_count mScopeStringSet_Count; // number of strings in vector below + const char** mScopeStringSet_Strings; // null-ended ascii scope strings +}; +#endif /*mdbScopeStringSet_struct*/ + +#ifndef mdbOpenPolicy_typedef +typedef struct mdbOpenPolicy mdbOpenPolicy; +# define mdbOpenPolicy_typedef 1 +#endif + +#ifndef mdbOpenPolicy_struct +# define mdbOpenPolicy_struct 1 +struct mdbOpenPolicy { // policies affecting db usage for ports and stores + mdbScopeStringSet mOpenPolicy_ScopePlan; // predeclare scope usage plan + mdb_bool mOpenPolicy_MaxLazy; // nonzero: do least work + mdb_bool mOpenPolicy_MinMemory; // nonzero: use least memory +}; +#endif /*mdbOpenPolicy_struct*/ + +#ifndef mdbTokenSet_typedef +typedef struct mdbTokenSet mdbTokenSet; +# define mdbTokenSet_typedef 1 +#endif + +#ifndef mdbTokenSet_struct +# define mdbTokenSet_struct 1 +struct mdbTokenSet { // array for a set of tokens, and actual slots used + mdb_count mTokenSet_Count; // number of token slots in the array + mdb_fill mTokenSet_Fill; // the subset of count slots actually used + mdb_more mTokenSet_More; // more tokens available for bigger array + mdb_token* mTokenSet_Tokens; // array of count mdb_token instances +}; +#endif /*mdbTokenSet_struct*/ + +#ifndef mdbUsagePolicy_typedef +typedef struct mdbUsagePolicy mdbUsagePolicy; +# define mdbUsagePolicy_typedef 1 +#endif + +/*| mdbUsagePolicy: another version of mdbOpenPolicy which uses tokens instead +**| of scope strings, because usage policies can be constructed for use with a +**| db that is already open, while an open policy must be constructed before a +**| db has yet been opened. +|*/ +#ifndef mdbUsagePolicy_struct +# define mdbUsagePolicy_struct 1 +struct mdbUsagePolicy { // policies affecting db usage for ports and stores + mdbTokenSet mUsagePolicy_ScopePlan; // current scope usage plan + mdb_bool mUsagePolicy_MaxLazy; // nonzero: do least work + mdb_bool mUsagePolicy_MinMemory; // nonzero: use least memory +}; +#endif /*mdbUsagePolicy_struct*/ + +#ifndef mdbOid_typedef +typedef struct mdbOid mdbOid; +# define mdbOid_typedef 1 +#endif + +#ifndef mdbOid_struct +# define mdbOid_struct 1 +struct mdbOid { // identity of some row or table inside a database + mdb_scope mOid_Scope; // scope token for an id's namespace + mdb_id mOid_Id; // identity of object inside scope namespace +}; +#endif /*mdbOid_struct*/ + +#ifndef mdbRange_typedef +typedef struct mdbRange mdbRange; +# define mdbRange_typedef 1 +#endif + +#ifndef mdbRange_struct +# define mdbRange_struct 1 +struct mdbRange { // range of row positions in a table + mdb_pos mRange_FirstPos; // position of first row + mdb_pos mRange_LastPos; // position of last row +}; +#endif /*mdbRange_struct*/ + +#ifndef mdbColumnSet_typedef +typedef struct mdbColumnSet mdbColumnSet; +# define mdbColumnSet_typedef 1 +#endif + +#ifndef mdbColumnSet_struct +# define mdbColumnSet_struct 1 +struct mdbColumnSet { // array of column tokens (just the same as mdbTokenSet) + mdb_count mColumnSet_Count; // number of columns + mdb_column* mColumnSet_Columns; // count mdb_column instances +}; +#endif /*mdbColumnSet_struct*/ + +#ifndef mdbYarn_typedef +typedef struct mdbYarn mdbYarn; +# define mdbYarn_typedef 1 +#endif + +#ifdef MDB_BEGIN_C_LINKAGE_define +# define MDB_BEGIN_C_LINKAGE_define 1 +# define MDB_BEGIN_C_LINKAGE extern "C" { +# define MDB_END_C_LINKAGE } +#endif /*MDB_BEGIN_C_LINKAGE_define*/ + +/*| mdbYarn_mGrow: an abstract API for growing the size of a mdbYarn +**| instance. With respect to a specific API that requires a caller +**| to supply a string (mdbYarn) that a callee fills with content +**| that might exceed the specified size, mdbYarn_mGrow is a caller- +**| supplied means of letting a callee attempt to increase the string +**| size to become large enough to receive all content available. +**| +**|| Grow(): a method for requesting that a yarn instance be made +**| larger in size. Note that such requests need not be honored, and +**| need not be honored in full if only partial size growth is desired. +**| (Note that no nsIMdbEnv instance is passed as argument, although one +**| might be needed in some circumstances. So if an nsIMdbEnv is needed, +**| a reference to one might be held inside a mdbYarn member slot.) +**| +**|| self: a yarn instance to be grown. Presumably this yarn is +**| the instance which holds the mYarn_Grow method pointer. Yarn +**| instancesshould only be passed to grow methods which they were +**| specifically designed to fit, as indicated by the mYarn_Grow slot. +**| +**|| inNewSize: the new desired value for slot mYarn_Size in self. +**| If mYarn_Size is already this big, then nothing should be done. +**| If inNewSize is larger than seems feasible or desirable to honor, +**| then any size restriction policy can be used to grow to some size +**| greater than mYarn_Size. (Grow() might even grow to a size +**| greater than inNewSize in order to make the increase in size seem +**| worthwhile, rather than growing in many smaller steps over time.) +|*/ +typedef void (*mdbYarn_mGrow)(mdbYarn* self, mdb_size inNewSize); +// mdbYarn_mGrow methods must be declared with C linkage in C++ + +/*| mdbYarn: a variable length "string" of arbitrary binary bytes, +**| whose length is mYarn_Fill, inside a buffer mYarn_Buf that has +**| at most mYarn_Size byte of physical space. +**| +**|| mYarn_Buf: a pointer to space containing content. This slot +**| might never be nil when mYarn_Size is nonzero, but checks for nil +**| are recommended anyway. +**| (Implementations of mdbYarn_mGrow methods should take care to +**| ensure the existence of a replacement before dropping old Bufs.) +**| Content in Buf can be anything in any format, but the mYarn_Form +**| implies the actual format by some caller-to-callee convention. +**| mYarn_Form==0 implies US-ASCII iso-8859-1 Latin1 string content. +**| +**|| mYarn_Size: the physical size of Buf in bytes. Note that if one +**| intends to terminate a string with a null byte, that it must not +**| be written at or after mYarn_Buf[mYarn_Size] because this is after +**| the last byte in the physical buffer space. Size can be zero, +**| which means the string has no content whatsoever; note that when +**| Size is zero, this is a suitable reason for Buf==nil as well. +**| +**|| mYarn_Fill: the logical content in Buf in bytes, where Fill must +**| never exceed mYarn_Size. Note that yarn strings might not have a +**| terminating null byte (since they might not even be C strings), but +**| when they do, such terminating nulls are considered part of content +**| and therefore Fill will count such null bytes. So an "empty" C +**| string will have Fill==1, because content includes one null byte. +**| Fill does not mean "length" when applied to C strings for this +**| reason. However, clients using yarns to hold C strings can infer +**| that length is equal to Fill-1 (but should take care to handle the +**| case where Fill==0). To be paranoid, one can always copy to a +**| destination with size exceeding Fill, and place a redundant null +**| byte in the Fill position when this simplifies matters. +**| +**|| mYarn_Form: a designation of content format within mYarn_Buf. +**| The semantics of this slot are the least well defined, since the +**| actual meaning is context dependent, to the extent that callers +**| and callees must agree on format encoding conventions when such +**| are not standardized in many computing contexts. However, in the +**| context of a specific mdb database, mYarn_Form is a token for an +**| atomized string in that database that typically names a preferred +**| mime type charset designation. If and when mdbYarn is used for +**| other purposes away from the mdb interface, folks can use another +**| convention system for encoding content formats. However, in all +**| contexts is it useful to maintain the convention that Form==0 +**| implies Buf contains US-ASCII iso-8859-1 Latin1 string content. +**| +**|| mYarn_Grow: either a mdbYarn_mGrow method, or else nil. When +**| a mdbYarn_mGrow method is provided, this method can be used to +**| request a yarn buf size increase. A caller who constructs the +**| original mdbYarn instance decides whether a grow method is necessary +**| or desirable, and uses only grow methods suitable for the buffering +**| nature of a specific mdbYarn instance. (For example, Buf might be a +**| statically allocated string space which switches to something heap-based +**| when grown, and subsequent calls to grow the yarn must distinguish the +**| original static string from heap allocated space, etc.) Note that the +**| method stored in mYarn_Grow can change, and this might be a common way +**| to track memory managent changes in policy for mYarn_Buf. +|*/ +#ifndef mdbYarn_struct +# define mdbYarn_struct 1 +struct mdbYarn { // buffer with caller space allocation semantics + void* mYarn_Buf; // space for holding any binary content + mdb_fill mYarn_Fill; // logical content in Buf in bytes + mdb_size mYarn_Size; // physical size of Buf in bytes + mdb_more mYarn_More; // more available bytes if Buf is bigger + mdb_cscode mYarn_Form; // charset format encoding + mdbYarn_mGrow mYarn_Grow; // optional method to grow mYarn_Buf + + // Subclasses might add further slots after mYarn_Grow in order to + // maintain bookkeeping needs, such as state info about mYarn_Buf. +}; +#endif /*mdbYarn_struct*/ + +// } %%%%% end C structs %%%%% + +// { %%%%% begin class forward defines %%%%% +class nsIMdbEnv; +class nsIMdbObject; +class nsIMdbErrorHook; +class nsIMdbThumb; +class nsIMdbFactory; +class nsIMdbFile; +class nsIMdbPort; +class nsIMdbStore; +class nsIMdbCursor; +class nsIMdbPortTableCursor; +class nsIMdbCollection; +class nsIMdbTable; +class nsIMdbTableRowCursor; +class nsIMdbRow; +class nsIMdbRowCellCursor; +class nsIMdbBlob; +class nsIMdbCell; +class nsIMdbSorting; +// } %%%%% end class forward defines %%%%% + +// { %%%%% begin C++ abstract class interfaces %%%%% + +/*| nsIMdbObject: base class for all message db class interfaces +**| +**|| factory: all nsIMdbObjects from the same code suite have the same factory +**| +**|| refcounting: both strong and weak references, to ensure strong refs are +**| acyclic, while weak refs can cause cycles. CloseMdbObject() is +**| called when (strong) use counts hit zero, but clients can call this close +**| method early for some reason, if absolutely necessary even though it will +**| thwart the other uses of the same object. Note that implementations must +**| cope with close methods being called arbitrary numbers of times. The COM +**| calls to AddRef() and release ref map directly to strong use ref calls, +**| but the total ref count for COM objects is the sum of weak & strong refs. +|*/ + +#define NS_IMDBOBJECT_IID_STR "5533ea4b-14c3-4bef-ac60-22f9e9a49084" + +#define NS_IMDBOBJECT_IID \ + { \ + 0x5533ea4b, 0x14c3, 0x4bef, { \ + 0xac, 0x60, 0x22, 0xf9, 0xe9, 0xa4, 0x90, 0x84 \ + } \ + } + +class nsIMdbObject : public nsISupports { // msg db base class + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBOBJECT_IID) + // { ===== begin nsIMdbObject methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly) = 0; + // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port. + // } ----- end attribute methods ----- + + // { ----- begin factory methods ----- + NS_IMETHOD GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory) = 0; + // } ----- end factory methods ----- + + // { ----- begin ref counting for well-behaved cyclic graphs ----- + NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev, // weak refs + mdb_count* outCount) = 0; + NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev, // strong refs + mdb_count* outCount) = 0; + + NS_IMETHOD AddWeakRef(nsIMdbEnv* ev) = 0; + NS_IMETHOD_(mork_uses) AddStrongRef(nsIMdbEnv* ev) = 0; + + NS_IMETHOD CutWeakRef(nsIMdbEnv* ev) = 0; + NS_IMETHOD CutStrongRef(nsIMdbEnv* ev) = 0; + + NS_IMETHOD CloseMdbObject(nsIMdbEnv* ev) = 0; // called at strong refs zero + NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen) = 0; + // } ----- end ref counting ----- + + // } ===== end nsIMdbObject methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbObject, NS_IMDBOBJECT_IID) + +/*| nsIMdbErrorHook: a base class for clients of this API to subclass, in order +**| to provide a callback installable in nsIMdbEnv for error notifications. If +**| apps that subclass nsIMdbErrorHook wish to maintain a reference to the env +**| that contains the hook, then this should be a weak ref to avoid cycles. +**| +**|| OnError: when nsIMdbEnv has an error condition that causes the total count +**| of errors to increase, then nsIMdbEnv should call OnError() to report the +**| error in some fashion when an instance of nsIMdbErrorHook is installed. The +**| variety of string flavors is currently due to the uncertainty here in the +**| nsIMdbBlob and nsIMdbCell interfaces. (Note that overloading by using the +**| same method name is not necessary here, and potentially less clear.) +|*/ +class nsIMdbErrorHook + : public nsISupports { // env callback handler to report errors + public: + // { ===== begin error methods ===== + NS_IMETHOD OnErrorString(nsIMdbEnv* ev, const char* inAscii) = 0; + NS_IMETHOD OnErrorYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0; + // } ===== end error methods ===== + + // { ===== begin warning methods ===== + NS_IMETHOD OnWarningString(nsIMdbEnv* ev, const char* inAscii) = 0; + NS_IMETHOD OnWarningYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0; + // } ===== end warning methods ===== + + // { ===== begin abort hint methods ===== + NS_IMETHOD OnAbortHintString(nsIMdbEnv* ev, const char* inAscii) = 0; + NS_IMETHOD OnAbortHintYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0; + // } ===== end abort hint methods ===== +}; + +/*| nsIMdbHeap: abstract memory allocation interface. +**| +**|| Alloc: return a block at least inSize bytes in size with alignment +**| suitable for any native type (such as long integers). When no such +**| block can be allocated, failure is indicated by a null address in +**| addition to reporting an error in the environment. +**| +**|| Free: deallocate a block allocated or resized earlier by the same +**| heap instance. If the inBlock parameter is nil, the heap should do +**| nothing (and crashing is strongly discouraged). +|*/ +class nsIMdbHeap { // caller-supplied memory management interface + public: + // { ===== begin nsIMdbHeap methods ===== + NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory + mdb_size inSize, // requested byte size of new memory block + void** outBlock) = + 0; // memory block of inSize bytes, or nil + + NS_IMETHOD Free(nsIMdbEnv* ev, // free block from Alloc or Resize() + void* ioBlock) = 0; // block to be destroyed/deallocated + + virtual size_t GetUsedSize() = 0; + + virtual ~nsIMdbHeap(){}; + // } ===== end nsIMdbHeap methods ===== +}; + +/*| nsIMdbCPlusHeap: Alloc() with global ::new(), Free() with global ::delete(). +**| Resize() is done by ::new() followed by ::delete(). +|*/ +class nsIMdbCPlusHeap { // caller-supplied memory management interface + public: + // { ===== begin nsIMdbHeap methods ===== + NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory + mdb_size inSize, // requested size of new memory block + void** outBlock); // memory block of inSize bytes, or nil + + NS_IMETHOD Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc() + void* inBlock); + + NS_IMETHOD HeapAddStrongRef(nsIMdbEnv* ev); + NS_IMETHOD HeapCutStrongRef(nsIMdbEnv* ev); + // } ===== end nsIMdbHeap methods ===== +}; + +/*| nsIMdbThumb: +|*/ + +#define NS_IMDBTHUMB_IID_STR "6d3ad7c1-a809-4e74-8577-49fa9a4562fa" + +#define NS_IMDBTHUMB_IID \ + { \ + 0x6d3ad7c1, 0xa809, 0x4e74, { \ + 0x85, 0x77, 0x49, 0xfa, 0x9a, 0x45, 0x62, 0xfa \ + } \ + } + +class nsIMdbThumb + : public nsISupports { // closure for repeating incremental method + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTHUMB_IID) + + // { ===== begin nsIMdbThumb methods ===== + NS_IMETHOD GetProgress( + nsIMdbEnv* ev, + mdb_count* outTotal, // total somethings to do in operation + mdb_count* outCurrent, // subportion of total completed so far + mdb_bool* outDone, // is operation finished? + mdb_bool* outBroken // is operation irreparably dead and broken? + ) = 0; + + NS_IMETHOD DoMore( + nsIMdbEnv* ev, + mdb_count* outTotal, // total somethings to do in operation + mdb_count* outCurrent, // subportion of total completed so far + mdb_bool* outDone, // is operation finished? + mdb_bool* outBroken // is operation irreparably dead and broken? + ) = 0; + + NS_IMETHOD CancelAndBreakThumb( // cancel pending operation + nsIMdbEnv* ev) = 0; + // } ===== end nsIMdbThumb methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbThumb, NS_IMDBTHUMB_IID) + +/*| nsIMdbEnv: a context parameter used when calling most abstract db methods. +**| The main purpose of such an object is to permit a database implementation +**| to avoid the use of globals to share information between various parts of +**| the implementation behind the abstract db interface. An environment acts +**| like a session object for a given calling thread, and callers should use +**| at least one different nsIMdbEnv instance for each thread calling the API. +**| While the database implementation might not be threaded, it is highly +**| desirable that the db be thread-safe if calling threads use distinct +**| instances of nsIMdbEnv. Callers can stop at one nsIMdbEnv per thread, or +they +**| might decide to make on nsIMdbEnv instance for every nsIMdbPort opened, so +that +**| error information is segregated by database instance. Callers create +**| instances of nsIMdbEnv by calling the MakeEnv() method in nsIMdbFactory. +**| +**|| tracing: an environment might support some kind of tracing, and this +**| boolean attribute permits such activity to be enabled or disabled. +**| +**|| errors: when a call to the abstract db interface returns, a caller might +**| check the number of outstanding errors to see whether the operation did +**| actually succeed. Each nsIMdbEnv should have all its errors cleared by a +**| call to ClearErrors() before making each call to the abstract db API, +**| because outstanding errors might disable further database actions. (This +**| is not done inside the db interface, because the db cannot in general know +**| when a call originates from inside or outside -- only the app knows this.) +**| +**|| error hook: callers can install an instance of nsIMdbErrorHook to receive +**| error notifications whenever the error count increases. The hook can +**| be uninstalled by passing a null pointer. +**| +|*/ + +#define NS_IMDBENV_IID_STR "a765e46b-efb6-41e6-b75b-c5d6bd710594" + +#define NS_IMDBENV_IID \ + { \ + 0xa765e46b, 0xefb6, 0x41e6, { \ + 0xb7, 0x5b, 0xc5, 0xd6, 0xbd, 0x71, 0x05, 0x94 \ + } \ + } + +class nsIMdbEnv : public nsISupports { // db specific context parameter + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBENV_IID) + // { ===== begin nsIMdbEnv methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD GetErrorCount(mdb_count* outCount, mdb_bool* outShouldAbort) = 0; + NS_IMETHOD GetWarningCount(mdb_count* outCount, mdb_bool* outShouldAbort) = 0; + + NS_IMETHOD GetEnvBeVerbose(mdb_bool* outBeVerbose) = 0; + NS_IMETHOD SetEnvBeVerbose(mdb_bool inBeVerbose) = 0; + + NS_IMETHOD GetDoTrace(mdb_bool* outDoTrace) = 0; + NS_IMETHOD SetDoTrace(mdb_bool inDoTrace) = 0; + + NS_IMETHOD GetAutoClear(mdb_bool* outAutoClear) = 0; + NS_IMETHOD SetAutoClear(mdb_bool inAutoClear) = 0; + + NS_IMETHOD GetErrorHook(nsIMdbErrorHook** acqErrorHook) = 0; + NS_IMETHOD SetErrorHook(nsIMdbErrorHook* ioErrorHook) = + 0; // becomes referenced + + NS_IMETHOD GetHeap(nsIMdbHeap** acqHeap) = 0; + NS_IMETHOD SetHeap(nsIMdbHeap* ioHeap) = 0; // becomes referenced + // } ----- end attribute methods ----- + + NS_IMETHOD ClearErrors() = 0; // clear errors beore re-entering db API + NS_IMETHOD ClearWarnings() = 0; // clear warnings + NS_IMETHOD ClearErrorsAndWarnings() = 0; // clear both errors & warnings + // } ===== end nsIMdbEnv methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbEnv, NS_IMDBENV_IID) + +/*| nsIMdbFactory: the main entry points to the abstract db interface. A DLL +**| that supports this mdb interface need only have a single exported method +**| that will return an instance of nsIMdbFactory, so that further methods in +**| the suite can be accessed from objects returned by nsIMdbFactory methods. +**| +**|| mdbYarn: note all nsIMdbFactory subclasses must guarantee null +**| termination of all strings written into mdbYarn instances, as long as +**| mYarn_Size and mYarn_Buf are nonzero. Even truncated string values must +**| be null terminated. This is more strict behavior than mdbYarn requires, +**| but it is part of the nsIMdbFactory interface. +**| +**|| envs: an environment instance is required as per-thread context for +**| most of the db method calls, so nsIMdbFactory creates such instances. +**| +**|| rows: callers must be able to create row instances that are independent +**| of storage space that is part of the db content graph. Many interfaces +**| for data exchange have strictly copy semantics, so that a row instance +**| has no specific identity inside the db content model, and the text in +**| cells are an independenty copy of unexposed content inside the db model. +**| Callers are expected to maintain one or more row instances as a buffer +**| for staging cell content copied into or out of a table inside the db. +**| Callers are urged to use an instance of nsIMdbRow created by the +nsIMdbFactory +**| code suite, because reading and writing might be much more efficient than +**| when using a hand-rolled nsIMdbRow subclass with no relation to the suite. +**| +**|| ports: a port is a readonly interface to a specific database file. Most +**| of the methods to access a db file are suitable for a readonly interface, +**| so a port is the basic minimum for accessing content. This makes it +**| possible to read other external formats for import purposes, without +**| needing the code or competence necessary to write every such format. So +**| we can write generic import code just once, as long as every format can +**| show a face based on nsIMdbPort. (However, same suite import can be faster.) +**| Given a file name and the first 512 bytes of a file, a factory can say if +**| a port can be opened by this factory. Presumably an app maintains chains +**| of factories for different suites, and asks each in turn about opening a +**| a prospective file for reading (as a port) or writing (as a store). I'm +**| not ready to tackle issues of format fidelity and factory chain ordering. +**| +**|| stores: a store is a mutable interface to a specific database file, and +**| includes the port interface plus any methods particular to writing, which +**| are few in number. Presumably the set of files that can be opened as +**| stores is a subset of the set of files that can be opened as ports. A +**| new store can be created with CreateNewFileStore() by supplying a new +**| file name which does not yet exist (callers are always responsible for +**| destroying any existing files before calling this method). +|*/ + +#define NS_IMDBFACTORY_IID_STR "2b80395c-b91e-4990-b1a7-023e99ab14e9" + +#define NS_IMDBFACTORY_IID \ + { \ + 0xf04aa4ab, 0x1fe, 0x4115, { \ + 0xa4, 0xa5, 0x68, 0x19, 0xdf, 0xf1, 0x10, 0x3d \ + } \ + } + +class nsIMdbFactory : public nsISupports { // suite entry points + using PathChar = mozilla::filesystem::Path::value_type; + + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBFACTORY_IID) + // { ===== begin nsIMdbFactory methods ===== + + // { ----- begin file methods ----- + NS_IMETHOD OpenOldFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + const PathChar* inFilePath, mdb_bool inFrozen, + nsIMdbFile** acqFile) = 0; + // Choose some subclass of nsIMdbFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be open and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. + + NS_IMETHOD CreateNewFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + const PathChar* inFilePath, + nsIMdbFile** acqFile) = 0; + // Choose some subclass of nsIMdbFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be created and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. + // } ----- end file methods ----- + + // { ----- begin env methods ----- + NS_IMETHOD MakeEnv(nsIMdbHeap* ioHeap, + nsIMdbEnv** acqEnv) = 0; // acquire new env + // ioHeap can be nil, causing a MakeHeap() style heap instance to be used + // } ----- end env methods ----- + + // { ----- begin heap methods ----- + NS_IMETHOD MakeHeap(nsIMdbEnv* ev, + nsIMdbHeap** acqHeap) = 0; // acquire new heap + // } ----- end heap methods ----- + + // { ----- begin row methods ----- + NS_IMETHOD MakeRow(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + nsIMdbRow** acqRow) = 0; // new row + // ioHeap can be nil, causing the heap associated with ev to be used + // } ----- end row methods ----- + + // { ----- begin port methods ----- + NS_IMETHOD CanOpenFilePort( + nsIMdbEnv* ev, // context + // const char* inFilePath, // the file to investigate + // const mdbYarn* inFirst512Bytes, + nsIMdbFile* ioFile, // db abstract file interface + mdb_bool* outCanOpen, // whether OpenFilePort() might succeed + mdbYarn* outFormatVersion) = 0; // informal file format description + + NS_IMETHOD OpenFilePort( + nsIMdbEnv* ev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // the file to open for readonly import + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental port open + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then call nsIMdbFactory::ThumbToOpenPort() to get the port instance. + + NS_IMETHOD + ThumbToOpenPort( // redeeming a completed thumb from OpenFilePort() + nsIMdbEnv* ev, // context + nsIMdbThumb* ioThumb, // thumb from OpenFilePort() with done status + nsIMdbPort** acqPort) = 0; // acquire new port object + // } ----- end port methods ----- + + // { ----- begin store methods ----- + NS_IMETHOD CanOpenFileStore( + nsIMdbEnv* ev, // context + // const char* inFilePath, // the file to investigate + // const mdbYarn* inFirst512Bytes, + nsIMdbFile* ioFile, // db abstract file interface + mdb_bool* outCanOpenAsStore, // whether OpenFileStore() might succeed + mdb_bool* outCanOpenAsPort, // whether OpenFilePort() might succeed + mdbYarn* outFormatVersion) = 0; // informal file format description + + NS_IMETHOD OpenFileStore( // open an existing database + nsIMdbEnv* ev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // the file to open for general db usage + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental store open + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then call nsIMdbFactory::ThumbToOpenStore() to get the store instance. + + NS_IMETHOD + ThumbToOpenStore( // redeem completed thumb from OpenFileStore() + nsIMdbEnv* ev, // context + nsIMdbThumb* ioThumb, // thumb from OpenFileStore() with done status + nsIMdbStore** acqStore) = 0; // acquire new db store object + + NS_IMETHOD CreateNewFileStore( // create a new db with minimal content + nsIMdbEnv* ev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // name of file which should not yet exist + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbStore** acqStore) = 0; // acquire new db store object + // } ----- end store methods ----- + + // } ===== end nsIMdbFactory methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbFactory, NS_IMDBFACTORY_IID) + +extern "C" nsIMdbFactory* MakeMdbFactory(); + +/*| nsIMdbFile: abstract file interface resembling the original morkFile +**| abstract interface (which was in turn modeled on the file interface +**| from public domain IronDoc). The design of this file interface is +**| complicated by the fact that some DB's will not find this interface +**| adequate for all runtime requirements (even though this file API is +**| enough to implement text-based DB's like Mork). For this reason, +**| more methods have been added to let a DB library force the file to +**| become closed so the DB can reopen the file in some other manner. +**| Folks are encouraged to suggest ways to tune this interface to suit +**| DB's that cannot manage to pull their maneuvers even given this API. +**| +**|| Tell: get the current i/o position in file +**| +**|| Seek: change the current i/o position in file +**| +**|| Eof: return file's total length in bytes +**| +**|| Read: input inSize bytes into outBuf, returning actual transfer size +**| +**|| Get: read starting at specific file offset (e.g. Seek(); Read();) +**| +**|| Write: output inSize bytes from inBuf, returning actual transfer size +**| +**|| Put: write starting at specific file offset (e.g. Seek(); Write();) +**| +**|| Flush: if written bytes are buffered, push them to final destination +**| +**|| Path: get file path in some string representation. This is intended +**| either to support the display of file name in a user presentation, or +**| to support the closing and reopening of the file when the DB needs more +**| exotic file access than is presented by the nsIMdbFile interface. +**| +**|| Steal: tell this file to close any associated i/o stream in the file +**| system, because the file ioThief intends to reopen the file in order +**| to provide the MDB implementation with more exotic file access than is +**| offered by the nsIMdbFile alone. Presumably the thief knows enough +**| from Path() in order to know which file to reopen. If Steal() is +**| successful, this file should probably delegate all future calls to +**| the nsIMdbFile interface down to the thief files, so that even after +**| the file has been stolen, it can still be read, written, or forcibly +**| closed (by a call to CloseMdbObject()). +**| +**|| Thief: acquire and return thief passed to an earlier call to Steal(). +|*/ + +#define NS_IMDBFILE_IID_STR "f04aa4ab-1fe7-4115-a4a5-6819dff1103d" + +#define NS_IMDBFILE_IID \ + { \ + 0xf04aa4ab, 0x1fe, 0x4115, { \ + 0xa4, 0xa5, 0x68, 0x19, 0xdf, 0xf1, 0x10, 0x3d \ + } \ + } + +class nsIMdbFile : public nsISupports { // minimal file interface + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBFILE_IID) + // { ===== begin nsIMdbFile methods ===== + + // { ----- begin pos methods ----- + NS_IMETHOD Tell(nsIMdbEnv* ev, mdb_pos* outPos) const = 0; + NS_IMETHOD Seek(nsIMdbEnv* ev, mdb_pos inPos, mdb_pos* outPos) = 0; + NS_IMETHOD Eof(nsIMdbEnv* ev, mdb_pos* outPos) = 0; + // } ----- end pos methods ----- + + // { ----- begin read methods ----- + NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mdb_size inSize, + mdb_size* outActualSize) = 0; + NS_IMETHOD Get(nsIMdbEnv* ev, void* outBuf, mdb_size inSize, mdb_pos inPos, + mdb_size* outActualSize) = 0; + // } ----- end read methods ----- + + // { ----- begin write methods ----- + NS_IMETHOD Write(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize, + mdb_size* outActualSize) = 0; + NS_IMETHOD Put(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize, + mdb_pos inPos, mdb_size* outActualSize) = 0; + NS_IMETHOD Flush(nsIMdbEnv* ev) = 0; + // } ----- end attribute methods ----- + + // { ----- begin path methods ----- + NS_IMETHOD Path(nsIMdbEnv* ev, mdbYarn* outFilePath) = 0; + // } ----- end path methods ----- + + // { ----- begin replacement methods ----- + NS_IMETHOD Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) = 0; + NS_IMETHOD Thief(nsIMdbEnv* ev, nsIMdbFile** acqThief) = 0; + // } ----- end replacement methods ----- + + // { ----- begin versioning methods ----- + NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev) = 0; + // If this file is a file version branch created by calling AcquireBud(), + // BecomeTrunk() causes this file's content to replace the original + // file's content, typically by assuming the original file's identity. + // This default implementation of BecomeTrunk() does nothing, and this + // is appropriate behavior for files which are not branches, and is + // also the right behavior for files returned from AcquireBud() which are + // in fact the original file that has been truncated down to zero length. + + NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + nsIMdbFile** acqBud) = + 0; // acquired file for new version of content + // AcquireBud() starts a new "branch" version of the file, empty of content, + // so that a new version of the file can be written. This new file + // can later be told to BecomeTrunk() the original file, so the branch + // created by budding the file will replace the original file. Some + // file subclasses might initially take the unsafe but expedient + // approach of simply truncating this file down to zero length, and + // then returning the same morkFile pointer as this, with an extra + // reference count increment. Note that the caller of AcquireBud() is + // expected to eventually call CutStrongRef() on the returned file + // in order to release the strong reference. High quality versions + // of morkFile subclasses will create entirely new files which later + // are renamed to become the old file, so that better transactional + // behavior is exhibited by the file, so crashes protect old files. + // Note that AcquireBud() is an illegal operation on readonly files. + // } ----- end versioning methods ----- + + // } ===== end nsIMdbFile methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbFile, NS_IMDBFILE_IID) + +/*| nsIMdbPort: a readonly interface to a specific database file. The mutable +**| nsIMdbStore interface is a subclass that includes writing behavior, but +**| most of the needed db methods appear in the readonly nsIMdbPort interface. +**| +**|| mdbYarn: note all nsIMdbPort and nsIMdbStore subclasses must guarantee null +**| termination of all strings written into mdbYarn instances, as long as +**| mYarn_Size and mYarn_Buf are nonzero. Even truncated string values must +**| be null terminated. This is more strict behavior than mdbYarn requires, +**| but it is part of the nsIMdbPort and nsIMdbStore interface. +**| +**|| attributes: methods are provided to distinguish a readonly port from a +**| mutable store, and whether a mutable store actually has any dirty content. +**| +**|| filepath: the file path used to open the port from the nsIMdbFactory can be +**| queried and discovered by GetPortFilePath(), which includes format info. +**| +**|| export: a port can write itself in other formats, with perhaps a typical +**| emphasis on text interchange formats used by other systems. A port can be +**| queried to determine its preferred export interchange format, and a port +**| can be queried to see whether a specific export format is supported. And +**| actually exporting a port requires a new destination file name and format. +**| +**|| tokens: a port supports queries about atomized strings to map tokens to +**| strings or strings to token integers. (All atomized strings must be in +**| US-ASCII iso-8859-1 Latin1 charset encoding.) When a port is actually a +**| mutable store and a string has not yet been atomized, then StringToToken() +**| will actually do so and modify the store. The QueryToken() method will not +**| atomize a string if it has not already been atomized yet, even in stores. +**| +**|| tables: other than string tokens, all port content is presented through +**| tables, which are ordered collections of rows. Tables are identified by +**| row scope and table kind, which might or might not be unique in a port, +**| depending on app convention. When tables are effectively unique, then +**| queries for specific scope and kind pairs will find those tables. To see +**| all tables that match specific row scope and table kind patterns, even in +**| the presence of duplicates, every port supports a GetPortTableCursor() +**| method that returns an iterator over all matching tables. Table kind is +**| considered scoped inside row scope, so passing a zero for table kind will +**| find all table kinds for some nonzero row scope. Passing a zero for row +**| scope will iterate over all tables in the port, in some undefined order. +**| (A new table can be added to a port using nsIMdbStore::NewTable(), even when +**| the requested scope and kind combination is already used by other tables.) +**| +**|| memory: callers can request that a database use less memory footprint in +**| several flavors, from an inconsequential idle flavor to a rather drastic +**| panic flavor. Callers might perform an idle purge very frequently if desired +**| with very little cost, since only normally scheduled memory management will +**| be conducted, such as freeing resources for objects scheduled to be dropped. +**| Callers should perform session memory purges infrequently because they might +**| involve costly scanning of data structures to removed cached content, and +**| session purges are recommended only when a caller experiences memory crunch. +**| Callers should only rarely perform a panic purge, in response to dire memory +**| straits, since this is likely to make db operations much more expensive +**| than they would be otherwise. A panic purge asks a database to free as much +**| memory as possible while staying effective and operational, because a caller +**| thinks application failure might otherwise occur. (Apps might better close +**| an open db, so panic purges only make sense when a db is urgently needed.) +|*/ +class nsIMdbPort : public nsISupports { + public: + // { ===== begin nsIMdbPort methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD GetIsPortReadonly(nsIMdbEnv* ev, mdb_bool* outBool) = 0; + NS_IMETHOD GetIsStore(nsIMdbEnv* ev, mdb_bool* outBool) = 0; + NS_IMETHOD GetIsStoreAndDirty(nsIMdbEnv* ev, mdb_bool* outBool) = 0; + + NS_IMETHOD GetUsagePolicy(nsIMdbEnv* ev, mdbUsagePolicy* ioUsagePolicy) = 0; + + NS_IMETHOD SetUsagePolicy(nsIMdbEnv* ev, + const mdbUsagePolicy* inUsagePolicy) = 0; + // } ----- end attribute methods ----- + + // { ----- begin memory policy methods ----- + NS_IMETHOD IdleMemoryPurge( // do memory management already scheduled + nsIMdbEnv* ev, // context + mdb_size* outEstimatedBytesFreed) = + 0; // 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) = + 0; // approximate bytes actually freed + + NS_IMETHOD PanicMemoryPurge( // desperately free all possible memory + nsIMdbEnv* ev, // context + mdb_size* outEstimatedBytesFreed) = + 0; // 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) = 0; // file format description + + NS_IMETHOD GetPortFile(nsIMdbEnv* ev, // context + nsIMdbFile** acqFile) = + 0; // 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) = 0; // file format description + + // some tentative suggested import/export formats + // "ns:msg:db:port:format:ldif:ns4.0:passthrough" // necessary + // "ns:msg:db:port:format:ldif:ns4.5:utf8" // necessary + // "ns:msg:db:port:format:ldif:ns4.5:tabbed" + // "ns:msg:db:port:format:ldif:ns4.5:binary" // necessary + // "ns:msg:db:port:format:html:ns3.0:addressbook" // necessary + // "ns:msg:db:port:format:html:display:verbose" + // "ns:msg:db:port:format:html:display:concise" + // "ns:msg:db:port:format:mork:zany:verbose" // necessary + // "ns:msg:db:port:format:mork:zany:atomized" // necessary + // "ns:msg:db:port:format:rdf:xml" + // "ns:msg:db:port:format:xml:mork" + // "ns:msg:db:port:format:xml:display:verbose" + // "ns:msg:db:port:format:xml:display:concise" + // "ns:msg:db:port:format:xml:print:verbose" // recommended + // "ns:msg:db:port:format:xml:print:concise" + + NS_IMETHOD + CanExportToFormat( // can export content in given specific format? + nsIMdbEnv* ev, // context + const char* inFormatVersion, // file format description + mdb_bool* outCanExport) = 0; // 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) = 0; // 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) = 0; // 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) = 0; // 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) = 0; // 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) = 0; // 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) = 0; // 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) = 0; // acquire specific row (or null) + + // NS_IMETHOD + // GetPortRowCursor( // get cursor for all rows in specific scope + // nsIMdbEnv* ev, // context + // mdb_scope inRowScope, // row scope for row ids + // nsIMdbPortRowCursor** acqCursor) = 0; // all such rows in the port + + 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) = 0; // 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) = 0; // whether GetTable() might succeed + + NS_IMETHOD GetTable( // access one table with specific oid + nsIMdbEnv* ev, // context + const mdbOid* inOid, // hypothetical table oid + nsIMdbTable** acqTable) = 0; // 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) = 0; // whether GetTableKind() might succeed + + // row scopes to be supported include the following suggestions: + // "ns:msg:db:row:scope:address:cards:all" + // "ns:msg:db:row:scope:mail:messages:all" + // "ns:msg:db:row:scope:news:articles:all" + + // table kinds to be supported include the following suggestions: + // "ns:msg:db:table:kind:address:cards:main" + // "ns:msg:db:table:kind:address:lists:all" + // "ns:msg:db:table:kind:address:list" + // "ns:msg:db:table:kind:news:threads:all" + // "ns:msg:db:table:kind:news:thread" + // "ns:msg:db:table:kind:mail:threads:all" + // "ns:msg:db:table:kind:mail:thread" + + 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) = 0; // 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) = 0; // 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) = 0; // 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 ===== +}; + +/*| nsIMdbStore: a mutable interface to a specific database file. +**| +**|| tables: one can force a new table to exist in a store with NewTable() +**| and nonzero values for both row scope and table kind. (If one wishes only +**| one table of a certain kind, then one might look for it first using the +**| GetTableKind() method). One can pass inMustBeUnique to force future +**| users of this store to be unable to create other tables with the same pair +**| of scope and kind attributes. When inMustBeUnique is true, and the table +**| with the given scope and kind pair already exists, then the existing one +**| is returned instead of making a new table. Similarly, if one passes false +**| for inMustBeUnique, but the table kind has already been marked unique by a +**| previous user of the store, then the existing unique table is returned. +**| +**|| import: all or some of another port's content can be imported by calling +**| AddPortContent() with a row scope identifying the extent of content to +**| be imported. A zero row scope will import everything. A nonzero row +**| scope will only import tables with a matching row scope. Note that one +**| must somehow find a way to negotiate possible conflicts between existing +**| row content and imported row content, and this involves a specific kind of +**| definition for row identity involving either row IDs or unique attributes, +**| or some combination of these two. At the moment I am just going to wave +**| my hands, and say the default behavior is to assign all new row identities +**| to all imported content, which will result in no merging of content; this +**| must change later because it is unacceptable in some contexts. +**| +**|| commits: to manage modifications in a mutable store, very few methods are +**| really needed to indicate global policy choices that are independent of +**| the actual modifications that happen in objects at the level of tables, +**| rows, and cells, etc. The most important policy to specify is which sets +**| of changes are considered associated in a manner such that they should be +**| applied together atomically to a given store. We call each such group of +**| changes a transaction. We handle three different grades of transaction, +**| but they differ only in semantic significance to the application, and are +**| not intended to nest. (If small transactions were nested inside large +**| transactions, that would imply that a single large transaction must be +**| atomic over all the contained small transactions; but actually we intend +**| smalls transaction never be undone once committed due to, say, aborting a +**| transaction of greater significance.) The small, large, and session level +**| commits have equal granularity, and differ only in risk of loss from the +**| perspective of an application. Small commits characterize changes that +**| can be lost with relatively small risk, so small transactions can delay +**| until later if they are expensive or impractical to commit. Large commits +**| involve changes that would probably inconvenience users if lost, so the +**| need to pay costs of writing is rather greater than with small commits. +**| Session commits are last ditch attempts to save outstanding changes before +**| stopping the use of a particular database, so there will be no later point +**| in time to save changes that have been delayed due to possible high cost. +**| If large commits are never delayed, then a session commit has about the +**| same performance effect as another large commit; but if small and large +**| commits are always delayed, then a session commit is likely to be rather +**| expensive as a runtime cost compared to any earlier database usage. +**| +**|| aborts: the only way to abort changes to a store is by closing the store. +**| So there is no specific method for causing any abort. Stores must discard +**| all changes made that are uncommitted when a store is closed. This design +**| choice makes the implementations of tables, rows, and cells much less +**| complex because they need not maintain a record of undobable changes. When +**| a store is closed, presumably this precipitates the closure of all tables, +**| rows, and cells in the store as well. So an application can revert the +**| state of a store in the user interface by quietly closing and reopening a +**| store, because this will discard uncommitted changes and show old content. +**| This implies an app that closes a store will need to send a "scramble" +**| event notification to any views that depend on old discarded content. +|*/ + +#define NS_IMDBSTORE_IID_STR "74d6218d-44b0-43b5-9ebe-69a17dfb562c" +#define NS_IMDBSTORE_IID \ + { \ + 0x74d6218d, 0x44b0, 0x43b5, { \ + 0x9e, 0xbe, 0x69, 0xa1, 0x7d, 0xfb, 0x56, 0x2c \ + } \ + } + +class nsIMdbStore : public nsIMdbPort { + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBSTORE_IID) + + // { ===== 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) = 0; // 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) = 0; // 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) = + 0; // 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) = + 0; // 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) = + 0; // 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) = 0; // create new row + + NS_IMETHOD NewRow(nsIMdbEnv* ev, // new row with db assigned oid + mdb_scope inRowScope, // row scope for row ids + nsIMdbRow** acqRow) = 0; // 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) = 0; // 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) = 0; // 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) = 0; // cols desired tokenized together + + NS_IMETHOD + AvoidAtomColumnsHint( // advise column with poor atomizing prospects + nsIMdbEnv* ev, // context + const mdbColumnSet* inColumnSet) = + 0; // 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) = 0; // 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) = 0; // 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) = 0; // 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 ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbStore, NS_IMDBSTORE_IID) + +/*| nsIMdbCursor: base cursor class for iterating row cells and table rows +**| +**|| count: the number of elements in the collection (table or row) +**| +**|| seed: the change count in the underlying collection, which is synced +**| with the collection when the iteration position is set, and henceforth +**| acts to show whether the iter has lost collection synchronization, in +**| case it matters to clients whether any change happens during iteration. +**| +**|| pos: the position of the current element in the collection. Negative +**| means a position logically before the first element. A positive value +**| equal to count (or larger) implies a position after the last element. +**| To iterate over all elements, set the position to negative, so subsequent +**| calls to any 'next' method will access the first collection element. +**| +**|| doFailOnSeedOutOfSync: whether a cursor should return an error if the +**| cursor's snapshot of a table's seed becomes stale with respect the table's +**| current seed value (which implies the iteration is less than total) in +**| between to cursor calls that actually access collection content. By +**| default, a cursor should assume this attribute is false until specified, +**| so that iterations quietly try to re-sync when they lose coherence. +|*/ + +#define NS_IMDBCURSOR_IID_STR "a0c37337-6ebc-474c-90db-e65ea0b850aa" + +#define NS_IMDBCURSOR_IID \ + { \ + 0xa0c37337, 0x6ebc, 0x474c, { \ + 0x90, 0xdb, 0xe6, 0x5e, 0xa0, 0xb8, 0x50, 0xaa \ + } \ + } + +class nsIMdbCursor : public nsISupports { // collection iterator + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBCURSOR_IID) + // { ===== begin nsIMdbCursor methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD GetCount(nsIMdbEnv* ev, mdb_count* outCount) = 0; // readonly + NS_IMETHOD GetSeed(nsIMdbEnv* ev, mdb_seed* outSeed) = 0; // readonly + + NS_IMETHOD SetPos(nsIMdbEnv* ev, mdb_pos inPos) = 0; // mutable + NS_IMETHOD GetPos(nsIMdbEnv* ev, mdb_pos* outPos) = 0; + + NS_IMETHOD SetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool inFail) = 0; + NS_IMETHOD GetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool* outFail) = 0; + // } ----- end attribute methods ----- + + // } ===== end nsIMdbCursor methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbCursor, NS_IMDBCURSOR_IID) + +#define NS_IMDBPORTTABLECURSOR_IID_STR = "f181a41e-933d-49b3-af93-20d3634b8b78" + +#define NS_IMDBPORTTABLECURSOR_IID \ + { \ + 0xf181a41e, 0x933d, 0x49b3, { \ + 0xaf, 0x93, 0x20, 0xd3, 0x63, 0x4b, 0x8b, 0x78 \ + } \ + } + +/*| nsIMdbPortTableCursor: cursor class for iterating port tables +**| +**|| port: the cursor is associated with a specific port, which can be +**| set to a different port (which resets the position to -1 so the +**| next table acquired is the first in the port. +**| +|*/ +class nsIMdbPortTableCursor : public nsISupports { // table collection iterator + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBPORTTABLECURSOR_IID) + // { ===== begin nsIMdbPortTableCursor methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD SetPort(nsIMdbEnv* ev, nsIMdbPort* ioPort) = 0; // sets pos to -1 + NS_IMETHOD GetPort(nsIMdbEnv* ev, nsIMdbPort** acqPort) = 0; + + NS_IMETHOD SetRowScope(nsIMdbEnv* ev, // sets pos to -1 + mdb_scope inRowScope) = 0; + NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) = 0; + // setting row scope to zero iterates over all row scopes in port + + NS_IMETHOD SetTableKind(nsIMdbEnv* ev, // sets pos to -1 + mdb_kind inTableKind) = 0; + NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) = 0; + // setting table kind to zero iterates over all table kinds in row scope + // } ----- end attribute methods ----- + + // { ----- begin table iteration methods ----- + NS_IMETHOD NextTable( // get table at next position in the db + nsIMdbEnv* ev, // context + nsIMdbTable** acqTable) = 0; // the next table in the iteration + // } ----- end table iteration methods ----- + + // } ===== end nsIMdbPortTableCursor methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbPortTableCursor, NS_IMDBPORTTABLECURSOR_IID) + +/*| nsIMdbCollection: an object that collects a set of other objects as members. +**| The main purpose of this base class is to unify the perceived semantics +**| of tables and rows where their collection behavior is similar. This helps +**| isolate the mechanics of collection behavior from the other semantics that +**| are more characteristic of rows and tables. +**| +**|| count: the number of objects in a collection is the member count. (Some +**| collection interfaces call this attribute the 'size', but that can be a +**| little ambiguous, and counting actual members is harder to confuse.) +**| +**|| seed: the seed of a collection is a counter for changes in membership in +**| a specific collection. This seed should change when members are added to +**| or removed from a collection, but not when a member changes internal state. +**| The seed should also change whenever the internal collection of members has +**| a complex state change that reorders member positions (say by sorting) that +**| would affect the nature of an iteration over that collection of members. +**| The purpose of a seed is to inform any outstanding collection cursors that +**| they might be stale, without incurring the cost of broadcasting an event +**| notification to such cursors, which would need more data structure support. +**| Presumably a cursor in a particular mdb code suite has much more direct +**| access to a collection seed member slot that this abstract COM interface, +**| so this information is intended more for clients outside mdb that want to +**| make inferences similar to those made by the collection cursors. The seed +**| value as an integer magnitude is not very important, and callers should not +**| assume meaningful information can be derived from an integer value beyond +**| whether it is equal or different from a previous inspection. A seed uses +**| integers of many bits in order to make the odds of wrapping and becoming +**| equal to an earlier seed value have probability that is vanishingly small. +**| +**|| port: every collection is associated with a specific database instance. +**| +**|| cursor: a subclass of nsIMdbCursor suitable for this specific collection +**| subclass. The ability to GetCursor() from the base nsIMdbCollection class +**| is not really as useful as getting a more specifically typed cursor more +**| directly from the base class without any casting involved. So including +**| this method here is more for conceptual illustration. +**| +**|| oid: every collection has an identity that persists from session to +**| session. Implementations are probably able to distinguish row IDs from +**| table IDs, but we don't specify anything official in this regard. A +**| collection has the same identity for the lifetime of the collection, +**| unless identity is swapped with another collection by means of a call to +**| BecomeContent(), which is considered a way to swap a new representation +**| for an old well-known object. (Even so, only content appears to change, +**| while the identity seems to stay the same.) +**| +**|| become: developers can effectively cause two objects to swap identities, +**| in order to effect a complete swap between what persistent content is +**| represented by two oids. The caller should consider this a content swap, +**| and not identity wap, because identities will seem to stay the same while +**| only content changes. However, implementations will likely do this +**| internally by swapping identities. Callers must swap content only +**| between objects of similar type, such as a row with another row, and a +**| table with another table, because implementations need not support +**| cross-object swapping because it might break object name spaces. +**| +**|| dropping: when a caller expects a row or table will no longer be used, the +**| caller can tell the collection to 'drop activity', which means the runtime +**| object can have its internal representation purged to save memory or any +**| other resource that is being consumed by the collection's representation. +**| This has no effect on the collection's persistent content or semantics, +**| and is only considered a runtime effect. After a collection drops +**| activity, the object should still be as usable as before (because it has +**| NOT been closed), but further usage can be expensive to re-instate because +**| it might involve reallocating space and/or re-reading disk space. But +**| since this future usage is not expected, the caller does not expect to +**| pay the extra expense. An implementation can choose to implement +**| 'dropping activity' in different ways, or even not at all if this +**| operation is not really feasible. Callers cannot ask objects whether they +**| are 'dropped' or not, so this should be transparent. (Note that +**| implementors might fear callers do not really know whether future +**| usage will occur, and therefore might delay the act of dropping until +**| the near future, until seeing whether the object is used again +**| immediately elsewhere. Such use soon after the drop request might cause +**| the drop to be cancelled.) +|*/ +class nsIMdbCollection : public nsISupports { // sequence of objects + public: + // { ===== begin nsIMdbCollection methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD GetSeed(nsIMdbEnv* ev, + mdb_seed* outSeed) = 0; // member change count + NS_IMETHOD GetCount(nsIMdbEnv* ev, + mdb_count* outCount) = 0; // member count + + NS_IMETHOD GetPort(nsIMdbEnv* ev, + nsIMdbPort** acqPort) = 0; // collection container + // } ----- end attribute methods ----- + + // { ----- begin cursor methods ----- + NS_IMETHOD GetCursor( // make a cursor starting iter at inMemberPos + nsIMdbEnv* ev, // context + mdb_pos inMemberPos, // zero-based ordinal pos of member in collection + nsIMdbCursor** acqCursor) = 0; // acquire new cursor instance + // } ----- end cursor methods ----- + + // { ----- begin ID methods ----- + NS_IMETHOD GetOid(nsIMdbEnv* ev, + mdbOid* outOid) = 0; // read object identity + NS_IMETHOD BecomeContent(nsIMdbEnv* ev, + const mdbOid* inOid) = 0; // exchange content + // } ----- end ID methods ----- + + // { ----- begin activity dropping methods ----- + NS_IMETHOD DropActivity( // tell collection usage no longer expected + nsIMdbEnv* ev) = 0; + // } ----- end activity dropping methods ----- + + // } ===== end nsIMdbCollection methods ===== +}; + +/*| nsIMdbTable: an ordered collection of rows +**| +**|| row scope: an integer token for an atomized string in this database +**| that names a space for row IDs. This attribute of a table is intended +**| as guidance metainformation that helps with searching a database for +**| tables that operate on collections of rows of the specific type. By +**| convention, a table with a specific row scope is expected to focus on +**| containing rows that belong to that scope, however exceptions are easily +**| allowed because all rows in a table are known by both row ID and scope. +**| (A table with zero row scope is never allowed because this would make it +**| ambiguous to use a zero row scope when iterating over tables in a port to +**| indicate that all row scopes should be seen by a cursor.) +**| +**|| table kind: an integer token for an atomized string in this database +**| that names a kind of table as a subset of the associated row scope. This +**| attribute is intended as guidance metainformation to clarify the role of +**| this table with respect to other tables in the same row scope, and this +**| also helps search for such tables in a database. By convention, a table +**| with a specific table kind has a consistent role for containing rows with +**| respect to other collections of such rows in the same row scope. Also by +**| convention, at least one table in a row scope has a table kind purporting +**| to contain ALL the rows that belong in that row scope, so that at least +**| one table exists that allows all rows in a scope to be iterated over. +**| (A table with zero table kind is never allowed because this would make it +**| ambiguous to use a zero table kind when iterating over tables in a port to +**| indicate that all table kinds in a row scope should be seen by a cursor.) +**| +**|| port: every table is considered part of some port that contains the +**| table, so that closing the containing port will cause the table to be +**| indirectly closed as well. We make it easy to get the containing port for +**| a table, because the port supports important semantic interfaces that will +**| affect how content in table is presented; the most important port context +**| that affects a table is specified by the set of token to string mappings +**| that affect all tokens used throughout the database, and which drive the +**| meanings of row scope, table kind, cell columns, etc. +**| +**|| cursor: a cursor that iterates over the rows in this table, where rows +**| have zero-based index positions from zero to count-1. Making a cursor +**| with negative position will next iterate over the first row in the table. +**| +**|| position: given any position from zero to count-1, a table will return +**| the row ID and row scope for the row at that position. (One can use the +**| GetRowAllCells() method to read that row, or else use a row cursor to both +**| get the row at some position and read its content at the same time.) The +**| position depends on whether a table is sorted, and upon the actual sort. +**| Note that moving a row's position is only possible in unsorted tables. +**| +**|| row set: every table contains a collection of rows, where a member row is +**| referenced by the table using the row ID and row scope for the row. No +**| single table owns a given row instance, because rows are effectively ref- +**| counted and destroyed only when the last table removes a reference to that +**| particular row. (But a row can be emptied of all content no matter how +**| many refs exist, and this might be the next best thing to destruction.) +**| Once a row exists in a least one table (after NewRow() is called), then it +**| can be added to any other table by calling AddRow(), or removed from any +**| table by calling CutRow(), or queried as a member by calling HasRow(). A +**| row can only be added to a table once, and further additions do nothing and +**| complain not at all. Cutting a row from a table only does something when +**| the row was actually a member, and otherwise does nothing silently. +**| +**|| row ref count: one can query the number of tables (and/or cells) +**| containing a row as a member or a child. +**| +**|| row content: one can access or modify the cell content in a table's row +**| by moving content to or from an instance of nsIMdbRow. Note that nsIMdbRow +**| never represents the actual row inside a table, and this is the reason +**| why nsIMdbRow instances do not have row IDs or row scopes. So an instance +**| of nsIMdbRow always and only contains a snapshot of some or all content in +**| past, present, or future persistent row inside a table. This means that +**| reading and writing rows in tables has strictly copy semantics, and we +**| currently do not plan any exceptions for specific performance reasons. +**| +**|| sorting: note all rows are assumed sorted by row ID as a secondary +**| sort following the primary column sort, when table rows are sorted. +**| +**|| indexes: +|*/ + +#define NS_IMDBTABLE_IID_STR = "fe11bc98-d02b-4128-9fac-87042fdf9639" + +#define NS_IMDBTABLE_IID \ + { \ + 0xfe11bc98, 0xd02b, 0x4128, { \ + 0x9f, 0xac, 0x87, 0x04, 0x2f, 0xdf, 0x96, 0x39 \ + } \ + } + +class nsIMdbTable : public nsIMdbCollection { // a collection of rows + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTABLE_IID) + // { ===== begin nsIMdbTable methods ===== + + // { ----- begin meta attribute methods ----- + NS_IMETHOD SetTablePriority(nsIMdbEnv* ev, mdb_priority inPrio) = 0; + NS_IMETHOD GetTablePriority(nsIMdbEnv* ev, mdb_priority* outPrio) = 0; + + NS_IMETHOD GetTableBeVerbose(nsIMdbEnv* ev, mdb_bool* outBeVerbose) = 0; + NS_IMETHOD SetTableBeVerbose(nsIMdbEnv* ev, mdb_bool inBeVerbose) = 0; + + NS_IMETHOD GetTableIsUnique(nsIMdbEnv* ev, mdb_bool* outIsUnique) = 0; + + NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) = 0; + NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) = 0; + + NS_IMETHOD GetMetaRow( + nsIMdbEnv* ev, // context + const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying + mdbOid* outOid, // output meta row oid, can be nil to suppress output + nsIMdbRow** acqRow) = 0; // 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 "m" (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. + // } ----- end meta attribute methods ----- + + // { ----- begin cursor methods ----- + NS_IMETHOD + GetTableRowCursor( // make a cursor, starting iteration at inRowPos + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbTableRowCursor** acqCursor) = 0; // acquire new cursor instance + // } ----- end row position methods ----- + + // { ----- begin row position methods ----- + NS_IMETHOD PosToOid( // get row member for a table position + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + mdbOid* outOid) = 0; // row oid at the specified position + + NS_IMETHOD OidToPos( // test for the table position of a row member + nsIMdbEnv* ev, // context + const mdbOid* inOid, // row to find in table + mdb_pos* outPos) = 0; // zero-based ordinal position of row in table + + NS_IMETHOD PosToRow( // test for the table position of a row member + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbRow** acqRow) = 0; // acquire row at table position inRowPos + + NS_IMETHOD RowToPos( // test for the table position of a row member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow, // row to find in table + mdb_pos* outPos) = 0; // zero-based ordinal position of row in table + // } ----- end row position methods ----- + + // { ----- begin oid set methods ----- + NS_IMETHOD AddOid( // make sure the row with inOid is a table member + nsIMdbEnv* ev, // context + const mdbOid* inOid) = 0; // row to ensure membership in table + + NS_IMETHOD HasOid( // test for the table position of a row member + nsIMdbEnv* ev, // context + const mdbOid* inOid, // row to find in table + mdb_bool* outHasOid) = 0; // whether inOid is a member row + + NS_IMETHOD CutOid( // make sure the row with inOid is not a member + nsIMdbEnv* ev, // context + const mdbOid* inOid) = 0; // row to remove from table + // } ----- end oid set methods ----- + + // { ----- begin row set methods ----- + NS_IMETHOD NewRow( // create a new row instance in table + nsIMdbEnv* ev, // context + mdbOid* + ioOid, // please use minus one (unbound) rowId for db-assigned IDs + nsIMdbRow** acqRow) = 0; // create new row + + NS_IMETHOD AddRow( // make sure the row with inOid is a table member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow) = 0; // row to ensure membership in table + + NS_IMETHOD HasRow( // test for the table position of a row member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow, // row to find in table + mdb_bool* outHasRow) = 0; // whether row is a table member + + NS_IMETHOD CutRow( // make sure the row with inOid is not a member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow) = 0; // row to remove from table + + NS_IMETHOD CutAllRows( // remove all rows from the table + nsIMdbEnv* ev) = 0; // context + // } ----- end row set methods ----- + + // { ----- begin hinting methods ----- + NS_IMETHOD SearchColumnsHint( // advise re future expected search cols + nsIMdbEnv* ev, // context + const mdbColumnSet* inColumnSet) = 0; // columns likely to be searched + + NS_IMETHOD SortColumnsHint( // advise re future expected sort columns + nsIMdbEnv* ev, // context + const mdbColumnSet* inColumnSet) = 0; // columns for likely sort requests + + NS_IMETHOD StartBatchChangeHint( // advise before many adds and cuts + nsIMdbEnv* ev, // context + const void* inLabel) = 0; // 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. + + NS_IMETHOD EndBatchChangeHint( // advise before many adds and cuts + nsIMdbEnv* ev, // context + const void* inLabel) = 0; // 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. + // } ----- end hinting methods ----- + + // { ----- begin searching methods ----- + NS_IMETHOD FindRowMatches( // search variable number of sorted cols + nsIMdbEnv* ev, // context + const mdbYarn* + inPrefix, // content to find as prefix in row's column cell + nsIMdbTableRowCursor** acqCursor) = 0; // set of matching rows + + NS_IMETHOD GetSearchColumns( // query columns used by FindRowMatches() + nsIMdbEnv* ev, // context + mdb_count* outCount, // context + mdbColumnSet* outColSet) = 0; // 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. + // } ----- end searching 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_IMETHOD + CanSortColumn( // query which column is currently used for sorting + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to query sorting potential + mdb_bool* outCanSort) = 0; // whether the column can be sorted + + NS_IMETHOD GetSorting( // view same table in particular sorting + nsIMdbEnv* ev, // context + mdb_column inColumn, // requested new column for sorting table + nsIMdbSorting** acqSorting) = 0; // acquire sorting for column + + NS_IMETHOD SetSearchSorting( // use this sorting in FindRowMatches() + nsIMdbEnv* ev, // context + mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn() + nsIMdbSorting* ioSorting) = 0; // 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). + // } ----- end sorting methods ----- + + // { ----- begin moving methods ----- + // moving a row does nothing unless a table is currently unsorted + + NS_IMETHOD MoveOid( // change position of row in unsorted table + nsIMdbEnv* ev, // 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 inRowId + mdb_pos* outActualPos) = 0; // actual new position of row in table + + NS_IMETHOD MoveRow( // change position of row in unsorted table + nsIMdbEnv* ev, // 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 inRowId + mdb_pos* outActualPos) = 0; // actual new position of row in table + // } ----- end moving methods ----- + + // { ----- begin index methods ----- + NS_IMETHOD AddIndex( // create a sorting index for column if possible + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column to sort by index + nsIMdbThumb** acqThumb) = + 0; // 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_IMETHOD CutIndex( // stop supporting a specific column index + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column with index to be removed + nsIMdbThumb** acqThumb) = + 0; // 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_IMETHOD HasIndex( // query for current presence of a column index + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column to investigate + mdb_bool* outHasIndex) = 0; // whether column has index for this column + + NS_IMETHOD EnableIndexOnSort( // create an index for col on first sort + nsIMdbEnv* ev, // context + mdb_column inColumn) = 0; // the column to index if ever sorted + + NS_IMETHOD QueryIndexOnSort( // check whether index on sort is enabled + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column to investigate + mdb_bool* outIndexOnSort) = + 0; // whether column has index-on-sort enabled + + NS_IMETHOD DisableIndexOnSort( // prevent future index creation on sort + nsIMdbEnv* ev, // context + mdb_column inColumn) = 0; // the column to index if ever sorted + // } ----- end index methods ----- + + // } ===== end nsIMdbTable methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbTable, NS_IMDBTABLE_IID) + +/*| nsIMdbSorting: a view of a table in some particular sort order. This +**| row order closely resembles a readonly array of rows with the same row +**| membership as the underlying table, but in a different order than the +**| table's explicit row order. But the sorting's row membership changes +**| whenever the table's membership changes (without any notification, so +**| keep this in mind when modifying the table). +**| +**|| table: every sorting is associated with a particular table. You +**| cannot change which table is used by a sorting (just ask some new +**| table for a suitable sorting instance instead). +**| +**|| compare: the ordering method used by a sorting, wrapped up in a +**| abstract plug-in interface. When this was never installed by an +**| explicit call to SetNewCompare(), a compare object is still returned, +**| and it might match the compare instance returned by the factory method +**| nsIMdbFactory::MakeCompare(), which represents a default sort order +**| (which we fervently hope is consistently ASCII byte ordering). +**| +**|| cursor: in case callers are more comfortable with a cursor style +**| of accessing row members, each sorting will happily return a cursor +**| instance with behavior very similar to a cursor returned from a call +**| to nsIMdbTable::GetTableRowCursor(), but with different row order. +**| A cursor should show exactly the same information as the pos methods. +**| +**|| pos: the PosToOid() and PosToRow() methods are just like the table +**| methods of the same name, except they show rows in the sort order of +**| the sorting, rather than that of the table. These methods are like +**| readonly array position accessor's, or like a C++ operator[]. +|*/ +class nsIMdbSorting : public nsIMdbObject { // sorting of some table + public: + // { ===== begin nsIMdbSorting methods ===== + + // { ----- begin attribute 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_IMETHOD GetTable(nsIMdbEnv* ev, nsIMdbTable** acqTable) = 0; + NS_IMETHOD GetSortColumn( // query which col is currently sorted + nsIMdbEnv* ev, // context + mdb_column* outColumn) = 0; // col the table uses for sorting (or zero) + + // } ----- end attribute methods ----- + + // { ----- begin cursor methods ----- + NS_IMETHOD GetSortingRowCursor( // make a cursor, starting at inRowPos + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbTableRowCursor** acqCursor) = 0; // acquire new cursor instance + // A cursor interface turning same info as PosToOid() or PosToRow(). + // } ----- end row position methods ----- + + // { ----- begin row position methods ----- + NS_IMETHOD PosToOid( // get row member for a table position + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + mdbOid* outOid) = 0; // row oid at the specified position + + NS_IMETHOD PosToRow( // test for the table position of a row member + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbRow** acqRow) = 0; // acquire row at table position inRowPos + // } ----- end row position methods ----- + + // } ===== end nsIMdbSorting methods ===== +}; + +/*| nsIMdbTableRowCursor: cursor class for iterating table rows +**| +**|| table: the cursor is associated with a specific table, which can be +**| set to a different table (which resets the position to -1 so the +**| next row acquired is the first in the table. +**| +**|| NextRowId: the rows in the table can be iterated by identity alone, +**| without actually reading the cells of any row with this method. +**| +**|| NextRowCells: read the next row in the table, but only read cells +**| from the table which are already present in the row (so no new cells +**| are added to the row, even if they are present in the table). All the +**| cells will have content specified, even it is the empty string. No +**| columns will be removed, even if missing from the row (because missing +**| and empty are semantically equivalent). +**| +**|| NextRowAllCells: read the next row in the table, and access all the +**| cells for this row in the table, adding any missing columns to the row +**| as needed until all cells are represented. All the +**| cells will have content specified, even it is the empty string. No +**| columns will be removed, even if missing from the row (because missing +**| and empty are semantically equivalent). +**| +|*/ + +#define NS_IMDBTABLEROWCURSOR_IID_STR = "4f325dad-0385-4b62-a992-c914ab93587e" + +#define NS_IMDBTABLEROWCURSOR_IID \ + { \ + 0x4f325dad, 0x0385, 0x4b62, { \ + 0xa9, 0x92, 0xc9, 0x14, 0xab, 0x93, 0x58, 0x7e \ + } \ + } + +class nsIMdbTableRowCursor : public nsISupports { // table row iterator + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTABLEROWCURSOR_IID) + + // { ===== begin nsIMdbTableRowCursor methods ===== + + // { ----- begin attribute methods ----- + // NS_IMETHOD SetTable(nsIMdbEnv* ev, nsIMdbTable* ioTable) = 0; // sets pos + // to -1 Method SetTable() cut and made obsolete in keeping with new sorting + // methods. + + NS_IMETHOD GetTable(nsIMdbEnv* ev, nsIMdbTable** acqTable) = 0; + // } ----- end attribute methods ----- + + // { ----- begin duplicate row removal methods ----- + NS_IMETHOD CanHaveDupRowMembers(nsIMdbEnv* ev, // cursor might hold dups? + mdb_bool* outCanHaveDups) = 0; + + NS_IMETHOD MakeUniqueCursor( // clone cursor, removing duplicate rows + nsIMdbEnv* ev, // context + nsIMdbTableRowCursor** acqCursor) = 0; // acquire clone with no dups + // Note that MakeUniqueCursor() is never necessary for a cursor which was + // created by table method nsIMdbTable::GetTableRowCursor(), because a table + // never contains the same row as a member more than once. However, a cursor + // created by table method nsIMdbTable::FindRowMatches() might contain the + // same row more than once, because the same row can generate a hit by more + // than one column with a matching string prefix. Note this method can + // return the very same cursor instance with just an incremented refcount, + // when the original cursor could not contain any duplicate rows (calling + // CanHaveDupRowMembers() shows this case on a false return). Otherwise + // this method returns a different cursor instance. Callers should not use + // this MakeUniqueCursor() method lightly, because it tends to defeat the + // purpose of lazy programming techniques, since it can force creation of + // an explicit row collection in a new cursor's representation, in order to + // inspect the row membership and remove any duplicates; this can have big + // impact if a collection holds tens of thousands of rows or more, when + // the original cursor with dups simply referenced rows indirectly by row + // position ranges, without using an explicit row set representation. + // Callers are encouraged to use nsIMdbCursor::GetCount() to determine + // whether the row collection is very large (tens of thousands), and to + // delay calling MakeUniqueCursor() when possible, until a user interface + // element actually demands the creation of an explicit set representation. + // } ----- end duplicate row removal methods ----- + + // { ----- begin oid iteration methods ----- + NS_IMETHOD NextRowOid( // get row id of next row in the table + nsIMdbEnv* ev, // context + mdbOid* outOid, // out row oid + mdb_pos* outRowPos) = 0; // zero-based position of the row in table + // } ----- end oid iteration methods ----- + + // { ----- begin row iteration methods ----- + NS_IMETHOD NextRow( // get row cells from table for cells already in row + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow, // acquire next row in table + mdb_pos* outRowPos) = 0; // zero-based position of the row in table + + NS_IMETHOD PrevRowOid( // get row id of previous row in the table + nsIMdbEnv* ev, // context + mdbOid* outOid, // out row oid + mdb_pos* outRowPos) = 0; // zero-based position of the row in table + // } ----- end oid iteration methods ----- + + // { ----- begin row iteration methods ----- + NS_IMETHOD PrevRow( // get row cells from table for cells already in row + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow, // acquire previous row in table + mdb_pos* outRowPos) = 0; // zero-based position of the row in table + + // } ----- end row iteration methods ----- + + // { ----- begin copy iteration methods ----- + // NS_IMETHOD NextRowCopy( // put row cells into sink only when already in + // sink + // nsIMdbEnv* ev, // context + // nsIMdbRow* ioSinkRow, // sink for row cells read from next row + // mdbOid* outOid, // out row oid + // mdb_pos* outRowPos) = 0; // zero-based position of the row in table + // + // NS_IMETHOD NextRowCopyAll( // put all row cells into sink, adding to sink + // nsIMdbEnv* ev, // context + // nsIMdbRow* ioSinkRow, // sink for row cells read from next row + // mdbOid* outOid, // out row oid + // mdb_pos* outRowPos) = 0; // zero-based position of the row in table + // } ----- end copy iteration methods ----- + + // } ===== end nsIMdbTableRowCursor methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbTableRowCursor, NS_IMDBTABLEROWCURSOR_IID) + +/*| nsIMdbRow: a collection of cells +**| +|*/ + +#define NS_IMDBROW_IID_STR "271e8d6e-183a-40e3-9f18-36913b4c7853" + +#define NS_IMDBROW_IID \ + { \ + 0x271e8d6e, 0x183a, 0x40e3, { \ + 0x9f, 0x18, 0x36, 0x91, 0x3b, 0x4c, 0x78, 0x53 \ + } \ + } + +class nsIMdbRow : public nsIMdbCollection { // cell tuple + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBROW_IID) + // { ===== begin nsIMdbRow methods ===== + + // { ----- begin cursor methods ----- + NS_IMETHOD GetRowCellCursor( // make a cursor starting iteration at inCellPos + nsIMdbEnv* ev, // context + mdb_pos inCellPos, // zero-based ordinal position of cell in row + nsIMdbRowCellCursor** acqCursor) = 0; // acquire new cursor instance + // } ----- end cursor methods ----- + + // { ----- begin column methods ----- + NS_IMETHOD AddColumn( // make sure a particular column is inside row + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to add + const mdbYarn* inYarn) = 0; // cell value to install + + NS_IMETHOD CutColumn( // make sure a column is absent from the row + nsIMdbEnv* ev, // context + mdb_column inColumn) = 0; // column to ensure absent from row + + NS_IMETHOD CutAllColumns( // remove all columns from the row + nsIMdbEnv* ev) = 0; // context + // } ----- end column methods ----- + + // { ----- begin cell methods ----- + NS_IMETHOD NewCell( // get cell for specified column, or add new one + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to add + nsIMdbCell** acqCell) = 0; // cell column and value + + NS_IMETHOD AddCell( // copy a cell from another row to this row + nsIMdbEnv* ev, // context + const nsIMdbCell* inCell) = 0; // cell column and value + + NS_IMETHOD GetCell( // find a cell in this row + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to find + nsIMdbCell** acqCell) = 0; // cell for specified column, or null + + NS_IMETHOD EmptyAllCells( // make all cells in row empty of content + nsIMdbEnv* ev) = 0; // context + // } ----- end cell methods ----- + + // { ----- begin row methods ----- + NS_IMETHOD AddRow( // add all cells in another row to this one + nsIMdbEnv* ev, // context + nsIMdbRow* ioSourceRow) = 0; // row to union with + + NS_IMETHOD SetRow( // make exact duplicate of another row + nsIMdbEnv* ev, // context + nsIMdbRow* ioSourceRow) = 0; // row to duplicate + // } ----- end row methods ----- + + // { ----- begin blob methods ----- + NS_IMETHOD SetCellYarn(nsIMdbEnv* ev, // synonym for AddColumn() + mdb_column inColumn, // column to write + const mdbYarn* inYarn) = 0; // reads from yarn slots + // make this text object contain content from the yarn's buffer + + NS_IMETHOD GetCellYarn(nsIMdbEnv* ev, + mdb_column inColumn, // column to read + mdbYarn* outYarn) = 0; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + + NS_IMETHOD AliasCellYarn(nsIMdbEnv* ev, + mdb_column inColumn, // column to alias + mdbYarn* outYarn) = 0; // writes ALL yarn slots + + NS_IMETHOD NextCellYarn(nsIMdbEnv* ev, // iterative version of GetCellYarn() + mdb_column* ioColumn, // next column to read + mdbYarn* outYarn) = 0; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + // + // The ioColumn argument is an inout parameter which initially contains the + // last column accessed and returns the next column corresponding to the + // content read into the yarn. Callers should start with a zero column + // value to say 'no previous column', which causes the first column to be + // read. Then the value returned in ioColumn is perfect for the next call + // to NextCellYarn(), since it will then be the previous column accessed. + // Callers need only examine the column token returned to see which cell + // in the row is being read into the yarn. When no more columns remain, + // and the iteration has ended, ioColumn will return a zero token again. + // So iterating over cells starts and ends with a zero column token. + + NS_IMETHOD SeekCellYarn( // resembles nsIMdbRowCellCursor::SeekCell() + nsIMdbEnv* ev, // context + mdb_pos inPos, // position of cell in row sequence + mdb_column* outColumn, // column for this particular cell + mdbYarn* outYarn) = 0; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + // Callers can pass nil for outYarn to indicate no interest in content, so + // only the outColumn value is returned. NOTE to subclasses: you must be + // able to ignore outYarn when the pointer is nil; please do not crash. + + // } ----- end blob methods ----- + + // } ===== end nsIMdbRow methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbRow, NS_IMDBROW_IID) + +/*| nsIMdbRowCellCursor: cursor class for iterating row cells +**| +**|| row: the cursor is associated with a specific row, which can be +**| set to a different row (which resets the position to -1 so the +**| next cell acquired is the first in the row. +**| +**|| NextCell: get the next cell in the row and return its position and +**| a new instance of a nsIMdbCell to represent this next cell. +|*/ + +#define NS_IMDBROWCELLCURSOR_IID_STR "b33371a7-5d63-4d10-85a8-e44dffe75c28" + +#define NS_IMDBROWCELLCURSOR_IID \ + { \ + 0x271e8d6e, 0x5d63, 0x4d10, { \ + 0x85, 0xa8, 0xe4, 0x4d, 0xff, 0xe7, 0x5c, 0x28 \ + } \ + } + +class nsIMdbRowCellCursor : public nsISupports { // cell collection iterator + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBROWCELLCURSOR_IID) + // { ===== begin nsIMdbRowCellCursor methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD SetRow(nsIMdbEnv* ev, nsIMdbRow* ioRow) = 0; // sets pos to -1 + NS_IMETHOD GetRow(nsIMdbEnv* ev, nsIMdbRow** acqRow) = 0; + // } ----- end attribute methods ----- + + // { ----- begin cell seeking methods ----- + NS_IMETHOD SeekCell(nsIMdbEnv* ev, // context + mdb_pos inPos, // position of cell in row sequence + mdb_column* outColumn, // column for this particular cell + nsIMdbCell** acqCell) = 0; // the cell at inPos + // } ----- end cell seeking methods ----- + + // { ----- begin cell iteration methods ----- + NS_IMETHOD NextCell( // get next cell in the row + nsIMdbEnv* ev, // context + nsIMdbCell** acqCell, // changes to the next cell in the iteration + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos) = 0; // position of cell in row sequence + + NS_IMETHOD PickNextCell( // get next cell in row within filter set + nsIMdbEnv* ev, // context + nsIMdbCell* ioCell, // changes to the next cell in the iteration + const mdbColumnSet* inFilterSet, // col set of actual caller interest + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos) = 0; // position of cell in row sequence + + // Note that inFilterSet should not have too many (many more than 10?) + // cols, since this might imply a potential excessive consumption of time + // over many cursor calls when looking for column and filter intersection. + // } ----- end cell iteration methods ----- + + // } ===== end nsIMdbRowCellCursor methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbRowCellCursor, NS_IMDBROWCELLCURSOR_IID) + +/*| nsIMdbBlob: a base class for objects composed mainly of byte sequence state. +**| (This provides a base class for nsIMdbCell, so that cells themselves can +**| be used to set state in another cell, without extracting a buffer.) +|*/ +class nsIMdbBlob : public nsISupports { // a string with associated charset + public: + // { ===== begin nsIMdbBlob methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD SetBlob(nsIMdbEnv* ev, + nsIMdbBlob* ioBlob) = 0; // reads inBlob slots + // when inBlob is in the same suite, this might be fastest cell-to-cell + + NS_IMETHOD ClearBlob( // make empty (so content has zero length) + nsIMdbEnv* ev) = 0; + // clearing a yarn is like SetYarn() with empty yarn instance content + + NS_IMETHOD GetBlobFill(nsIMdbEnv* ev, + mdb_fill* outFill) = 0; // size of blob + // Same value that would be put into mYarn_Fill, if one called GetYarn() + // with a yarn instance that had mYarn_Buf==nil and mYarn_Size==0. + + NS_IMETHOD SetYarn(nsIMdbEnv* ev, + const mdbYarn* inYarn) = 0; // reads from yarn slots + // make this text object contain content from the yarn's buffer + + NS_IMETHOD GetYarn(nsIMdbEnv* ev, + mdbYarn* outYarn) = 0; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + + NS_IMETHOD AliasYarn(nsIMdbEnv* ev, + mdbYarn* outYarn) = 0; // writes ALL yarn slots + // AliasYarn() reveals sensitive internal text buffer state to the caller + // by setting mYarn_Buf to point into the guts of this text implementation. + // + // The caller must take great care to avoid writing on this space, and to + // avoid calling any method that would cause the state of this text object + // to change (say by directly or indirectly setting the text to hold more + // content that might grow the size of the buffer and free the old buffer). + // In particular, callers should scrupulously avoid making calls into the + // mdb interface to write any content while using the buffer pointer found + // in the returned yarn instance. Best safe usage involves copying content + // into some other kind of external content representation beyond mdb. + // + // (The original design of this method a week earlier included the concept + // of very fast and efficient cooperative locking via a pointer to some lock + // member slot. But let's ignore that complexity in the current design.) + // + // AliasYarn() is specifically intended as the first step in transferring + // content from nsIMdbBlob to a nsString representation, without forcing extra + // allocations and/or memory copies. (A standard nsIMdbBlob_AsString() utility + // will use AliasYarn() as the first step in setting a nsString instance.) + // + // This is an alternative to the GetYarn() method, which has copy semantics + // only; AliasYarn() relaxes a robust safety principle only for performance + // reasons, to accommodate the need for callers to transform text content to + // some other canonical representation that would necessitate an additional + // copy and transformation when such is incompatible with the mdbYarn format. + // + // The implementation of AliasYarn() should have extremely little overhead + // besides the virtual dispatch to the method implementation, and the code + // necessary to populate all the mdbYarn member slots with internal buffer + // address and metainformation that describes the buffer content. Note that + // mYarn_Grow must always be set to nil to indicate no resizing is allowed. + + // } ----- end attribute methods ----- + + // } ===== end nsIMdbBlob methods ===== +}; + +/*| nsIMdbCell: the text in a single column of a row. The base nsIMdbBlob +**| class provides all the interface related to accessing cell text. +**| +**|| column: each cell in a row appears in a specific column, where this +**| column is identified by the an integer mdb_scope value (generated by +**| the StringToScopeToken() method in the containing nsIMdbPort instance). +**| Because a row cannot have more than one cell with the same column, +**| something must give if one calls SetColumn() with an existing column +**| in the same row. When this happens, the other cell is replaced with +**| this cell (and the old cell is closed if it has outstanding refs). +**| +**|| row: every cell instance is a part of some row, and every cell knows +**| which row is the parent row. (Note this should be represented by a +**| weak backpointer, so that outstanding cell references cannot keep a +**| row open that should be closed. Otherwise we'd have ref graph cycles.) +**| +**|| text: a cell can either be text, or it can have a child row or table, +**| but not both at once. If text is read from a cell with a child, the text +**| content should be empty (for AliasYarn()) or a description of the type +**| of child (perhaps "mdb:cell:child:row" or "mdb:cell:child:table"). +**| +**|| child: a cell might reference another row or a table, rather than text. +**| The interface for putting and getting children rows and tables was first +**| defined in the nsIMdbTable interface, but then this was moved to this cell +**| interface as more natural. +|*/ + +#define NS_IMDBCELL_IID \ + { \ + 0xa3b62f71, 0xa181, 0x4a91, { \ + 0xb6, 0x6b, 0x27, 0x10, 0x9b, 0x88, 0x98, 0x35 \ + } \ + } + +#define NS_IMDBCELL_IID_STR = "a3b62f71-a181-4a91-b66b-27109b889835" + +class nsIMdbCell + : public nsIMdbBlob { // text attribute in row with column scope + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTABLEROWCURSOR_IID) + // { ===== begin nsIMdbCell methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD SetColumn(nsIMdbEnv* ev, mdb_column inColumn) = 0; + NS_IMETHOD GetColumn(nsIMdbEnv* ev, mdb_column* outColumn) = 0; + + NS_IMETHOD GetCellInfo( // all cell metainfo except actual content + nsIMdbEnv* ev, + mdb_column* outColumn, // the column in the containing row + mdb_fill* outBlobFill, // the size of text content in bytes + mdbOid* outChildOid, // oid of possible row or table child + mdb_bool* outIsRowChild) = 0; // nonzero if child, and a row child + + // Checking all cell metainfo is a good way to avoid forcing a large cell + // in to memory when you don't actually want to use the content. + + NS_IMETHOD GetRow(nsIMdbEnv* ev, // parent row for this cell + nsIMdbRow** acqRow) = 0; + NS_IMETHOD GetPort(nsIMdbEnv* ev, // port containing cell + nsIMdbPort** acqPort) = 0; + // } ----- end attribute methods ----- + + // { ----- begin children methods ----- + NS_IMETHOD HasAnyChild( // does cell have a child instead of text? + nsIMdbEnv* ev, + mdbOid* outOid, // out id of row or table (or unbound if no child) + mdb_bool* outIsRow) = + 0; // nonzero if child is a row (rather than a table) + + NS_IMETHOD GetAnyChild( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow, // child row (or null) + nsIMdbTable** acqTable) = 0; // child table (or null) + + NS_IMETHOD SetChildRow( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow) = 0; // inRow must be bound inside this same db port + + NS_IMETHOD GetChildRow( // access row of specific attribute + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow) = 0; // acquire child row (or nil if no child) + + NS_IMETHOD SetChildTable( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbTable* inTable) = + 0; // table must be bound inside this same db port + + NS_IMETHOD GetChildTable( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbTable** acqTable) = 0; // acquire child table (or nil if no child) + // } ----- end children methods ----- + + // } ===== end nsIMdbCell methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbCell, NS_IMDBTABLEROWCURSOR_IID) + +// } %%%%% end C++ abstract class interfaces %%%%% + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MDB_ */ diff --git a/comm/mailnews/db/mork/mork.h b/comm/mailnews/db/mork/mork.h new file mode 100644 index 0000000000..ec48e67046 --- /dev/null +++ b/comm/mailnews/db/mork/mork.h @@ -0,0 +1,255 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MORK_ +#define _MORK_ 1 + +#ifndef _MDB_ +# include "mdb.h" +#endif + +#include "nscore.h" +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// { %%%%% begin disable unused param warnings %%%%% +#define MORK_USED_1(x) (void)(&x) +#define MORK_USED_2(x, y) \ + (void)(&x); \ + (void)(&y); +#define MORK_USED_3(x, y, z) \ + (void)(&x); \ + (void)(&y); \ + (void)(&z); +#define MORK_USED_4(w, x, y, z) \ + (void)(&w); \ + (void)(&x); \ + (void)(&y); \ + (void)(&z); + +// } %%%%% end disable unused param warnings %%%%% + +// { %%%%% begin macro for finding class member offset %%%%% + +/*| OffsetOf: the unsigned integer offset of a class or struct +**| field from the beginning of that class or struct. This is +**| the same as the similarly named public domain IronDoc macro, +**| and is also the same as another macro appearing in stdlib.h. +**| We want these offsets so we can correctly convert pointers +**| to member slots back into pointers to enclosing objects, and +**| have this exactly match what the compiler thinks is true. +**| +**|| Basically we are asking the compiler to determine the offset at +**| compile time, and we use the definition of address artithmetic +**| to do this. By casting integer zero to a pointer of type obj*, +**| we can reference the address of a slot in such an object that +**| is hypothetically physically placed at address zero, but without +**| actually dereferencing a memory location. The absolute address +**| of slot is the same as offset of that slot, when the object is +**| placed at address zero. +|*/ +#define mork_OffsetOf(obj, slot) ((unsigned int)&((obj*)0)->slot) + +// } %%%%% end macro for finding class member offset %%%%% + +// { %%%%% begin specific-size integer scalar typedefs %%%%% +typedef unsigned char mork_u1; // make sure this is one byte +typedef unsigned short mork_u2; // make sure this is two bytes +typedef short mork_i2; // make sure this is two bytes +typedef uint32_t mork_u4; // make sure this is four bytes +typedef int32_t mork_i4; // make sure this is four bytes +typedef PRWord mork_ip; // make sure sizeof(mork_ip) == sizeof(void*) + +typedef mork_u1 mork_ch; // small byte-sized character (never wide) +typedef mork_u1 mork_flags; // one byte's worth of predicate bit flags + +typedef mork_u2 mork_base; // 2-byte magic class signature slot in object +typedef mork_u2 mork_derived; // 2-byte magic class signature slot in object + +typedef mork_u4 mork_token; // unsigned token for atomized string +typedef mork_token mork_scope; // token used to id scope for rows +typedef mork_token mork_kind; // token used to id kind for tables +typedef mork_token mork_cscode; // token used to id charset names +typedef mork_token mork_aid; // token used to id atomize cell values + +typedef mork_token mork_column; // token used to id columns for rows +typedef mork_column mork_delta; // mork_column plus mork_change + +typedef mork_token mork_color; // bead ID +#define morkColor_kNone ((mork_color)0) + +typedef mork_u4 mork_magic; // unsigned magic signature + +typedef mork_u4 mork_seed; // unsigned collection change counter +typedef mork_u4 mork_count; // unsigned collection member count +typedef mork_count mork_num; // synonym for count +typedef mork_u4 mork_size; // unsigned physical media size +typedef mork_u4 mork_fill; // unsigned logical content size +typedef mork_u4 mork_more; // more available bytes for larger buffer + +typedef mdb_u4 mork_percent; // 0..100, with values >100 same as 100 + +typedef mork_i4 mork_pos; // negative means "before first" (at zero pos) +typedef mork_i4 mork_line; // negative means "before first line in file" + +typedef mork_u1 mork_usage; // 1-byte magic usage signature slot in object +typedef mork_u1 mork_access; // 1-byte magic access signature slot in object + +typedef mork_u1 mork_change; // add, cut, put, set, nil +typedef mork_u1 mork_priority; // 0..9, for a total of ten different values + +typedef mork_u1 mork_able; // on, off, asleep (clone IronDoc's fe_able) +typedef mork_u1 mork_load; // dirty or clean (clone IronDoc's fe_load) +// } %%%%% end specific-size integer scalar typedefs %%%%% + +// 'test' is a public domain Mithril for key equality tests in probe maps +typedef mork_i2 mork_test; /* neg=>kVoid, zero=>kHit, pos=>kMiss */ + +#define morkTest_kVoid ((mork_test)-1) /* -1: nil key slot, no key order */ +#define morkTest_kHit ((mork_test)0) /* 0: keys are equal, a map hit */ +#define morkTest_kMiss ((mork_test)1) /* 1: keys not equal, a map miss */ + +// { %%%%% begin constants for Mork scalar types %%%%% +#define morkPriority_kHi ((mork_priority)0) /* best priority */ +#define morkPriority_kMin ((mork_priority)0) /* best priority is smallest */ + +#define morkPriority_kLo ((mork_priority)9) /* worst priority */ +#define morkPriority_kMax ((mork_priority)9) /* worst priority is biggest */ + +#define morkPriority_kCount 10 /* number of distinct priority values */ + +#define morkAble_kEnabled ((mork_able)0x55) /* same as IronDoc constant */ +#define morkAble_kDisabled ((mork_able)0xAA) /* same as IronDoc constant */ +#define morkAble_kAsleep ((mork_able)0x5A) /* same as IronDoc constant */ + +#define morkChange_kAdd 'a' /* add member */ +#define morkChange_kCut 'c' /* cut member */ +#define morkChange_kPut 'p' /* put member */ +#define morkChange_kSet 's' /* set all members */ +#define morkChange_kNil 0 /* no change in this member */ +#define morkChange_kDup 'd' /* duplicate changes have no effect */ +// kDup is intended to replace another change constant in an object as a +// conclusion about change feasibility while staging intended alterations. + +#define morkLoad_kDirty ((mork_load)0xDD) /* same as IronDoc constant */ +#define morkLoad_kClean ((mork_load)0x22) /* same as IronDoc constant */ + +#define morkAccess_kOpen 'o' +#define morkAccess_kClosing 'c' +#define morkAccess_kShut 's' +#define morkAccess_kDead 'd' +// } %%%%% end constants for Mork scalar types %%%%% + +// { %%%%% begin non-specific-size integer scalar typedefs %%%%% +typedef int mork_char; // nominal type for ints used to hold input byte +#define morkChar_IsWhite(c) \ + ((c) == 0xA || (c) == 0x9 || (c) == 0xD || (c) == ' ') +// } %%%%% end non-specific-size integer scalar typedefs %%%%% + +// { %%%%% begin mdb-driven scalar typedefs %%%%% +// easier to define bool exactly the same as mdb: +typedef mdb_bool mork_bool; // unsigned byte with zero=false, nonzero=true + +/* canonical boolean constants provided only for code clarity: */ +#define morkBool_kTrue ((mork_bool)1) /* actually any nonzero means true */ +#define morkBool_kFalse ((mork_bool)0) /* only zero means false */ + +// mdb clients can assign these, so we cannot pick maximum size: +typedef mdb_id mork_id; // unsigned object identity in a scope +typedef mork_id mork_rid; // unsigned row identity inside scope +typedef mork_id mork_tid; // unsigned table identity inside scope +typedef mork_id mork_gid; // unsigned group identity without any scope + +// we only care about neg, zero, pos -- so we don't care about size: +typedef mdb_order mork_order; // neg:lessthan, zero:equalto, pos:greaterthan +// } %%%%% end mdb-driven scalar typedefs %%%%% + +#define morkId_kMinusOne ((mdb_id)-1) + +// { %%%%% begin class forward defines %%%%% +// try to put these in alphabetical order for easier examination: +class morkMid; +class morkAtom; +class morkAtomSpace; +class morkBookAtom; +class morkBuf; +class morkBuilder; +class morkCell; +class morkCellObject; +class morkCursor; +class morkEnv; +class morkFactory; +class morkFile; +class morkHandle; +class morkHandleFace; // just an opaque cookie type +class morkHandleFrame; +class morkHashArrays; +class morkMap; +class morkNode; +class morkObject; +class morkOidAtom; +class morkParser; +class morkPool; +class morkPlace; +class morkPort; +class morkPortTableCursor; +class morkProbeMap; +class morkRow; +class morkRowCellCursor; +class morkRowObject; +class morkRowSpace; +class morkSorting; +class morkSortingRowCursor; +class morkSpace; +class morkSpan; +class morkStore; +class morkStream; +class morkTable; +class morkTableChange; +class morkTableRowCursor; +class morkThumb; +class morkWriter; +class morkZone; +// } %%%%% end class forward defines %%%%% + +// include this config file last for platform & environment specific stuff: +#ifndef _MORKCONFIG_ +# include "morkConfig.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORK_ */ diff --git a/comm/mailnews/db/mork/morkArray.cpp b/comm/mailnews/db/mork/morkArray.cpp new file mode 100644 index 0000000000..fff5f8a626 --- /dev/null +++ b/comm/mailnews/db/mork/morkArray.cpp @@ -0,0 +1,250 @@ +/* -*- 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/. */ + +#include "nscore.h" + +#ifndef _MDB_ +# include "mdb.h" +#endif + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKARRAY_ +# include "morkArray.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkArray::CloseMorkNode( + morkEnv* ev) // CloseTable() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseArray(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkArray::~morkArray() // assert CloseTable() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); + MORK_ASSERT(mArray_Slots == 0); +} + +/*public non-poly*/ +morkArray::morkArray(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_size inSize, nsIMdbHeap* ioSlotHeap) + : morkNode(ev, inUsage, ioHeap), + mArray_Slots(0), + mArray_Heap(0), + mArray_Fill(0), + mArray_Size(0), + mArray_Seed( + (mork_u4)NS_PTR_TO_INT32(this)) // "random" integer assignment +{ + if (ev->Good()) { + if (ioSlotHeap) { + nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mArray_Heap); + if (ev->Good()) { + if (inSize < 3) inSize = 3; + mdb_size byteSize = inSize * sizeof(void*); + void** block = 0; + ioSlotHeap->Alloc(ev->AsMdbEnv(), byteSize, (void**)&block); + if (block && ev->Good()) { + mArray_Slots = block; + mArray_Size = inSize; + MORK_MEMSET(mArray_Slots, 0, byteSize); + if (ev->Good()) mNode_Derived = morkDerived_kArray; + } + } + } else + ev->NilPointerError(); + } +} + +/*public non-poly*/ void morkArray::CloseArray( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + if (mArray_Heap && mArray_Slots) + mArray_Heap->Free(ev->AsMdbEnv(), mArray_Slots); + + mArray_Slots = 0; + mArray_Size = 0; + mArray_Fill = 0; + ++mArray_Seed; + nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*)0, ev, &mArray_Heap); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void morkArray::NonArrayTypeError(morkEnv* ev) { + ev->NewError("non morkArray"); +} + +/*static*/ void morkArray::IndexBeyondEndError(morkEnv* ev) { + ev->NewError("array index beyond end"); +} + +/*static*/ void morkArray::NilSlotsAddressError(morkEnv* ev) { + ev->NewError("nil mArray_Slots"); +} + +/*static*/ void morkArray::FillBeyondSizeError(morkEnv* ev) { + ev->NewError("mArray_Fill > mArray_Size"); +} + +mork_bool morkArray::Grow(morkEnv* ev, mork_size inNewSize) +// Grow() returns true if capacity becomes >= inNewSize and ev->Good() +{ + if (ev->Good() && inNewSize > mArray_Size) // make array larger? + { + if (mArray_Fill <= mArray_Size) // fill and size fit the invariant? + { + if (mArray_Size <= 3) + inNewSize = mArray_Size + 3; + else + inNewSize = mArray_Size * + 2; // + 3; // try doubling size here - used to grow by 3 + + mdb_size newByteSize = inNewSize * sizeof(void*); + void** newBlock = 0; + mArray_Heap->Alloc(ev->AsMdbEnv(), newByteSize, (void**)&newBlock); + if (newBlock && ev->Good()) // okay new block? + { + void** oldSlots = mArray_Slots; + void** oldEnd = oldSlots + mArray_Fill; + + void** newSlots = newBlock; + void** newEnd = newBlock + inNewSize; + + while (oldSlots < oldEnd) *newSlots++ = *oldSlots++; + + while (newSlots < newEnd) *newSlots++ = (void*)0; + + oldSlots = mArray_Slots; + mArray_Size = inNewSize; + mArray_Slots = newBlock; + mArray_Heap->Free(ev->AsMdbEnv(), oldSlots); + } + } else + this->FillBeyondSizeError(ev); + } + ++mArray_Seed; // always modify seed, since caller intends to add slots + return (ev->Good() && mArray_Size >= inNewSize); +} + +void* morkArray::SafeAt(morkEnv* ev, mork_pos inPos) { + if (mArray_Slots) { + if (inPos >= 0 && inPos < (mork_pos)mArray_Fill) + return mArray_Slots[inPos]; + else + this->IndexBeyondEndError(ev); + } else + this->NilSlotsAddressError(ev); + + return (void*)0; +} + +void morkArray::SafeAtPut(morkEnv* ev, mork_pos inPos, void* ioSlot) { + if (mArray_Slots) { + if (inPos >= 0 && inPos < (mork_pos)mArray_Fill) { + mArray_Slots[inPos] = ioSlot; + ++mArray_Seed; + } else + this->IndexBeyondEndError(ev); + } else + this->NilSlotsAddressError(ev); +} + +mork_pos morkArray::AppendSlot(morkEnv* ev, void* ioSlot) { + mork_pos outPos = -1; + if (mArray_Slots) { + mork_fill fill = mArray_Fill; + if (this->Grow(ev, fill + 1)) { + outPos = (mork_pos)fill; + mArray_Slots[fill] = ioSlot; + mArray_Fill = fill + 1; + // note Grow() increments mArray_Seed + } + } else + this->NilSlotsAddressError(ev); + + return outPos; +} + +void morkArray::AddSlot(morkEnv* ev, mork_pos inPos, void* ioSlot) { + if (mArray_Slots) { + mork_fill fill = mArray_Fill; + if (this->Grow(ev, fill + 1)) { + void** slot = mArray_Slots; // the slot vector + void** end = slot + fill; // one past the last used array slot + slot += inPos; // the slot to be added + + while (--end >= slot) // another slot to move upward? + end[1] = *end; + + *slot = ioSlot; + mArray_Fill = fill + 1; + // note Grow() increments mArray_Seed + } + } else + this->NilSlotsAddressError(ev); +} + +void morkArray::CutSlot(morkEnv* ev, mork_pos inPos) { + MORK_USED_1(ev); + mork_fill fill = mArray_Fill; + if (inPos >= 0 && + inPos < (mork_pos)fill) // cutting slot in used array portion? + { + void** slot = mArray_Slots; // the slot vector + void** end = slot + fill; // one past the last used array slot + slot += inPos; // the slot to be cut + + while (++slot < end) // another slot to move downward? + slot[-1] = *slot; + + slot[-1] = 0; // clear the last used slot which is now unused + + // note inPos<fill implies fill>0, so fill-1 must be nonnegative: + mArray_Fill = fill - 1; + ++mArray_Seed; + } +} + +void morkArray::CutAllSlots(morkEnv* ev) { + if (mArray_Slots) { + if (mArray_Fill <= mArray_Size) { + mdb_size oldByteSize = mArray_Fill * sizeof(void*); + MORK_MEMSET(mArray_Slots, 0, oldByteSize); + } else + this->FillBeyondSizeError(ev); + } else + this->NilSlotsAddressError(ev); + + ++mArray_Seed; + mArray_Fill = 0; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkArray.h b/comm/mailnews/db/mork/morkArray.h new file mode 100644 index 0000000000..daf9c96f35 --- /dev/null +++ b/comm/mailnews/db/mork/morkArray.h @@ -0,0 +1,97 @@ +/* -*- 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 _MORKARRAY_ +#define _MORKARRAY_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kArray /*i*/ 0x4179 /* ascii 'Ay' */ + +class morkArray : public morkNode { // row iterator + + // public: // slots inherited from morkObject (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 + + public: // state is public because the entire Mork system is private + void** mArray_Slots; // array of pointers + nsIMdbHeap* mArray_Heap; // required heap for allocating mArray_Slots + mork_fill mArray_Fill; // logical count of used slots in mArray_Slots + mork_size mArray_Size; // physical count of mArray_Slots ( >= Fill) + mork_seed mArray_Seed; // change counter for syncing with iterators + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseArray() + virtual ~morkArray(); // assert that close executed earlier + + public: // morkArray construction & destruction + morkArray(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_size inSize, nsIMdbHeap* ioSlotHeap); + void CloseArray(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkArray(const morkArray& other); + morkArray& operator=(const morkArray& other); + + public: // dynamic type identification + mork_bool IsArray() const { + return IsNode() && mNode_Derived == morkDerived_kArray; + } + // } ===== end morkNode methods ===== + + public: // typing & errors + static void NonArrayTypeError(morkEnv* ev); + static void IndexBeyondEndError(morkEnv* ev); + static void NilSlotsAddressError(morkEnv* ev); + static void FillBeyondSizeError(morkEnv* ev); + + public: // other table row cursor methods + mork_fill Length() const { return mArray_Fill; } + mork_size Capacity() const { return mArray_Size; } + + mork_bool Grow(morkEnv* ev, mork_size inNewSize); + // Grow() returns true if capacity becomes >= inNewSize and ev->Good() + + void* At(mork_pos inPos) const { return mArray_Slots[inPos]; } + void AtPut(mork_pos inPos, void* ioSlot) { mArray_Slots[inPos] = ioSlot; } + + void* SafeAt(morkEnv* ev, mork_pos inPos); + void SafeAtPut(morkEnv* ev, mork_pos inPos, void* ioSlot); + + mork_pos AppendSlot(morkEnv* ev, void* ioSlot); + void AddSlot(morkEnv* ev, mork_pos inPos, void* ioSlot); + void CutSlot(morkEnv* ev, mork_pos inPos); + void CutAllSlots(morkEnv* ev); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakArray(morkArray* me, morkEnv* ev, morkArray** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongArray(morkArray* me, morkEnv* ev, morkArray** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKTABLEROWCURSOR_ */ diff --git a/comm/mailnews/db/mork/morkAtom.cpp b/comm/mailnews/db/mork/morkAtom.cpp new file mode 100644 index 0000000000..ad3b1d53bf --- /dev/null +++ b/comm/mailnews/db/mork/morkAtom.cpp @@ -0,0 +1,432 @@ +/* -*- 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 _MORKBLOB_ +# include "morkBlob.h" +#endif + +#ifndef _MORKATOM_ +# include "morkAtom.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKATOMSPACE_ +# include "morkAtomSpace.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/* static */ +mork_bool morkAtom::GetYarn(const morkAtom* atom, mdbYarn* outYarn) { + const void* source = 0; + mdb_fill fill = 0; + mdb_cscode form = 0; + outYarn->mYarn_More = 0; + + if (atom) { + if (atom->IsWeeBook()) { + morkWeeBookAtom* weeBook = (morkWeeBookAtom*)atom; + source = weeBook->mWeeBookAtom_Body; + fill = weeBook->mAtom_Size; + } else if (atom->IsBigBook()) { + morkBigBookAtom* bigBook = (morkBigBookAtom*)atom; + source = bigBook->mBigBookAtom_Body; + fill = bigBook->mBigBookAtom_Size; + form = bigBook->mBigBookAtom_Form; + } else if (atom->IsWeeAnon()) { + morkWeeAnonAtom* weeAnon = (morkWeeAnonAtom*)atom; + source = weeAnon->mWeeAnonAtom_Body; + fill = weeAnon->mAtom_Size; + } else if (atom->IsBigAnon()) { + morkBigAnonAtom* bigAnon = (morkBigAnonAtom*)atom; + source = bigAnon->mBigAnonAtom_Body; + fill = bigAnon->mBigAnonAtom_Size; + form = bigAnon->mBigAnonAtom_Form; + } + } + + if (source && fill) // have an atom with nonempty content? + { + // if we have too many bytes, and yarn seems growable: + if (fill > outYarn->mYarn_Size && outYarn->mYarn_Grow) // try grow? + (*outYarn->mYarn_Grow)(outYarn, (mdb_size)fill); // request bigger + + mdb_size size = outYarn->mYarn_Size; // max dest size + if (fill > size) // too much atom content? + { + outYarn->mYarn_More = fill - size; // extra atom bytes omitted + fill = size; // copy no more bytes than size of yarn buffer + } + void* dest = outYarn->mYarn_Buf; // where bytes are going + if (!dest) // nil destination address buffer? + fill = 0; // we can't write any content at all + + if (fill) // anything to copy? + MORK_MEMCPY(dest, source, fill); // copy fill bytes to yarn + + outYarn->mYarn_Fill = fill; // tell yarn size of copied content + } else // no content to put into the yarn + { + outYarn->mYarn_Fill = 0; // tell yarn that atom has no bytes + } + outYarn->mYarn_Form = form; // always update the form slot + + return (source != 0); +} + +/* static */ +mork_bool morkAtom::AliasYarn(const morkAtom* atom, mdbYarn* outYarn) { + outYarn->mYarn_More = 0; + outYarn->mYarn_Form = 0; + + if (atom) { + if (atom->IsWeeBook()) { + morkWeeBookAtom* weeBook = (morkWeeBookAtom*)atom; + outYarn->mYarn_Buf = weeBook->mWeeBookAtom_Body; + outYarn->mYarn_Fill = weeBook->mAtom_Size; + outYarn->mYarn_Size = weeBook->mAtom_Size; + } else if (atom->IsBigBook()) { + morkBigBookAtom* bigBook = (morkBigBookAtom*)atom; + outYarn->mYarn_Buf = bigBook->mBigBookAtom_Body; + outYarn->mYarn_Fill = bigBook->mBigBookAtom_Size; + outYarn->mYarn_Size = bigBook->mBigBookAtom_Size; + outYarn->mYarn_Form = bigBook->mBigBookAtom_Form; + } else if (atom->IsWeeAnon()) { + morkWeeAnonAtom* weeAnon = (morkWeeAnonAtom*)atom; + outYarn->mYarn_Buf = weeAnon->mWeeAnonAtom_Body; + outYarn->mYarn_Fill = weeAnon->mAtom_Size; + outYarn->mYarn_Size = weeAnon->mAtom_Size; + } else if (atom->IsBigAnon()) { + morkBigAnonAtom* bigAnon = (morkBigAnonAtom*)atom; + outYarn->mYarn_Buf = bigAnon->mBigAnonAtom_Body; + outYarn->mYarn_Fill = bigAnon->mBigAnonAtom_Size; + outYarn->mYarn_Size = bigAnon->mBigAnonAtom_Size; + outYarn->mYarn_Form = bigAnon->mBigAnonAtom_Form; + } else + atom = 0; // show desire to put empty content in yarn + } + + if (!atom) // empty content for yarn? + { + outYarn->mYarn_Buf = 0; + outYarn->mYarn_Fill = 0; + outYarn->mYarn_Size = 0; + // outYarn->mYarn_Grow = 0; // please don't modify the Grow slot + } + return (atom != 0); +} + +mork_aid morkAtom::GetBookAtomAid() const // zero or book atom's ID +{ + return (this->IsBook()) ? ((morkBookAtom*)this)->mBookAtom_Id : 0; +} + +mork_scope morkAtom::GetBookAtomSpaceScope( + morkEnv* ev) const // zero or book's space's scope +{ + mork_scope outScope = 0; + if (this->IsBook()) { + const morkBookAtom* bookAtom = (const morkBookAtom*)this; + morkAtomSpace* space = bookAtom->mBookAtom_Space; + if (space->IsAtomSpace()) + outScope = space->SpaceScope(); + else + space->NonAtomSpaceTypeError(ev); + } + + return outScope; +} + +void morkAtom::MakeCellUseForever(morkEnv* ev) { + MORK_USED_1(ev); + mAtom_CellUses = morkAtom_kForeverCellUses; +} + +mork_u1 morkAtom::AddCellUse(morkEnv* ev) { + MORK_USED_1(ev); + if (mAtom_CellUses < morkAtom_kMaxCellUses) // not already maxed out? + ++mAtom_CellUses; + + return mAtom_CellUses; +} + +mork_u1 morkAtom::CutCellUse(morkEnv* ev) { + if (mAtom_CellUses) // any outstanding uses to cut? + { + if (mAtom_CellUses < morkAtom_kMaxCellUses) // not frozen at max? + --mAtom_CellUses; + } else + this->CellUsesUnderflowWarning(ev); + + return mAtom_CellUses; +} + +/*static*/ void morkAtom::CellUsesUnderflowWarning(morkEnv* ev) { + ev->NewWarning("mAtom_CellUses underflow"); +} + +/*static*/ void morkAtom::BadAtomKindError(morkEnv* ev) { + ev->NewError("bad mAtom_Kind"); +} + +/*static*/ void morkAtom::ZeroAidError(morkEnv* ev) { + ev->NewError("zero atom ID"); +} + +/*static*/ void morkAtom::AtomSizeOverflowError(morkEnv* ev) { + ev->NewError("atom mAtom_Size overflow"); +} + +void morkOidAtom::InitRowOidAtom(morkEnv* ev, const mdbOid& inOid) { + MORK_USED_1(ev); + mAtom_CellUses = 0; + mAtom_Kind = morkAtom_kKindRowOid; + mAtom_Change = morkChange_kNil; + mAtom_Size = 0; + mOidAtom_Oid = inOid; // bitwise copy +} + +void morkOidAtom::InitTableOidAtom(morkEnv* ev, const mdbOid& inOid) { + MORK_USED_1(ev); + mAtom_CellUses = 0; + mAtom_Kind = morkAtom_kKindTableOid; + mAtom_Change = morkChange_kNil; + mAtom_Size = 0; + mOidAtom_Oid = inOid; // bitwise copy +} + +void morkWeeAnonAtom::InitWeeAnonAtom(morkEnv* ev, const morkBuf& inBuf) { + mAtom_Kind = 0; + mAtom_Change = morkChange_kNil; + if (inBuf.mBuf_Fill <= morkAtom_kMaxByteSize) { + mAtom_CellUses = 0; + mAtom_Kind = morkAtom_kKindWeeAnon; + mork_size size = inBuf.mBuf_Fill; + mAtom_Size = (mork_u1)size; + if (size && inBuf.mBuf_Body) + MORK_MEMCPY(mWeeAnonAtom_Body, inBuf.mBuf_Body, size); + + mWeeAnonAtom_Body[size] = 0; + } else + this->AtomSizeOverflowError(ev); +} + +void morkBigAnonAtom::InitBigAnonAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm) { + MORK_USED_1(ev); + mAtom_CellUses = 0; + mAtom_Kind = morkAtom_kKindBigAnon; + mAtom_Change = morkChange_kNil; + mAtom_Size = 0; + mBigAnonAtom_Form = inForm; + mork_size size = inBuf.mBuf_Fill; + mBigAnonAtom_Size = size; + if (size && inBuf.mBuf_Body) + MORK_MEMCPY(mBigAnonAtom_Body, inBuf.mBuf_Body, size); + + mBigAnonAtom_Body[size] = 0; +} + +/*static*/ void morkBookAtom::NonBookAtomTypeError(morkEnv* ev) { + ev->NewError("non morkBookAtom"); +} + +mork_u4 morkBookAtom::HashFormAndBody(morkEnv* ev) const { + // This hash is obviously a variation of the dragon book string hash. + // (I won't bother to explain or rationalize this usage for you.) + + mork_u4 outHash = 0; // hash value returned + unsigned char c; // next character + const mork_u1* body; // body of bytes to hash + mork_size size = 0; // the number of bytes to hash + + if (this->IsWeeBook()) { + size = mAtom_Size; + body = ((const morkWeeBookAtom*)this)->mWeeBookAtom_Body; + } else if (this->IsBigBook()) { + size = ((const morkBigBookAtom*)this)->mBigBookAtom_Size; + body = ((const morkBigBookAtom*)this)->mBigBookAtom_Body; + } else if (this->IsFarBook()) { + size = ((const morkFarBookAtom*)this)->mFarBookAtom_Size; + body = ((const morkFarBookAtom*)this)->mFarBookAtom_Body; + } else { + this->NonBookAtomTypeError(ev); + return 0; + } + + const mork_u1* end = body + size; + while (body < end) { + c = *body++; + outHash <<= 4; + outHash += c; + mork_u4 top = outHash & 0xF0000000L; // top four bits + if (top) // any of high four bits equal to one? + { + outHash ^= (top >> 24); // fold down high bits + outHash ^= top; // zero top four bits + } + } + + return outHash; +} + +mork_bool morkBookAtom::EqualFormAndBody(morkEnv* ev, + const morkBookAtom* inAtom) const { + mork_bool outEqual = morkBool_kFalse; + + const mork_u1* body = 0; // body of inAtom bytes to compare + mork_size size; // the number of inAtom bytes to compare + mork_cscode form; // nominal charset for ioAtom + + if (inAtom->IsWeeBook()) { + size = inAtom->mAtom_Size; + body = ((const morkWeeBookAtom*)inAtom)->mWeeBookAtom_Body; + form = 0; + } else if (inAtom->IsBigBook()) { + size = ((const morkBigBookAtom*)inAtom)->mBigBookAtom_Size; + body = ((const morkBigBookAtom*)inAtom)->mBigBookAtom_Body; + form = ((const morkBigBookAtom*)inAtom)->mBigBookAtom_Form; + } else if (inAtom->IsFarBook()) { + size = ((const morkFarBookAtom*)inAtom)->mFarBookAtom_Size; + body = ((const morkFarBookAtom*)inAtom)->mFarBookAtom_Body; + form = ((const morkFarBookAtom*)inAtom)->mFarBookAtom_Form; + } else { + inAtom->NonBookAtomTypeError(ev); + return morkBool_kFalse; + } + + const mork_u1* thisBody = 0; // body of bytes in this to compare + mork_size thisSize; // the number of bytes in this to compare + mork_cscode thisForm; // nominal charset for this atom + + if (this->IsWeeBook()) { + thisSize = mAtom_Size; + thisBody = ((const morkWeeBookAtom*)this)->mWeeBookAtom_Body; + thisForm = 0; + } else if (this->IsBigBook()) { + thisSize = ((const morkBigBookAtom*)this)->mBigBookAtom_Size; + thisBody = ((const morkBigBookAtom*)this)->mBigBookAtom_Body; + thisForm = ((const morkBigBookAtom*)this)->mBigBookAtom_Form; + } else if (this->IsFarBook()) { + thisSize = ((const morkFarBookAtom*)this)->mFarBookAtom_Size; + thisBody = ((const morkFarBookAtom*)this)->mFarBookAtom_Body; + thisForm = ((const morkFarBookAtom*)this)->mFarBookAtom_Form; + } else { + this->NonBookAtomTypeError(ev); + return morkBool_kFalse; + } + + // if atoms are empty, form is irrelevant + if (body && thisBody && size == thisSize && (!size || form == thisForm)) + outEqual = (MORK_MEMCMP(body, thisBody, size) == 0); + + return outEqual; +} + +void morkBookAtom::CutBookAtomFromSpace(morkEnv* ev) { + morkAtomSpace* space = mBookAtom_Space; + if (space) { + mBookAtom_Space = 0; + space->mAtomSpace_AtomBodies.CutAtom(ev, this); + space->mAtomSpace_AtomAids.CutAtom(ev, this); + } else + ev->NilPointerError(); +} + +morkWeeBookAtom::morkWeeBookAtom(mork_aid inAid) { + mAtom_Kind = morkAtom_kKindWeeBook; + mAtom_CellUses = 0; + mAtom_Change = morkChange_kNil; + mAtom_Size = 0; + + mBookAtom_Space = 0; + mBookAtom_Id = inAid; + + mWeeBookAtom_Body[0] = 0; +} + +void morkWeeBookAtom::InitWeeBookAtom(morkEnv* ev, const morkBuf& inBuf, + morkAtomSpace* ioSpace, mork_aid inAid) { + mAtom_Kind = 0; + mAtom_Change = morkChange_kNil; + if (ioSpace) { + if (inAid) { + if (inBuf.mBuf_Fill <= morkAtom_kMaxByteSize) { + mAtom_CellUses = 0; + mAtom_Kind = morkAtom_kKindWeeBook; + mBookAtom_Space = ioSpace; + mBookAtom_Id = inAid; + mork_size size = inBuf.mBuf_Fill; + mAtom_Size = (mork_u1)size; + if (size && inBuf.mBuf_Body) + MORK_MEMCPY(mWeeBookAtom_Body, inBuf.mBuf_Body, size); + + mWeeBookAtom_Body[size] = 0; + } else + this->AtomSizeOverflowError(ev); + } else + this->ZeroAidError(ev); + } else + ev->NilPointerError(); +} + +void morkBigBookAtom::InitBigBookAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm, + morkAtomSpace* ioSpace, mork_aid inAid) { + mAtom_Kind = 0; + mAtom_Change = morkChange_kNil; + if (ioSpace) { + if (inAid) { + mAtom_CellUses = 0; + mAtom_Kind = morkAtom_kKindBigBook; + mAtom_Size = 0; + mBookAtom_Space = ioSpace; + mBookAtom_Id = inAid; + mBigBookAtom_Form = inForm; + mork_size size = inBuf.mBuf_Fill; + mBigBookAtom_Size = size; + if (size && inBuf.mBuf_Body) + MORK_MEMCPY(mBigBookAtom_Body, inBuf.mBuf_Body, size); + + mBigBookAtom_Body[size] = 0; + } else + this->ZeroAidError(ev); + } else + ev->NilPointerError(); +} + +void morkFarBookAtom::InitFarBookAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm, + morkAtomSpace* ioSpace, mork_aid inAid) { + mAtom_Kind = 0; + mAtom_Change = morkChange_kNil; + if (ioSpace) { + if (inAid) { + mAtom_CellUses = 0; + mAtom_Kind = morkAtom_kKindFarBook; + mAtom_Size = 0; + mBookAtom_Space = ioSpace; + mBookAtom_Id = inAid; + mFarBookAtom_Form = inForm; + mFarBookAtom_Size = inBuf.mBuf_Fill; + mFarBookAtom_Body = (mork_u1*)inBuf.mBuf_Body; + } else + this->ZeroAidError(ev); + } else + ev->NilPointerError(); +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkAtom.h b/comm/mailnews/db/mork/morkAtom.h new file mode 100644 index 0000000000..4313a2e8fa --- /dev/null +++ b/comm/mailnews/db/mork/morkAtom.h @@ -0,0 +1,362 @@ +/* -*- 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 _MORKATOM_ +#define _MORKATOM_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkAtom_kMaxByteSize 255 /* max for 8-bit integer */ +#define morkAtom_kForeverCellUses 0x0FF /* max for 8-bit integer */ +#define morkAtom_kMaxCellUses 0x07F /* max for 7-bit integer */ + +#define morkAtom_kKindWeeAnon 'a' /* means morkWeeAnonAtom subclass */ +#define morkAtom_kKindBigAnon 'A' /* means morkBigAnonAtom subclass */ +#define morkAtom_kKindWeeBook 'b' /* means morkWeeBookAtom subclass */ +#define morkAtom_kKindBigBook 'B' /* means morkBigBookAtom subclass */ +#define morkAtom_kKindFarBook 'f' /* means morkFarBookAtom subclass */ +#define morkAtom_kKindRowOid 'r' /* means morkOidAtom subclass */ +#define morkAtom_kKindTableOid 't' /* means morkOidAtom subclass */ + +/*| Atom: . +|*/ +class morkAtom { // + + public: + mork_u1 mAtom_Kind; // identifies a specific atom subclass + mork_u1 mAtom_CellUses; // number of persistent uses in a cell + mork_change mAtom_Change; // how has this atom been changed? + mork_u1 mAtom_Size; // only for atoms smaller than 256 bytes + + public: + morkAtom(mork_aid inAid, mork_u1 inKind); + + mork_bool IsWeeAnon() const { return mAtom_Kind == morkAtom_kKindWeeAnon; } + mork_bool IsBigAnon() const { return mAtom_Kind == morkAtom_kKindBigAnon; } + mork_bool IsWeeBook() const { return mAtom_Kind == morkAtom_kKindWeeBook; } + mork_bool IsBigBook() const { return mAtom_Kind == morkAtom_kKindBigBook; } + mork_bool IsFarBook() const { return mAtom_Kind == morkAtom_kKindFarBook; } + mork_bool IsRowOid() const { return mAtom_Kind == morkAtom_kKindRowOid; } + mork_bool IsTableOid() const { return mAtom_Kind == morkAtom_kKindTableOid; } + + mork_bool IsBook() const { return this->IsWeeBook() || this->IsBigBook(); } + + public: // clean vs dirty + void SetAtomClean() { mAtom_Change = morkChange_kNil; } + void SetAtomDirty() { mAtom_Change = morkChange_kAdd; } + + mork_bool IsAtomClean() const { return mAtom_Change == morkChange_kNil; } + mork_bool IsAtomDirty() const { return mAtom_Change == morkChange_kAdd; } + + public: // atom space scope if IsBook() is true, or else zero: + mork_scope GetBookAtomSpaceScope(morkEnv* ev) const; + // zero or book's space's scope + + mork_aid GetBookAtomAid() const; + // zero or book atom's ID + + public: // empty construction does nothing + morkAtom() {} + + public: // one-byte refcounting, freezing at maximum + void MakeCellUseForever(morkEnv* ev); + mork_u1 AddCellUse(morkEnv* ev); + mork_u1 CutCellUse(morkEnv* ev); + + mork_bool IsCellUseForever() const { + return mAtom_CellUses == morkAtom_kForeverCellUses; + } + + private: // warnings + static void CellUsesUnderflowWarning(morkEnv* ev); + + public: // errors + static void BadAtomKindError(morkEnv* ev); + static void ZeroAidError(morkEnv* ev); + static void AtomSizeOverflowError(morkEnv* ev); + + public: // yarns + static mork_bool AliasYarn(const morkAtom* atom, mdbYarn* outYarn); + static mork_bool GetYarn(const morkAtom* atom, mdbYarn* outYarn); + + private: // copying is not allowed + morkAtom(const morkAtom& other); + morkAtom& operator=(const morkAtom& other); +}; + +/*| OidAtom: an atom that references a row or table by identity. +|*/ +class morkOidAtom : public morkAtom { // + + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms + + public: + mdbOid mOidAtom_Oid; // identity of referenced object + + public: // empty construction does nothing + morkOidAtom() {} + void InitRowOidAtom(morkEnv* ev, const mdbOid& inOid); + void InitTableOidAtom(morkEnv* ev, const mdbOid& inOid); + + private: // copying is not allowed + morkOidAtom(const morkOidAtom& other); + morkOidAtom& operator=(const morkOidAtom& other); +}; + +/*| WeeAnonAtom: an atom whose content immediately follows morkAtom slots +**| in an inline fashion, so that morkWeeAnonAtom contains both leading +**| atom slots and then the content bytes without further overhead. Note +**| that charset encoding is not indicated, so zero is implied for Latin1. +**| (Non-Latin1 content must be stored in a morkBigAnonAtom with a charset.) +**| +**|| An anon (anonymous) atom has no identity, with no associated bookkeeping +**| for lookup needed for sharing like a book atom. +**| +**|| A wee anon atom is immediate but not shared with any other users of this +**| atom, so no bookkeeping for sharing is needed. This means the atom has +**| no ID, because the atom has no identity other than this immediate content, +**| and no hash table is needed to look up this particular atom. This also +**| applies to the larger format morkBigAnonAtom, which has more slots. +|*/ +class morkWeeAnonAtom : public morkAtom { // + + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // only for atoms smaller than 256 bytes + + public: + mork_u1 mWeeAnonAtom_Body[1]; // 1st byte of immediate content vector + + public: // empty construction does nothing + morkWeeAnonAtom() {} + void InitWeeAnonAtom(morkEnv* ev, const morkBuf& inBuf); + + // allow extra trailing byte for a null byte: + static mork_size SizeForFill(mork_fill inFill) { + return sizeof(morkWeeAnonAtom) + inFill; + } + + private: // copying is not allowed + morkWeeAnonAtom(const morkWeeAnonAtom& other); + morkWeeAnonAtom& operator=(const morkWeeAnonAtom& other); +}; + +/*| BigAnonAtom: another immediate atom that cannot be encoded as the smaller +**| morkWeeAnonAtom format because either the size is too great, and/or the +**| charset is not the default zero for Latin1 and must be explicitly noted. +**| +**|| An anon (anonymous) atom has no identity, with no associated bookkeeping +**| for lookup needed for sharing like a book atom. +|*/ +class morkBigAnonAtom : public morkAtom { // + + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms + + public: + mork_cscode mBigAnonAtom_Form; // charset format encoding + mork_size mBigAnonAtom_Size; // size of content vector + mork_u1 mBigAnonAtom_Body[1]; // 1st byte of immed content vector + + public: // empty construction does nothing + morkBigAnonAtom() {} + void InitBigAnonAtom(morkEnv* ev, const morkBuf& inBuf, mork_cscode inForm); + + // allow extra trailing byte for a null byte: + static mork_size SizeForFill(mork_fill inFill) { + return sizeof(morkBigAnonAtom) + inFill; + } + + private: // copying is not allowed + morkBigAnonAtom(const morkBigAnonAtom& other); + morkBigAnonAtom& operator=(const morkBigAnonAtom& other); +}; + +#define morkBookAtom_kMaxBodySize 1024 /* if larger, cannot be shared */ + +/*| BookAtom: the common subportion of wee book atoms and big book atoms that +**| includes the atom ID and the pointer to the space referencing this atom +**| through a hash table. +|*/ +class morkBookAtom : public morkAtom { // + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // only for atoms smaller than 256 bytes + + public: + morkAtomSpace* + mBookAtom_Space; // mBookAtom_Space->SpaceScope() is atom scope + mork_aid mBookAtom_Id; // identity token for this shared atom + + public: // empty construction does nothing + morkBookAtom() {} + + static void NonBookAtomTypeError(morkEnv* ev); + + public: // Hash() and Equal() for atom ID maps are same for all subclasses: + mork_u4 HashAid() const { return mBookAtom_Id; } + mork_bool EqualAid(const morkBookAtom* inAtom) const { + return (mBookAtom_Id == inAtom->mBookAtom_Id); + } + + public: // Hash() and Equal() for atom body maps know about subclasses: + // YOU CANNOT SUBCLASS morkBookAtom WITHOUT FIXING Hash and Equal METHODS: + + mork_u4 HashFormAndBody(morkEnv* ev) const; + mork_bool EqualFormAndBody(morkEnv* ev, const morkBookAtom* inAtom) const; + + public: // separation from containing space + void CutBookAtomFromSpace(morkEnv* ev); + + private: // copying is not allowed + morkBookAtom(const morkBookAtom& other); + morkBookAtom& operator=(const morkBookAtom& other); +}; + +/*| FarBookAtom: this alternative format for book atoms was introduced +**| in May 2000 in order to support finding atoms in hash tables without +**| first copying the strings from original parsing buffers into a new +**| atom format. This was consuming too much time. However, we can +**| use morkFarBookAtom to stage a hash table query, as long as we then +**| fix HashFormAndBody() and EqualFormAndBody() to use morkFarBookAtom +**| correctly. +**| +**|| Note we do NOT intend that instances of morkFarBookAtom will ever +**| be installed in hash tables, because this is not space efficient. +**| We only expect to create temp instances for table lookups. +|*/ +class morkFarBookAtom : public morkBookAtom { // + + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms + + // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope + // mork_aid mBookAtom_Id; // identity token for this shared atom + + public: + mork_cscode mFarBookAtom_Form; // charset format encoding + mork_size mFarBookAtom_Size; // size of content vector + mork_u1* mFarBookAtom_Body; // bytes are elsewhere, out of line + + public: // empty construction does nothing + morkFarBookAtom() {} + void InitFarBookAtom(morkEnv* ev, const morkBuf& inBuf, mork_cscode inForm, + morkAtomSpace* ioSpace, mork_aid inAid); + + private: // copying is not allowed + morkFarBookAtom(const morkFarBookAtom& other); + morkFarBookAtom& operator=(const morkFarBookAtom& other); +}; + +/*| WeeBookAtom: . +|*/ +class morkWeeBookAtom : public morkBookAtom { // + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // only for atoms smaller than 256 bytes + + // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope + // mork_aid mBookAtom_Id; // identity token for this shared atom + + public: + mork_u1 mWeeBookAtom_Body[1]; // 1st byte of immed content vector + + public: // empty construction does nothing + morkWeeBookAtom() {} + explicit morkWeeBookAtom(mork_aid inAid); + + void InitWeeBookAtom(morkEnv* ev, const morkBuf& inBuf, + morkAtomSpace* ioSpace, mork_aid inAid); + + // allow extra trailing byte for a null byte: + static mork_size SizeForFill(mork_fill inFill) { + return sizeof(morkWeeBookAtom) + inFill; + } + + private: // copying is not allowed + morkWeeBookAtom(const morkWeeBookAtom& other); + morkWeeBookAtom& operator=(const morkWeeBookAtom& other); +}; + +/*| BigBookAtom: . +|*/ +class morkBigBookAtom : public morkBookAtom { // + + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms + + // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope + // mork_aid mBookAtom_Id; // identity token for this shared atom + + public: + mork_cscode mBigBookAtom_Form; // charset format encoding + mork_size mBigBookAtom_Size; // size of content vector + mork_u1 mBigBookAtom_Body[1]; // 1st byte of immed content vector + + public: // empty construction does nothing + morkBigBookAtom() {} + void InitBigBookAtom(morkEnv* ev, const morkBuf& inBuf, mork_cscode inForm, + morkAtomSpace* ioSpace, mork_aid inAid); + + // allow extra trailing byte for a null byte: + static mork_size SizeForFill(mork_fill inFill) { + return sizeof(morkBigBookAtom) + inFill; + } + + private: // copying is not allowed + morkBigBookAtom(const morkBigBookAtom& other); + morkBigBookAtom& operator=(const morkBigBookAtom& other); +}; + +/*| MaxBookAtom: . +|*/ +class morkMaxBookAtom : public morkBigBookAtom { // + + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms + + // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope + // mork_aid mBookAtom_Id; // identity token for this shared atom + + // mork_cscode mBigBookAtom_Form; // charset format encoding + // mork_size mBigBookAtom_Size; // size of content vector + // mork_u1 mBigBookAtom_Body[ 1 ]; // 1st byte of immed content vector + + public: + mork_u1 mMaxBookAtom_Body[morkBookAtom_kMaxBodySize + 3]; // max bytes + + public: // empty construction does nothing + morkMaxBookAtom() {} + void InitMaxBookAtom(morkEnv* ev, const morkBuf& inBuf, mork_cscode inForm, + morkAtomSpace* ioSpace, mork_aid inAid) { + this->InitBigBookAtom(ev, inBuf, inForm, ioSpace, inAid); + } + + private: // copying is not allowed + morkMaxBookAtom(const morkMaxBookAtom& other); + morkMaxBookAtom& operator=(const morkMaxBookAtom& other); +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKATOM_ */ diff --git a/comm/mailnews/db/mork/morkAtomMap.cpp b/comm/mailnews/db/mork/morkAtomMap.cpp new file mode 100644 index 0000000000..3ae3422b5a --- /dev/null +++ b/comm/mailnews/db/mork/morkAtomMap.cpp @@ -0,0 +1,378 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +#ifndef _MORKATOMMAP_ +# include "morkAtomMap.h" +#endif + +#ifndef _MORKATOM_ +# include "morkAtom.h" +#endif + +#ifndef _MORKINTMAP_ +# include "morkIntMap.h" +#endif + +#ifndef _MORKROW_ +# include "morkRow.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkAtomAidMap::CloseMorkNode( + morkEnv* ev) // CloseAtomAidMap() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseAtomAidMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkAtomAidMap::~morkAtomAidMap() // assert CloseAtomAidMap() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkAtomAidMap::morkAtomAidMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +#ifdef MORK_ENABLE_PROBE_MAPS + : morkProbeMap(ev, inUsage, ioHeap, + /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0, + ioSlotHeap, morkAtomAidMap_kStartSlotCount, + /*inZeroIsClearKey*/ morkBool_kTrue) +#else /*MORK_ENABLE_PROBE_MAPS*/ + : morkMap(ev, inUsage, ioHeap, + /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0, + morkAtomAidMap_kStartSlotCount, ioSlotHeap, + /*inHoldChanges*/ morkBool_kFalse) +#endif /*MORK_ENABLE_PROBE_MAPS*/ +{ + if (ev->Good()) mNode_Derived = morkDerived_kAtomAidMap; +} + +/*public non-poly*/ void morkAtomAidMap::CloseAtomAidMap( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { +#ifdef MORK_ENABLE_PROBE_MAPS + this->CloseProbeMap(ev); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->CloseMap(ev); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +#ifdef MORK_ENABLE_PROBE_MAPS + +/*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b) +morkAtomAidMap::MapTest(morkEnv* ev, const void* inMapKey, + const void* inAppKey) const { + MORK_USED_1(ev); + const morkBookAtom* key = *(const morkBookAtom**)inMapKey; + if (key) { + mork_bool hit = key->EqualAid(*(const morkBookAtom**)inAppKey); + return (hit) ? morkTest_kHit : morkTest_kMiss; + } else + return morkTest_kVoid; +} + +/*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b) +morkAtomAidMap::MapHash(morkEnv* ev, const void* inAppKey) const { + const morkBookAtom* key = *(const morkBookAtom**)inAppKey; + if (key) + return key->HashAid(); + else { + ev->NilPointerWarning(); + return 0; + } +} + +/*virtual*/ mork_u4 morkAtomAidMap::ProbeMapHashMapKey( + morkEnv* ev, const void* inMapKey) const { + const morkBookAtom* key = *(const morkBookAtom**)inMapKey; + if (key) + return key->HashAid(); + else { + ev->NilPointerWarning(); + return 0; + } +} +#else /*MORK_ENABLE_PROBE_MAPS*/ +// { ===== begin morkMap poly interface ===== +/*virtual*/ mork_bool // +morkAtomAidMap::Equal(morkEnv* ev, const void* inKeyA, + const void* inKeyB) const { + MORK_USED_1(ev); + return (*(const morkBookAtom**)inKeyA) + ->EqualAid(*(const morkBookAtom**)inKeyB); +} + +/*virtual*/ mork_u4 // +morkAtomAidMap::Hash(morkEnv* ev, const void* inKey) const { + MORK_USED_1(ev); + return (*(const morkBookAtom**)inKey)->HashAid(); +} +// } ===== end morkMap poly interface ===== +#endif /*MORK_ENABLE_PROBE_MAPS*/ + +mork_bool morkAtomAidMap::AddAtom(morkEnv* ev, morkBookAtom* ioAtom) { + if (ev->Good()) { +#ifdef MORK_ENABLE_PROBE_MAPS + this->MapAtPut(ev, &ioAtom, /*val*/ (void*)0, + /*key*/ (void*)0, /*val*/ (void*)0); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->Put(ev, &ioAtom, /*val*/ (void*)0, + /*key*/ (void*)0, /*val*/ (void*)0, (mork_change**)0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + } + return ev->Good(); +} + +morkBookAtom* morkAtomAidMap::CutAtom(morkEnv* ev, const morkBookAtom* inAtom) { + morkBookAtom* oldKey = 0; + +#ifdef MORK_ENABLE_PROBE_MAPS + MORK_USED_1(inAtom); + morkProbeMap::ProbeMapCutError(ev); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->Cut(ev, &inAtom, &oldKey, /*val*/ (void*)0, (mork_change**)0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + return oldKey; +} + +morkBookAtom* morkAtomAidMap::GetAtom(morkEnv* ev, const morkBookAtom* inAtom) { + morkBookAtom* key = 0; // old val in the map + +#ifdef MORK_ENABLE_PROBE_MAPS + this->MapAt(ev, &inAtom, &key, /*val*/ (void*)0); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->Get(ev, &inAtom, &key, /*val*/ (void*)0, (mork_change**)0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + return key; +} + +morkBookAtom* morkAtomAidMap::GetAid(morkEnv* ev, mork_aid inAid) { + morkWeeBookAtom weeAtom(inAid); + morkBookAtom* key = &weeAtom; // we need a pointer + morkBookAtom* oldKey = 0; // old key in the map + +#ifdef MORK_ENABLE_PROBE_MAPS + this->MapAt(ev, &key, &oldKey, /*val*/ (void*)0); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->Get(ev, &key, &oldKey, /*val*/ (void*)0, (mork_change**)0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + return oldKey; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkAtomBodyMap::CloseMorkNode( + morkEnv* ev) // CloseAtomBodyMap() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseAtomBodyMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkAtomBodyMap::~morkAtomBodyMap() // assert CloseAtomBodyMap() executed + // earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkAtomBodyMap::morkAtomBodyMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +#ifdef MORK_ENABLE_PROBE_MAPS + : morkProbeMap(ev, inUsage, ioHeap, + /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0, + ioSlotHeap, morkAtomBodyMap_kStartSlotCount, + /*inZeroIsClearKey*/ morkBool_kTrue) +#else /*MORK_ENABLE_PROBE_MAPS*/ + : morkMap(ev, inUsage, ioHeap, + /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0, + morkAtomBodyMap_kStartSlotCount, ioSlotHeap, + /*inHoldChanges*/ morkBool_kFalse) +#endif /*MORK_ENABLE_PROBE_MAPS*/ +{ + if (ev->Good()) mNode_Derived = morkDerived_kAtomBodyMap; +} + +/*public non-poly*/ void morkAtomBodyMap::CloseAtomBodyMap( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { +#ifdef MORK_ENABLE_PROBE_MAPS + this->CloseProbeMap(ev); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->CloseMap(ev); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` +#ifdef MORK_ENABLE_PROBE_MAPS + +/*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b) +morkAtomBodyMap::MapTest(morkEnv* ev, const void* inMapKey, + const void* inAppKey) const { + const morkBookAtom* key = *(const morkBookAtom**)inMapKey; + if (key) { + return (key->EqualFormAndBody(ev, *(const morkBookAtom**)inAppKey)) + ? morkTest_kHit + : morkTest_kMiss; + } else + return morkTest_kVoid; +} + +/*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b) +morkAtomBodyMap::MapHash(morkEnv* ev, const void* inAppKey) const { + const morkBookAtom* key = *(const morkBookAtom**)inAppKey; + if (key) + return key->HashFormAndBody(ev); + else + return 0; +} + +/*virtual*/ mork_u4 morkAtomBodyMap::ProbeMapHashMapKey( + morkEnv* ev, const void* inMapKey) const { + const morkBookAtom* key = *(const morkBookAtom**)inMapKey; + if (key) + return key->HashFormAndBody(ev); + else + return 0; +} +#else /*MORK_ENABLE_PROBE_MAPS*/ +// { ===== begin morkMap poly interface ===== +/*virtual*/ mork_bool // +morkAtomBodyMap::Equal(morkEnv* ev, const void* inKeyA, + const void* inKeyB) const { + return (*(const morkBookAtom**)inKeyA) + ->EqualFormAndBody(ev, *(const morkBookAtom**)inKeyB); +} + +/*virtual*/ mork_u4 // +morkAtomBodyMap::Hash(morkEnv* ev, const void* inKey) const { + return (*(const morkBookAtom**)inKey)->HashFormAndBody(ev); +} +// } ===== end morkMap poly interface ===== +#endif /*MORK_ENABLE_PROBE_MAPS*/ + +mork_bool morkAtomBodyMap::AddAtom(morkEnv* ev, morkBookAtom* ioAtom) { + if (ev->Good()) { +#ifdef MORK_ENABLE_PROBE_MAPS + this->MapAtPut(ev, &ioAtom, /*val*/ (void*)0, + /*key*/ (void*)0, /*val*/ (void*)0); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->Put(ev, &ioAtom, /*val*/ (void*)0, + /*key*/ (void*)0, /*val*/ (void*)0, (mork_change**)0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + } + return ev->Good(); +} + +morkBookAtom* morkAtomBodyMap::CutAtom(morkEnv* ev, + const morkBookAtom* inAtom) { + morkBookAtom* oldKey = 0; + +#ifdef MORK_ENABLE_PROBE_MAPS + MORK_USED_1(inAtom); + morkProbeMap::ProbeMapCutError(ev); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->Cut(ev, &inAtom, &oldKey, /*val*/ (void*)0, (mork_change**)0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + return oldKey; +} + +morkBookAtom* morkAtomBodyMap::GetAtom(morkEnv* ev, + const morkBookAtom* inAtom) { + morkBookAtom* key = 0; // old val in the map +#ifdef MORK_ENABLE_PROBE_MAPS + this->MapAt(ev, &inAtom, &key, /*val*/ (void*)0); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->Get(ev, &inAtom, &key, /*val*/ (void*)0, (mork_change**)0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + return key; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +morkAtomRowMap::~morkAtomRowMap() {} + +// I changed to sizeof(mork_ip) from sizeof(mork_aid) to fix a crash on +// 64 bit machines. I am not sure it was the right way to fix the problem, +// but it does stop the crash. Perhaps we should be using the +// morkPointerMap instead? +morkAtomRowMap::morkAtomRowMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, + mork_column inIndexColumn) + : morkIntMap(ev, inUsage, sizeof(mork_ip), ioHeap, ioSlotHeap, + /*inHoldChanges*/ morkBool_kFalse), + mAtomRowMap_IndexColumn(inIndexColumn) { + if (ev->Good()) mNode_Derived = morkDerived_kAtomRowMap; +} + +void morkAtomRowMap::AddRow(morkEnv* ev, morkRow* ioRow) +// add ioRow only if it contains a cell in mAtomRowMap_IndexColumn. +{ + mork_aid aid = ioRow->GetCellAtomAid(ev, mAtomRowMap_IndexColumn); + if (aid) this->AddAid(ev, aid, ioRow); +} + +void morkAtomRowMap::CutRow(morkEnv* ev, morkRow* ioRow) +// cut ioRow only if it contains a cell in mAtomRowMap_IndexColumn. +{ + mork_aid aid = ioRow->GetCellAtomAid(ev, mAtomRowMap_IndexColumn); + if (aid) this->CutAid(ev, aid); +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkAtomMap.h b/comm/mailnews/db/mork/morkAtomMap.h new file mode 100644 index 0000000000..895fbedc74 --- /dev/null +++ b/comm/mailnews/db/mork/morkAtomMap.h @@ -0,0 +1,394 @@ +/* -*- 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 _MORKATOMMAP_ +#define _MORKATOMMAP_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +#ifndef _MORKPROBEMAP_ +# include "morkProbeMap.h" +#endif + +#ifndef _MORKINTMAP_ +# include "morkIntMap.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kAtomAidMap /*i*/ 0x6141 /* ascii 'aA' */ + +#define morkAtomAidMap_kStartSlotCount 23 + +/*| morkAtomAidMap: keys of morkBookAtom organized by atom ID +|*/ +#ifdef MORK_ENABLE_PROBE_MAPS +class morkAtomAidMap : public morkProbeMap { // for mapping tokens to maps +#else /*MORK_ENABLE_PROBE_MAPS*/ +class morkAtomAidMap : public morkMap { // for mapping tokens to maps +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseAtomAidMap() only if open + virtual ~morkAtomAidMap(); // assert that CloseAtomAidMap() executed earlier + + public: // morkMap construction & destruction + morkAtomAidMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + void CloseAtomAidMap(morkEnv* ev); // called by CloseMorkNode(); + + public: // dynamic type identification + mork_bool IsAtomAidMap() const { + return IsNode() && mNode_Derived == morkDerived_kAtomAidMap; + } + // } ===== end morkNode methods ===== + + public: +#ifdef MORK_ENABLE_PROBE_MAPS + // { ===== begin morkProbeMap methods ===== + virtual mork_test // hit(a,b) implies hash(a) == hash(b) + MapTest(morkEnv* ev, const void* inMapKey, + const void* inAppKey) const override; + + virtual mork_u4 // hit(a,b) implies hash(a) == hash(b) + MapHash(morkEnv* ev, const void* inAppKey) const override; + + virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, + const void* inMapKey) const override; + + // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey); + + // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' into all keys + // inside map + // void* ioMapKey, mork_count inKeyCount); // array of keys inside map + + // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map + // const void* inAppKey, const void* inAppVal, // (key,val) outside map + // void* outMapKey, void* outMapVal); // (key,val) inside map + + // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the + // map + // const void* inMapKey, const void* inMapVal, // (key,val) inside map + // void* outAppKey, void* outAppVal) const; // (key,val) outside map + // } ===== end morkProbeMap methods ===== +#else /*MORK_ENABLE_PROBE_MAPS*/ + // { ===== begin morkMap poly interface ===== + virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b) + Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override; + // implemented using morkBookAtom::HashAid() + + virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b) + Hash(morkEnv* ev, const void* inKey) const override; + // implemented using morkBookAtom::EqualAid() +// } ===== end morkMap poly interface ===== +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + public: // other map methods + mork_bool AddAtom(morkEnv* ev, morkBookAtom* ioAtom); + // AddAtom() returns ev->Good() + + morkBookAtom* CutAtom(morkEnv* ev, const morkBookAtom* inAtom); + // CutAtom() returns the atom removed equal to inAtom, if there was one + + morkBookAtom* GetAtom(morkEnv* ev, const morkBookAtom* inAtom); + // GetAtom() returns the atom equal to inAtom, or else nil + + morkBookAtom* GetAid(morkEnv* ev, mork_aid inAid); + // GetAid() returns the atom equal to inAid, or else nil + + // note the atoms are owned elsewhere, usually by morkAtomSpace + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakAtomAidMap(morkAtomAidMap* me, morkEnv* ev, + morkAtomAidMap** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongAtomAidMap(morkAtomAidMap* me, morkEnv* ev, + morkAtomAidMap** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +#ifdef MORK_ENABLE_PROBE_MAPS +class morkAtomAidMapIter : public morkProbeMapIter { // typesafe wrapper class +#else /*MORK_ENABLE_PROBE_MAPS*/ +class morkAtomAidMapIter : public morkMapIter { // typesafe wrapper class +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + public: +#ifdef MORK_ENABLE_PROBE_MAPS + morkAtomAidMapIter(morkEnv* ev, morkAtomAidMap* ioMap) + : morkProbeMapIter(ev, ioMap) {} + + morkAtomAidMapIter() : morkProbeMapIter() {} +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkAtomAidMapIter(morkEnv* ev, morkAtomAidMap* ioMap) + : morkMapIter(ev, ioMap) {} + + morkAtomAidMapIter() : morkMapIter() {} +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + void InitAtomAidMapIter(morkEnv* ev, morkAtomAidMap* ioMap) { + this->InitMapIter(ev, ioMap); + } + + mork_change* FirstAtom(morkEnv* ev, morkBookAtom** outAtomPtr) { + return this->First(ev, outAtomPtr, /*val*/ (void*)0); + } + + mork_change* NextAtom(morkEnv* ev, morkBookAtom** outAtomPtr) { + return this->Next(ev, outAtomPtr, /*val*/ (void*)0); + } + + mork_change* HereAtom(morkEnv* ev, morkBookAtom** outAtomPtr) { + return this->Here(ev, outAtomPtr, /*val*/ (void*)0); + } + + mork_change* CutHereAtom(morkEnv* ev, morkBookAtom** outAtomPtr) { + return this->CutHere(ev, outAtomPtr, /*val*/ (void*)0); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kAtomBodyMap /*i*/ 0x6142 /* ascii 'aB' */ + +#define morkAtomBodyMap_kStartSlotCount 23 + +/*| morkAtomBodyMap: keys of morkBookAtom organized by body bytes +|*/ +#ifdef MORK_ENABLE_PROBE_MAPS +class morkAtomBodyMap : public morkProbeMap { // for mapping tokens to maps +#else /*MORK_ENABLE_PROBE_MAPS*/ +class morkAtomBodyMap : public morkMap { // for mapping tokens to maps +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseAtomBodyMap() only if open + virtual ~morkAtomBodyMap(); // assert CloseAtomBodyMap() executed earlier + + public: // morkMap construction & destruction + morkAtomBodyMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + void CloseAtomBodyMap(morkEnv* ev); // called by CloseMorkNode(); + + public: // dynamic type identification + mork_bool IsAtomBodyMap() const { + return IsNode() && mNode_Derived == morkDerived_kAtomBodyMap; + } + // } ===== end morkNode methods ===== + + public: +#ifdef MORK_ENABLE_PROBE_MAPS + // { ===== begin morkProbeMap methods ===== + virtual mork_test // hit(a,b) implies hash(a) == hash(b) + MapTest(morkEnv* ev, const void* inMapKey, + const void* inAppKey) const override; + + virtual mork_u4 // hit(a,b) implies hash(a) == hash(b) + MapHash(morkEnv* ev, const void* inAppKey) const override; + + virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, + const void* inMapKey) const override; + + // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey); + + // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' into all keys + // inside map + // void* ioMapKey, mork_count inKeyCount); // array of keys inside map + + // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map + // const void* inAppKey, const void* inAppVal, // (key,val) outside map + // void* outMapKey, void* outMapVal); // (key,val) inside map + + // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the + // map + // const void* inMapKey, const void* inMapVal, // (key,val) inside map + // void* outAppKey, void* outAppVal) const; // (key,val) outside map + // } ===== end morkProbeMap methods ===== +#else /*MORK_ENABLE_PROBE_MAPS*/ + // { ===== begin morkMap poly interface ===== + virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b) + Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override; + // implemented using morkBookAtom::EqualFormAndBody() + + virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b) + Hash(morkEnv* ev, const void* inKey) const override; + // implemented using morkBookAtom::HashFormAndBody() +// } ===== end morkMap poly interface ===== +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + public: // other map methods + mork_bool AddAtom(morkEnv* ev, morkBookAtom* ioAtom); + // AddAtom() returns ev->Good() + + morkBookAtom* CutAtom(morkEnv* ev, const morkBookAtom* inAtom); + // CutAtom() returns the atom removed equal to inAtom, if there was one + + morkBookAtom* GetAtom(morkEnv* ev, const morkBookAtom* inAtom); + // GetAtom() returns the atom equal to inAtom, or else nil + + // note the atoms are owned elsewhere, usually by morkAtomSpace + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakAtomBodyMap(morkAtomBodyMap* me, morkEnv* ev, + morkAtomBodyMap** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongAtomBodyMap(morkAtomBodyMap* me, morkEnv* ev, + morkAtomBodyMap** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +#ifdef MORK_ENABLE_PROBE_MAPS +class morkAtomBodyMapIter : public morkProbeMapIter { // typesafe wrapper class +#else /*MORK_ENABLE_PROBE_MAPS*/ +class morkAtomBodyMapIter : public morkMapIter { // typesafe wrapper class +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + public: +#ifdef MORK_ENABLE_PROBE_MAPS + morkAtomBodyMapIter(morkEnv* ev, morkAtomBodyMap* ioMap) + : morkProbeMapIter(ev, ioMap) {} + + morkAtomBodyMapIter() : morkProbeMapIter() {} +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkAtomBodyMapIter(morkEnv* ev, morkAtomBodyMap* ioMap) + : morkMapIter(ev, ioMap) {} + + morkAtomBodyMapIter() : morkMapIter() {} +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + void InitAtomBodyMapIter(morkEnv* ev, morkAtomBodyMap* ioMap) { + this->InitMapIter(ev, ioMap); + } + + mork_change* FirstAtom(morkEnv* ev, morkBookAtom** outAtomPtr) { + return this->First(ev, outAtomPtr, /*val*/ (void*)0); + } + + mork_change* NextAtom(morkEnv* ev, morkBookAtom** outAtomPtr) { + return this->Next(ev, outAtomPtr, /*val*/ (void*)0); + } + + mork_change* HereAtom(morkEnv* ev, morkBookAtom** outAtomPtr) { + return this->Here(ev, outAtomPtr, /*val*/ (void*)0); + } + + mork_change* CutHereAtom(morkEnv* ev, morkBookAtom** outAtomPtr) { + return this->CutHere(ev, outAtomPtr, /*val*/ (void*)0); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kAtomRowMap /*i*/ 0x6152 /* ascii 'aR' */ + +/*| morkAtomRowMap: maps morkAtom* -> morkRow* +|*/ +class morkAtomRowMap : public morkIntMap { // for mapping atoms to rows + + public: + mork_column mAtomRowMap_IndexColumn; // row column being indexed + + public: + virtual ~morkAtomRowMap(); + morkAtomRowMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap, mork_column inIndexColumn); + + public: // adding and cutting from morkRow instance candidate + void AddRow(morkEnv* ev, morkRow* ioRow); + // add ioRow only if it contains a cell in mAtomRowMap_IndexColumn. + + void CutRow(morkEnv* ev, morkRow* ioRow); + // cut ioRow only if it contains a cell in mAtomRowMap_IndexColumn. + + public: // other map methods + mork_bool AddAid(morkEnv* ev, mork_aid inAid, morkRow* ioRow) { + return this->AddInt(ev, inAid, ioRow); + } + // the AddAid() boolean return equals ev->Good(). + + mork_bool CutAid(morkEnv* ev, mork_aid inAid) { + return this->CutInt(ev, inAid); + } + // The CutAid() boolean return indicates whether removal happened. + + morkRow* GetAid(morkEnv* ev, mork_aid inAid) { + return (morkRow*)this->GetInt(ev, inAid); + } + // Note the returned space does NOT have an increase in refcount for this. + + public: // dynamic type identification + mork_bool IsAtomRowMap() const { + return IsNode() && mNode_Derived == morkDerived_kAtomRowMap; + } + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakAtomRowMap(morkAtomRowMap* me, morkEnv* ev, + morkAtomRowMap** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongAtomRowMap(morkAtomRowMap* me, morkEnv* ev, + morkAtomRowMap** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +class morkAtomRowMapIter : public morkMapIter { // typesafe wrapper class + + public: + morkAtomRowMapIter(morkEnv* ev, morkAtomRowMap* ioMap) + : morkMapIter(ev, ioMap) {} + + morkAtomRowMapIter() : morkMapIter() {} + void InitAtomRowMapIter(morkEnv* ev, morkAtomRowMap* ioMap) { + this->InitMapIter(ev, ioMap); + } + + mork_change* FirstAtomAndRow(morkEnv* ev, morkAtom** outAtom, + morkRow** outRow) { + return this->First(ev, outAtom, outRow); + } + + mork_change* NextAtomAndRow(morkEnv* ev, morkAtom** outAtom, + morkRow** outRow) { + return this->Next(ev, outAtom, outRow); + } + + mork_change* HereAtomAndRow(morkEnv* ev, morkAtom** outAtom, + morkRow** outRow) { + return this->Here(ev, outAtom, outRow); + } + + mork_change* CutHereAtomAndRow(morkEnv* ev, morkAtom** outAtom, + morkRow** outRow) { + return this->CutHere(ev, outAtom, outRow); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKATOMMAP_ */ diff --git a/comm/mailnews/db/mork/morkAtomSpace.cpp b/comm/mailnews/db/mork/morkAtomSpace.cpp new file mode 100644 index 0000000000..5ecdfe2b4c --- /dev/null +++ b/comm/mailnews/db/mork/morkAtomSpace.cpp @@ -0,0 +1,233 @@ +/* -*- 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 _MORKSPACE_ +# include "morkSpace.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKSPACE_ +# include "morkSpace.h" +#endif + +#ifndef _MORKATOMSPACE_ +# include "morkAtomSpace.h" +#endif + +#ifndef _MORKPOOL_ +# include "morkPool.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +#ifndef _MORKATOM_ +# include "morkAtom.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkAtomSpace::CloseMorkNode( + morkEnv* ev) // CloseAtomSpace() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseAtomSpace(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkAtomSpace::~morkAtomSpace() // assert CloseAtomSpace() executed earlier +{ + MORK_ASSERT(mAtomSpace_HighUnderId == 0); + MORK_ASSERT(mAtomSpace_HighOverId == 0); + MORK_ASSERT(this->IsShutNode()); + MORK_ASSERT(mAtomSpace_AtomAids.IsShutNode()); + MORK_ASSERT(mAtomSpace_AtomBodies.IsShutNode()); +} + +/*public non-poly*/ +morkAtomSpace::morkAtomSpace(morkEnv* ev, const morkUsage& inUsage, + mork_scope inScope, morkStore* ioStore, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) + : morkSpace(ev, inUsage, inScope, ioStore, ioHeap, ioSlotHeap), + mAtomSpace_HighUnderId(morkAtomSpace_kMinUnderId), + mAtomSpace_HighOverId(morkAtomSpace_kMinOverId), + mAtomSpace_AtomAids(ev, morkUsage::kMember, (nsIMdbHeap*)0, ioSlotHeap), + mAtomSpace_AtomBodies(ev, morkUsage::kMember, (nsIMdbHeap*)0, + ioSlotHeap) { + // the morkSpace base constructor handles any dirty propagation + if (ev->Good()) mNode_Derived = morkDerived_kAtomSpace; +} + +/*public non-poly*/ void morkAtomSpace::CloseAtomSpace( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + mAtomSpace_AtomBodies.CloseMorkNode(ev); + morkStore* store = mSpace_Store; + if (store) this->CutAllAtoms(ev, &store->mStore_Pool); + + mAtomSpace_AtomAids.CloseMorkNode(ev); + this->CloseSpace(ev); + mAtomSpace_HighUnderId = 0; + mAtomSpace_HighOverId = 0; + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void morkAtomSpace::NonAtomSpaceTypeError(morkEnv* ev) { + ev->NewError("non morkAtomSpace"); +} + +mork_num morkAtomSpace::CutAllAtoms(morkEnv* ev, morkPool* ioPool) { +#ifdef MORK_ENABLE_ZONE_ARENAS + MORK_USED_2(ev, ioPool); + return 0; +#else /*MORK_ENABLE_ZONE_ARENAS*/ + if (this->IsAtomSpaceClean()) this->MaybeDirtyStoreAndSpace(); + + mork_num outSlots = mAtomSpace_AtomAids.MapFill(); + morkBookAtom* a = 0; // old key atom in the map + + morkStore* store = mSpace_Store; + mork_change* c = 0; + morkAtomAidMapIter i(ev, &mAtomSpace_AtomAids); + for (c = i.FirstAtom(ev, &a); c; c = i.NextAtom(ev, &a)) { + if (a) ioPool->ZapAtom(ev, a, &store->mStore_Zone); + +# ifdef MORK_ENABLE_PROBE_MAPS + // do not cut anything from the map +# else /*MORK_ENABLE_PROBE_MAPS*/ + i.CutHereAtom(ev, /*key*/ (morkBookAtom**)0); +# endif /*MORK_ENABLE_PROBE_MAPS*/ + } + + return outSlots; +#endif /*MORK_ENABLE_ZONE_ARENAS*/ +} + +morkBookAtom* morkAtomSpace::MakeBookAtomCopyWithAid( + morkEnv* ev, const morkFarBookAtom& inAtom, mork_aid inAid) +// Make copy of inAtom and put it in both maps, using specified ID. +{ + morkBookAtom* outAtom = 0; + morkStore* store = mSpace_Store; + if (ev->Good() && store) { + morkPool* pool = this->GetSpaceStorePool(); + outAtom = pool->NewFarBookAtomCopy(ev, inAtom, &store->mStore_Zone); + if (outAtom) { + if (store->mStore_CanDirty) { + outAtom->SetAtomDirty(); + if (this->IsAtomSpaceClean()) this->MaybeDirtyStoreAndSpace(); + } + + outAtom->mBookAtom_Id = inAid; + outAtom->mBookAtom_Space = this; + mAtomSpace_AtomAids.AddAtom(ev, outAtom); + mAtomSpace_AtomBodies.AddAtom(ev, outAtom); + if (this->SpaceScope() == morkAtomSpace_kColumnScope) + outAtom->MakeCellUseForever(ev); + + if (mAtomSpace_HighUnderId <= inAid) mAtomSpace_HighUnderId = inAid + 1; + } + } + return outAtom; +} + +morkBookAtom* morkAtomSpace::MakeBookAtomCopy(morkEnv* ev, + const morkFarBookAtom& inAtom) +// make copy of inAtom and put it in both maps, using a new ID as needed. +{ + morkBookAtom* outAtom = 0; + morkStore* store = mSpace_Store; + if (ev->Good() && store) { + if (store->mStore_CanAutoAssignAtomIdentity) { + morkPool* pool = this->GetSpaceStorePool(); + morkBookAtom* atom = + pool->NewFarBookAtomCopy(ev, inAtom, &mSpace_Store->mStore_Zone); + if (atom) { + mork_aid id = this->MakeNewAtomId(ev, atom); + if (id) { + if (store->mStore_CanDirty) { + atom->SetAtomDirty(); + if (this->IsAtomSpaceClean()) this->MaybeDirtyStoreAndSpace(); + } + + outAtom = atom; + atom->mBookAtom_Space = this; + mAtomSpace_AtomAids.AddAtom(ev, atom); + mAtomSpace_AtomBodies.AddAtom(ev, atom); + if (this->SpaceScope() == morkAtomSpace_kColumnScope) + outAtom->MakeCellUseForever(ev); + } else + pool->ZapAtom(ev, atom, &mSpace_Store->mStore_Zone); + } + } else + mSpace_Store->CannotAutoAssignAtomIdentityError(ev); + } + return outAtom; +} + +mork_aid morkAtomSpace::MakeNewAtomId(morkEnv* ev, morkBookAtom* ioAtom) { + mork_aid outAid = 0; + mork_tid id = mAtomSpace_HighUnderId; + mork_num count = 8; // try up to eight times + + while (!outAid && count) // still trying to find an unused table ID? + { + --count; + ioAtom->mBookAtom_Id = id; + if (!mAtomSpace_AtomAids.GetAtom(ev, ioAtom)) + outAid = id; + else { + MORK_ASSERT(morkBool_kFalse); // alert developer about ID problems + ++id; + } + } + + mAtomSpace_HighUnderId = id + 1; + return outAid; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +morkAtomSpaceMap::~morkAtomSpaceMap() {} + +morkAtomSpaceMap::morkAtomSpaceMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) + : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap) { + if (ev->Good()) mNode_Derived = morkDerived_kAtomSpaceMap; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkAtomSpace.h b/comm/mailnews/db/mork/morkAtomSpace.h new file mode 100644 index 0000000000..d057ad6cfd --- /dev/null +++ b/comm/mailnews/db/mork/morkAtomSpace.h @@ -0,0 +1,227 @@ +/* -*- 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 _MORKATOMSPACE_ +#define _MORKATOMSPACE_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKSPACE_ +# include "morkSpace.h" +#endif + +#ifndef _MORKATOMMAP_ +# include "morkAtomMap.h" +#endif + +#ifndef _MORKNODEMAP_ +# include "morkNodeMap.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*| kMinUnderId: the smallest ID we auto-assign to the 'under' namespace +**| reserved for tokens expected to occur very frequently, such as the names +**| of columns. We reserve single byte ids in the ASCII range to correspond +**| one-to-one to those tokens consisting single ASCII characters (so that +**| this assignment is always known and constant). So we start at 0x80, and +**| then reserve the upper half of two hex digit ids and all the three hex +**| digit IDs for the 'under' namespace for common tokens. +|*/ +#define morkAtomSpace_kMinUnderId 0x80 /* low 7 bits mean byte tokens */ + +#define morkAtomSpace_kMaxSevenBitAid 0x7F /* low seven bit integer ID */ + +/*| kMinOverId: the smallest ID we auto-assign to the 'over' namespace that +**| might include very large numbers of tokens that are used infrequently, +**| so that we care less whether the shortest hex representation is used. +**| So we start all IDs for 'over' category tokens at a value range that +**| needs at least four hex digits, so we can reserve three hex digits and +**| shorter for more commonly occurring tokens in the 'under' category. +|*/ +#define morkAtomSpace_kMinOverId 0x1000 /* using at least four hex bytes */ + +#define morkDerived_kAtomSpace /*i*/ 0x6153 /* ascii 'aS' */ + +#define morkAtomSpace_kColumnScope \ + ((mork_scope)'c') /* column scope is forever */ + +/*| morkAtomSpace: +|*/ +class morkAtomSpace : public morkSpace { // + + // public: // slots inherited from morkSpace (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // morkStore* mSpace_Store; // weak ref to containing store + + // mork_bool mSpace_DoAutoIDs; // whether db should assign member IDs + // mork_bool mSpace_HaveDoneAutoIDs; // whether actually auto assigned IDs + // mork_u1 mSpace_Pad[ 2 ]; // pad to u4 alignment + + public: // state is public because the entire Mork system is private + mork_aid mAtomSpace_HighUnderId; // high ID in 'under' range + mork_aid mAtomSpace_HighOverId; // high ID in 'over' range + + morkAtomAidMap mAtomSpace_AtomAids; // all atoms in space by ID + morkAtomBodyMap mAtomSpace_AtomBodies; // all atoms in space by body + + public: // more specific dirty methods for atom space: + void SetAtomSpaceDirty() { this->SetNodeDirty(); } + void SetAtomSpaceClean() { this->SetNodeClean(); } + + mork_bool IsAtomSpaceClean() const { return this->IsNodeClean(); } + mork_bool IsAtomSpaceDirty() const { return this->IsNodeDirty(); } + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseAtomSpace() only if open + virtual ~morkAtomSpace(); // assert that CloseAtomSpace() executed earlier + + public: // morkMap construction & destruction + morkAtomSpace(morkEnv* ev, const morkUsage& inUsage, mork_scope inScope, + morkStore* ioStore, nsIMdbHeap* ioNodeHeap, + nsIMdbHeap* ioSlotHeap); + void CloseAtomSpace(morkEnv* ev); // called by CloseMorkNode(); + + public: // dynamic type identification + mork_bool IsAtomSpace() const { + return IsNode() && mNode_Derived == morkDerived_kAtomSpace; + } + // } ===== end morkNode methods ===== + + public: // typing + void NonAtomSpaceTypeError(morkEnv* ev); + + public: // setup + mork_bool MarkAllAtomSpaceContentDirty(morkEnv* ev); + // MarkAllAtomSpaceContentDirty() visits every space object 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: // other space methods + // void ReserveColumnAidCount(mork_count inCount) + // { + // mAtomSpace_HighUnderId = morkAtomSpace_kMinUnderId + inCount; + // mAtomSpace_HighOverId = morkAtomSpace_kMinOverId + inCount; + // } + + mork_num CutAllAtoms(morkEnv* ev, morkPool* ioPool); + // CutAllAtoms() puts all the atoms back in the pool. + + morkBookAtom* MakeBookAtomCopyWithAid(morkEnv* ev, + const morkFarBookAtom& inAtom, + mork_aid inAid); + // Make copy of inAtom and put it in both maps, using specified ID. + + morkBookAtom* MakeBookAtomCopy(morkEnv* ev, const morkFarBookAtom& inAtom); + // Make copy of inAtom and put it in both maps, using a new ID as needed. + + mork_aid MakeNewAtomId(morkEnv* ev, morkBookAtom* ioAtom); + // generate an unused atom id. + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakAtomSpace(morkAtomSpace* me, morkEnv* ev, + morkAtomSpace** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongAtomSpace(morkAtomSpace* me, morkEnv* ev, + morkAtomSpace** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kAtomSpaceMap /*i*/ 0x615A /* ascii 'aZ' */ + +/*| morkAtomSpaceMap: maps mork_scope -> morkAtomSpace +|*/ +class morkAtomSpaceMap : public morkNodeMap { // for mapping tokens to tables + + public: + virtual ~morkAtomSpaceMap(); + morkAtomSpaceMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + + public: // other map methods + mork_bool AddAtomSpace(morkEnv* ev, morkAtomSpace* ioAtomSpace) { + return this->AddNode(ev, ioAtomSpace->SpaceScope(), ioAtomSpace); + } + // the AddAtomSpace() boolean return equals ev->Good(). + + mork_bool CutAtomSpace(morkEnv* ev, mork_scope inScope) { + return this->CutNode(ev, inScope); + } + // The CutAtomSpace() boolean return indicates whether removal happened. + + morkAtomSpace* GetAtomSpace(morkEnv* ev, mork_scope inScope) { + return (morkAtomSpace*)this->GetNode(ev, inScope); + } + // Note the returned space does NOT have an increase in refcount for this. + + mork_num CutAllAtomSpaces(morkEnv* ev) { return this->CutAllNodes(ev); } + // CutAllAtomSpaces() releases all the referenced table values. +}; + +class morkAtomSpaceMapIter : public morkMapIter { // typesafe wrapper class + + public: + morkAtomSpaceMapIter(morkEnv* ev, morkAtomSpaceMap* ioMap) + : morkMapIter(ev, ioMap) {} + + morkAtomSpaceMapIter() : morkMapIter() {} + void InitAtomSpaceMapIter(morkEnv* ev, morkAtomSpaceMap* ioMap) { + this->InitMapIter(ev, ioMap); + } + + mork_change* FirstAtomSpace(morkEnv* ev, mork_scope* outScope, + morkAtomSpace** outAtomSpace) { + return this->First(ev, outScope, outAtomSpace); + } + + mork_change* NextAtomSpace(morkEnv* ev, mork_scope* outScope, + morkAtomSpace** outAtomSpace) { + return this->Next(ev, outScope, outAtomSpace); + } + + mork_change* HereAtomSpace(morkEnv* ev, mork_scope* outScope, + morkAtomSpace** outAtomSpace) { + return this->Here(ev, outScope, outAtomSpace); + } + + mork_change* CutHereAtomSpace(morkEnv* ev, mork_scope* outScope, + morkAtomSpace** outAtomSpace) { + return this->CutHere(ev, outScope, outAtomSpace); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKATOMSPACE_ */ diff --git a/comm/mailnews/db/mork/morkBead.cpp b/comm/mailnews/db/mork/morkBead.cpp new file mode 100644 index 0000000000..839322621f --- /dev/null +++ b/comm/mailnews/db/mork/morkBead.cpp @@ -0,0 +1,361 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKBEAD_ +# include "morkBead.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkBead::CloseMorkNode( + morkEnv* ev) // CloseBead() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseBead(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkBead::~morkBead() // assert CloseBead() executed earlier +{ + MORK_ASSERT(mBead_Color == 0 || mNode_Usage == morkUsage_kStack); +} + +/*public non-poly*/ +morkBead::morkBead(mork_color inBeadColor) + : morkNode(morkUsage_kStack), mBead_Color(inBeadColor) {} + +/*public non-poly*/ +morkBead::morkBead(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_color inBeadColor) + : morkNode(inUsage, ioHeap), mBead_Color(inBeadColor) {} + +/*public non-poly*/ +morkBead::morkBead(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_color inBeadColor) + : morkNode(ev, inUsage, ioHeap), mBead_Color(inBeadColor) { + if (ev->Good()) { + if (ev->Good()) mNode_Derived = morkDerived_kBead; + } +} + +/*public non-poly*/ void morkBead::CloseBead( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + if (!this->IsShutNode()) { + mBead_Color = 0; + this->MarkShut(); + } + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkBeadMap::CloseMorkNode( + morkEnv* ev) // CloseBeadMap() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseBeadMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkBeadMap::~morkBeadMap() // assert CloseBeadMap() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkBeadMap::morkBeadMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) + : morkMap(ev, inUsage, ioHeap, sizeof(morkBead*), /*inValSize*/ 0, + /*slotCount*/ 11, ioSlotHeap, /*holdChanges*/ morkBool_kFalse) { + if (ev->Good()) mNode_Derived = morkDerived_kBeadMap; +} + +/*public non-poly*/ void morkBeadMap::CloseBeadMap( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + this->CutAllBeads(ev); + this->CloseMap(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +mork_bool morkBeadMap::AddBead(morkEnv* ev, morkBead* ioBead) +// the AddBead() boolean return equals ev->Good(). +{ + if (ioBead && ev->Good()) { + morkBead* oldBead = 0; // old key in the map + + mork_bool put = + this->Put(ev, &ioBead, /*val*/ (void*)0, + /*key*/ &oldBead, /*val*/ (void*)0, (mork_change**)0); + + if (put) // replaced an existing key? + { + if (oldBead != ioBead) // new bead was not already in table? + ioBead->AddStrongRef(ev); // now there's another ref + + if (oldBead && oldBead != ioBead) // need to release old node? + oldBead->CutStrongRef(ev); + } else + ioBead->AddStrongRef(ev); // another ref if not already in table + } else if (!ioBead) + ev->NilPointerError(); + + return ev->Good(); +} + +mork_bool morkBeadMap::CutBead(morkEnv* ev, mork_color inColor) { + morkBead* oldBead = 0; // old key in the map + morkBead bead(inColor); + morkBead* key = &bead; + + mork_bool outCutNode = + this->Cut(ev, &key, + /*key*/ &oldBead, /*val*/ (void*)0, (mork_change**)0); + + if (oldBead) oldBead->CutStrongRef(ev); + + bead.CloseBead(ev); + return outCutNode; +} + +morkBead* morkBeadMap::GetBead(morkEnv* ev, mork_color inColor) +// Note the returned bead does NOT have an increase in refcount for this. +{ + morkBead* oldBead = 0; // old key in the map + morkBead bead(inColor); + morkBead* key = &bead; + + this->Get(ev, &key, /*key*/ &oldBead, /*val*/ (void*)0, (mork_change**)0); + + bead.CloseBead(ev); + return oldBead; +} + +mork_num morkBeadMap::CutAllBeads(morkEnv* ev) +// CutAllBeads() releases all the referenced beads. +{ + mork_num outSlots = mMap_Slots; + + morkBeadMapIter i(ev, this); + morkBead* b = i.FirstBead(ev); + + while (b) { + b->CutStrongRef(ev); + i.CutHereBead(ev); + b = i.NextBead(ev); + } + + return outSlots; +} + +// { ===== begin morkMap poly interface ===== +/*virtual*/ mork_bool morkBeadMap::Equal(morkEnv* ev, const void* inKeyA, + const void* inKeyB) const { + MORK_USED_1(ev); + return (*(const morkBead**)inKeyA)->BeadEqual(*(const morkBead**)inKeyB); +} + +/*virtual*/ mork_u4 morkBeadMap::Hash(morkEnv* ev, const void* inKey) const { + MORK_USED_1(ev); + return (*(const morkBead**)inKey)->BeadHash(); +} +// } ===== end morkMap poly interface ===== + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +morkBead* morkBeadMapIter::FirstBead(morkEnv* ev) { + morkBead* bead = 0; + this->First(ev, &bead, /*val*/ (void*)0); + return bead; +} + +morkBead* morkBeadMapIter::NextBead(morkEnv* ev) { + morkBead* bead = 0; + this->Next(ev, &bead, /*val*/ (void*)0); + return bead; +} + +morkBead* morkBeadMapIter::HereBead(morkEnv* ev) { + morkBead* bead = 0; + this->Here(ev, &bead, /*val*/ (void*)0); + return bead; +} + +void morkBeadMapIter::CutHereBead(morkEnv* ev) { + this->CutHere(ev, /*key*/ (void*)0, /*val*/ (void*)0); +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkBeadProbeMap::CloseMorkNode( + morkEnv* ev) // CloseBeadProbeMap() if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseBeadProbeMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkBeadProbeMap::~morkBeadProbeMap() // assert CloseBeadProbeMap() earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkBeadProbeMap::morkBeadProbeMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) + : morkProbeMap(ev, inUsage, ioHeap, + /*inKeySize*/ sizeof(morkBead*), /*inValSize*/ 0, ioSlotHeap, + /*startSlotCount*/ 11, + /*inZeroIsClearKey*/ morkBool_kTrue) { + if (ev->Good()) mNode_Derived = morkDerived_kBeadProbeMap; +} + +/*public non-poly*/ void morkBeadProbeMap::CloseBeadProbeMap( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + this->CutAllBeads(ev); + this->CloseProbeMap(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b) +morkBeadProbeMap::MapTest(morkEnv* ev, const void* inMapKey, + const void* inAppKey) const { + MORK_USED_1(ev); + const morkBead* key = *(const morkBead**)inMapKey; + if (key) { + mork_bool hit = key->BeadEqual(*(const morkBead**)inAppKey); + return (hit) ? morkTest_kHit : morkTest_kMiss; + } else + return morkTest_kVoid; +} + +/*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b) +morkBeadProbeMap::MapHash(morkEnv* ev, const void* inAppKey) const { + const morkBead* key = *(const morkBead**)inAppKey; + if (key) + return key->BeadHash(); + else { + ev->NilPointerWarning(); + return 0; + } +} + +/*virtual*/ mork_u4 morkBeadProbeMap::ProbeMapHashMapKey( + morkEnv* ev, const void* inMapKey) const { + const morkBead* key = *(const morkBead**)inMapKey; + if (key) + return key->BeadHash(); + else { + ev->NilPointerWarning(); + return 0; + } +} + +mork_bool morkBeadProbeMap::AddBead(morkEnv* ev, morkBead* ioBead) { + if (ioBead && ev->Good()) { + morkBead* bead = 0; // old key in the map + + mork_bool put = this->MapAtPut(ev, &ioBead, /*val*/ (void*)0, + /*key*/ &bead, /*val*/ (void*)0); + + if (put) // replaced an existing key? + { + if (bead != ioBead) // new bead was not already in table? + ioBead->AddStrongRef(ev); // now there's another ref + + if (bead && bead != ioBead) // need to release old node? + bead->CutStrongRef(ev); + } else + ioBead->AddStrongRef(ev); // now there's another ref + } else if (!ioBead) + ev->NilPointerError(); + + return ev->Good(); +} + +morkBead* morkBeadProbeMap::GetBead(morkEnv* ev, mork_color inColor) { + morkBead* oldBead = 0; // old key in the map + morkBead bead(inColor); + morkBead* key = &bead; + + this->MapAt(ev, &key, &oldBead, /*val*/ (void*)0); + + bead.CloseBead(ev); + return oldBead; +} + +mork_num morkBeadProbeMap::CutAllBeads(morkEnv* ev) +// CutAllBeads() releases all the referenced bead values. +{ + mork_num outSlots = sMap_Slots; + + morkBeadProbeMapIter i(ev, this); + morkBead* b = i.FirstBead(ev); + + while (b) { + b->CutStrongRef(ev); + b = i.NextBead(ev); + } + this->MapCutAll(ev); + + return outSlots; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkBead.h b/comm/mailnews/db/mork/morkBead.h new file mode 100644 index 0000000000..deccec0ba6 --- /dev/null +++ b/comm/mailnews/db/mork/morkBead.h @@ -0,0 +1,244 @@ +/* -*- 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 _MORKBEAD_ +#define _MORKBEAD_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +#ifndef _MORKPROBEMAP_ +# include "morkProbeMap.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kBead /*i*/ 0x426F /* ascii 'Bo' */ + +/*| morkBead: subclass of morkNode that adds knowledge of db suite factory +**| and containing port to those objects that are exposed as instances of +**| nsIMdbBead in the public interface. +|*/ +class morkBead : public morkNode { + // public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + public: // state is public because the entire Mork system is private + mork_color mBead_Color; // ID for this bead + + public: // Hash() and Equal() for bead maps are same for all subclasses: + mork_u4 BeadHash() const { return (mork_u4)mBead_Color; } + mork_bool BeadEqual(const morkBead* inBead) const { + return (mBead_Color == inBead->mBead_Color); + } + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseBead() only if open + virtual ~morkBead(); // assert that CloseBead() executed earlier + + public: // special case for stack construction for map usage: + explicit morkBead(mork_color inBeadColor); // stack-based bead instance + + protected: // special case for morkObject: + morkBead(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_color inBeadColor); + + public: // morkEnv construction & destruction + morkBead(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_color inBeadColor); + void CloseBead(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkBead(const morkBead& other); + morkBead& operator=(const morkBead& other); + + public: // dynamic type identification + mork_bool IsBead() const { + return IsNode() && mNode_Derived == morkDerived_kBead; + } + // } ===== end morkNode methods ===== + + // void NewNilHandleError(morkEnv* ev); // mBead_Handle is nil + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakBead(morkBead* me, morkEnv* ev, morkBead** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongBead(morkBead* me, morkEnv* ev, morkBead** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kBeadMap /*i*/ 0x744D /* ascii 'bM' */ + +/*| morkBeadMap: maps bead -> bead (key only using mBead_Color) +|*/ +class morkBeadMap : public morkMap { + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseBeadMap() only if open + virtual ~morkBeadMap(); // assert that CloseBeadMap() executed earlier + + public: // morkMap construction & destruction + morkBeadMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + void CloseBeadMap(morkEnv* ev); // called by CloseMorkNode(); + + public: // dynamic type identification + mork_bool IsBeadMap() const { + return IsNode() && mNode_Derived == morkDerived_kBeadMap; + } + // } ===== end morkNode methods ===== + + // { ===== begin morkMap poly interface ===== + public: + virtual mork_bool // *((mork_u4*) inKeyA) == *((mork_u4*) inKeyB) + Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override; + + virtual mork_u4 // some integer function of *((mork_u4*) inKey) + Hash(morkEnv* ev, const void* inKey) const override; + // } ===== end morkMap poly interface ===== + + public: // other map methods + mork_bool AddBead(morkEnv* ev, morkBead* ioBead); + // the AddBead() boolean return equals ev->Good(). + + mork_bool CutBead(morkEnv* ev, mork_color inColor); + // The CutBead() boolean return indicates whether removal happened. + + morkBead* GetBead(morkEnv* ev, mork_color inColor); + // Note the returned bead does NOT have an increase in refcount for this. + + mork_num CutAllBeads(morkEnv* ev); + // CutAllBeads() releases all the referenced beads. +}; + +class morkBeadMapIter : public morkMapIter { // typesafe wrapper class + + public: + morkBeadMapIter(morkEnv* ev, morkBeadMap* ioMap) : morkMapIter(ev, ioMap) {} + + morkBeadMapIter() : morkMapIter() {} + void InitBeadMapIter(morkEnv* ev, morkBeadMap* ioMap) { + this->InitMapIter(ev, ioMap); + } + + morkBead* FirstBead(morkEnv* ev); + morkBead* NextBead(morkEnv* ev); + morkBead* HereBead(morkEnv* ev); + void CutHereBead(morkEnv* ev); +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kBeadProbeMap /*i*/ 0x6D74 /* ascii 'mb' */ + +/*| morkBeadProbeMap: maps bead -> bead (key only using mBead_Color) +|*/ +class morkBeadProbeMap : public morkProbeMap { + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseBeadProbeMap() only if open + virtual ~morkBeadProbeMap(); // assert that CloseBeadProbeMap() executed + // earlier + + public: // morkMap construction & destruction + morkBeadProbeMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + void CloseBeadProbeMap(morkEnv* ev); // called by CloseMorkNode(); + + public: // dynamic type identification + mork_bool IsBeadProbeMap() const { + return IsNode() && mNode_Derived == morkDerived_kBeadProbeMap; + } + // } ===== end morkNode methods ===== + + // { ===== begin morkProbeMap methods ===== + public: + virtual mork_test // hit(a,b) implies hash(a) == hash(b) + MapTest(morkEnv* ev, const void* inMapKey, + const void* inAppKey) const override; + + virtual mork_u4 // hit(a,b) implies hash(a) == hash(b) + MapHash(morkEnv* ev, const void* inAppKey) const override; + + virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, + const void* inMapKey) const override; + + // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey); + + // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' into all keys + // inside map + // void* ioMapKey, mork_count inKeyCount); // array of keys inside map + + // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map + // const void* inAppKey, const void* inAppVal, // (key,val) outside map + // void* outMapKey, void* outMapVal); // (key,val) inside map + + // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the + // map + // const void* inMapKey, const void* inMapVal, // (key,val) inside map + // void* outAppKey, void* outAppVal) const; // (key,val) outside map + // } ===== end morkProbeMap methods ===== + + public: // other map methods + mork_bool AddBead(morkEnv* ev, morkBead* ioBead); + // the AddBead() boolean return equals ev->Good(). + + morkBead* GetBead(morkEnv* ev, mork_color inColor); + // Note the returned bead does NOT have an increase in refcount for this. + + mork_num CutAllBeads(morkEnv* ev); + // CutAllBeads() releases all the referenced bead values. +}; + +class morkBeadProbeMapIter + : public morkProbeMapIter { // typesafe wrapper class + + public: + morkBeadProbeMapIter(morkEnv* ev, morkBeadProbeMap* ioMap) + : morkProbeMapIter(ev, ioMap) {} + + morkBeadProbeMapIter() : morkProbeMapIter() {} + void InitBeadProbeMapIter(morkEnv* ev, morkBeadProbeMap* ioMap) { + this->InitProbeMapIter(ev, ioMap); + } + + morkBead* FirstBead(morkEnv* ev) { return (morkBead*)this->IterFirstKey(ev); } + + morkBead* NextBead(morkEnv* ev) { return (morkBead*)this->IterNextKey(ev); } + + morkBead* HereBead(morkEnv* ev) { return (morkBead*)this->IterHereKey(ev); } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKBEAD_ */ diff --git a/comm/mailnews/db/mork/morkBlob.cpp b/comm/mailnews/db/mork/morkBlob.cpp new file mode 100644 index 0000000000..d0fdf104ff --- /dev/null +++ b/comm/mailnews/db/mork/morkBlob.cpp @@ -0,0 +1,96 @@ +/* -*- 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 _MORKBLOB_ +# include "morkBlob.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*static*/ void morkBuf::NilBufBodyError(morkEnv* ev) { + ev->NewError("nil mBuf_Body"); +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*static*/ void morkBlob::BlobFillOverSizeError(morkEnv* ev) { + ev->NewError("mBuf_Fill > mBlob_Size"); +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +mork_bool morkBlob::GrowBlob(morkEnv* ev, nsIMdbHeap* ioHeap, + mork_size inNewSize) { + if (ioHeap) { + if (!mBuf_Body) // no body? implies zero sized? + mBlob_Size = 0; + + if (mBuf_Fill > mBlob_Size) // fill more than size? + { + ev->NewWarning("mBuf_Fill > mBlob_Size"); + mBuf_Fill = mBlob_Size; + } + + if (inNewSize > mBlob_Size) // need to allocate larger blob? + { + mork_u1* body = 0; + ioHeap->Alloc(ev->AsMdbEnv(), inNewSize, (void**)&body); + if (body && ev->Good()) { + void* oldBody = mBuf_Body; + if (mBlob_Size) // any old content to transfer? + MORK_MEMCPY(body, oldBody, mBlob_Size); + + mBlob_Size = inNewSize; // install new size + mBuf_Body = body; // install new body + + if (oldBody) // need to free old buffer body? + ioHeap->Free(ev->AsMdbEnv(), oldBody); + } + } + } else + ev->NilPointerError(); + + if (ev->Good() && mBlob_Size < inNewSize) + ev->NewError("mBlob_Size < inNewSize"); + + return ev->Good(); +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +morkCoil::morkCoil(morkEnv* ev, nsIMdbHeap* ioHeap) { + mBuf_Body = 0; + mBuf_Fill = 0; + mBlob_Size = 0; + mText_Form = 0; + mCoil_Heap = ioHeap; + if (!ioHeap) ev->NilPointerError(); +} + +void morkCoil::CloseCoil(morkEnv* ev) { + void* body = mBuf_Body; + nsIMdbHeap* heap = mCoil_Heap; + + mBuf_Body = 0; + mCoil_Heap = 0; + + if (body && heap) { + heap->Free(ev->AsMdbEnv(), body); + } +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkBlob.h b/comm/mailnews/db/mork/morkBlob.h new file mode 100644 index 0000000000..8ce923d232 --- /dev/null +++ b/comm/mailnews/db/mork/morkBlob.h @@ -0,0 +1,140 @@ +/* -*- 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 _MORKBLOB_ +#define _MORKBLOB_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*| Buf: the minimum needed to describe location and content length. +**| This is typically only enough to read from this buffer, since +**| one cannot write effectively without knowing the size of a buf. +|*/ +class morkBuf { // subset of nsIMdbYarn slots + public: + void* mBuf_Body; // space for holding any binary content + mork_fill mBuf_Fill; // logical content in Buf in bytes + + public: + morkBuf() {} + morkBuf(const void* ioBuf, mork_fill inFill) + : mBuf_Body((void*)ioBuf), mBuf_Fill(inFill) {} + + void ClearBufFill() { mBuf_Fill = 0; } + + static void NilBufBodyError(morkEnv* ev); + + private: // copying is not allowed + morkBuf(const morkBuf& other); + morkBuf& operator=(const morkBuf& other); +}; + +/*| Blob: a buffer with an associated size, to increase known buf info +**| to include max capacity in addition to buf location and content. +**| This form factor allows us to allocate a vector of such blobs, +**| which can share the same managing heap stored elsewhere, and that +**| is why we don't include a pointer to a heap in this blob class. +|*/ +class morkBlob : public morkBuf { // greater subset of nsIMdbYarn slots + + // void* mBuf_Body; // space for holding any binary content + // mdb_fill mBuf_Fill; // logical content in Buf in bytes + public: + mork_size mBlob_Size; // physical size of Buf in bytes + + public: + morkBlob() {} + morkBlob(const void* ioBuf, mork_fill inFill, mork_size inSize) + : morkBuf(ioBuf, inFill), mBlob_Size(inSize) {} + + static void BlobFillOverSizeError(morkEnv* ev); + + public: + mork_bool GrowBlob(morkEnv* ev, nsIMdbHeap* ioHeap, mork_size inNewSize); + + private: // copying is not allowed + morkBlob(const morkBlob& other); + morkBlob& operator=(const morkBlob& other); +}; + +/*| Text: a blob with an associated charset annotation, where the +**| charset actually includes the general notion of typing, and not +**| just a specification of character set alone; we want to permit +**| arbitrary charset annotations for ad hoc binary types as well. +**| (We avoid including a nsIMdbHeap pointer in morkText for the same +**| reason morkBlob does: we want minimal size vectors of morkText.) +|*/ +class morkText : public morkBlob { // greater subset of nsIMdbYarn slots + + // void* mBuf_Body; // space for holding any binary content + // mdb_fill mBuf_Fill; // logical content in Buf in bytes + // mdb_size mBlob_Size; // physical size of Buf in bytes + + public: + mork_cscode mText_Form; // charset format encoding + + morkText() {} + + private: // copying is not allowed + morkText(const morkText& other); + morkText& operator=(const morkText& other); +}; + +/*| Coil: a text with an associated nsIMdbHeap instance that provides +**| all memory management for the space pointed to by mBuf_Body. (This +**| was the hardest type to give a name in this small class hierarchy, +**| because it's hard to characterize self-management of one's space.) +**| A coil is a self-contained blob that knows how to grow itself as +**| necessary to hold more content when necessary. Coil descends from +**| morkText to include the mText_Form slot, even though this won't be +**| needed always, because we are not as concerned about the overall +**| size of this particular Coil object (if we were concerned about +**| the size of an array of Coil instances, we would not bother with +**| a separate heap pointer for each of them). +**| +**|| A coil makes a good medium in which to stream content as a sink, +**| so we will have a subclass of morkSink called morkCoil that +**| will stream bytes into this self-contained coil object. The name +**| of this morkCoil class derives more from this intended usage than +**| from anything else. The Mork code to parse db content will use +**| coils with associated sinks to accumulate parsed strings. +**| +**|| Heap: this is the heap used for memory allocation. This instance +**| is NOT refcounted, since this coil always assumes the heap is held +**| through a reference elsewhere (for example, through the same object +**| that contains or holds the coil itself. This lack of refcounting +**| is consistent with the fact that morkCoil itself is not refcounted, +**| and is not intended for use as a standalone object. +|*/ +class morkCoil : public morkText { // self-managing text blob object + + // void* mBuf_Body; // space for holding any binary content + // mdb_fill mBuf_Fill; // logical content in Buf in bytes + // mdb_size mBlob_Size; // physical size of Buf in bytes + // mdb_cscode mText_Form; // charset format encoding + public: + nsIMdbHeap* mCoil_Heap; // storage manager for mBuf_Body pointer + + public: + morkCoil(morkEnv* ev, nsIMdbHeap* ioHeap); + + void CloseCoil(morkEnv* ev); + + mork_bool GrowCoil(morkEnv* ev, mork_size inNewSize) { + return this->GrowBlob(ev, mCoil_Heap, inNewSize); + } + + private: // copying is not allowed + morkCoil(const morkCoil& other); + morkCoil& operator=(const morkCoil& other); +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKBLOB_ */ diff --git a/comm/mailnews/db/mork/morkBuilder.cpp b/comm/mailnews/db/mork/morkBuilder.cpp new file mode 100644 index 0000000000..4e96209929 --- /dev/null +++ b/comm/mailnews/db/mork/morkBuilder.cpp @@ -0,0 +1,892 @@ +/* -*- 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 _MORKPARSER_ +# include "morkParser.h" +#endif + +#ifndef _MORKBUILDER_ +# include "morkBuilder.h" +#endif + +#ifndef _MORKCELL_ +# include "morkCell.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +#ifndef _MORKTABLE_ +# include "morkTable.h" +#endif + +#ifndef _MORKROW_ +# include "morkRow.h" +#endif + +#ifndef _MORKCELL_ +# include "morkCell.h" +#endif + +#ifndef _MORKATOM_ +# include "morkAtom.h" +#endif + +#ifndef _MORKATOMSPACE_ +# include "morkAtomSpace.h" +#endif + +#ifndef _MORKROWSPACE_ +# include "morkRowSpace.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkBuilder::CloseMorkNode( + morkEnv* ev) // CloseBuilder() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseBuilder(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkBuilder::~morkBuilder() // assert CloseBuilder() executed earlier +{ + MORK_ASSERT(mBuilder_Store == 0); + MORK_ASSERT(mBuilder_Row == 0); + MORK_ASSERT(mBuilder_Table == 0); + MORK_ASSERT(mBuilder_Cell == 0); + MORK_ASSERT(mBuilder_RowSpace == 0); + MORK_ASSERT(mBuilder_AtomSpace == 0); +} + +/*public non-poly*/ +morkBuilder::morkBuilder(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkStream* ioStream, + mdb_count inBytesPerParseSegment, + nsIMdbHeap* ioSlotHeap, morkStore* ioStore) + + : morkParser(ev, inUsage, ioHeap, ioStream, inBytesPerParseSegment, + ioSlotHeap) + + , + mBuilder_Store(0) + + , + mBuilder_Table(0), + mBuilder_Row(0), + mBuilder_Cell(0) + + , + mBuilder_RowSpace(0), + mBuilder_AtomSpace(0) + + , + mBuilder_OidAtomSpace(0), + mBuilder_ScopeAtomSpace(0) + + , + mBuilder_PortForm(0), + mBuilder_PortRowScope((mork_scope)'r'), + mBuilder_PortAtomScope((mork_scope)'v') + + , + mBuilder_TableForm(0), + mBuilder_TableRowScope((mork_scope)'r'), + mBuilder_TableAtomScope((mork_scope)'v'), + mBuilder_TableKind(0) + + , + mBuilder_TablePriority(morkPriority_kLo), + mBuilder_TableIsUnique(morkBool_kFalse), + mBuilder_TableIsVerbose(morkBool_kFalse), + mBuilder_TablePadByte(0) + + , + mBuilder_RowForm(0), + mBuilder_RowRowScope((mork_scope)'r'), + mBuilder_RowAtomScope((mork_scope)'v') + + , + mBuilder_CellForm(0), + mBuilder_CellAtomScope((mork_scope)'v') + + , + mBuilder_DictForm(0), + mBuilder_DictAtomScope((mork_scope)'v') + + , + mBuilder_MetaTokenSlot(0) + + , + mBuilder_DoCutRow(morkBool_kFalse), + mBuilder_DoCutCell(morkBool_kFalse), + mBuilder_CellsVecFill(0) { + if (ev->Good()) { + if (ioStore) { + morkStore::SlotWeakStore(ioStore, ev, &mBuilder_Store); + if (ev->Good()) mNode_Derived = morkDerived_kBuilder; + } else + ev->NilPointerError(); + } +} + +/*public non-poly*/ void morkBuilder::CloseBuilder( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + mBuilder_Row = 0; + mBuilder_Cell = 0; + mBuilder_MetaTokenSlot = 0; + + morkTable::SlotStrongTable((morkTable*)0, ev, &mBuilder_Table); + morkStore::SlotWeakStore((morkStore*)0, ev, &mBuilder_Store); + + morkRowSpace::SlotStrongRowSpace((morkRowSpace*)0, ev, &mBuilder_RowSpace); + + morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*)0, ev, + &mBuilder_AtomSpace); + + morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*)0, ev, + &mBuilder_OidAtomSpace); + + morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*)0, ev, + &mBuilder_ScopeAtomSpace); + this->CloseParser(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void morkBuilder::NonBuilderTypeError(morkEnv* ev) { + ev->NewError("non morkBuilder"); +} + +/*static*/ void morkBuilder::NilBuilderCellError(morkEnv* ev) { + ev->NewError("nil mBuilder_Cell"); +} + +/*static*/ void morkBuilder::NilBuilderRowError(morkEnv* ev) { + ev->NewError("nil mBuilder_Row"); +} + +/*static*/ void morkBuilder::NilBuilderTableError(morkEnv* ev) { + ev->NewError("nil mBuilder_Table"); +} + +/*static*/ void morkBuilder::NonColumnSpaceScopeError(morkEnv* ev) { + ev->NewError("column space != 'c'"); +} + +void morkBuilder::LogGlitch(morkEnv* ev, const morkGlitch& inGlitch, + const char* inKind) { + MORK_USED_2(inGlitch, inKind); + ev->NewWarning("parsing glitch"); +} + +/*virtual*/ void morkBuilder::MidToYarn( + morkEnv* ev, + const morkMid& inMid, // typically an alias to concat with strings + mdbYarn* outYarn) +// The parser might ask that some aliases be turned into yarns, so they +// can be concatenated into longer blobs under some circumstances. This +// is an alternative to using a long and complex callback for many parts +// for a single cell value. +{ + mBuilder_Store->MidToYarn(ev, inMid, outYarn); +} + +/*virtual*/ void morkBuilder::OnNewPort(morkEnv* ev, const morkPlace& inPlace) +// mp:Start ::= OnNewPort mp:PortItem* OnPortEnd +// mp:PortItem ::= mp:Content | mp:Group | OnPortGlitch +// mp:Content ::= mp:PortRow | mp:Dict | mp:Table | mp:Row +{ + MORK_USED_2(ev, inPlace); + // mParser_InPort = morkBool_kTrue; + mBuilder_PortForm = 0; + mBuilder_PortRowScope = (mork_scope)'r'; + mBuilder_PortAtomScope = (mork_scope)'v'; +} + +/*virtual*/ void morkBuilder::OnPortGlitch(morkEnv* ev, + const morkGlitch& inGlitch) { + this->LogGlitch(ev, inGlitch, "port"); +} + +/*virtual*/ void morkBuilder::OnPortEnd(morkEnv* ev, const morkSpan& inSpan) +// mp:Start ::= OnNewPort mp:PortItem* OnPortEnd +{ + MORK_USED_2(ev, inSpan); + // ev->StubMethodOnlyError(); + // nothing to do? + // mParser_InPort = morkBool_kFalse; +} + +/*virtual*/ void morkBuilder::OnNewGroup(morkEnv* ev, const morkPlace& inPlace, + mork_gid inGid) { + MORK_USED_1(inPlace); + mParser_InGroup = morkBool_kTrue; + mork_pos startPos = inPlace.mPlace_Pos; + + morkStore* store = mBuilder_Store; + if (store) { + if (inGid >= store->mStore_CommitGroupIdentity) + store->mStore_CommitGroupIdentity = inGid + 1; + + if (!store->mStore_FirstCommitGroupPos) + store->mStore_FirstCommitGroupPos = startPos; + else if (!store->mStore_SecondCommitGroupPos) + store->mStore_SecondCommitGroupPos = startPos; + } +} + +/*virtual*/ void morkBuilder::OnGroupGlitch(morkEnv* ev, + const morkGlitch& inGlitch) { + this->LogGlitch(ev, inGlitch, "group"); +} + +/*virtual*/ void morkBuilder::OnGroupCommitEnd(morkEnv* ev, + const morkSpan& inSpan) { + MORK_USED_2(ev, inSpan); + // mParser_InGroup = morkBool_kFalse; + // ev->StubMethodOnlyError(); +} + +/*virtual*/ void morkBuilder::OnGroupAbortEnd(morkEnv* ev, + const morkSpan& inSpan) { + MORK_USED_1(inSpan); + // mParser_InGroup = morkBool_kFalse; + ev->StubMethodOnlyError(); +} + +/*virtual*/ void morkBuilder::OnNewPortRow(morkEnv* ev, + const morkPlace& inPlace, + const morkMid& inMid, + mork_change inChange) { + MORK_USED_3(inMid, inPlace, inChange); + // mParser_InPortRow = morkBool_kTrue; + ev->StubMethodOnlyError(); +} + +/*virtual*/ void morkBuilder::OnPortRowGlitch(morkEnv* ev, + const morkGlitch& inGlitch) { + this->LogGlitch(ev, inGlitch, "port row"); +} + +/*virtual*/ void morkBuilder::OnPortRowEnd(morkEnv* ev, + const morkSpan& inSpan) { + MORK_USED_1(inSpan); + // mParser_InPortRow = morkBool_kFalse; + ev->StubMethodOnlyError(); +} + +/*virtual*/ void morkBuilder::OnNewTable(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, + mork_bool inCutAllRows) +// mp:Table ::= OnNewTable mp:TableItem* OnTableEnd +// mp:TableItem ::= mp:Row | mp:MetaTable | OnTableGlitch +// mp:MetaTable ::= OnNewMeta mp:MetaItem* mp:Row OnMetaEnd +// mp:Meta ::= OnNewMeta mp:MetaItem* OnMetaEnd +// mp:MetaItem ::= mp:Cell | OnMetaGlitch +{ + MORK_USED_1(inPlace); + // mParser_InTable = morkBool_kTrue; + mBuilder_TableForm = mBuilder_PortForm; + mBuilder_TableRowScope = mBuilder_PortRowScope; + mBuilder_TableAtomScope = mBuilder_PortAtomScope; + mBuilder_TableKind = morkStore_kNoneToken; + + mBuilder_TablePriority = morkPriority_kLo; + mBuilder_TableIsUnique = morkBool_kFalse; + mBuilder_TableIsVerbose = morkBool_kFalse; + + morkTable* table = mBuilder_Store->MidToTable(ev, inMid); + morkTable::SlotStrongTable(table, ev, &mBuilder_Table); + if (table) { + if (table->mTable_RowSpace) + mBuilder_TableRowScope = table->mTable_RowSpace->SpaceScope(); + + if (inCutAllRows) table->CutAllRows(ev); + } +} + +/*virtual*/ void morkBuilder::OnTableGlitch(morkEnv* ev, + const morkGlitch& inGlitch) { + this->LogGlitch(ev, inGlitch, "table"); +} + +/*virtual*/ void morkBuilder::OnTableEnd(morkEnv* ev, const morkSpan& inSpan) +// mp:Table ::= OnNewTable mp:TableItem* OnTableEnd +{ + MORK_USED_1(inSpan); + // mParser_InTable = morkBool_kFalse; + if (mBuilder_Table) { + mBuilder_Table->mTable_Priority = mBuilder_TablePriority; + + if (mBuilder_TableIsUnique) mBuilder_Table->SetTableUnique(); + + if (mBuilder_TableIsVerbose) mBuilder_Table->SetTableVerbose(); + + morkTable::SlotStrongTable((morkTable*)0, ev, &mBuilder_Table); + } else + this->NilBuilderTableError(ev); + + mBuilder_Row = 0; + mBuilder_Cell = 0; + + mBuilder_TablePriority = morkPriority_kLo; + mBuilder_TableIsUnique = morkBool_kFalse; + mBuilder_TableIsVerbose = morkBool_kFalse; + + if (mBuilder_TableKind == morkStore_kNoneToken) + ev->NewError("missing table kind"); + + mBuilder_CellAtomScope = mBuilder_RowAtomScope = mBuilder_TableAtomScope = + mBuilder_PortAtomScope; + + mBuilder_DoCutCell = morkBool_kFalse; + mBuilder_DoCutRow = morkBool_kFalse; +} + +/*virtual*/ void morkBuilder::OnNewMeta(morkEnv* ev, const morkPlace& inPlace) +// mp:Meta ::= OnNewMeta mp:MetaItem* OnMetaEnd +// mp:MetaItem ::= mp:Cell | OnMetaGlitch +// mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd +// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch +// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid +{ + MORK_USED_2(ev, inPlace); + // mParser_InMeta = morkBool_kTrue; +} + +/*virtual*/ void morkBuilder::OnMetaGlitch(morkEnv* ev, + const morkGlitch& inGlitch) { + this->LogGlitch(ev, inGlitch, "meta"); +} + +/*virtual*/ void morkBuilder::OnMetaEnd(morkEnv* ev, const morkSpan& inSpan) +// mp:Meta ::= OnNewMeta mp:MetaItem* OnMetaEnd +{ + MORK_USED_2(ev, inSpan); + // mParser_InMeta = morkBool_kFalse; +} + +/*virtual*/ void morkBuilder::OnMinusRow(morkEnv* ev) { + MORK_USED_1(ev); + mBuilder_DoCutRow = morkBool_kTrue; +} + +/*virtual*/ void morkBuilder::OnNewRow(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, + mork_bool inCutAllCols) +// mp:Table ::= OnNewTable mp:TableItem* OnTableEnd +// mp:TableItem ::= mp:Row | mp:MetaTable | OnTableGlitch +// mp:MetaTable ::= OnNewMeta mp:MetaItem* mp:Row OnMetaEnd +// mp:Row ::= OnMinusRow? OnNewRow mp:RowItem* OnRowEnd +// mp:RowItem ::= mp:Cell | mp:Meta | OnRowGlitch +// mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd +// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch +// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid +{ + MORK_USED_1(inPlace); + // mParser_InRow = morkBool_kTrue; + + mBuilder_CellForm = mBuilder_RowForm = mBuilder_TableForm; + mBuilder_CellAtomScope = mBuilder_RowAtomScope = mBuilder_TableAtomScope; + mBuilder_RowRowScope = mBuilder_TableRowScope; + morkStore* store = mBuilder_Store; + + if (!inMid.mMid_Buf && !inMid.mMid_Oid.mOid_Scope) { + morkMid mid(inMid); + mid.mMid_Oid.mOid_Scope = mBuilder_RowRowScope; + mBuilder_Row = store->MidToRow(ev, mid); + } else { + mBuilder_Row = store->MidToRow(ev, inMid); + } + morkRow* row = mBuilder_Row; + if (row && inCutAllCols) { + row->CutAllColumns(ev); + } + + morkTable* table = mBuilder_Table; + if (table) { + if (row) { + if (mParser_InMeta) { + morkRow* metaRow = table->mTable_MetaRow; + if (!metaRow) { + table->mTable_MetaRow = row; + table->mTable_MetaRowOid = row->mRow_Oid; + row->AddRowGcUse(ev); + } else if (metaRow != row) // not identical? + ev->NewError("duplicate table meta row"); + } else { + if (mBuilder_DoCutRow) + table->CutRow(ev, row); + else + table->AddRow(ev, row); + } + } + } + // else // it is now okay to have rows outside a table: + // this->NilBuilderTableError(ev); + + mBuilder_DoCutRow = morkBool_kFalse; +} + +/*virtual*/ void morkBuilder::OnRowPos(morkEnv* ev, mork_pos inRowPos) { + if (mBuilder_Row && mBuilder_Table && !mParser_InMeta) { + mork_pos hintFromPos = 0; // best hint when we don't know position + mBuilder_Table->MoveRow(ev, mBuilder_Row, hintFromPos, inRowPos); + } +} + +/*virtual*/ void morkBuilder::OnRowGlitch(morkEnv* ev, + const morkGlitch& inGlitch) { + this->LogGlitch(ev, inGlitch, "row"); +} + +void morkBuilder::FlushBuilderCells(morkEnv* ev) { + if (mBuilder_Row) { + morkPool* pool = mBuilder_Store->StorePool(); + morkCell* cells = mBuilder_CellsVec; + mork_fill fill = mBuilder_CellsVecFill; + mBuilder_Row->TakeCells(ev, cells, fill, mBuilder_Store); + + morkCell* end = cells + fill; + --cells; // prepare for preincrement + while (++cells < end) { + if (cells->mCell_Atom) cells->SetAtom(ev, (morkAtom*)0, pool); + } + mBuilder_CellsVecFill = 0; + } else + this->NilBuilderRowError(ev); +} + +/*virtual*/ void morkBuilder::OnRowEnd(morkEnv* ev, const morkSpan& inSpan) +// mp:Row ::= OnMinusRow? OnNewRow mp:RowItem* OnRowEnd +{ + MORK_USED_1(inSpan); + // mParser_InRow = morkBool_kFalse; + if (mBuilder_Row) { + this->FlushBuilderCells(ev); + } else + this->NilBuilderRowError(ev); + + mBuilder_Row = 0; + mBuilder_Cell = 0; + + mBuilder_DoCutCell = morkBool_kFalse; + mBuilder_DoCutRow = morkBool_kFalse; +} + +/*virtual*/ void morkBuilder::OnNewDict(morkEnv* ev, const morkPlace& inPlace) +// mp:Dict ::= OnNewDict mp:DictItem* OnDictEnd +// mp:DictItem ::= OnAlias | OnAliasGlitch | mp:Meta | OnDictGlitch +{ + MORK_USED_2(ev, inPlace); + // mParser_InDict = morkBool_kTrue; + + mBuilder_CellForm = mBuilder_DictForm = mBuilder_PortForm; + mBuilder_CellAtomScope = mBuilder_DictAtomScope = mBuilder_PortAtomScope; +} + +/*virtual*/ void morkBuilder::OnDictGlitch(morkEnv* ev, + const morkGlitch& inGlitch) { + this->LogGlitch(ev, inGlitch, "dict"); +} + +/*virtual*/ void morkBuilder::OnDictEnd(morkEnv* ev, const morkSpan& inSpan) +// mp:Dict ::= OnNewDict mp:DictItem* OnDictEnd +{ + MORK_USED_2(ev, inSpan); + // mParser_InDict = morkBool_kFalse; + + mBuilder_DictForm = 0; + mBuilder_DictAtomScope = 0; +} + +/*virtual*/ void morkBuilder::OnAlias(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) { + MORK_USED_1(inSpan); + if (mParser_InDict) { + morkMid mid = inMid; // local copy for modification + mid.mMid_Oid.mOid_Scope = mBuilder_DictAtomScope; + mBuilder_Store->AddAlias(ev, mid, mBuilder_DictForm); + } else + ev->NewError("alias not in dict"); +} + +/*virtual*/ void morkBuilder::OnAliasGlitch(morkEnv* ev, + const morkGlitch& inGlitch) { + this->LogGlitch(ev, inGlitch, "alias"); +} + +morkCell* morkBuilder::AddBuilderCell(morkEnv* ev, const morkMid& inMid, + mork_change inChange) { + morkCell* outCell = 0; + mork_column column = inMid.mMid_Oid.mOid_Id; + + if (ev->Good()) { + if (mBuilder_CellsVecFill >= morkBuilder_kCellsVecSize) + this->FlushBuilderCells(ev); + if (ev->Good()) { + if (mBuilder_CellsVecFill < morkBuilder_kCellsVecSize) { + mork_fill indx = mBuilder_CellsVecFill++; + outCell = mBuilder_CellsVec + indx; + outCell->SetColumnAndChange(column, inChange); + outCell->mCell_Atom = 0; + } else + ev->NewError("out of builder cells"); + } + } + return outCell; +} + +/*virtual*/ void morkBuilder::OnMinusCell(morkEnv* ev) { + MORK_USED_1(ev); + mBuilder_DoCutCell = morkBool_kTrue; +} + +/*virtual*/ void morkBuilder::OnNewCell(morkEnv* ev, const morkPlace& inPlace, + const morkMid* inMid, + const morkBuf* inBuf) +// Exactly one of inMid and inBuf is nil, and the other is non-nil. +// When hex ID syntax is used for a column, then inMid is not nil, and +// when a naked string names a column, then inBuf is not nil. + +// mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd +// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch +// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid +{ + MORK_USED_1(inPlace); + // mParser_InCell = morkBool_kTrue; + + mork_change cellChange = + (mBuilder_DoCutCell) ? morkChange_kCut : morkChange_kAdd; + + mBuilder_DoCutCell = morkBool_kFalse; + + mBuilder_CellAtomScope = mBuilder_RowAtomScope; + + mBuilder_Cell = 0; // nil until determined for a row + morkStore* store = mBuilder_Store; + mork_scope scope = morkStore_kColumnSpaceScope; + morkMid tempMid; // space for local and modifiable cell mid + morkMid* cellMid = &tempMid; // default to local if inMid==0 + + if (inMid) // mid parameter is actually provided? + { + *cellMid = *inMid; // bitwise copy for modifiable local mid + + if (!cellMid->mMid_Oid.mOid_Scope) { + if (cellMid->mMid_Buf) { + scope = store->BufToToken(ev, cellMid->mMid_Buf); + cellMid->mMid_Buf = 0; // don't do scope lookup again + ev->NewWarning("column mids need column scope"); + } + cellMid->mMid_Oid.mOid_Scope = scope; + } + } else if (inBuf) // buf points to naked column string name? + { + cellMid->ClearMid(); + cellMid->mMid_Oid.mOid_Id = store->BufToToken(ev, inBuf); + cellMid->mMid_Oid.mOid_Scope = scope; // kColumnSpaceScope + } else + ev->NilPointerError(); // either inMid or inBuf must be non-nil + + mork_column column = cellMid->mMid_Oid.mOid_Id; + + if (mBuilder_Row && ev->Good()) // this cell must be inside a row + { + // mBuilder_Cell = this->AddBuilderCell(ev, *cellMid, cellChange); + + if (mBuilder_CellsVecFill >= morkBuilder_kCellsVecSize) + this->FlushBuilderCells(ev); + if (ev->Good()) { + if (mBuilder_CellsVecFill < morkBuilder_kCellsVecSize) { + mork_fill ix = mBuilder_CellsVecFill++; + morkCell* cell = mBuilder_CellsVec + ix; + cell->SetColumnAndChange(column, cellChange); + + cell->mCell_Atom = 0; + mBuilder_Cell = cell; + } else + ev->NewError("out of builder cells"); + } + } + + else if (mParser_InMeta && ev->Good()) // cell is in metainfo structure? + { + if (scope == morkStore_kColumnSpaceScope) { + if (mParser_InTable) // metainfo for table? + { + if (column == morkStore_kKindColumn) + mBuilder_MetaTokenSlot = &mBuilder_TableKind; + else if (column == morkStore_kStatusColumn) + mBuilder_MetaTokenSlot = &mBuilder_TableStatus; + else if (column == morkStore_kRowScopeColumn) + mBuilder_MetaTokenSlot = &mBuilder_TableRowScope; + else if (column == morkStore_kAtomScopeColumn) + mBuilder_MetaTokenSlot = &mBuilder_TableAtomScope; + else if (column == morkStore_kFormColumn) + mBuilder_MetaTokenSlot = &mBuilder_TableForm; + } else if (mParser_InDict) // metainfo for dict? + { + if (column == morkStore_kAtomScopeColumn) + mBuilder_MetaTokenSlot = &mBuilder_DictAtomScope; + else if (column == morkStore_kFormColumn) + mBuilder_MetaTokenSlot = &mBuilder_DictForm; + } else if (mParser_InRow) // metainfo for row? + { + if (column == morkStore_kAtomScopeColumn) + mBuilder_MetaTokenSlot = &mBuilder_RowAtomScope; + else if (column == morkStore_kRowScopeColumn) + mBuilder_MetaTokenSlot = &mBuilder_RowRowScope; + else if (column == morkStore_kFormColumn) + mBuilder_MetaTokenSlot = &mBuilder_RowForm; + } + } else + ev->NewWarning("expected column scope"); + } +} + +/*virtual*/ void morkBuilder::OnCellGlitch(morkEnv* ev, + const morkGlitch& inGlitch) { + this->LogGlitch(ev, inGlitch, "cell"); +} + +/*virtual*/ void morkBuilder::OnCellForm(morkEnv* ev, + mork_cscode inCharsetFormat) { + morkCell* cell = mBuilder_Cell; + if (cell) { + mBuilder_CellForm = inCharsetFormat; + } else + this->NilBuilderCellError(ev); +} + +/*virtual*/ void morkBuilder::OnCellEnd(morkEnv* ev, const morkSpan& inSpan) +// mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd +{ + MORK_USED_2(ev, inSpan); + // mParser_InCell = morkBool_kFalse; + + mBuilder_MetaTokenSlot = 0; + mBuilder_CellAtomScope = mBuilder_RowAtomScope; +} + +/*virtual*/ void morkBuilder::OnValue(morkEnv* ev, const morkSpan& inSpan, + const morkBuf& inBuf) +// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch +// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid +{ + MORK_USED_1(inSpan); + morkStore* store = mBuilder_Store; + morkCell* cell = mBuilder_Cell; + if (cell) { + mdbYarn yarn; + yarn.mYarn_Buf = inBuf.mBuf_Body; + yarn.mYarn_Fill = yarn.mYarn_Size = inBuf.mBuf_Fill; + yarn.mYarn_More = 0; + yarn.mYarn_Form = mBuilder_CellForm; + yarn.mYarn_Grow = 0; + morkAtom* atom = store->YarnToAtom(ev, &yarn, true /* create */); + cell->SetAtom(ev, atom, store->StorePool()); + } else if (mParser_InMeta) { + mork_token* metaSlot = mBuilder_MetaTokenSlot; + if (metaSlot) { + if (metaSlot == &mBuilder_TableStatus) // table status? + { + if (mParser_InTable && mBuilder_Table) { + const char* body = (const char*)inBuf.mBuf_Body; + mork_fill bufFill = inBuf.mBuf_Fill; + if (body && bufFill) { + const char* bodyEnd = body + bufFill; + while (body < bodyEnd) { + int c = *body++; + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + mBuilder_TablePriority = (mork_priority)(c - '0'); + break; + + case 'u': + case 'U': + mBuilder_TableIsUnique = morkBool_kTrue; + break; + + case 'v': + case 'V': + mBuilder_TableIsVerbose = morkBool_kTrue; + break; + } + } + } + } + } else { + mork_token token = store->BufToToken(ev, &inBuf); + if (token) { + *metaSlot = token; + if (metaSlot == &mBuilder_TableKind) // table kind? + { + if (mParser_InTable && mBuilder_Table) + mBuilder_Table->mTable_Kind = token; + } + } + } + } + } else + this->NilBuilderCellError(ev); +} + +/*virtual*/ void morkBuilder::OnValueMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) +// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch +// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid +{ + MORK_USED_1(inSpan); + morkStore* store = mBuilder_Store; + morkCell* cell = mBuilder_Cell; + + morkMid valMid; // local mid for modifications + mdbOid* valOid = &valMid.mMid_Oid; // ref to oid inside mid + *valOid = inMid.mMid_Oid; // bitwise copy inMid's oid + + if (inMid.mMid_Buf) { + if (!valOid->mOid_Scope) store->MidToOid(ev, inMid, valOid); + } else if (!valOid->mOid_Scope) + valOid->mOid_Scope = mBuilder_CellAtomScope; + + if (cell) { + morkBookAtom* atom = store->MidToAtom(ev, valMid); + if (atom) + cell->SetAtom(ev, atom, store->StorePool()); + else + ev->NewError("undefined cell value alias"); + } else if (mParser_InMeta) { + mork_token* metaSlot = mBuilder_MetaTokenSlot; + if (metaSlot) { + mork_scope valScope = valOid->mOid_Scope; + if (!valScope || valScope == morkStore_kColumnSpaceScope) { + if (ev->Good() && valMid.HasSomeId()) { + *metaSlot = valOid->mOid_Id; + if (metaSlot == &mBuilder_TableKind) // table kind? + { + if (mParser_InTable && mBuilder_Table) { + mBuilder_Table->mTable_Kind = valOid->mOid_Id; + } else + ev->NewWarning("mBuilder_TableKind not in table"); + } else if (metaSlot == &mBuilder_TableStatus) // table status? + { + if (mParser_InTable && mBuilder_Table) { + // $$ what here?? + } else + ev->NewWarning("mBuilder_TableStatus not in table"); + } + } + } else + this->NonColumnSpaceScopeError(ev); + } + } else + this->NilBuilderCellError(ev); +} + +/*virtual*/ void morkBuilder::OnRowMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) +// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch +// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid +{ + MORK_USED_1(inSpan); + morkStore* store = mBuilder_Store; + morkCell* cell = mBuilder_Cell; + if (cell) { + mdbOid rowOid = inMid.mMid_Oid; + if (inMid.mMid_Buf) { + if (!rowOid.mOid_Scope) store->MidToOid(ev, inMid, &rowOid); + } else if (!rowOid.mOid_Scope) + rowOid.mOid_Scope = mBuilder_RowRowScope; + + if (ev->Good()) { + morkPool* pool = store->StorePool(); + morkAtom* atom = pool->NewRowOidAtom(ev, rowOid, &store->mStore_Zone); + if (atom) { + cell->SetAtom(ev, atom, pool); + morkRow* row = store->OidToRow(ev, &rowOid); + if (row) // found or created such a row? + row->AddRowGcUse(ev); + } + } + } else + this->NilBuilderCellError(ev); +} + +/*virtual*/ void morkBuilder::OnTableMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) +// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch +// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid +{ + MORK_USED_1(inSpan); + morkStore* store = mBuilder_Store; + morkCell* cell = mBuilder_Cell; + if (cell) { + mdbOid tableOid = inMid.mMid_Oid; + if (inMid.mMid_Buf) { + if (!tableOid.mOid_Scope) store->MidToOid(ev, inMid, &tableOid); + } else if (!tableOid.mOid_Scope) + tableOid.mOid_Scope = mBuilder_RowRowScope; + + if (ev->Good()) { + morkPool* pool = store->StorePool(); + morkAtom* atom = pool->NewTableOidAtom(ev, tableOid, &store->mStore_Zone); + if (atom) { + cell->SetAtom(ev, atom, pool); + morkTable* table = store->OidToTable(ev, &tableOid, + /*optionalMetaRowOid*/ (mdbOid*)0); + if (table) // found or created such a table? + table->AddTableGcUse(ev); + } + } + } else + this->NilBuilderCellError(ev); +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkBuilder.h b/comm/mailnews/db/mork/morkBuilder.h new file mode 100644 index 0000000000..2c8b2e573f --- /dev/null +++ b/comm/mailnews/db/mork/morkBuilder.h @@ -0,0 +1,303 @@ +/* -*- 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 _MORKBUILDER_ +#define _MORKBUILDER_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKPARSER_ +# include "morkParser.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*| kCellsVecSize: length of cell vector buffer inside morkBuilder +|*/ +#define morkBuilder_kCellsVecSize 64 + +#define morkBuilder_kDefaultBytesPerParseSegment 512 /* plausible to big */ + +#define morkDerived_kBuilder /*i*/ 0x4275 /* ascii 'Bu' */ + +class morkBuilder /*d*/ : public morkParser { + // public: // slots inherited from morkParser (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // nsIMdbHeap* mParser_Heap; // refcounted heap used for allocation + // morkStream* mParser_Stream; // refcounted input stream + + // mork_u4 mParser_Tag; // must equal morkParser_kTag + // mork_count mParser_MoreGranularity; // constructor + // inBytesPerParseSegment + + // mork_u4 mParser_State; // state where parser should resume + + // after finding ends of group transactions, we can re-seek the start: + // mork_pos mParser_GroupContentStartPos; // start of this group + + // mdbOid mParser_TableOid; // table oid if inside a table + // mdbOid mParser_RowOid; // row oid if inside a row + // mork_gid mParser_GroupId; // group ID if inside a group + + // mork_bool mParser_InPort; // called OnNewPort but not OnPortEnd? + // mork_bool mParser_InDict; // called OnNewDict but not OnDictEnd? + // mork_bool mParser_InCell; // called OnNewCell but not OnCellEnd? + // mork_bool mParser_InMeta; // called OnNewMeta but not OnMetaEnd? + + // morkMid mParser_Mid; // current alias being parsed + // note that mParser_Mid.mMid_Buf points at mParser_ScopeCoil below: + + // blob coils allocated in mParser_Heap + // morkCoil mParser_ScopeCoil; // place to accumulate ID scope blobs + // morkCoil mParser_ValueCoil; // place to accumulate value blobs + // morkCoil mParser_ColumnCoil; // place to accumulate column blobs + // morkCoil mParser_StringCoil; // place to accumulate string blobs + + // morkSpool mParser_ScopeSpool; // writes to mParser_ScopeCoil + // morkSpool mParser_ValueSpool; // writes to mParser_ValueCoil + // morkSpool mParser_ColumnSpool; // writes to mParser_ColumnCoil + // morkSpool mParser_StringSpool; // writes to mParser_StringCoil + + // yarns allocated in mParser_Heap + // morkYarn mParser_MidYarn; // place to receive from MidToYarn() + + // span showing current ongoing file position status: + // morkSpan mParser_PortSpan; // span of current db port file + + // various spans denoting nested subspaces inside the file's port span: + // morkSpan mParser_GroupSpan; // span of current transaction group + // morkSpan mParser_DictSpan; + // morkSpan mParser_AliasSpan; + // morkSpan mParser_MetaDictSpan; + // morkSpan mParser_TableSpan; + // morkSpan mParser_MetaTableSpan; + // morkSpan mParser_RowSpan; + // morkSpan mParser_MetaRowSpan; + // morkSpan mParser_CellSpan; + // morkSpan mParser_ColumnSpan; + // morkSpan mParser_SlotSpan; + + // ````` ````` ````` ````` ````` ````` ````` ````` + protected: // protected morkBuilder members + // weak refs that do not prevent closure of referenced nodes: + morkStore* mBuilder_Store; // weak ref to builder's store + + // strong refs that do indeed prevent closure of referenced nodes: + morkTable* mBuilder_Table; // current table being built (or nil) + morkRow* mBuilder_Row; // current row being built (or nil) + morkCell* mBuilder_Cell; // current cell within CellsVec (or nil) + + morkRowSpace* mBuilder_RowSpace; // space for mBuilder_CellRowScope + morkAtomSpace* mBuilder_AtomSpace; // space for mBuilder_CellAtomScope + + morkAtomSpace* mBuilder_OidAtomSpace; // ground atom space for oids + morkAtomSpace* mBuilder_ScopeAtomSpace; // ground atom space for scopes + + // scoped object ids for current objects under construction: + mdbOid mBuilder_TableOid; // full oid for current table + mdbOid mBuilder_RowOid; // full oid for current row + + // tokens that become set as the result of meta cells in port rows: + mork_cscode mBuilder_PortForm; // default port charset format + mork_scope mBuilder_PortRowScope; // port row scope + mork_scope mBuilder_PortAtomScope; // port atom scope + + // tokens that become set as the result of meta cells in meta tables: + mork_cscode mBuilder_TableForm; // default table charset format + mork_scope mBuilder_TableRowScope; // table row scope + mork_scope mBuilder_TableAtomScope; // table atom scope + mork_kind mBuilder_TableKind; // table kind + + mork_token mBuilder_TableStatus; // dummy: priority/unique/verbose + + mork_priority mBuilder_TablePriority; // table priority + mork_bool mBuilder_TableIsUnique; // table uniqueness + mork_bool mBuilder_TableIsVerbose; // table verboseness + mork_u1 mBuilder_TablePadByte; // for u4 alignment + + // tokens that become set as the result of meta cells in meta rows: + mork_cscode mBuilder_RowForm; // default row charset format + mork_scope mBuilder_RowRowScope; // row scope per row metainfo + mork_scope mBuilder_RowAtomScope; // row atom scope + + // meta tokens currently in force, driven by meta info slots above: + mork_cscode mBuilder_CellForm; // cell charset format + mork_scope mBuilder_CellAtomScope; // cell atom scope + + mork_cscode mBuilder_DictForm; // dict charset format + mork_scope mBuilder_DictAtomScope; // dict atom scope + + mork_token* mBuilder_MetaTokenSlot; // pointer to some slot above + + // If any of these 'cut' bools are true, it means a minus was seen in the + // Mork source text to indicate removal of content from some container. + // (Note there is no corresponding 'add' bool, since add is the default.) + // CutRow implies the current row should be cut from the table. + // CutCell implies the current column should be cut from the row. + mork_bool mBuilder_DoCutRow; // row with kCut change + mork_bool mBuilder_DoCutCell; // cell with kCut change + mork_u1 mBuilder_row_pad; // pad to u4 alignment + mork_u1 mBuilder_cell_pad; // pad to u4 alignment + + morkCell mBuilder_CellsVec[morkBuilder_kCellsVecSize + 1]; + mork_fill mBuilder_CellsVecFill; // count used in CellsVec + // Note when mBuilder_CellsVecFill equals morkBuilder_kCellsVecSize, and + // another cell is added, this means all the cells in the vector above + // must be flushed to the current row being built to create more room. + + protected: // protected inlines + mork_bool CellVectorIsFull() const { + return (mBuilder_CellsVecFill == morkBuilder_kCellsVecSize); + } + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseBuilder() only if open + virtual ~morkBuilder(); // assert that CloseBuilder() executed earlier + + public: // morkYarn construction & destruction + morkBuilder(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkStream* ioStream, // the readonly stream for input bytes + mdb_count inBytesPerParseSegment, // target for ParseMore() + nsIMdbHeap* ioSlotHeap, morkStore* ioStore); + + void CloseBuilder(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkBuilder(const morkBuilder& other); + morkBuilder& operator=(const morkBuilder& other); + + public: // dynamic type identification + mork_bool IsBuilder() const { + return IsNode() && mNode_Derived == morkDerived_kBuilder; + } + // } ===== end morkNode methods ===== + + public: // errors + static void NonBuilderTypeError(morkEnv* ev); + static void NilBuilderCellError(morkEnv* ev); + static void NilBuilderRowError(morkEnv* ev); + static void NilBuilderTableError(morkEnv* ev); + static void NonColumnSpaceScopeError(morkEnv* ev); + + void LogGlitch(morkEnv* ev, const morkGlitch& inGlitch, const char* inKind); + + public: // other builder methods + morkCell* AddBuilderCell(morkEnv* ev, const morkMid& inMid, + mork_change inChange); + + void FlushBuilderCells(morkEnv* ev); + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // in virtual morkParser methods, data flow subclass to parser + virtual void MidToYarn( + morkEnv* ev, + const morkMid& inMid, // typically an alias to concat with strings + mdbYarn* outYarn) override; + // The parser might ask that some aliases be turned into yarns, so they + // can be concatenated into longer blobs under some circumstances. This + // is an alternative to using a long and complex callback for many parts + // for a single cell value. + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // out virtual morkParser methods, data flow parser to subclass + virtual void OnNewPort(morkEnv* ev, const morkPlace& inPlace) override; + virtual void OnPortGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnPortEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnNewGroup(morkEnv* ev, const morkPlace& inPlace, + mork_gid inGid) override; + virtual void OnGroupGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnGroupCommitEnd(morkEnv* ev, const morkSpan& inSpan) override; + virtual void OnGroupAbortEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnNewPortRow(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, + mork_change inChange) override; + virtual void OnPortRowGlitch(morkEnv* ev, + const morkGlitch& inGlitch) override; + virtual void OnPortRowEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnNewTable(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, + mork_bool inCutAllRows) override; + virtual void OnTableGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnTableEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnNewMeta(morkEnv* ev, const morkPlace& inPlace) override; + virtual void OnMetaGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnMetaEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnMinusRow(morkEnv* ev) override; + virtual void OnNewRow(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, mork_bool inCutAllCols) override; + virtual void OnRowPos(morkEnv* ev, mork_pos inRowPos) override; + virtual void OnRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnRowEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnNewDict(morkEnv* ev, const morkPlace& inPlace) override; + virtual void OnDictGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnDictEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnAlias(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) override; + + virtual void OnAliasGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + + virtual void OnMinusCell(morkEnv* ev) override; + virtual void OnNewCell(morkEnv* ev, const morkPlace& inPlace, + const morkMid* inMid, const morkBuf* inBuf) override; + // Exactly one of inMid and inBuf is nil, and the other is non-nil. + // When hex ID syntax is used for a column, then inMid is not nil, and + // when a naked string names a column, then inBuf is not nil. + + virtual void OnCellGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnCellForm(morkEnv* ev, mork_cscode inCharsetFormat) override; + virtual void OnCellEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnValue(morkEnv* ev, const morkSpan& inSpan, + const morkBuf& inBuf) override; + + virtual void OnValueMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) override; + + virtual void OnRowMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) override; + + virtual void OnTableMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) override; + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // public non-poly morkBuilder methods + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakBuilder(morkBuilder* me, morkEnv* ev, + morkBuilder** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongBuilder(morkBuilder* me, morkEnv* ev, + morkBuilder** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKBUILDER_ */ diff --git a/comm/mailnews/db/mork/morkCell.cpp b/comm/mailnews/db/mork/morkCell.cpp new file mode 100644 index 0000000000..5e0ca9128c --- /dev/null +++ b/comm/mailnews/db/mork/morkCell.cpp @@ -0,0 +1,99 @@ +/* -*- 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 _MORKSTORE_ +# include "morkStore.h" +#endif + +#ifndef _MORKPOOL_ +# include "morkPool.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKCELL_ +# include "morkCell.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +void morkCell::SetYarn(morkEnv* ev, const mdbYarn* inYarn, morkStore* ioStore) { + morkAtom* atom = ioStore->YarnToAtom(ev, inYarn, true /* create */); + if (atom) this->SetAtom(ev, atom, ioStore->StorePool()); // refcounts atom +} + +void morkCell::GetYarn(morkEnv* ev, mdbYarn* outYarn) const { + MORK_USED_1(ev); + morkAtom::GetYarn(mCell_Atom, outYarn); +} + +void morkCell::AliasYarn(morkEnv* ev, mdbYarn* outYarn) const { + MORK_USED_1(ev); + morkAtom::AliasYarn(mCell_Atom, outYarn); +} + +void morkCell::SetCellClean() { + mork_column col = this->GetColumn(); + this->SetColumnAndChange(col, morkChange_kNil); +} + +void morkCell::SetCellDirty() { + mork_column col = this->GetColumn(); + this->SetColumnAndChange(col, morkChange_kAdd); +} + +void morkCell::SetAtom(morkEnv* ev, morkAtom* ioAtom, morkPool* ioPool) +// SetAtom() "acquires" the new ioAtom if non-nil, by calling AddCellUse() +// to increase the refcount, and puts ioAtom into mCell_Atom. If the old +// atom in mCell_Atom is non-nil, then it is "released" first by a call to +// CutCellUse(), and if the use count then becomes zero, then the old atom +// is deallocated by returning it to the pool ioPool. (And this is +// why ioPool is a parameter to this method.) Note that ioAtom can be nil +// to cause the cell to refer to nothing, and the old atom in mCell_Atom +// can also be nil, and all the atom refcounting is handled correctly. +// +// Note that if ioAtom was just created, it typically has a zero use count +// before calling SetAtom(). But use count is one higher after SetAtom(). +{ + morkAtom* oldAtom = mCell_Atom; + if (oldAtom != ioAtom) // ioAtom is not already installed in this cell? + { + if (oldAtom) { + mCell_Atom = 0; + if (oldAtom->CutCellUse(ev) == 0) { + // this was zapping atoms still in use - comment out until davidmc + // can figure out a better fix. + // if ( ioPool ) + // { + // if ( oldAtom->IsBook() ) + // ((morkBookAtom*) oldAtom)->CutBookAtomFromSpace(ev); + + // ioPool->ZapAtom(ev, oldAtom); + // } + // else + // ev->NilPointerError(); + } + } + if (ioAtom) ioAtom->AddCellUse(ev); + + mCell_Atom = ioAtom; + } +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkCell.h b/comm/mailnews/db/mork/morkCell.h new file mode 100644 index 0000000000..5b5194ccc4 --- /dev/null +++ b/comm/mailnews/db/mork/morkCell.h @@ -0,0 +1,91 @@ +/* -*- 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 _MORKCELL_ +#define _MORKCELL_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDelta_kShift 8 /* 8 bit shift */ +#define morkDelta_kChangeMask 0x0FF /* low 8 bit mask */ +#define morkDelta_kColumnMask (~(mork_column)morkDelta_kChangeMask) +#define morkDelta_Init(self, cl, ch) \ + ((self) = ((cl) << morkDelta_kShift) | (ch)) +#define morkDelta_Change(self) ((mork_change)((self)&morkDelta_kChangeMask)) +#define morkDelta_Column(self) ((self) >> morkDelta_kShift) + +class morkCell { // minimal cell format + + public: + mork_delta mCell_Delta; // encoding of both column and change + morkAtom* mCell_Atom; // content in this cell + + public: + morkCell() : mCell_Delta(0), mCell_Atom(0) {} + + morkCell(const morkCell& c) + : mCell_Delta(c.mCell_Delta), mCell_Atom(c.mCell_Atom) {} + + // note if ioAtom is non-nil, caller needs to call ioAtom->AddCellUse(): + morkCell(mork_column inCol, mork_change inChange, morkAtom* ioAtom) { + morkDelta_Init(mCell_Delta, inCol, inChange); + mCell_Atom = ioAtom; + } + + // note if ioAtom is non-nil, caller needs to call ioAtom->AddCellUse(): + void Init(mork_column inCol, mork_change inChange, morkAtom* ioAtom) { + morkDelta_Init(mCell_Delta, inCol, inChange); + mCell_Atom = ioAtom; + } + + mork_column GetColumn() const { return morkDelta_Column(mCell_Delta); } + mork_change GetChange() const { return morkDelta_Change(mCell_Delta); } + + mork_bool IsCellClean() const { return GetChange() == morkChange_kNil; } + mork_bool IsCellDirty() const { return GetChange() != morkChange_kNil; } + + void SetCellClean(); // set change to kNil + void SetCellDirty(); // set change to kAdd + + void SetCellColumnDirty(mork_column inCol) { + this->SetColumnAndChange(inCol, morkChange_kAdd); + } + + void SetCellColumnClean(mork_column inCol) { + this->SetColumnAndChange(inCol, morkChange_kNil); + } + + void SetColumnAndChange(mork_column inCol, mork_change inChange) { + morkDelta_Init(mCell_Delta, inCol, inChange); + } + + morkAtom* GetAtom() { return mCell_Atom; } + + void SetAtom(morkEnv* ev, morkAtom* ioAtom, morkPool* ioPool); + // SetAtom() "acquires" the new ioAtom if non-nil, by calling AddCellUse() + // to increase the refcount, and puts ioAtom into mCell_Atom. If the old + // atom in mCell_Atom is non-nil, then it is "released" first by a call to + // CutCellUse(), and if the use count then becomes zero, then the old atom + // is deallocated by returning it to the pool ioPool. (And this is + // why ioPool is a parameter to this method.) Note that ioAtom can be nil + // to cause the cell to refer to nothing, and the old atom in mCell_Atom + // can also be nil, and all the atom refcounting is handled correctly. + // + // Note that if ioAtom was just created, it typically has a zero use count + // before calling SetAtom(). But use count is one higher after SetAtom(). + + void SetYarn(morkEnv* ev, const mdbYarn* inYarn, morkStore* ioStore); + + void AliasYarn(morkEnv* ev, mdbYarn* outYarn) const; + void GetYarn(morkEnv* ev, mdbYarn* outYarn) const; +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKCELL_ */ diff --git a/comm/mailnews/db/mork/morkCellObject.cpp b/comm/mailnews/db/mork/morkCellObject.cpp new file mode 100644 index 0000000000..7ad8402348 --- /dev/null +++ b/comm/mailnews/db/mork/morkCellObject.cpp @@ -0,0 +1,453 @@ +/* -*- 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 _MORKOBJECT_ +# include "morkObject.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKCELLOBJECT_ +# include "morkCellObject.h" +#endif + +#ifndef _MORKROWOBJECT_ +# include "morkRowObject.h" +#endif + +#ifndef _MORKROW_ +# include "morkRow.h" +#endif + +#ifndef _MORKCELL_ +# include "morkCell.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkCellObject::CloseMorkNode( + morkEnv* ev) // CloseCellObject() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseCellObject(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkCellObject::~morkCellObject() // assert CloseCellObject() executed earlier +{ + CloseMorkNode(mMorkEnv); + MORK_ASSERT(mCellObject_Row == 0); +} + +/*public non-poly*/ +morkCellObject::morkCellObject(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkRow* ioRow, + morkCell* ioCell, mork_column inCol, + mork_pos inPos) + : morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*)0), + mCellObject_RowObject(0), + mCellObject_Row(0), + mCellObject_Cell(0), + mCellObject_Col(inCol), + mCellObject_RowSeed(0), + mCellObject_Pos((mork_u2)inPos) { + if (ev->Good()) { + if (ioRow && ioCell) { + if (ioRow->IsRow()) { + morkStore* store = ioRow->GetRowSpaceStore(ev); + if (store) { + morkRowObject* rowObj = ioRow->AcquireRowObject(ev, store); + if (rowObj) { + mCellObject_Row = ioRow; + mCellObject_Cell = ioCell; + mCellObject_RowSeed = ioRow->mRow_Seed; + + // morkRowObject::SlotStrongRowObject(rowObj, ev, + // &mCellObject_RowObject); + + mCellObject_RowObject = rowObj; // assume control of strong ref + } + if (ev->Good()) mNode_Derived = morkDerived_kCellObject; + } + } else + ioRow->NonRowTypeError(ev); + } else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkCellObject, morkObject, nsIMdbCell) + +/*public non-poly*/ void morkCellObject::CloseCellObject( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + NS_RELEASE(mCellObject_RowObject); + mCellObject_Row = 0; + mCellObject_Cell = 0; + mCellObject_RowSeed = 0; + this->CloseObject(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +mork_bool morkCellObject::ResyncWithRow(morkEnv* ev) { + morkRow* row = mCellObject_Row; + mork_pos pos = 0; + morkCell* cell = row->GetCell(ev, mCellObject_Col, &pos); + if (cell) { + mCellObject_Pos = (mork_u2)pos; + mCellObject_Cell = cell; + mCellObject_RowSeed = row->mRow_Seed; + } else { + mCellObject_Cell = 0; + this->MissingRowColumnError(ev); + } + return ev->Good(); +} + +morkAtom* morkCellObject::GetCellAtom(morkEnv* ev) const { + morkCell* cell = mCellObject_Cell; + if (cell) + return cell->GetAtom(); + else + this->NilCellError(ev); + + return (morkAtom*)0; +} + +/*static*/ void morkCellObject::WrongRowObjectRowError(morkEnv* ev) { + ev->NewError("mCellObject_Row != mCellObject_RowObject->mRowObject_Row"); +} + +/*static*/ void morkCellObject::NilRowError(morkEnv* ev) { + ev->NewError("nil mCellObject_Row"); +} + +/*static*/ void morkCellObject::NilRowObjectError(morkEnv* ev) { + ev->NewError("nil mCellObject_RowObject"); +} + +/*static*/ void morkCellObject::NilCellError(morkEnv* ev) { + ev->NewError("nil mCellObject_Cell"); +} + +/*static*/ void morkCellObject::NonCellObjectTypeError(morkEnv* ev) { + ev->NewError("non morkCellObject"); +} + +/*static*/ void morkCellObject::MissingRowColumnError(morkEnv* ev) { + ev->NewError("mCellObject_Col not in mCellObject_Row"); +} + +nsIMdbCell* morkCellObject::AcquireCellHandle(morkEnv* ev) { + nsIMdbCell* outCell = this; + NS_ADDREF(outCell); + return outCell; +} + +morkEnv* morkCellObject::CanUseCell(nsIMdbEnv* mev, mork_bool inMutable, + nsresult* outErr, morkCell** outCell) { + morkEnv* outEnv = 0; + morkCell* cell = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (IsCellObject()) { + if (IsMutable() || !inMutable) { + morkRowObject* rowObj = mCellObject_RowObject; + if (rowObj) { + morkRow* row = mCellObject_Row; + if (row) { + if (rowObj->mRowObject_Row == row) { + mork_u2 oldSeed = mCellObject_RowSeed; + if (row->mRow_Seed == oldSeed || ResyncWithRow(ev)) { + cell = mCellObject_Cell; + if (cell) { + outEnv = ev; + } else + NilCellError(ev); + } + } else + WrongRowObjectRowError(ev); + } else + NilRowError(ev); + } else + NilRowObjectError(ev); + } else + NonMutableNodeError(ev); + } else + NonCellObjectTypeError(ev); + } + *outErr = ev->AsErr(); + MORK_ASSERT(outEnv); + *outCell = cell; + + return outEnv; +} + +// { ----- begin attribute methods ----- +NS_IMETHODIMP morkCellObject::SetBlob(nsIMdbEnv* /* mev */, + nsIMdbBlob* /* ioBlob */) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} // reads inBlob slots + +// when inBlob is in the same suite, this might be fastest cell-to-cell + +NS_IMETHODIMP +morkCellObject::ClearBlob( // make empty (so content has zero length) + nsIMdbEnv* /* mev */) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; + // remember row->MaybeDirtySpaceStoreAndRow(); +} +// clearing a yarn is like SetYarn() with empty yarn instance content + +NS_IMETHODIMP morkCellObject::GetBlobFill(nsIMdbEnv* mev, mdb_fill* outFill) +// Same value that would be put into mYarn_Fill, if one called GetYarn() +// with a yarn instance that had mYarn_Buf==nil and mYarn_Size==0. +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} // size of blob + +NS_IMETHODIMP morkCellObject::SetYarn(nsIMdbEnv* mev, const mdbYarn* inYarn) { + nsresult outErr = NS_OK; + morkCell* cell = 0; + morkEnv* ev = + this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue, &outErr, &cell); + if (ev) { + morkRow* row = mCellObject_Row; + if (row) { + morkStore* store = row->GetRowSpaceStore(ev); + if (store) { + cell->SetYarn(ev, inYarn, store); + if (row->IsRowClean() && store->mStore_CanDirty) + row->MaybeDirtySpaceStoreAndRow(); + } + } else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + + return outErr; +} // reads from yarn slots +// make this text object contain content from the yarn's buffer + +NS_IMETHODIMP morkCellObject::GetYarn(nsIMdbEnv* mev, mdbYarn* outYarn) { + nsresult outErr = NS_OK; + morkCell* cell = 0; + morkEnv* ev = + this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue, &outErr, &cell); + if (ev) { + morkAtom* atom = cell->GetAtom(); + morkAtom::GetYarn(atom, outYarn); + outErr = ev->AsErr(); + } + + return outErr; +} // writes some yarn slots +// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + +NS_IMETHODIMP morkCellObject::AliasYarn(nsIMdbEnv* mev, mdbYarn* outYarn) { + nsresult outErr = NS_OK; + morkCell* cell = 0; + morkEnv* ev = + this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue, &outErr, &cell); + if (ev) { + morkAtom* atom = cell->GetAtom(); + morkAtom::AliasYarn(atom, outYarn); + outErr = ev->AsErr(); + } + + return outErr; +} // writes ALL yarn slots + +// } ----- end attribute methods ----- + +// } ===== end nsIMdbBlob methods ===== + +// { ===== begin nsIMdbCell methods ===== + +// { ----- begin attribute methods ----- +NS_IMETHODIMP morkCellObject::SetColumn(nsIMdbEnv* mev, mdb_column inColumn) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; + // remember row->MaybeDirtySpaceStoreAndRow(); +} + +NS_IMETHODIMP morkCellObject::GetColumn(nsIMdbEnv* mev, mdb_column* outColumn) { + nsresult outErr = NS_OK; + mdb_column col = 0; + morkCell* cell = 0; + morkEnv* ev = + this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue, &outErr, &cell); + if (ev) { + col = mCellObject_Col; + outErr = ev->AsErr(); + } + if (outColumn) *outColumn = col; + return outErr; +} + +NS_IMETHODIMP +morkCellObject::GetCellInfo( // all cell metainfo except actual content + nsIMdbEnv* mev, + mdb_column* outColumn, // the column in the containing row + mdb_fill* outBlobFill, // the size of text content in bytes + mdbOid* outChildOid, // oid of possible row or table child + mdb_bool* outIsRowChild) // nonzero if child, and a row child +// Checking all cell metainfo is a good way to avoid forcing a large cell +// in to memory when you don't actually want to use the content. +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP morkCellObject::GetRow( + nsIMdbEnv* mev, // parent row for this cell + nsIMdbRow** acqRow) { + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkCell* cell = 0; + morkEnv* ev = + this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue, &outErr, &cell); + if (ev) { + outRow = mCellObject_RowObject->AcquireRowHandle(ev); + + outErr = ev->AsErr(); + } + if (acqRow) *acqRow = outRow; + return outErr; +} + +NS_IMETHODIMP morkCellObject::GetPort(nsIMdbEnv* mev, // port containing cell + nsIMdbPort** acqPort) { + nsresult outErr = NS_OK; + nsIMdbPort* outPort = 0; + morkCell* cell = 0; + morkEnv* ev = + this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue, &outErr, &cell); + if (ev) { + if (mCellObject_Row) { + morkStore* store = mCellObject_Row->GetRowSpaceStore(ev); + if (store) outPort = store->AcquireStoreHandle(ev); + } else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if (acqPort) *acqPort = outPort; + return outErr; +} +// } ----- end attribute methods ----- + +// { ----- begin children methods ----- +NS_IMETHODIMP +morkCellObject::HasAnyChild( // does cell have a child instead of text? + nsIMdbEnv* mev, + mdbOid* outOid, // out id of row or table (or unbound if no child) + mdb_bool* outIsRow) // nonzero if child is a row (rather than a table) +{ + nsresult outErr = NS_OK; + mdb_bool isRow = morkBool_kFalse; + outOid->mOid_Scope = 0; + outOid->mOid_Id = morkId_kMinusOne; + morkCell* cell = 0; + morkEnv* ev = + this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue, &outErr, &cell); + if (ev) { + morkAtom* atom = GetCellAtom(ev); + if (atom) { + isRow = atom->IsRowOid(); + if (isRow || atom->IsTableOid()) + *outOid = ((morkOidAtom*)atom)->mOidAtom_Oid; + } + + outErr = ev->AsErr(); + } + if (outIsRow) *outIsRow = isRow; + + return outErr; +} + +NS_IMETHODIMP +morkCellObject::GetAnyChild( // access table of specific attribute + nsIMdbEnv* mev, // context + nsIMdbRow** acqRow, // child row (or null) + nsIMdbTable** acqTable) // child table (or null) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkCellObject::SetChildRow( // access table of specific attribute + nsIMdbEnv* mev, // context + nsIMdbRow* ioRow) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} // inRow must be bound inside this same db port + +NS_IMETHODIMP morkCellObject::GetChildRow( // access row of specific attribute + nsIMdbEnv* mev, // context + nsIMdbRow** acqRow) // acquire child row (or nil if no child) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkCellObject::SetChildTable( // access table of specific attribute + nsIMdbEnv* mev, // context + nsIMdbTable* inTable) // table must be bound inside this same db port +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; + // remember row->MaybeDirtySpaceStoreAndRow(); +} + +NS_IMETHODIMP +morkCellObject::GetChildTable( // access table of specific attribute + nsIMdbEnv* mev, // context + nsIMdbTable** acqTable) // acquire child tabdle (or nil if no chil) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} +// } ----- end children methods ----- + +// } ===== end nsIMdbCell methods ===== + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkCellObject.h b/comm/mailnews/db/mork/morkCellObject.h new file mode 100644 index 0000000000..1ef8718ab2 --- /dev/null +++ b/comm/mailnews/db/mork/morkCellObject.h @@ -0,0 +1,180 @@ +/* -*- 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 _MORKCELLOBJECT_ +#define _MORKCELLOBJECT_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKOBJECT_ +# include "morkObject.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kCellObject /*i*/ 0x634F /* ascii 'cO' */ + +class morkCellObject : public morkObject, + public nsIMdbCell { // blob attribute in column scope + + // public: // slots inherited from morkObject (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 + + // mork_color mBead_Color; // ID for this bead + // morkHandle* mObject_Handle; // weak ref to handle for this object + + public: // state is public because the entire Mork system is private + NS_DECL_ISUPPORTS_INHERITED + + morkRowObject* mCellObject_RowObject; // strong ref to row's object + morkRow* mCellObject_Row; // cell's row if still in row object + morkCell* mCellObject_Cell; // cell in row if rowseed matches + mork_column mCellObject_Col; // col of cell last living in pos + mork_u2 mCellObject_RowSeed; // copy of row's seed + mork_u2 mCellObject_Pos; // position of cell in row + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseCellObject() only if open + + public: // morkCellObject construction & destruction + morkCellObject(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkRow* ioRow, morkCell* ioCell, mork_column inCol, + mork_pos inPos); + void CloseCellObject(morkEnv* ev); // called by CloseMorkNode(); + + NS_IMETHOD SetBlob(nsIMdbEnv* ev, + nsIMdbBlob* ioBlob) override; // reads inBlob slots + // when inBlob is in the same suite, this might be fastest cell-to-cell + + NS_IMETHOD ClearBlob( // make empty (so content has zero length) + nsIMdbEnv* ev) override; + // clearing a yarn is like SetYarn() with empty yarn instance content + + NS_IMETHOD GetBlobFill(nsIMdbEnv* ev, + mdb_fill* outFill) override; // size of blob + // Same value that would be put into mYarn_Fill, if one called GetYarn() + // with a yarn instance that had mYarn_Buf==nil and mYarn_Size==0. + + NS_IMETHOD SetYarn(nsIMdbEnv* ev, + const mdbYarn* inYarn) override; // reads from yarn slots + // make this text object contain content from the yarn's buffer + + NS_IMETHOD GetYarn(nsIMdbEnv* ev, + mdbYarn* outYarn) override; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + + NS_IMETHOD AliasYarn(nsIMdbEnv* ev, + mdbYarn* outYarn) override; // writes ALL yarn slots + NS_IMETHOD SetColumn(nsIMdbEnv* ev, mdb_column inColumn) override; + NS_IMETHOD GetColumn(nsIMdbEnv* ev, mdb_column* outColumn) override; + + NS_IMETHOD GetCellInfo( // all cell metainfo except actual content + nsIMdbEnv* ev, + mdb_column* outColumn, // the column in the containing row + mdb_fill* outBlobFill, // the size of text content in bytes + mdbOid* outChildOid, // oid of possible row or table child + mdb_bool* outIsRowChild) override; // nonzero if child, and a row child + + // Checking all cell metainfo is a good way to avoid forcing a large cell + // in to memory when you don't actually want to use the content. + + NS_IMETHOD GetRow(nsIMdbEnv* ev, // parent row for this cell + nsIMdbRow** acqRow) override; + NS_IMETHOD GetPort(nsIMdbEnv* ev, // port containing cell + nsIMdbPort** acqPort) override; + // } ----- end attribute methods ----- + + // { ----- begin children methods ----- + NS_IMETHOD HasAnyChild( // does cell have a child instead of text? + nsIMdbEnv* ev, + mdbOid* outOid, // out id of row or table (or unbound if no child) + mdb_bool* outIsRow) + override; // nonzero if child is a row (rather than a table) + + NS_IMETHOD GetAnyChild( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow, // child row (or null) + nsIMdbTable** acqTable) override; // child table (or null) + + NS_IMETHOD SetChildRow( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow) + override; // inRow must be bound inside this same db port + + NS_IMETHOD GetChildRow( // access row of specific attribute + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow) override; // acquire child row (or nil if no child) + + NS_IMETHOD SetChildTable( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbTable* inTable) + override; // table must be bound inside this same db port + + NS_IMETHOD GetChildTable( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbTable** acqTable) + override; // acquire child table (or nil if no child) + + // } ----- end children methods ----- + + // } ===== end nsIMdbCell methods ===== + private: // copying is not allowed + virtual ~morkCellObject(); // assert that CloseCellObject() executed earlier + morkCellObject(const morkCellObject& other); + morkCellObject& operator=(const morkCellObject& other); + + public: // dynamic type identification + mork_bool IsCellObject() const { + return IsNode() && mNode_Derived == morkDerived_kCellObject; + } + // } ===== end morkNode methods ===== + + public: // other cell node methods + morkEnv* CanUseCell(nsIMdbEnv* mev, mork_bool inMutable, nsresult* outErr, + morkCell** outCell); + + mork_bool ResyncWithRow(morkEnv* ev); // return ev->Good() + morkAtom* GetCellAtom(morkEnv* ev) const; + + static void MissingRowColumnError(morkEnv* ev); + static void NilRowError(morkEnv* ev); + static void NilCellError(morkEnv* ev); + static void NilRowObjectError(morkEnv* ev); + static void WrongRowObjectRowError(morkEnv* ev); + static void NonCellObjectTypeError(morkEnv* ev); + + nsIMdbCell* AcquireCellHandle(morkEnv* ev); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakCellObject(morkCellObject* me, morkEnv* ev, + morkCellObject** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongCellObject(morkCellObject* me, morkEnv* ev, + morkCellObject** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKCELLOBJECT_ */ diff --git a/comm/mailnews/db/mork/morkCh.cpp b/comm/mailnews/db/mork/morkCh.cpp new file mode 100644 index 0000000000..334d9c689c --- /dev/null +++ b/comm/mailnews/db/mork/morkCh.cpp @@ -0,0 +1,344 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MDB_ +# include "mdb.h" +#endif + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKCH_ +# include "morkCh.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/* this byte char predicate source file derives from public domain Mithril */ +/* (that means much of this has a copyright dedicated to the public domain) */ + +/*============================================================================*/ +/* morkCh_Type */ + +const mork_flags morkCh_Type[] = /* derives from public domain Mithril table */ + { + 0, /* 0x0 */ + 0, /* 0x1 */ + 0, /* 0x2 */ + 0, /* 0x3 */ + 0, /* 0x4 */ + 0, /* 0x5 */ + 0, /* 0x6 */ + 0, /* 0x7 */ + morkCh_kW, /* 0x8 backspace */ + morkCh_kW, /* 0x9 tab */ + morkCh_kW, /* 0xA linefeed */ + 0, /* 0xB */ + morkCh_kW, /* 0xC page */ + morkCh_kW, /* 0xD return */ + 0, /* 0xE */ + 0, /* 0xF */ + 0, /* 0x10 */ + 0, /* 0x11 */ + 0, /* 0x12 */ + 0, /* 0x13 */ + 0, /* 0x14 */ + 0, /* 0x15 */ + 0, /* 0x16 */ + 0, /* 0x17 */ + 0, /* 0x18 */ + 0, /* 0x19 */ + 0, /* 0x1A */ + 0, /* 0x1B */ + 0, /* 0x1C */ + 0, /* 0x1D */ + 0, /* 0x1E */ + 0, /* 0x1F */ + + morkCh_kV | morkCh_kW, /* 0x20 space */ + morkCh_kV | morkCh_kM, /* 0x21 ! */ + morkCh_kV, /* 0x22 " */ + morkCh_kV, /* 0x23 # */ + 0, /* 0x24 $ cannot be kV because needs escape */ + morkCh_kV, /* 0x25 % */ + morkCh_kV, /* 0x26 & */ + morkCh_kV, /* 0x27 ' */ + morkCh_kV, /* 0x28 ( */ + 0, /* 0x29 ) cannot be kV because needs escape */ + morkCh_kV, /* 0x2A * */ + morkCh_kV | morkCh_kM, /* 0x2B + */ + morkCh_kV, /* 0x2C , */ + morkCh_kV | morkCh_kM, /* 0x2D - */ + morkCh_kV, /* 0x2E . */ + morkCh_kV, /* 0x2F / */ + + morkCh_kV | morkCh_kD | morkCh_kX, /* 0x30 0 */ + morkCh_kV | morkCh_kD | morkCh_kX, /* 0x31 1 */ + morkCh_kV | morkCh_kD | morkCh_kX, /* 0x32 2 */ + morkCh_kV | morkCh_kD | morkCh_kX, /* 0x33 3 */ + morkCh_kV | morkCh_kD | morkCh_kX, /* 0x34 4 */ + morkCh_kV | morkCh_kD | morkCh_kX, /* 0x35 5 */ + morkCh_kV | morkCh_kD | morkCh_kX, /* 0x36 6 */ + morkCh_kV | morkCh_kD | morkCh_kX, /* 0x37 7 */ + morkCh_kV | morkCh_kD | morkCh_kX, /* 0x38 8 */ + morkCh_kV | morkCh_kD | morkCh_kX, /* 0x39 9 */ + morkCh_kV | morkCh_kN | morkCh_kM, /* 0x3A : */ + morkCh_kV, /* 0x3B ; */ + morkCh_kV, /* 0x3C < */ + morkCh_kV, /* 0x3D = */ + morkCh_kV, /* 0x3E > */ + morkCh_kV | morkCh_kM, /* 0x3F ? */ + + morkCh_kV, /* 0x40 @ */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU | morkCh_kX, /* 0x41 A */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU | morkCh_kX, /* 0x42 B */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU | morkCh_kX, /* 0x43 C */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU | morkCh_kX, /* 0x44 D */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU | morkCh_kX, /* 0x45 E */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU | morkCh_kX, /* 0x46 F */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x47 G */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x48 H */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x49 I */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x4A J */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x4B K */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x4C L */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x4D M */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x4E N */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x4F O */ + + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x50 P */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x51 Q */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x52 R */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x53 S */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x54 T */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x55 U */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x56 V */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x57 W */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x58 X */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x59 Y */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kU, /* 0x5A Z */ + morkCh_kV, /* 0x5B [ */ + 0, /* 0x5C \ cannot be kV because needs escape */ + morkCh_kV, /* 0x5D ] */ + morkCh_kV, /* 0x5E ^ */ + morkCh_kV | morkCh_kN | morkCh_kM, /* 0x5F _ */ + + morkCh_kV, /* 0x60 ` */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL | morkCh_kX, /* 0x61 a */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL | morkCh_kX, /* 0x62 b */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL | morkCh_kX, /* 0x63 c */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL | morkCh_kX, /* 0x64 d */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL | morkCh_kX, /* 0x65 e */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL | morkCh_kX, /* 0x66 f */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x67 g */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x68 h */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x69 i */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x6A j */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x6B k */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x6C l */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x6D m */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x6E n */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x6F o */ + + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x70 p */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x71 q */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x72 r */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x73 s */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x74 t */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x75 u */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x76 v */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x77 w */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x78 x */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x79 y */ + morkCh_kV | morkCh_kN | morkCh_kM | morkCh_kL, /* 0x7A z */ + morkCh_kV, /* 0x7B { */ + morkCh_kV, /* 0x7C | */ + morkCh_kV, /* 0x7D } */ + morkCh_kV, /* 0x7E ~ */ + morkCh_kW, /* 0x7F rubout */ + + /* $"80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F" */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* $"90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F" */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* $"A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF" */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* $"B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF" */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* $"C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF" */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* $"D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF" */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* $"E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF" */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* $"F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF" */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkCh.h b/comm/mailnews/db/mork/morkCh.h new file mode 100644 index 0000000000..a3fc155a4d --- /dev/null +++ b/comm/mailnews/db/mork/morkCh.h @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MORKCH_ +# define _MORKCH_ 1 + +# ifndef _MORK_ +# include "mork.h" +# endif + +/* this byte char predicate header file derives from public domain Mithril */ +/* (that means much of this has a copyright dedicated to the public domain) */ + +/* Use all 8 pred bits; lose some pred bits only if we need to reuse them. */ + +/* ch pred bits: W:white D:digit V:value U:upper L:lower N:name M:more */ +# define morkCh_kW (1 << 0) +# define morkCh_kD (1 << 1) +# define morkCh_kV (1 << 2) +# define morkCh_kU (1 << 3) +# define morkCh_kL (1 << 4) +# define morkCh_kX (1 << 5) +# define morkCh_kN (1 << 6) +# define morkCh_kM (1 << 7) + +extern const mork_flags morkCh_Type[]; /* 256 byte predicate bits ch map */ + +/* is a numeric decimal digit: (note memory access might be slower) */ +/* define morkCh_IsDigit(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kD ) */ +# define morkCh_IsDigit(c) (((mork_ch)c) >= '0' && ((mork_ch)c) <= '9') + +/* is a numeric octal digit: */ +# define morkCh_IsOctal(c) (((mork_ch)c) >= '0' && ((mork_ch)c) <= '7') + +/* is a numeric hexadecimal digit: */ +# define morkCh_IsHex(c) (morkCh_Type[(mork_ch)(c)] & morkCh_kX) + +/* is value (can be printed in Mork value without needing hex or escape): */ +# define morkCh_IsValue(c) (morkCh_Type[(mork_ch)(c)] & morkCh_kV) + +/* is white space : */ +# define morkCh_IsWhite(c) (morkCh_Type[(mork_ch)(c)] & morkCh_kW) + +/* is name (can start a Mork name): */ +# define morkCh_IsName(c) (morkCh_Type[(mork_ch)(c)] & morkCh_kN) + +/* is name (can continue a Mork name): */ +# define morkCh_IsMore(c) (morkCh_Type[(mork_ch)(c)] & morkCh_kM) + +/* is alphabetic upper or lower case */ +# define morkCh_IsAlpha(c) \ + (morkCh_Type[(mork_ch)(c)] & (morkCh_kL | morkCh_kU)) + +/* is alphanumeric, including lower case, upper case, and digits */ +# define morkCh_IsAlphaNum(c) \ + (morkCh_Type[(mork_ch)(c)] & (morkCh_kL | morkCh_kU | morkCh_kD)) + +/* ````` repeated testing of predicate bits in single flag byte ````` */ + +# define morkCh_GetFlags(c) (morkCh_Type[(mork_ch)(c)]) + +# define morkFlags_IsDigit(f) ((f)&morkCh_kD) +# define morkFlags_IsHex(f) ((f)&morkCh_kX) +# define morkFlags_IsValue(f) ((f)&morkCh_kV) +# define morkFlags_IsWhite(f) ((f)&morkCh_kW) +# define morkFlags_IsName(f) ((f)&morkCh_kN) +# define morkFlags_IsMore(f) ((f)&morkCh_kM) +# define morkFlags_IsAlpha(f) ((f) & (morkCh_kL | morkCh_kU)) +# define morkFlags_IsAlphaNum(f) ((f) & (morkCh_kL | morkCh_kU | morkCh_kD)) + +# define morkFlags_IsUpper(f) ((f)&morkCh_kU) +# define morkFlags_IsLower(f) ((f)&morkCh_kL) + +/* ````` character case (e.g. for case insensitive operations) ````` */ + +# define morkCh_IsAscii(c) (((mork_u1)c) <= 0x7F) +# define morkCh_IsSevenBitChar(c) (((mork_u1)c) <= 0x7F) + +/* ````` character case (e.g. for case insensitive operations) ````` */ + +# define morkCh_ToLower(c) ((c) - 'A' + 'a') +# define morkCh_ToUpper(c) ((c) - 'a' + 'A') + +/* extern int morkCh_IsUpper (int c); */ +# define morkCh_IsUpper(c) (morkCh_Type[(mork_ch)(c)] & morkCh_kU) + +/* extern int morkCh_IsLower (int c); */ +# define morkCh_IsLower(c) (morkCh_Type[(mork_ch)(c)] & morkCh_kL) + +#endif +/* _MORKCH_ */ diff --git a/comm/mailnews/db/mork/morkConfig.cpp b/comm/mailnews/db/mork/morkConfig.cpp new file mode 100644 index 0000000000..a02cecead9 --- /dev/null +++ b/comm/mailnews/db/mork/morkConfig.cpp @@ -0,0 +1,173 @@ +/* -*- 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 _MORKCONFIG_ +# include "morkConfig.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +void mork_assertion_signal(const char* inMessage) { NS_ERROR(inMessage); } + +#ifdef MORK_PROVIDE_STDLIB + +MORK_LIB_IMPL(mork_i4) +mork_memcmp(const void* inOne, const void* inTwo, mork_size inSize) { + const mork_u1* t = (const mork_u1*)inTwo; + const mork_u1* s = (const mork_u1*)inOne; + const mork_u1* end = s + inSize; + mork_i4 delta; + + while (s < end) { + delta = ((mork_i4)*s) - ((mork_i4)*t); + if (delta) + return delta; + else { + ++t; + ++s; + } + } + return 0; +} + +MORK_LIB_IMPL(void) +mork_memcpy(void* outDst, const void* inSrc, mork_size inSize) { + mork_u1* d = (mork_u1*)outDst; + mork_u1* end = d + inSize; + const mork_u1* s = ((const mork_u1*)inSrc); + + while (inSize >= 8) { + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + + inSize -= 8; + } + + while (d < end) *d++ = *s++; +} + +MORK_LIB_IMPL(void) +mork_memmove(void* outDst, const void* inSrc, mork_size inSize) { + mork_u1* d = (mork_u1*)outDst; + const mork_u1* s = (const mork_u1*)inSrc; + if (d != s && inSize) // copy is necessary? + { + const mork_u1* srcEnd = s + inSize; // one past last source byte + + if (d > s && d < srcEnd) // overlap? need to copy backwards? + { + s = srcEnd; // start one past last source byte + d += inSize; // start one past last dest byte + mork_u1* dstBegin = d; // last byte to write is first in dest range + while (d - dstBegin >= 8) { + *--d = *--s; + *--d = *--s; + *--d = *--s; + *--d = *--s; + + *--d = *--s; + *--d = *--s; + *--d = *--s; + *--d = *--s; + } + while (d > dstBegin) *--d = *--s; + } else // can copy forwards without any overlap + { + mork_u1* dstEnd = d + inSize; + while (dstEnd - d >= 8) { + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + } + while (d < dstEnd) *d++ = *s++; + } + } +} + +MORK_LIB_IMPL(void) +mork_memset(void* outDst, int inByte, mork_size inSize) { + mork_u1* d = (mork_u1*)outDst; + mork_u1* end = d + inSize; + while (d < end) *d++ = (mork_u1)inByte; +} + +MORK_LIB_IMPL(void) +mork_strcpy(void* outDst, const void* inSrc) { + // back up one first to support preincrement + mork_u1* d = ((mork_u1*)outDst) - 1; + const mork_u1* s = ((const mork_u1*)inSrc) - 1; + while ((*++d = *++s) != 0) + ; /* empty */ +} + +MORK_LIB_IMPL(mork_i4) +mork_strcmp(const void* inOne, const void* inTwo) { + const mork_u1* t = (const mork_u1*)inTwo; + const mork_u1* s = ((const mork_u1*)inOne); + mork_i4 a; + mork_i4 b; + mork_i4 delta; + + do { + a = (mork_i4)*s++; + b = (mork_i4)*t++; + delta = a - b; + } while (!delta && a && b); + + return delta; +} + +MORK_LIB_IMPL(mork_i4) +mork_strncmp(const void* inOne, const void* inTwo, mork_size inSize) { + const mork_u1* t = (const mork_u1*)inTwo; + const mork_u1* s = (const mork_u1*)inOne; + const mork_u1* end = s + inSize; + mork_i4 delta; + mork_i4 a; + mork_i4 b; + + while (s < end) { + a = (mork_i4)*s++; + b = (mork_i4)*t++; + delta = a - b; + if (delta || !a || !b) return delta; + } + return 0; +} + +MORK_LIB_IMPL(mork_size) +mork_strlen(const void* inString) { + // back up one first to support preincrement + const mork_u1* s = ((const mork_u1*)inString) - 1; + while (*++s) /* preincrement is cheapest */ + ; /* empty */ + + return s - ((const mork_u1*)inString); // distance from original address +} + +#endif /*MORK_PROVIDE_STDLIB*/ + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkConfig.h b/comm/mailnews/db/mork/morkConfig.h new file mode 100644 index 0000000000..812641ee09 --- /dev/null +++ b/comm/mailnews/db/mork/morkConfig.h @@ -0,0 +1,170 @@ +/* -*- 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 _MORKCONFIG_ +#define _MORKCONFIG_ 1 + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// { %%%%% begin debug mode options in Mork %%%%% +#define MORK_DEBUG 1 +// } %%%%% end debug mode options in Mork %%%%% + +#ifdef MORK_DEBUG +# define MORK_MAX_CODE_COMPILE 1 +#endif + +// { %%%%% begin platform defs peculiar to Mork %%%%% + +#ifdef XP_MACOSX +# define MORK_MAC 1 +#endif + +#ifdef XP_WIN +# define MORK_WIN 1 +#endif + +#ifdef XP_UNIX +# define MORK_UNIX 1 +#endif + +// } %%%%% end platform defs peculiar to Mork %%%%% + +#if defined(MORK_WIN) || defined(MORK_UNIX) || defined(MORK_MAC) +# include <stdio.h> +# include <ctype.h> +# include <errno.h> +# include <string.h> +# ifdef HAVE_MEMORY_H +# include <memory.h> +# endif +# ifdef HAVE_UNISTD_H +# include <unistd.h> /* for SEEK_SET, SEEK_END */ +# endif + +# include "nsDebug.h" + +# define MORK_ISPRINT(c) isprint(c) + +# define MORK_FILETELL(file) ftell(file) +# define MORK_FILESEEK(file, where, how) fseek(file, where, how) +# define MORK_FILEREAD(outbuf, insize, file) fread(outbuf, 1, insize, file) +# if defined(MORK_WIN) +void mork_fileflush(FILE* file); +# define MORK_FILEFLUSH(file) mork_fileflush(file) +# else +# define MORK_FILEFLUSH(file) fflush(file) +# endif /*MORK_WIN*/ + +# if defined(MORK_WIN) +# define MORK_FILEOPEN(file, how) \ + _wfopen(char16ptr_t(file), NS_ConvertASCIItoUTF16(how).get()) +# else +# define MORK_FILEOPEN(file, how) fopen(file, how) +# endif /*MORK_WIN*/ +# define MORK_FILECLOSE(file) fclose(file) +#endif /*defined(MORK_WIN) || defined(MORK_UNIX) || defined(MORK_MAC)*/ + +/* ===== separating switchable features ===== */ + +#define MORK_ENABLE_ZONE_ARENAS 1 /* using morkZone for pooling */ + +// #define MORK_ENABLE_PROBE_MAPS 1 /* use smaller hash tables */ + +#define MORK_BEAD_OVER_NODE_MAPS 1 /* use bead not node maps */ + +/* ===== pooling ===== */ + +#if defined(HAVE_64BIT_BUILD) +# define MORK_CONFIG_ALIGN_8 1 /* must have 8 byte alignment */ +#else +# define MORK_CONFIG_PTR_SIZE_4 1 /* sizeof(void*) == 4 */ +#endif + +// #define MORK_DEBUG_HEAP_STATS 1 /* analyze per-block heap usage */ + +/* ===== ===== ===== ===== line characters ===== ===== ===== ===== */ +#define mork_kCR 0x0D +#define mork_kLF 0x0A +#define mork_kVTAB '\013' +#define mork_kFF '\014' +#define mork_kTAB '\011' +#define mork_kCRLF "\015\012" /* A CR LF equivalent string */ + +#if defined(MORK_MAC) +# define mork_kNewline "\015" +# define mork_kNewlineSize 1 +#else +# if defined(MORK_WIN) +# define mork_kNewline "\015\012" +# define mork_kNewlineSize 2 +# else +# if defined(MORK_UNIX) +# define mork_kNewline "\012" +# define mork_kNewlineSize 1 +# endif /* MORK_UNIX */ +# endif /* MORK_WIN */ +#endif /* MORK_MAC */ + +// { %%%%% begin assertion macro %%%%% +extern void mork_assertion_signal(const char* inMessage); +#define MORK_ASSERTION_SIGNAL(Y) mork_assertion_signal(Y) +#define MORK_ASSERT(X) \ + if (!(X)) MORK_ASSERTION_SIGNAL(#X) +// } %%%%% end assertion macro %%%%% + +#define MORK_LIB(return) return /*API return declaration*/ +#define MORK_LIB_IMPL(return) return /*implementation return declaration*/ + +// { %%%%% begin standard c utility methods %%%%% + +#if defined(MORK_WIN) || defined(MORK_UNIX) || defined(MORK_MAC) +# define MORK_USE_C_STDLIB 1 +#endif /*MORK_WIN*/ + +#ifdef MORK_USE_C_STDLIB +# define MORK_MEMCMP(src1, src2, size) memcmp(src1, src2, size) +# define MORK_MEMCPY(dest, src, size) memcpy(dest, src, size) +# define MORK_MEMMOVE(dest, src, size) memmove(dest, src, size) +# define MORK_MEMSET(dest, byte, size) memset(dest, byte, size) +# if defined(MORK_WIN) +# define MORK_STRCPY(dest, src) wcscpy(char16ptr_t(dest), char16ptr_t(src)) +# else +# define MORK_STRCPY(dest, src) strcpy(dest, src) +# endif /*MORK_WIN*/ +# define MORK_STRCMP(one, two) strcmp(one, two) +# define MORK_STRNCMP(one, two, length) strncmp(one, two, length) +# if defined(MORK_WIN) +# define MORK_STRLEN(string) wcslen(char16ptr_t(string)) +# else +# define MORK_STRLEN(string) strlen(string) +# endif /*MORK_WIN*/ +#endif /*MORK_USE_C_STDLIB*/ + +#ifdef MORK_PROVIDE_STDLIB +MORK_LIB(mork_i4) mork_memcmp(const void* a, const void* b, mork_size inSize); +MORK_LIB(void) mork_memcpy(void* dst, const void* src, mork_size inSize); +MORK_LIB(void) mork_memmove(void* dst, const void* src, mork_size inSize); +MORK_LIB(void) mork_memset(void* dst, int inByte, mork_size inSize); +MORK_LIB(void) mork_strcpy(void* dst, const void* src); +MORK_LIB(mork_i4) mork_strcmp(const void* a, const void* b); +MORK_LIB(mork_i4) mork_strncmp(const void* a, const void* b, mork_size inSize); +MORK_LIB(mork_size) mork_strlen(const void* inString); + +# define MORK_MEMCMP(src1, src2, size) mork_memcmp(src1, src2, size) +# define MORK_MEMCPY(dest, src, size) mork_memcpy(dest, src, size) +# define MORK_MEMMOVE(dest, src, size) mork_memmove(dest, src, size) +# define MORK_MEMSET(dest, byte, size) mork_memset(dest, byte, size) +# define MORK_STRCPY(dest, src) mork_strcpy(dest, src) +# define MORK_STRCMP(one, two) mork_strcmp(one, two) +# define MORK_STRNCMP(one, two, length) mork_strncmp(one, two, length) +# define MORK_STRLEN(string) mork_strlen(string) +#endif /*MORK_PROVIDE_STDLIB*/ + +// } %%%%% end standard c utility methods %%%%% + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKCONFIG_ */ diff --git a/comm/mailnews/db/mork/morkCursor.cpp b/comm/mailnews/db/mork/morkCursor.cpp new file mode 100644 index 0000000000..407aa9b3fb --- /dev/null +++ b/comm/mailnews/db/mork/morkCursor.cpp @@ -0,0 +1,173 @@ +/* -*- 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 _MORKCURSOR_ +# include "morkCursor.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkCursor::CloseMorkNode( + morkEnv* ev) // CloseCursor() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseCursor(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkCursor::~morkCursor() // assert CloseCursor() executed earlier +{} + +/*public non-poly*/ +morkCursor::morkCursor(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap) + : morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*)0), + mCursor_Seed(0), + mCursor_Pos(-1), + mCursor_DoFailOnSeedOutOfSync(morkBool_kFalse) { + if (ev->Good()) mNode_Derived = morkDerived_kCursor; +} + +NS_IMPL_ISUPPORTS_INHERITED(morkCursor, morkObject, nsIMdbCursor) + +/*public non-poly*/ void morkCursor::CloseCursor( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + mCursor_Seed = 0; + mCursor_Pos = -1; + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// { ----- begin ref counting for well-behaved cyclic graphs ----- +NS_IMETHODIMP +morkCursor::GetWeakRefCount(nsIMdbEnv* mev, // weak refs + mdb_count* outCount) { + *outCount = WeakRefsOnly(); + return NS_OK; +} +NS_IMETHODIMP +morkCursor::GetStrongRefCount(nsIMdbEnv* mev, // strong refs + mdb_count* outCount) { + *outCount = StrongRefsOnly(); + return NS_OK; +} +// ### TODO - clean up this cast, if required +NS_IMETHODIMP +morkCursor::AddWeakRef(nsIMdbEnv* mev) { + // XXX Casting mork_refs to nsresult + return static_cast<nsresult>(morkNode::AddWeakRef((morkEnv*)mev)); +} + +#ifndef _MSC_VER +NS_IMETHODIMP_(mork_uses) +morkCursor::AddStrongRef(morkEnv* mev) { return morkNode::AddStrongRef(mev); } +#endif + +NS_IMETHODIMP_(mork_uses) +morkCursor::AddStrongRef(nsIMdbEnv* mev) { + return morkNode::AddStrongRef((morkEnv*)mev); +} + +NS_IMETHODIMP +morkCursor::CutWeakRef(nsIMdbEnv* mev) { + // XXX Casting mork_refs to nsresult + return static_cast<nsresult>(morkNode::CutWeakRef((morkEnv*)mev)); +} + +#ifndef _MSC_VER +NS_IMETHODIMP_(mork_uses) +morkCursor::CutStrongRef(morkEnv* mev) { return morkNode::CutStrongRef(mev); } +#endif + +NS_IMETHODIMP +morkCursor::CutStrongRef(nsIMdbEnv* mev) { + // XXX Casting mork_uses to nsresult + return static_cast<nsresult>(morkNode::CutStrongRef((morkEnv*)mev)); +} + +NS_IMETHODIMP +morkCursor::CloseMdbObject(nsIMdbEnv* mev) { + return morkNode::CloseMdbObject((morkEnv*)mev); +} + +NS_IMETHODIMP +morkCursor::IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen) { + *outOpen = IsOpenNode(); + return NS_OK; +} +NS_IMETHODIMP +morkCursor::IsFrozenMdbObject(nsIMdbEnv* mev, mdb_bool* outIsReadonly) { + *outIsReadonly = IsFrozen(); + return NS_OK; +} +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +NS_IMETHODIMP +morkCursor::GetCount(nsIMdbEnv* mev, mdb_count* outCount) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkCursor::GetSeed(nsIMdbEnv* mev, mdb_seed* outSeed) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkCursor::SetPos(nsIMdbEnv* mev, mdb_pos inPos) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkCursor::GetPos(nsIMdbEnv* mev, mdb_pos* outPos) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkCursor::SetDoFailOnSeedOutOfSync(nsIMdbEnv* mev, mdb_bool inFail) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkCursor::GetDoFailOnSeedOutOfSync(nsIMdbEnv* mev, mdb_bool* outFail) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkCursor.h b/comm/mailnews/db/mork/morkCursor.h new file mode 100644 index 0000000000..11c8ec8839 --- /dev/null +++ b/comm/mailnews/db/mork/morkCursor.h @@ -0,0 +1,134 @@ +/* -*- 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 _MORKCURSOR_ +#define _MORKCURSOR_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKOBJECT_ +# include "morkObject.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kCursor /*i*/ 0x4375 /* ascii 'Cu' */ + +class morkCursor : public morkObject, + public nsIMdbCursor { // collection iterator + + // public: // slots inherited from morkObject (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 + + // mork_color mBead_Color; // ID for this bead + // morkHandle* mObject_Handle; // weak ref to handle for this object + + public: // state is public because the entire Mork system is private + NS_DECL_ISUPPORTS_INHERITED + + // { ----- begin attribute methods ----- + NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly) override; + // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port. + // } ----- end attribute 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 nsIMdbCursor methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD GetCount(nsIMdbEnv* ev, mdb_count* outCount) override; // readonly + NS_IMETHOD GetSeed(nsIMdbEnv* ev, mdb_seed* outSeed) override; // readonly + + NS_IMETHOD SetPos(nsIMdbEnv* ev, mdb_pos inPos) override; // mutable + NS_IMETHOD GetPos(nsIMdbEnv* ev, mdb_pos* outPos) override; + + NS_IMETHOD SetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool inFail) override; + NS_IMETHOD GetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, + mdb_bool* outFail) override; + // } ----- end attribute methods ----- + + // } ===== end nsIMdbCursor methods ===== + + // } ----- end attribute methods ----- + + mork_seed mCursor_Seed; + mork_pos mCursor_Pos; + mork_bool mCursor_DoFailOnSeedOutOfSync; + mork_u1 mCursor_Pad[3]; // explicitly pad to u4 alignment + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseCursor() only if open + + public: // morkCursor construction & destruction + morkCursor(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap); + void CloseCursor(morkEnv* ev); // called by CloseMorkNode(); + + protected: + virtual ~morkCursor(); // assert that CloseCursor() executed earlier + + private: // copying is not allowed + morkCursor(const morkCursor& other); + morkCursor& operator=(const morkCursor& other); + + public: // dynamic type identification + mork_bool IsCursor() const { + return IsNode() && mNode_Derived == morkDerived_kCursor; + } + // } ===== end morkNode methods ===== + + public: // other cursor methods + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakCursor(morkCursor* me, morkEnv* ev, morkCursor** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongCursor(morkCursor* me, morkEnv* ev, + morkCursor** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKCURSOR_ */ diff --git a/comm/mailnews/db/mork/morkDeque.cpp b/comm/mailnews/db/mork/morkDeque.cpp new file mode 100644 index 0000000000..7490aef84b --- /dev/null +++ b/comm/mailnews/db/mork/morkDeque.cpp @@ -0,0 +1,246 @@ +/************************************************************************* +This software is part of a public domain IronDoc source code distribution, +and is provided on an "AS IS" basis, with all risks borne by the consumers +or users of the IronDoc software. There are no warranties, guarantees, or +promises about quality of any kind; and no remedies for failure exist. + +Permission is hereby granted to use this IronDoc software for any purpose +at all, without need for written agreements, without royalty or license +fees, and without fees or obligations of any other kind. Anyone can use, +copy, change and distribute this software for any purpose, and nothing is +required, implicitly or otherwise, in exchange for this usage. + +You cannot apply your own copyright to this software, but otherwise you +are encouraged to enjoy the use of this software in any way you see fit. +However, it would be rude to remove names of developers from the code. +(IronDoc is also known by the short name "Fe" and a longer name "Ferrum", +which are used interchangeably with the name IronDoc in the sources.) +*************************************************************************/ +/* + * File: morkDeque.cpp + * Contains: Ferrum deque (double ended queue (linked list)) + * + * Copied directly from public domain IronDoc, with minor naming tweaks: + * Designed and written by David McCusker, but all this code is public domain. + * There are no warranties, no guarantees, no promises, and no remedies. + */ + +#ifndef _MDB_ +# include "mdb.h" +#endif + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKDEQUE_ +# include "morkDeque.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +/*============================================================================= + * morkNext: linked list node for very simple, singly-linked list + */ + +morkNext::morkNext() : mNext_Link(0) {} + +/*static*/ void* morkNext::MakeNewNext(size_t inSize, nsIMdbHeap& ioHeap, + morkEnv* ev) { + void* next = 0; + ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void**)&next); + if (!next) ev->OutOfMemoryError(); + + return next; +} + +/*static*/ +void morkNext::ZapOldNext(morkEnv* ev, nsIMdbHeap* ioHeap) { + if (ioHeap) { + ioHeap->Free(ev->AsMdbEnv(), this); + } else + ev->NilPointerError(); +} + +/*============================================================================= + * morkList: simple, singly-linked list + */ + +morkList::morkList() : mList_Head(0), mList_Tail(0) {} + +void morkList::CutAndZapAllListMembers(morkEnv* ev, nsIMdbHeap* ioHeap) +// make empty list, zapping every member by calling ZapOldNext() +{ + if (ioHeap) { + morkNext* next = 0; + while ((next = this->PopHead()) != 0) next->ZapOldNext(ev, ioHeap); + + mList_Head = 0; + mList_Tail = 0; + } else + ev->NilPointerError(); +} + +void morkList::CutAllListMembers() +// just make list empty, dropping members without zapping +{ + while (this->PopHead()) + ; /* empty */ + + mList_Head = 0; + mList_Tail = 0; +} + +morkNext* morkList::PopHead() // cut head of list +{ + morkNext* outHead = mList_Head; + if (outHead) // anything to cut from list? + { + morkNext* next = outHead->mNext_Link; + mList_Head = next; + if (!next) // cut the last member, so tail no longer exists? + mList_Tail = 0; + + outHead->mNext_Link = 0; // nil outgoing node link; unnecessary, but tidy + } + return outHead; +} + +void morkList::PushHead(morkNext* ioLink) // add to head of list +{ + morkNext* head = mList_Head; // old head of list + morkNext* tail = mList_Tail; // old tail of list + + MORK_ASSERT((head && tail) || (!head && !tail)); + + ioLink->mNext_Link = head; // make old head follow the new link + if (!head) // list was previously empty? + mList_Tail = ioLink; // head is also tail for first member added + + mList_Head = ioLink; // head of list is the new link +} + +void morkList::PushTail(morkNext* ioLink) // add to tail of list +{ + morkNext* head = mList_Head; // old head of list + morkNext* tail = mList_Tail; // old tail of list + + MORK_ASSERT((head && tail) || (!head && !tail)); + + ioLink->mNext_Link = 0; + if (tail) { + tail->mNext_Link = ioLink; + mList_Tail = ioLink; + } else // list was previously empty? + mList_Head = mList_Tail = + ioLink; // tail is also head for first member added +} + +/*============================================================================= + * morkLink: linked list node embedded in objs to allow insertion in morkDeques + */ + +morkLink::morkLink() : mLink_Next(0), mLink_Prev(0) {} + +/*static*/ void* morkLink::MakeNewLink(size_t inSize, nsIMdbHeap& ioHeap, + morkEnv* ev) { + void* alink = 0; + ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void**)&alink); + if (!alink) ev->OutOfMemoryError(); + + return alink; +} + +/*static*/ +void morkLink::ZapOldLink(morkEnv* ev, nsIMdbHeap* ioHeap) { + if (ioHeap) { + ioHeap->Free(ev->AsMdbEnv(), this); + } else + ev->NilPointerError(); +} + +/*============================================================================= + * morkDeque: doubly linked list modeled after VAX queue instructions + */ + +morkDeque::morkDeque() { mDeque_Head.SelfRefer(); } + +/*| RemoveFirst: +|*/ +morkLink* morkDeque::RemoveFirst() /*i*/ +{ + morkLink* alink = mDeque_Head.mLink_Next; + if (alink != &mDeque_Head) { + (mDeque_Head.mLink_Next = alink->mLink_Next)->mLink_Prev = &mDeque_Head; + return alink; + } + return (morkLink*)0; +} + +/*| RemoveLast: +|*/ +morkLink* morkDeque::RemoveLast() /*i*/ +{ + morkLink* alink = mDeque_Head.mLink_Prev; + if (alink != &mDeque_Head) { + (mDeque_Head.mLink_Prev = alink->mLink_Prev)->mLink_Next = &mDeque_Head; + return alink; + } + return (morkLink*)0; +} + +/*| At: +|*/ +morkLink* morkDeque::At(mork_pos index) const /*i*/ +/* indexes are one based (and not zero based) */ +{ + mork_num count = 0; + morkLink* alink; + for (alink = this->First(); alink; alink = this->After(alink)) { + if (++count == (mork_num)index) break; + } + return alink; +} + +/*| IndexOf: +|*/ +mork_pos morkDeque::IndexOf(const morkLink* member) const /*i*/ +/* indexes are one based (and not zero based) */ +/* zero means member is not in deque */ +{ + mork_num count = 0; + const morkLink* alink; + for (alink = this->First(); alink; alink = this->After(alink)) { + ++count; + if (member == alink) return (mork_pos)count; + } + return 0; +} + +/*| Length: +|*/ +mork_num morkDeque::Length() const /*i*/ +{ + mork_num count = 0; + morkLink* alink; + for (alink = this->First(); alink; alink = this->After(alink)) ++count; + return count; +} + +/*| LengthCompare: +|*/ +int morkDeque::LengthCompare(mork_num c) const /*i*/ +{ + mork_num count = 0; + const morkLink* alink; + for (alink = this->First(); alink; alink = this->After(alink)) { + if (++count > c) return 1; + } + return (count == c) ? 0 : -1; +} diff --git a/comm/mailnews/db/mork/morkDeque.h b/comm/mailnews/db/mork/morkDeque.h new file mode 100644 index 0000000000..54e5080254 --- /dev/null +++ b/comm/mailnews/db/mork/morkDeque.h @@ -0,0 +1,244 @@ +/************************************************************************* +This software is part of a public domain IronDoc source code distribution, +and is provided on an "AS IS" basis, with all risks borne by the consumers +or users of the IronDoc software. There are no warranties, guarantees, or +promises about quality of any kind; and no remedies for failure exist. + +Permission is hereby granted to use this IronDoc software for any purpose +at all, without need for written agreements, without royalty or license +fees, and without fees or obligations of any other kind. Anyone can use, +copy, change and distribute this software for any purpose, and nothing is +required, implicitly or otherwise, in exchange for this usage. + +You cannot apply your own copyright to this software, but otherwise you +are encouraged to enjoy the use of this software in any way you see fit. +However, it would be rude to remove names of developers from the code. +(IronDoc is also known by the short name "Fe" and a longer name "Ferrum", +which are used interchangeably with the name IronDoc in the sources.) +*************************************************************************/ +/* + * File: morkDeque.h + * Contains: Ferrum deque (double ended queue (linked list)) + * + * Copied directly from public domain IronDoc, with minor naming tweaks: + * Designed and written by David McCusker, but all this code is public domain. + * There are no warranties, no guarantees, no promises, and no remedies. + */ + +#ifndef _MORKDEQUE_ +#define _MORKDEQUE_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +/*============================================================================= + * morkNext: linked list node for very simple, singly-linked list + */ + +class morkNext /*d*/ { + public: + morkNext* mNext_Link; + + public: + explicit morkNext(int inZero) : mNext_Link(0) {} + + explicit morkNext(morkNext* ioLink) : mNext_Link(ioLink) {} + + morkNext(); // mNext_Link( 0 ), { } + + public: + morkNext* GetNextLink() const { return mNext_Link; } + + public: // link memory management methods + static void* MakeNewNext(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev); + void ZapOldNext(morkEnv* ev, nsIMdbHeap* ioHeap); + + public: // link memory management operators + void* operator new(size_t inSize, nsIMdbHeap& ioHeap, + morkEnv* ev) noexcept(true) { + return morkNext::MakeNewNext(inSize, ioHeap, ev); + } + + void operator delete(void* ioAddress) // DO NOT CALL THIS, hope to crash: + { + ((morkNext*)0)->ZapOldNext((morkEnv*)0, (nsIMdbHeap*)0); + } // boom +}; + +/*============================================================================= + * morkList: simple, singly-linked list + */ + +/*| morkList: a list of singly-linked members (instances of morkNext), where +**| the number of list members might be so numerous that we must about cost +**| for two pointer link slots per member (as happens with morkLink). +**| +**|| morkList is intended to support lists of changes in morkTable, where we +**| are worried about the space cost of representing such changes. (Later we +**| can use an array instead, when we get even more worried, to avoid cost +**| of link slots at all, per member). +**| +**|| Do NOT create cycles in links using this list class, since we do not +**| deal with them very nicely. +|*/ +class morkList /*d*/ { + public: + morkNext* mList_Head; // first link in the list + morkNext* mList_Tail; // last link in the list + + public: + morkNext* GetListHead() const { return mList_Head; } + morkNext* GetListTail() const { return mList_Tail; } + + mork_bool IsListEmpty() const { return (mList_Head == 0); } + mork_bool HasListMembers() const { return (mList_Head != 0); } + + public: + morkList(); // : mList_Head( 0 ), mList_Tail( 0 ) { } + + void CutAndZapAllListMembers(morkEnv* ev, nsIMdbHeap* ioHeap); + // make empty list, zapping every member by calling ZapOldNext() + + void CutAllListMembers(); + // just make list empty, dropping members without zapping + + public: + morkNext* PopHead(); // cut head of list + + // Note we don't support PopTail(), so use morkDeque if you need that. + + void PushHead(morkNext* ioLink); // add to head of list + void PushTail(morkNext* ioLink); // add to tail of list +}; + +/*============================================================================= + * morkLink: linked list node embedded in objs to allow insertion in morkDeques + */ + +class morkLink /*d*/ { + public: + morkLink* mLink_Next; + morkLink* mLink_Prev; + + public: + explicit morkLink(int inZero) : mLink_Next(0), mLink_Prev(0) {} + + morkLink(); // mLink_Next( 0 ), mLink_Prev( 0 ) { } + + public: + morkLink* Next() const { return mLink_Next; } + morkLink* Prev() const { return mLink_Prev; } + + void SelfRefer() { mLink_Next = mLink_Prev = this; } + void Clear() { mLink_Next = mLink_Prev = 0; } + + void AddBefore(morkLink* old) { + ((old)->mLink_Prev->mLink_Next = (this))->mLink_Prev = (old)->mLink_Prev; + ((this)->mLink_Next = (old))->mLink_Prev = this; + } + + void AddAfter(morkLink* old) { + ((old)->mLink_Next->mLink_Prev = (this))->mLink_Next = (old)->mLink_Next; + ((this)->mLink_Prev = (old))->mLink_Next = this; + } + + void Remove() { + (mLink_Prev->mLink_Next = mLink_Next)->mLink_Prev = mLink_Prev; + } + + public: // link memory management methods + static void* MakeNewLink(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev); + void ZapOldLink(morkEnv* ev, nsIMdbHeap* ioHeap); + + public: // link memory management operators + void* operator new(size_t inSize, nsIMdbHeap& ioHeap, + morkEnv* ev) noexcept(true) { + return morkLink::MakeNewLink(inSize, ioHeap, ev); + } +}; + +/*============================================================================= + * morkDeque: doubly linked list modeled after VAX queue instructions + */ + +class morkDeque /*d*/ { + public: + morkLink mDeque_Head; + + public: // construction + morkDeque(); // { mDeque_Head.SelfRefer(); } + + public: // methods + morkLink* RemoveFirst(); + + morkLink* RemoveLast(); + + morkLink* At(mork_pos index) const; /* one-based, not zero-based */ + + mork_pos IndexOf(const morkLink* inMember) const; + /* one-based index ; zero means member is not in deque */ + + mork_num Length() const; + + /* the following method is more efficient for long lists: */ + int LengthCompare(mork_num inCount) const; + /* -1: length < count, 0: length == count, 1: length > count */ + + public: // inlines + mork_bool IsEmpty() const { + return (mDeque_Head.mLink_Next == (morkLink*)&mDeque_Head); + } + + morkLink* After(const morkLink* old) const { + return (((old)->mLink_Next != &mDeque_Head) ? (old)->mLink_Next + : (morkLink*)0); + } + + morkLink* Before(const morkLink* old) const { + return (((old)->mLink_Prev != &mDeque_Head) ? (old)->mLink_Prev + : (morkLink*)0); + } + + morkLink* First() const { + return ((mDeque_Head.mLink_Next != &mDeque_Head) ? mDeque_Head.mLink_Next + : (morkLink*)0); + } + + morkLink* Last() const { + return ((mDeque_Head.mLink_Prev != &mDeque_Head) ? mDeque_Head.mLink_Prev + : (morkLink*)0); + } + + /* + From IronDoc documentation for AddFirst: + +--------+ +--------+ +--------+ +--------+ +--------+ + | h.next |-->| b.next | | h.next |-->| a.next |-->| b.next | + +--------+ +--------+ ==> +--------+ +--------+ +--------+ + | h.prev |<--| b.prev | | h.prev |<--| a.prev |<--| b.prev | + +--------+ +--------+ +--------+ +--------+ +--------+ + */ + + void AddFirst(morkLink* in) /*i*/ + { + (mDeque_Head.mLink_Next->mLink_Prev = in)->mLink_Next = + mDeque_Head.mLink_Next; + (in->mLink_Prev = &mDeque_Head)->mLink_Next = in; + } + /* + From IronDoc documentation for AddLast: + +--------+ +--------+ +--------+ +--------+ +--------+ + | y.next |-->| h.next | | y.next |-->| z.next |-->| h.next | + +--------+ +--------+ ==> +--------+ +--------+ +--------+ + | y.prev |<--| h.prev | | y.prev |<--| z.prev |<--| h.prev | + +--------+ +--------+ +--------+ +--------+ +--------+ + */ + + void AddLast(morkLink* in) { + (mDeque_Head.mLink_Prev->mLink_Next = in)->mLink_Prev = + mDeque_Head.mLink_Prev; + (in->mLink_Next = &mDeque_Head)->mLink_Prev = in; + } +}; + +#endif /* _MORKDEQUE_ */ diff --git a/comm/mailnews/db/mork/morkEnv.cpp b/comm/mailnews/db/mork/morkEnv.cpp new file mode 100644 index 0000000000..c7c67f1e9e --- /dev/null +++ b/comm/mailnews/db/mork/morkEnv.cpp @@ -0,0 +1,519 @@ +/* -*- 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 _MORKCH_ +# include "morkCh.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKFACTORY_ +# include "morkFactory.h" +#endif + +#include "mozilla/Char16.h" + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkEnv::CloseMorkNode( + morkEnv* ev) /*i*/ // CloseEnv() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseEnv(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkEnv::~morkEnv() /*i*/ // assert CloseEnv() executed earlier +{ + CloseMorkNode(mMorkEnv); + if (mEnv_Heap) { + mork_bool ownsHeap = mEnv_OwnsHeap; + nsIMdbHeap* saveHeap = mEnv_Heap; + + if (ownsHeap) { +#ifdef MORK_DEBUG_HEAP_STATS + printf("%d blocks remaining \n", + ((orkinHeap*)saveHeap)->HeapBlockCount()); + mork_u4* array = (mork_u4*)this; + array -= 3; + // null out heap ptr in mem block so we won't crash trying to use it to + // delete the env. + *array = nullptr; +#endif // MORK_DEBUG_HEAP_STATS + // whoops, this is our heap - hmm. Can't delete it, or not allocate env's + // from an orkinHeap. + delete saveHeap; + } + } + // MORK_ASSERT(mEnv_SelfAsMdbEnv==0); + MORK_ASSERT(mEnv_ErrorHook == 0); +} + +/* choose morkBool_kTrue or morkBool_kFalse for kBeVerbose: */ +#define morkEnv_kBeVerbose morkBool_kFalse + +/*public non-poly*/ +morkEnv::morkEnv(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkFactory* ioFactory, nsIMdbHeap* ioSlotHeap) + : morkObject(inUsage, ioHeap, morkColor_kNone), + mEnv_Factory(ioFactory), + mEnv_Heap(ioSlotHeap) + + , + mEnv_SelfAsMdbEnv(0), + mEnv_ErrorHook(0), + mEnv_HandlePool(0) + + , + mEnv_ErrorCount(0), + mEnv_WarningCount(0) + + , + mEnv_ErrorCode(NS_OK) + + , + mEnv_DoTrace(morkBool_kFalse), + mEnv_AutoClear(morkAble_kDisabled), + mEnv_ShouldAbort(morkBool_kFalse), + mEnv_BeVerbose(morkEnv_kBeVerbose), + mEnv_OwnsHeap(morkBool_kFalse) { + MORK_ASSERT(ioSlotHeap && ioFactory); + if (ioSlotHeap) { + // mEnv_Heap is NOT refcounted: + // nsIMdbHeap_SlotStrongHeap(ioSlotHeap, this, &mEnv_Heap); + + mEnv_HandlePool = + new morkPool(morkUsage::kGlobal, (nsIMdbHeap*)0, ioSlotHeap); + + MORK_ASSERT(mEnv_HandlePool); + if (mEnv_HandlePool && this->Good()) { + mNode_Derived = morkDerived_kEnv; + mNode_Refs += morkEnv_kWeakRefCountEnvBonus; + } + } +} + +/*public non-poly*/ +morkEnv::morkEnv(morkEnv* ev, /*i*/ + const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbEnv* inSelfAsMdbEnv, morkFactory* ioFactory, + nsIMdbHeap* ioSlotHeap) + : morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*)0), + mEnv_Factory(ioFactory), + mEnv_Heap(ioSlotHeap) + + , + mEnv_SelfAsMdbEnv(inSelfAsMdbEnv), + mEnv_ErrorHook(0), + mEnv_HandlePool(0) + + , + mEnv_ErrorCount(0), + mEnv_WarningCount(0) + + , + mEnv_ErrorCode(NS_OK) + + , + mEnv_DoTrace(morkBool_kFalse), + mEnv_AutoClear(morkAble_kDisabled), + mEnv_ShouldAbort(morkBool_kFalse), + mEnv_BeVerbose(morkEnv_kBeVerbose), + mEnv_OwnsHeap(morkBool_kFalse) { + // $$$ do we need to refcount the inSelfAsMdbEnv nsIMdbEnv?? + + if (ioFactory && inSelfAsMdbEnv && ioSlotHeap) { + // mEnv_Heap is NOT refcounted: + // nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mEnv_Heap); + + mEnv_HandlePool = new (*ioSlotHeap, ev) + morkPool(ev, morkUsage::kHeap, ioSlotHeap, ioSlotHeap); + + MORK_ASSERT(mEnv_HandlePool); + if (mEnv_HandlePool && ev->Good()) { + mNode_Derived = morkDerived_kEnv; + mNode_Refs += morkEnv_kWeakRefCountEnvBonus; + } + } else + ev->NilPointerError(); +} + +NS_IMPL_ISUPPORTS_INHERITED(morkEnv, morkObject, nsIMdbEnv) +/*public non-poly*/ void morkEnv::CloseEnv( + morkEnv* ev) /*i*/ // called by CloseMorkNode(); +{ + if (this->IsNode()) { + // $$$ release mEnv_SelfAsMdbEnv?? + // $$$ release mEnv_ErrorHook?? + + mEnv_SelfAsMdbEnv = 0; + mEnv_ErrorHook = 0; + + morkPool* savePool = mEnv_HandlePool; + morkPool::SlotStrongPool((morkPool*)0, ev, &mEnv_HandlePool); + // free the pool + if (mEnv_SelfAsMdbEnv) { + if (savePool && mEnv_Heap) mEnv_Heap->Free(this->AsMdbEnv(), savePool); + } else { + if (savePool) { + if (savePool->IsOpenNode()) savePool->CloseMorkNode(ev); + delete savePool; + } + // how do we free this? might need to get rid of asserts. + } + // mEnv_Factory is NOT refcounted + + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +mork_size morkEnv::OidAsHex(void* outBuf, const mdbOid& inOid) +// sprintf(buf, "%lX:^%lX", (long) inOid.mOid_Id, (long) inOid.mOid_Scope); +{ + mork_u1* p = (mork_u1*)outBuf; + mork_size outSize = this->TokenAsHex(p, inOid.mOid_Id); + p += outSize; + *p++ = ':'; + + mork_scope scope = inOid.mOid_Scope; + if (scope < 0x80 && morkCh_IsName((mork_ch)scope)) { + *p++ = (mork_u1)scope; + *p = 0; // null termination + outSize += 2; + } else { + *p++ = '^'; + mork_size scopeSize = this->TokenAsHex(p, scope); + outSize += scopeSize + 2; + } + return outSize; +} + +mork_u1 morkEnv::HexToByte(mork_ch inFirstHex, mork_ch inSecondHex) { + mork_u1 hi = 0; // high four hex bits + mork_flags f = morkCh_GetFlags(inFirstHex); + if (morkFlags_IsDigit(f)) + hi = (mork_u1)(inFirstHex - (mork_ch)'0'); + else if (morkFlags_IsUpper(f)) + hi = (mork_u1)((inFirstHex - (mork_ch)'A') + 10); + else if (morkFlags_IsLower(f)) + hi = (mork_u1)((inFirstHex - (mork_ch)'a') + 10); + + mork_u1 lo = 0; // low four hex bits + f = morkCh_GetFlags(inSecondHex); + if (morkFlags_IsDigit(f)) + lo = (mork_u1)(inSecondHex - (mork_ch)'0'); + else if (morkFlags_IsUpper(f)) + lo = (mork_u1)((inSecondHex - (mork_ch)'A') + 10); + else if (morkFlags_IsLower(f)) + lo = (mork_u1)((inSecondHex - (mork_ch)'a') + 10); + + return (mork_u1)((hi << 4) | lo); +} + +// TokenAsHex() is the same as sprintf(outBuf, "%lX", (long) inToken); +// Writes up to 32 hex digits, plus a NUL-terminator. So outBuf must +// be at least 33 bytes. +// Return value is number of characters written, excluding the NUL. +mork_size morkEnv::TokenAsHex(void* outBuf, mork_token inToken) { + static const char morkEnv_kHexDigits[] = "0123456789ABCDEF"; + char* p = (char*)outBuf; + char* end = p + 32; // write no more than 32 digits for safety + if (inToken) { + // first write all the hex digits in backwards order: + while (p < end && inToken) // more digits to write? + { + *p++ = morkEnv_kHexDigits[inToken & 0x0F]; // low four bits + inToken >>= 4; // we fervently hope this does not sign extend + } + *p = 0; // end the string with a null byte + char* s = (char*)outBuf; // first byte in string + mork_size size = (mork_size)(p - s); // distance from start + + // now reverse the string in place: + // note that p starts on the null byte, so we need predecrement: + while (--p > s) // need to swap another byte in the string? + { + char c = *p; // temp for swap + *p = *s; + *s++ = c; // move s forward here, and p backward in the test + } + return size; + } else // special case for zero integer + { + *p++ = '0'; // write a zero digit + *p = 0; // end with a null byte + return 1; // one digit in hex representation + } +} + +void morkEnv::StringToYarn(const PathChar* inString, mdbYarn* outYarn) { + if (outYarn) { + mdb_fill fill = + (inString) ? (mdb_fill)MORK_STRLEN(inString) * sizeof(PathChar) : 0; + + if (fill) // have nonempty content? + { + mdb_size size = outYarn->mYarn_Size; // max dest size + if (fill > size) // too much string content? + { + outYarn->mYarn_More = fill - size; // extra string bytes omitted + fill = size; // copy no more bytes than size of yarn buffer + } + void* dest = outYarn->mYarn_Buf; // where bytes are going + if (!dest) // nil destination address buffer? + fill = 0; // we can't write any content at all + + if (fill) // anything to copy? + MORK_MEMCPY(dest, inString, fill); // copy fill bytes to yarn + + outYarn->mYarn_Fill = fill; // tell yarn size of copied content + } else // no content to put into the yarn + { + outYarn->mYarn_Fill = 0; // tell yarn that string has no bytes + } + outYarn->mYarn_Form = 0; // always update the form slot + } else + this->NilPointerError(); +} + +morkEnv::PathChar* morkEnv::CopyString(nsIMdbHeap* ioHeap, + const PathChar* inString) { + PathChar* outString = nullptr; + if (ioHeap && inString) { + mork_size size = (MORK_STRLEN(inString) + 1) * sizeof(PathChar); + ioHeap->Alloc(this->AsMdbEnv(), size, (void**)&outString); + if (outString) MORK_STRCPY(outString, inString); + } else + this->NilPointerError(); + return outString; +} + +void morkEnv::FreeString(nsIMdbHeap* ioHeap, PathChar* ioString) { + if (ioHeap) { + if (ioString) ioHeap->Free(this->AsMdbEnv(), ioString); + } else + this->NilPointerError(); +} + +void morkEnv::NewError(const char* inString) { + MORK_ASSERT(morkBool_kFalse); // get developer's attention + + ++mEnv_ErrorCount; + mEnv_ErrorCode = NS_ERROR_FAILURE; + + if (mEnv_ErrorHook) mEnv_ErrorHook->OnErrorString(this->AsMdbEnv(), inString); +} + +void morkEnv::NewWarning(const char* inString) { + MORK_ASSERT(morkBool_kFalse); // get developer's attention + + ++mEnv_WarningCount; + if (mEnv_ErrorHook) + mEnv_ErrorHook->OnWarningString(this->AsMdbEnv(), inString); +} + +void morkEnv::StubMethodOnlyError() { this->NewError("method is stub only"); } + +void morkEnv::OutOfMemoryError() { this->NewError("out of memory"); } + +void morkEnv::CantMakeWhenBadError() { + this->NewError("can't make an object when ev->Bad()"); +} + +static const char morkEnv_kNilPointer[] = "nil pointer"; + +void morkEnv::NilPointerError() { this->NewError(morkEnv_kNilPointer); } + +void morkEnv::NilPointerWarning() { this->NewWarning(morkEnv_kNilPointer); } + +void morkEnv::NewNonEnvError() { this->NewError("non-env instance"); } + +void morkEnv::NilEnvSlotError() { + if (!mEnv_HandlePool || !mEnv_Factory) { + if (!mEnv_HandlePool) this->NewError("nil mEnv_HandlePool"); + if (!mEnv_Factory) this->NewError("nil mEnv_Factory"); + } else + this->NewError("unknown nil env slot"); +} + +void morkEnv::NonEnvTypeError(morkEnv* ev) { ev->NewError("non morkEnv"); } + +void morkEnv::ClearMorkErrorsAndWarnings() { + mEnv_ErrorCount = 0; + mEnv_WarningCount = 0; + mEnv_ErrorCode = NS_OK; + mEnv_ShouldAbort = morkBool_kFalse; +} + +void morkEnv::AutoClearMorkErrorsAndWarnings() { + if (this->DoAutoClear()) { + mEnv_ErrorCount = 0; + mEnv_WarningCount = 0; + mEnv_ErrorCode = NS_OK; + mEnv_ShouldAbort = morkBool_kFalse; + } +} + +/*static*/ morkEnv* morkEnv::FromMdbEnv( + nsIMdbEnv* ioEnv) // dynamic type checking +{ + morkEnv* outEnv = 0; + if (ioEnv) { + // Note this cast is expected to perform some address adjustment of the + // pointer, so oenv likely does not equal ioEnv. Do not cast to void* + // first to force an exactly equal pointer (we tried it and it's wrong). + morkEnv* ev = (morkEnv*)ioEnv; + if (ev && ev->IsEnv()) { + if (ev->DoAutoClear()) { + ev->mEnv_ErrorCount = 0; + ev->mEnv_WarningCount = 0; + ev->mEnv_ErrorCode = NS_OK; + } + outEnv = ev; + } else + MORK_ASSERT(outEnv); + } else + MORK_ASSERT(outEnv); + return outEnv; +} + +NS_IMETHODIMP +morkEnv::GetErrorCount(mdb_count* outCount, mdb_bool* outShouldAbort) { + if (outCount) *outCount = mEnv_ErrorCount; + if (outShouldAbort) *outShouldAbort = mEnv_ShouldAbort; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::GetWarningCount(mdb_count* outCount, mdb_bool* outShouldAbort) { + if (outCount) *outCount = mEnv_WarningCount; + if (outShouldAbort) *outShouldAbort = mEnv_ShouldAbort; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::GetEnvBeVerbose(mdb_bool* outBeVerbose) { + NS_ENSURE_ARG_POINTER(outBeVerbose); + *outBeVerbose = mEnv_BeVerbose; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::SetEnvBeVerbose(mdb_bool inBeVerbose) { + mEnv_BeVerbose = inBeVerbose; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::GetDoTrace(mdb_bool* outDoTrace) { + NS_ENSURE_ARG_POINTER(outDoTrace); + *outDoTrace = mEnv_DoTrace; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::SetDoTrace(mdb_bool inDoTrace) { + mEnv_DoTrace = inDoTrace; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::GetAutoClear(mdb_bool* outAutoClear) { + NS_ENSURE_ARG_POINTER(outAutoClear); + *outAutoClear = DoAutoClear(); + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::SetAutoClear(mdb_bool inAutoClear) { + if (inAutoClear) + EnableAutoClear(); + else + DisableAutoClear(); + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::GetErrorHook(nsIMdbErrorHook** acqErrorHook) { + NS_ENSURE_ARG_POINTER(acqErrorHook); + *acqErrorHook = mEnv_ErrorHook; + NS_IF_ADDREF(mEnv_ErrorHook); + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::SetErrorHook(nsIMdbErrorHook* ioErrorHook) // becomes referenced +{ + mEnv_ErrorHook = ioErrorHook; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::GetHeap(nsIMdbHeap** acqHeap) { + NS_ENSURE_ARG_POINTER(acqHeap); + nsIMdbHeap* outHeap = mEnv_Heap; + + if (acqHeap) *acqHeap = outHeap; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::SetHeap(nsIMdbHeap* ioHeap) // becomes referenced +{ + nsIMdbHeap_SlotStrongHeap(ioHeap, this, &mEnv_Heap); + return NS_OK; +} +// } ----- end attribute methods ----- + +NS_IMETHODIMP +morkEnv::ClearErrors() // clear errors beore re-entering db API +{ + mEnv_ErrorCount = 0; + mEnv_ErrorCode = NS_OK; + mEnv_ShouldAbort = morkBool_kFalse; + + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::ClearWarnings() // clear warning +{ + mEnv_WarningCount = 0; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::ClearErrorsAndWarnings() // clear both errors & warnings +{ + ClearMorkErrorsAndWarnings(); + return NS_OK; +} +// } ===== end nsIMdbEnv methods ===== + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkEnv.h b/comm/mailnews/db/mork/morkEnv.h new file mode 100644 index 0000000000..e9b635051d --- /dev/null +++ b/comm/mailnews/db/mork/morkEnv.h @@ -0,0 +1,221 @@ +/* -*- 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 _MORKENV_ +#define _MORKENV_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKOBJECT_ +# include "morkObject.h" +#endif + +#ifndef _MORKPOOL_ +# include "morkPool.h" +#endif + +// sean was here +#include "mozilla/Path.h" +#include "nsError.h" + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kEnv /*i*/ 0x4576 /* ascii 'Ev' */ + +// use NS error codes to make Mork easier to use with the rest of mozilla +#define morkEnv_kNoError NS_SUCCEEDED /* no error has happened */ +#define morkEnv_kNonEnvTypeError \ + NS_ERROR_FAILURE /* morkEnv::IsEnv() is false */ + +#define morkEnv_kStubMethodOnlyError NS_ERROR_NO_INTERFACE +#define morkEnv_kOutOfMemoryError NS_ERROR_OUT_OF_MEMORY +#define morkEnv_kNilPointerError NS_ERROR_NULL_POINTER +#define morkEnv_kNewNonEnvError NS_ERROR_FAILURE +#define morkEnv_kNilEnvSlotError NS_ERROR_FAILURE + +#define morkEnv_kBadFactoryError NS_ERROR_FACTORY_NOT_LOADED +#define morkEnv_kBadFactoryEnvError NS_ERROR_FACTORY_NOT_LOADED +#define morkEnv_kBadEnvError NS_ERROR_FAILURE + +#define morkEnv_kNonHandleTypeError NS_ERROR_FAILURE +#define morkEnv_kNonOpenNodeError NS_ERROR_FAILURE + +/* try NOT to leak all env instances */ +#define morkEnv_kWeakRefCountEnvBonus 0 + +/*| morkEnv: +|*/ +class morkEnv : public morkObject, public nsIMdbEnv { + using PathChar = mozilla::filesystem::Path::value_type; + NS_DECL_ISUPPORTS_INHERITED + + // public: // slots inherited from morkObject (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // mork_color mBead_Color; // ID for this bead + // morkHandle* mObject_Handle; // weak ref to handle for this object + + public: // state is public because the entire Mork system is private + morkFactory* mEnv_Factory; // NON-refcounted factory + nsIMdbHeap* mEnv_Heap; // NON-refcounted heap + + nsIMdbEnv* mEnv_SelfAsMdbEnv; + nsIMdbErrorHook* mEnv_ErrorHook; + + morkPool* mEnv_HandlePool; // pool for re-using handles + + mork_u2 mEnv_ErrorCount; + mork_u2 mEnv_WarningCount; + + nsresult mEnv_ErrorCode; + + mork_bool mEnv_DoTrace; + mork_able mEnv_AutoClear; + mork_bool mEnv_ShouldAbort; + mork_bool mEnv_BeVerbose; + mork_bool mEnv_OwnsHeap; + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseEnv() only if open + virtual ~morkEnv(); // assert that CloseEnv() executed earlier + + // { ----- begin attribute methods ----- + NS_IMETHOD GetErrorCount(mdb_count* outCount, + mdb_bool* outShouldAbort) override; + NS_IMETHOD GetWarningCount(mdb_count* outCount, + mdb_bool* outShouldAbort) override; + + NS_IMETHOD GetEnvBeVerbose(mdb_bool* outBeVerbose) override; + NS_IMETHOD SetEnvBeVerbose(mdb_bool inBeVerbose) override; + + NS_IMETHOD GetDoTrace(mdb_bool* outDoTrace) override; + NS_IMETHOD SetDoTrace(mdb_bool inDoTrace) override; + + NS_IMETHOD GetAutoClear(mdb_bool* outAutoClear) override; + NS_IMETHOD SetAutoClear(mdb_bool inAutoClear) override; + + NS_IMETHOD GetErrorHook(nsIMdbErrorHook** acqErrorHook) override; + NS_IMETHOD SetErrorHook( + nsIMdbErrorHook* ioErrorHook) override; // becomes referenced + + NS_IMETHOD GetHeap(nsIMdbHeap** acqHeap) override; + NS_IMETHOD SetHeap(nsIMdbHeap* ioHeap) override; // becomes referenced + // } ----- end attribute methods ----- + + NS_IMETHOD ClearErrors() override; // clear errors beore re-entering db API + NS_IMETHOD ClearWarnings() override; // clear warnings + NS_IMETHOD ClearErrorsAndWarnings() override; // clear both errors & warnings + // } ===== end nsIMdbEnv methods ===== + public: // morkEnv construction & destruction + morkEnv(const morkUsage& inUsage, nsIMdbHeap* ioHeap, morkFactory* ioFactory, + nsIMdbHeap* ioSlotHeap); + morkEnv(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbEnv* inSelfAsMdbEnv, morkFactory* ioFactory, + nsIMdbHeap* ioSlotHeap); + void CloseEnv(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkEnv(const morkEnv& other); + morkEnv& operator=(const morkEnv& other); + + public: // dynamic type identification + mork_bool IsEnv() const { + return IsNode() && mNode_Derived == morkDerived_kEnv; + } + // } ===== end morkNode methods ===== + + public: // utility env methods + mork_u1 HexToByte(mork_ch inFirstHex, mork_ch inSecondHex); + + mork_size TokenAsHex(void* outBuf, mork_token inToken); + // TokenAsHex() is the same as sprintf(outBuf, "%lX", (long) inToken); + + mork_size OidAsHex(void* outBuf, const mdbOid& inOid); + // sprintf(buf, "%lX:^%lX", (long) inOid.mOid_Id, (long) inOid.mOid_Scope); + + PathChar* CopyString(nsIMdbHeap* ioHeap, const PathChar* inString); + void FreeString(nsIMdbHeap* ioHeap, PathChar* ioString); + void StringToYarn(const PathChar* inString, mdbYarn* outYarn); + + public: // other env methods + morkHandleFace* NewHandle(mork_size inSize) { + return mEnv_HandlePool->NewHandle(this, inSize, (morkZone*)0); + } + + void ZapHandle(morkHandleFace* ioHandle) { + mEnv_HandlePool->ZapHandle(this, ioHandle); + } + + void EnableAutoClear() { mEnv_AutoClear = morkAble_kEnabled; } + void DisableAutoClear() { mEnv_AutoClear = morkAble_kDisabled; } + + mork_bool DoAutoClear() const { return mEnv_AutoClear == morkAble_kEnabled; } + + void NewError(const char* inString); + void NewWarning(const char* inString); + + void ClearMorkErrorsAndWarnings(); // clear both errors & warnings + void AutoClearMorkErrorsAndWarnings(); // clear if auto is enabled + + void StubMethodOnlyError(); + void OutOfMemoryError(); + void NilPointerError(); + void NilPointerWarning(); + void CantMakeWhenBadError(); + void NewNonEnvError(); + void NilEnvSlotError(); + + void NonEnvTypeError(morkEnv* ev); + + // canonical env convenience methods to check for presence of errors: + mork_bool Good() const { return (mEnv_ErrorCount == 0); } + mork_bool Bad() const { return (mEnv_ErrorCount != 0); } + + nsIMdbEnv* AsMdbEnv() { return (nsIMdbEnv*)this; } + static morkEnv* FromMdbEnv(nsIMdbEnv* ioEnv); // dynamic type checking + + nsresult AsErr() const { return mEnv_ErrorCode; } + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakEnv(morkEnv* me, morkEnv* ev, morkEnv** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongEnv(morkEnv* me, morkEnv* ev, morkEnv** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +#undef MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING +#ifdef MOZ_IS_DESTRUCTIBLE +# define MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(X) \ + static_assert( \ + !MOZ_IS_DESTRUCTIBLE(X) || mozilla::IsSame<X, morkEnv>::value, \ + "Reference-counted class " #X \ + " should not have a public destructor. " \ + "Try to make this class's destructor non-public. If that is really " \ + "not possible, you can whitelist this class by providing a " \ + "HasDangerousPublicDestructor specialization for it."); +#else +# define MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(X) +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKENV_ */ diff --git a/comm/mailnews/db/mork/morkFactory.cpp b/comm/mailnews/db/mork/morkFactory.cpp new file mode 100644 index 0000000000..09a76ba86a --- /dev/null +++ b/comm/mailnews/db/mork/morkFactory.cpp @@ -0,0 +1,521 @@ +/* -*- 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 _MORKOBJECT_ +# include "morkObject.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKFACTORY_ +# include "morkFactory.h" +#endif + +#ifndef _ORKINHEAP_ +# include "orkinHeap.h" +#endif + +#ifndef _MORKFILE_ +# include "morkFile.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +#ifndef _MORKTHUMB_ +# include "morkThumb.h" +#endif + +#ifndef _MORKWRITER_ +# include "morkWriter.h" +#endif +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkFactory::CloseMorkNode( + morkEnv* ev) /*i*/ // CloseFactory() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseFactory(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkFactory::~morkFactory() /*i*/ // assert CloseFactory() executed earlier +{ + CloseFactory(&mFactory_Env); + MORK_ASSERT(mFactory_Env.IsShutNode()); + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkFactory::morkFactory() // uses orkinHeap + : morkObject(morkUsage::kGlobal, (nsIMdbHeap*)0, morkColor_kNone), + mFactory_Env(morkUsage::kMember, (nsIMdbHeap*)0, this, new orkinHeap()), + mFactory_Heap() { + if (mFactory_Env.Good()) { + mNode_Derived = morkDerived_kFactory; + mNode_Refs += morkFactory_kWeakRefCountBonus; + } +} + +/*public non-poly*/ +morkFactory::morkFactory(nsIMdbHeap* ioHeap) + : morkObject(morkUsage::kHeap, ioHeap, morkColor_kNone), + mFactory_Env(morkUsage::kMember, (nsIMdbHeap*)0, this, ioHeap), + mFactory_Heap() { + if (mFactory_Env.Good()) { + mNode_Derived = morkDerived_kFactory; + mNode_Refs += morkFactory_kWeakRefCountBonus; + } +} + +/*public non-poly*/ +morkFactory::morkFactory(morkEnv* ev, /*i*/ + const morkUsage& inUsage, nsIMdbHeap* ioHeap) + : morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*)0), + mFactory_Env(morkUsage::kMember, (nsIMdbHeap*)0, this, ioHeap), + mFactory_Heap() { + if (ev->Good()) { + mNode_Derived = morkDerived_kFactory; + mNode_Refs += morkFactory_kWeakRefCountBonus; + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkFactory, morkObject, nsIMdbFactory) + +extern "C" nsIMdbFactory* MakeMdbFactory() { + return new morkFactory(new orkinHeap()); +} + +/*public non-poly*/ void morkFactory::CloseFactory( + morkEnv* ev) /*i*/ // called by CloseMorkNode(); +{ + if (this->IsNode()) { + mFactory_Env.CloseMorkNode(ev); + this->CloseObject(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +morkEnv* morkFactory::GetInternalFactoryEnv(nsresult* outErr) { + morkEnv* outEnv = 0; + if (IsNode() && IsOpenNode() && IsFactory()) { + morkEnv* fenv = &mFactory_Env; + if (fenv && fenv->IsNode() && fenv->IsOpenNode() && fenv->IsEnv()) { + fenv->ClearMorkErrorsAndWarnings(); // drop any earlier errors + outEnv = fenv; + } else + *outErr = morkEnv_kBadFactoryEnvError; + } else + *outErr = morkEnv_kBadFactoryError; + + return outEnv; +} + +void morkFactory::NonFactoryTypeError(morkEnv* ev) { + ev->NewError("non morkFactory"); +} + +NS_IMETHODIMP +morkFactory::OpenOldFile(nsIMdbEnv* mev, nsIMdbHeap* ioHeap, + const PathChar* inFilePath, mork_bool inFrozen, + nsIMdbFile** acqFile) +// Choose some subclass of nsIMdbFile to instantiate, in order to read +// (and write if not frozen) the file known by inFilePath. The file +// returned should be open and ready for use, and presumably positioned +// at the first byte position of the file. The exact manner in which +// files must be opened is considered a subclass specific detail, and +// other portions or Mork source code don't want to know how it's done. +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + morkFile* file = nullptr; + if (ev) { + if (!ioHeap) ioHeap = &mFactory_Heap; + + file = morkFile::OpenOldFile(ev, ioHeap, inFilePath, inFrozen); + NS_IF_ADDREF(file); + + outErr = ev->AsErr(); + } + if (acqFile) *acqFile = file; + + return outErr; +} + +NS_IMETHODIMP +morkFactory::CreateNewFile(nsIMdbEnv* mev, nsIMdbHeap* ioHeap, + const PathChar* inFilePath, nsIMdbFile** acqFile) +// Choose some subclass of nsIMdbFile to instantiate, in order to read +// (and write if not frozen) the file known by inFilePath. The file +// returned should be created and ready for use, and presumably positioned +// at the first byte position of the file. The exact manner in which +// files must be opened is considered a subclass specific detail, and +// other portions or Mork source code don't want to know how it's done. +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + morkFile* file = nullptr; + if (ev) { + if (!ioHeap) ioHeap = &mFactory_Heap; + + file = morkFile::CreateNewFile(ev, ioHeap, inFilePath); + if (file) NS_ADDREF(file); + + outErr = ev->AsErr(); + } + if (acqFile) *acqFile = file; + + return outErr; +} +// } ----- end file methods ----- + +// { ----- begin env methods ----- +NS_IMETHODIMP +morkFactory::MakeEnv(nsIMdbHeap* ioHeap, nsIMdbEnv** acqEnv) +// ioHeap can be nil, causing a MakeHeap() style heap instance to be used +{ + nsresult outErr = NS_OK; + nsIMdbEnv* outEnv = 0; + mork_bool ownsHeap = (ioHeap == 0); + if (!ioHeap) ioHeap = new orkinHeap(); + + if (acqEnv && ioHeap) { + morkEnv* fenv = this->GetInternalFactoryEnv(&outErr); + if (fenv) { + morkEnv* newEnv = + new (*ioHeap, fenv) morkEnv(morkUsage::kHeap, ioHeap, this, ioHeap); + + if (newEnv) { + newEnv->mEnv_OwnsHeap = ownsHeap; + newEnv->mNode_Refs += morkEnv_kWeakRefCountEnvBonus; + NS_ADDREF(newEnv); + newEnv->mEnv_SelfAsMdbEnv = newEnv; + outEnv = newEnv; + } else + outErr = morkEnv_kOutOfMemoryError; + } + + *acqEnv = outEnv; + } else + outErr = morkEnv_kNilPointerError; + + return outErr; +} +// } ----- end env methods ----- + +// { ----- begin heap methods ----- +NS_IMETHODIMP +morkFactory::MakeHeap(nsIMdbEnv* mev, nsIMdbHeap** acqHeap) { + nsresult outErr = NS_OK; + nsIMdbHeap* outHeap = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + outHeap = new orkinHeap(); + if (!outHeap) ev->OutOfMemoryError(); + } + MORK_ASSERT(acqHeap); + if (acqHeap) *acqHeap = outHeap; + return outErr; +} +// } ----- end heap methods ----- + +// { ----- begin row methods ----- +NS_IMETHODIMP +morkFactory::MakeRow(nsIMdbEnv* mev, nsIMdbHeap* ioHeap, nsIMdbRow** acqRow) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} +// ioHeap can be nil, causing the heap associated with ev to be used +// } ----- end row methods ----- + +// { ----- begin port methods ----- +NS_IMETHODIMP +morkFactory::CanOpenFilePort( + nsIMdbEnv* mev, // context + // const char* inFilePath, // the file to investigate + // const mdbYarn* inFirst512Bytes, + nsIMdbFile* ioFile, // db abstract file interface + mdb_bool* outCanOpen, // whether OpenFilePort() might succeed + mdbYarn* outFormatVersion) { + nsresult outErr = NS_OK; + if (outFormatVersion) { + outFormatVersion->mYarn_Fill = 0; + } + mdb_bool canOpenAsPort = morkBool_kFalse; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (ioFile && outCanOpen) { + canOpenAsPort = this->CanOpenMorkTextFile(ev, ioFile); + } else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + + if (outCanOpen) *outCanOpen = canOpenAsPort; + + return outErr; +} + +NS_IMETHODIMP +morkFactory::OpenFilePort( + nsIMdbEnv* mev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // the file to open for readonly import + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbThumb** acqThumb) { + NS_ASSERTION(false, "this doesn't look implemented"); + MORK_USED_1(ioHeap); + nsresult outErr = NS_OK; + nsIMdbThumb* outThumb = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (ioFile && inOpenPolicy && acqThumb) { + } else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if (acqThumb) *acqThumb = outThumb; + return outErr; +} +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then call nsIMdbFactory::ThumbToOpenPort() to get the port instance. + +NS_IMETHODIMP +morkFactory::ThumbToOpenPort( // redeeming a completed thumb from + // OpenFilePort() + nsIMdbEnv* mev, // context + nsIMdbThumb* ioThumb, // thumb from OpenFilePort() with done status + nsIMdbPort** acqPort) { + nsresult outErr = NS_OK; + nsIMdbPort* outPort = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (ioThumb && acqPort) { + morkThumb* thumb = (morkThumb*)ioThumb; + morkStore* store = thumb->ThumbToOpenStore(ev); + if (store) { + store->mStore_CanAutoAssignAtomIdentity = morkBool_kTrue; + store->mStore_CanDirty = morkBool_kTrue; + store->SetStoreAndAllSpacesCanDirty(ev, morkBool_kTrue); + + NS_ADDREF(store); + outPort = store; + } + } else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if (acqPort) *acqPort = outPort; + return outErr; +} +// } ----- end port methods ----- + +mork_bool morkFactory::CanOpenMorkTextFile(morkEnv* ev, + // const mdbYarn* inFirst512Bytes, + nsIMdbFile* ioFile) { + MORK_USED_1(ev); + mork_bool outBool = morkBool_kFalse; + mork_size headSize = strlen(morkWriter_kFileHeader); + + char localBuf[256 + 4]; // for extra for sloppy safety + mdbYarn localYarn; + mdbYarn* y = &localYarn; + y->mYarn_Buf = localBuf; // space to hold content + y->mYarn_Fill = 0; // no logical content yet + y->mYarn_Size = 256; // physical capacity is 256 bytes + y->mYarn_More = 0; + y->mYarn_Form = 0; + y->mYarn_Grow = 0; + + if (ioFile) { + nsIMdbEnv* menv = ev->AsMdbEnv(); + mdb_size actualSize = 0; + ioFile->Get(menv, y->mYarn_Buf, y->mYarn_Size, /*pos*/ 0, &actualSize); + y->mYarn_Fill = actualSize; + + if (y->mYarn_Buf && actualSize >= headSize && ev->Good()) { + mork_u1* buf = (mork_u1*)y->mYarn_Buf; + outBool = (MORK_MEMCMP(morkWriter_kFileHeader, buf, headSize) == 0); + } + } else + ev->NilPointerError(); + + return outBool; +} + +// { ----- begin store methods ----- +NS_IMETHODIMP +morkFactory::CanOpenFileStore( + nsIMdbEnv* mev, // context + // const char* inFilePath, // the file to investigate + // const mdbYarn* inFirst512Bytes, + nsIMdbFile* ioFile, // db abstract file interface + mdb_bool* outCanOpenAsStore, // whether OpenFileStore() might succeed + mdb_bool* outCanOpenAsPort, // whether OpenFilePort() might succeed + mdbYarn* outFormatVersion) { + mdb_bool canOpenAsStore = morkBool_kFalse; + mdb_bool canOpenAsPort = morkBool_kFalse; + if (outFormatVersion) { + outFormatVersion->mYarn_Fill = 0; + } + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (ioFile && outCanOpenAsStore) { + // right now always say true; later we should look for magic patterns + canOpenAsStore = this->CanOpenMorkTextFile(ev, ioFile); + canOpenAsPort = canOpenAsStore; + } else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if (outCanOpenAsStore) *outCanOpenAsStore = canOpenAsStore; + + if (outCanOpenAsPort) *outCanOpenAsPort = canOpenAsPort; + + return outErr; +} + +NS_IMETHODIMP +morkFactory::OpenFileStore( // open an existing database + nsIMdbEnv* mev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // the file to open for general db usage + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbThumb** acqThumb) { + nsresult outErr = NS_OK; + nsIMdbThumb* outThumb = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (!ioHeap) // need to use heap from env? + ioHeap = ev->mEnv_Heap; + + if (ioFile && inOpenPolicy && acqThumb) { + morkStore* store = new (*ioHeap, ev) + morkStore(ev, morkUsage::kHeap, ioHeap, this, ioHeap); + + if (store) { + mork_bool frozen = morkBool_kFalse; // open store mutable access + if (store->OpenStoreFile(ev, frozen, ioFile, inOpenPolicy)) { + morkThumb* thumb = morkThumb::Make_OpenFileStore(ev, ioHeap, store); + if (thumb) { + outThumb = thumb; + thumb->AddRef(); + } + } + // store->CutStrongRef(mev); // always cut ref (handle has its + // own ref) + } + } else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if (acqThumb) *acqThumb = outThumb; + return outErr; +} +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then call nsIMdbFactory::ThumbToOpenStore() to get the store instance. + +NS_IMETHODIMP +morkFactory::ThumbToOpenStore( // redeem completed thumb from OpenFileStore() + nsIMdbEnv* mev, // context + nsIMdbThumb* ioThumb, // thumb from OpenFileStore() with done status + nsIMdbStore** acqStore) { + nsresult outErr = NS_OK; + nsIMdbStore* outStore = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (ioThumb && acqStore) { + morkThumb* thumb = (morkThumb*)ioThumb; + morkStore* store = thumb->ThumbToOpenStore(ev); + if (store) { + store->mStore_CanAutoAssignAtomIdentity = morkBool_kTrue; + store->mStore_CanDirty = morkBool_kTrue; + store->SetStoreAndAllSpacesCanDirty(ev, morkBool_kTrue); + + outStore = store; + NS_ADDREF(store); + } + } else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if (acqStore) *acqStore = outStore; + return outErr; +} + +NS_IMETHODIMP +morkFactory::CreateNewFileStore( // create a new db with minimal content + nsIMdbEnv* mev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // name of file which should not yet exist + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbStore** acqStore) { + nsresult outErr = NS_OK; + nsIMdbStore* outStore = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (!ioHeap) // need to use heap from env? + ioHeap = ev->mEnv_Heap; + + if (ioFile && inOpenPolicy && acqStore && ioHeap) { + morkStore* store = new (*ioHeap, ev) + morkStore(ev, morkUsage::kHeap, ioHeap, this, ioHeap); + + if (store) { + store->mStore_CanAutoAssignAtomIdentity = morkBool_kTrue; + store->mStore_CanDirty = morkBool_kTrue; + store->SetStoreAndAllSpacesCanDirty(ev, morkBool_kTrue); + + if (store->CreateStoreFile(ev, ioFile, inOpenPolicy)) outStore = store; + NS_ADDREF(store); + } + } else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if (acqStore) *acqStore = outStore; + return outErr; +} +// } ----- end store methods ----- + +// } ===== end nsIMdbFactory methods ===== + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkFactory.h b/comm/mailnews/db/mork/morkFactory.h new file mode 100644 index 0000000000..c04d478edf --- /dev/null +++ b/comm/mailnews/db/mork/morkFactory.h @@ -0,0 +1,214 @@ +/* -*- 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 _MORKFACTORY_ +#define _MORKFACTORY_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKOBJECT_ +# include "morkObject.h" +#endif + +#ifndef _ORKINHEAP_ +# include "orkinHeap.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class nsIMdbFactory; + +#define morkDerived_kFactory /*i*/ 0x4663 /* ascii 'Fc' */ +#define morkFactory_kWeakRefCountBonus 0 /* try NOT to leak all factories */ + +/*| morkFactory: +|*/ +class morkFactory : public morkObject, public nsIMdbFactory { // nsIMdbObject + using PathChar = mozilla::filesystem::Path::value_type; + + // public: // slots inherited from morkObject (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 + + // mork_color mBead_Color; // ID for this bead + // morkHandle* mObject_Handle; // weak ref to handle for this object + + public: // state is public because the entire Mork system is private + morkEnv mFactory_Env; // private env instance used internally + orkinHeap mFactory_Heap; + + NS_DECL_ISUPPORTS_INHERITED + // { ===== begin morkNode interface ===== + public: // morkFactory virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseFactory() only if open + + // { ===== begin nsIMdbFactory methods ===== + + // { ----- begin file methods ----- + NS_IMETHOD OpenOldFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + const PathChar* inFilePath, mdb_bool inFrozen, + nsIMdbFile** acqFile) override; + // Choose some subclass of nsIMdbFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be open and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. + + NS_IMETHOD CreateNewFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + const PathChar* inFilePath, + nsIMdbFile** acqFile) override; + // Choose some subclass of nsIMdbFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be created and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. + // } ----- end file methods ----- + + // { ----- begin env methods ----- + NS_IMETHOD MakeEnv(nsIMdbHeap* ioHeap, + nsIMdbEnv** acqEnv) override; // new env + // ioHeap can be nil, causing a MakeHeap() style heap instance to be used + // } ----- end env methods ----- + + // { ----- begin heap methods ----- + NS_IMETHOD MakeHeap(nsIMdbEnv* ev, + nsIMdbHeap** acqHeap) override; // new heap + // } ----- end heap methods ----- + + // { ----- begin row methods ----- + NS_IMETHOD MakeRow(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + nsIMdbRow** acqRow) override; // new row + // ioHeap can be nil, causing the heap associated with ev to be used + // } ----- end row methods ----- + + // { ----- begin port methods ----- + NS_IMETHOD CanOpenFilePort( + nsIMdbEnv* ev, // context + // const char* inFilePath, // the file to investigate + // const mdbYarn* inFirst512Bytes, + nsIMdbFile* ioFile, // db abstract file interface + mdb_bool* outCanOpen, // whether OpenFilePort() might succeed + mdbYarn* outFormatVersion) override; // informal file format description + + NS_IMETHOD OpenFilePort( + nsIMdbEnv* ev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // the file to open for readonly import + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbThumb** acqThumb) + override; // acquire thumb for incremental port open + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then call nsIMdbFactory::ThumbToOpenPort() to get the port instance. + + NS_IMETHOD + ThumbToOpenPort( // redeeming a completed thumb from OpenFilePort() + nsIMdbEnv* ev, // context + nsIMdbThumb* ioThumb, // thumb from OpenFilePort() with done status + nsIMdbPort** acqPort) override; // acquire new port object + // } ----- end port methods ----- + + // { ----- begin store methods ----- + NS_IMETHOD CanOpenFileStore( + nsIMdbEnv* ev, // context + // const char* inFilePath, // the file to investigate + // const mdbYarn* inFirst512Bytes, + nsIMdbFile* ioFile, // db abstract file interface + mdb_bool* outCanOpenAsStore, // whether OpenFileStore() might succeed + mdb_bool* outCanOpenAsPort, // whether OpenFilePort() might succeed + mdbYarn* outFormatVersion) override; // informal file format description + + NS_IMETHOD OpenFileStore( // open an existing database + nsIMdbEnv* ev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // the file to open for general db usage + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbThumb** acqThumb) + override; // acquire thumb for incremental store open + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then call nsIMdbFactory::ThumbToOpenStore() to get the store instance. + + NS_IMETHOD + ThumbToOpenStore( // redeem completed thumb from OpenFileStore() + nsIMdbEnv* ev, // context + nsIMdbThumb* ioThumb, // thumb from OpenFileStore() with done status + nsIMdbStore** acqStore) override; // acquire new db store object + + NS_IMETHOD CreateNewFileStore( // create a new db with minimal content + nsIMdbEnv* ev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // name of file which should not yet exist + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbStore** acqStore) override; // acquire new db store object + + // } ----- end store methods ----- + + // } ===== end nsIMdbFactory methods ===== + + public: // morkYarn construction & destruction + morkFactory(); // uses orkinHeap + explicit morkFactory(nsIMdbHeap* ioHeap); // caller supplied heap + morkFactory(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap); + void CloseFactory(morkEnv* ev); // called by CloseMorkNode(); + + public: // morkNode memory management operators + void* operator new(size_t inSize) noexcept(true) { + return ::operator new(inSize); + } + + void* operator new(size_t inSize, nsIMdbHeap& ioHeap, + morkEnv* ev) noexcept(true) { + return morkNode::MakeNew(inSize, ioHeap, ev); + } + + private: // copying is not allowed + morkFactory(const morkFactory& other); + morkFactory& operator=(const morkFactory& other); + virtual ~morkFactory(); // assert that CloseFactory() executed earlier + + public: // dynamic type identification + mork_bool IsFactory() const { + return IsNode() && mNode_Derived == morkDerived_kFactory; + } + // } ===== end morkNode methods ===== + + public: // other factory methods + void NonFactoryTypeError(morkEnv* ev); + morkEnv* GetInternalFactoryEnv(nsresult* outErr); + mork_bool CanOpenMorkTextFile(morkEnv* ev, nsIMdbFile* ioFile); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakFactory(morkFactory* me, morkEnv* ev, + morkFactory** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongFactory(morkFactory* me, morkEnv* ev, + morkFactory** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKFACTORY_ */ diff --git a/comm/mailnews/db/mork/morkFile.cpp b/comm/mailnews/db/mork/morkFile.cpp new file mode 100644 index 0000000000..b7b7848cc2 --- /dev/null +++ b/comm/mailnews/db/mork/morkFile.cpp @@ -0,0 +1,738 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKFILE_ +# include "morkFile.h" +#endif + +#ifdef MORK_WIN +# include "io.h" +# include <windows.h> +#endif + +#include "mozilla/Unused.h" +#include "nsString.h" + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkFile::CloseMorkNode( + morkEnv* ev) // CloseFile() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseFile(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkFile::~morkFile() // assert CloseFile() executed earlier +{ + MORK_ASSERT(mFile_Frozen == 0); + MORK_ASSERT(mFile_DoTrace == 0); + MORK_ASSERT(mFile_IoOpen == 0); + MORK_ASSERT(mFile_Active == 0); +} + +/*public non-poly*/ +morkFile::morkFile(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap) + : morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*)0), + mFile_Frozen(0), + mFile_DoTrace(0), + mFile_IoOpen(0), + mFile_Active(0) + + , + mFile_SlotHeap(0), + mFile_Name(0), + mFile_Thief(0) { + if (ev->Good()) { + if (ioSlotHeap) { + nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mFile_SlotHeap); + if (ev->Good()) mNode_Derived = morkDerived_kFile; + } else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkFile, morkObject, nsIMdbFile) +/*public non-poly*/ void morkFile::CloseFile( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + mFile_Frozen = 0; + mFile_DoTrace = 0; + mFile_IoOpen = 0; + mFile_Active = 0; + + if (mFile_Name) this->SetFileName(ev, nullptr); + + nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*)0, ev, &mFile_SlotHeap); + nsIMdbFile_SlotStrongFile((nsIMdbFile*)0, ev, &mFile_Thief); + + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ morkFile* morkFile::OpenOldFile(morkEnv* ev, nsIMdbHeap* ioHeap, + const PathChar* inFilePath, + mork_bool inFrozen) +// Choose some subclass of morkFile to instantiate, in order to read +// (and write if not frozen) the file known by inFilePath. The file +// returned should be open and ready for use, and presumably positioned +// at the first byte position of the file. The exact manner in which +// files must be opened is considered a subclass specific detail, and +// other portions or Mork source code don't want to know how it's done. +{ + return morkStdioFile::OpenOldStdioFile(ev, ioHeap, inFilePath, inFrozen); +} + +/*static*/ morkFile* morkFile::CreateNewFile(morkEnv* ev, nsIMdbHeap* ioHeap, + const PathChar* inFilePath) +// Choose some subclass of morkFile to instantiate, in order to read +// (and write if not frozen) the file known by inFilePath. The file +// returned should be created and ready for use, and presumably positioned +// at the first byte position of the file. The exact manner in which +// files must be opened is considered a subclass specific detail, and +// other portions or Mork source code don't want to know how it's done. +{ + return morkStdioFile::CreateNewStdioFile(ev, ioHeap, inFilePath); +} + +void morkFile::NewMissingIoError(morkEnv* ev) const { + ev->NewError("file missing io"); +} + +/*static*/ void morkFile::NonFileTypeError(morkEnv* ev) { + ev->NewError("non morkFile"); +} + +/*static*/ void morkFile::NilSlotHeapError(morkEnv* ev) { + ev->NewError("nil mFile_SlotHeap"); +} + +/*static*/ void morkFile::NilFileNameError(morkEnv* ev) { + ev->NewError("nil mFile_Name"); +} + +void morkFile::SetThief(morkEnv* ev, nsIMdbFile* ioThief) { + nsIMdbFile_SlotStrongFile(ioThief, ev, &mFile_Thief); +} + +void morkFile::SetFileName(morkEnv* ev, + const PathChar* inName) // inName can be nil +{ + nsIMdbHeap* heap = mFile_SlotHeap; + if (heap) { + PathChar* name = mFile_Name; + if (name) { + mFile_Name = 0; + ev->FreeString(heap, name); + } + if (ev->Good() && inName) mFile_Name = ev->CopyString(heap, inName); + } else + this->NilSlotHeapError(ev); +} + +void morkFile::NewFileDownError(morkEnv* ev) const +// call NewFileDownError() when either IsOpenAndActiveFile() +// is false, or when IsOpenActiveAndMutableFile() is false. +{ + if (this->IsOpenNode()) { + if (this->FileActive()) { + if (this->FileFrozen()) { + ev->NewError("file frozen"); + } else + ev->NewError("unknown file problem"); + } else + ev->NewError("file not active"); + } else + ev->NewError("file not open"); +} + +void morkFile::NewFileErrnoError(morkEnv* ev) const +// call NewFileErrnoError() to convert std C errno into AB fault +{ + const char* errnoString = strerror(errno); + ev->NewError(errnoString); // maybe pass value of strerror() instead +} + +// ````` ````` ````` ````` newlines ````` ````` ````` ````` + +#if defined(MORK_MAC) +static const char morkFile_kNewlines[] = + "\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015"; +# define morkFile_kNewlinesCount 16 +#else +# if defined(MORK_WIN) +static const char morkFile_kNewlines[] = + "\015\012\015\012\015\012\015\012\015\012\015\012\015\012\015\012"; +# define morkFile_kNewlinesCount 8 +# else +# ifdef MORK_UNIX +static const char morkFile_kNewlines[] = + "\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012"; +# define morkFile_kNewlinesCount 16 +# endif /* MORK_UNIX */ +# endif /* MORK_WIN */ +#endif /* MORK_MAC */ + +mork_size morkFile::WriteNewlines(morkEnv* ev, mork_count inNewlines) +// WriteNewlines() returns the number of bytes written. +{ + mork_size outSize = 0; + while (inNewlines && ev->Good()) // more newlines to write? + { + mork_u4 quantum = inNewlines; + if (quantum > morkFile_kNewlinesCount) quantum = morkFile_kNewlinesCount; + + mork_size quantumSize = quantum * mork_kNewlineSize; + mdb_size bytesWritten; + this->Write(ev->AsMdbEnv(), morkFile_kNewlines, quantumSize, &bytesWritten); + outSize += quantumSize; + inNewlines -= quantum; + } + return outSize; +} + +NS_IMETHODIMP +morkFile::Eof(nsIMdbEnv* mev, mdb_pos* outPos) { + nsresult outErr = NS_OK; + mdb_pos pos = -1; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + pos = Length(ev); + outErr = ev->AsErr(); + if (outPos) *outPos = pos; + return outErr; +} + +NS_IMETHODIMP +morkFile::Get(nsIMdbEnv* mev, void* outBuf, mdb_size inSize, mdb_pos inPos, + mdb_size* outActualSize) { + nsresult rv = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + mdb_pos outPos; + Seek(mev, inPos, &outPos); + if (ev->Good()) rv = Read(mev, outBuf, inSize, outActualSize); + } + return rv; +} + +NS_IMETHODIMP +morkFile::Put(nsIMdbEnv* mev, const void* inBuf, mdb_size inSize, mdb_pos inPos, + mdb_size* outActualSize) { + nsresult outErr = NS_OK; + *outActualSize = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + mdb_pos outPos; + + Seek(mev, inPos, &outPos); + if (ev->Good()) Write(mev, inBuf, inSize, outActualSize); + outErr = ev->AsErr(); + } + return outErr; +} + +// { ----- begin path methods ----- +NS_IMETHODIMP +morkFile::Path(nsIMdbEnv* mev, mdbYarn* outFilePath) { + nsresult outErr = NS_OK; + if (outFilePath) outFilePath->mYarn_Fill = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + ev->StringToYarn(GetFileNameString(), outFilePath); + outErr = ev->AsErr(); + } + return outErr; +} + +// } ----- end path methods ----- + +// { ----- begin replacement methods ----- + +NS_IMETHODIMP +morkFile::Thief(nsIMdbEnv* mev, nsIMdbFile** acqThief) { + nsresult outErr = NS_OK; + nsIMdbFile* outThief = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + outThief = GetThief(); + NS_IF_ADDREF(outThief); + outErr = ev->AsErr(); + } + if (acqThief) *acqThief = outThief; + return outErr; +} + +// } ----- end replacement methods ----- + +// { ----- begin versioning methods ----- + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkStdioFile::CloseMorkNode( + morkEnv* ev) // CloseStdioFile() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseStdioFile(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkStdioFile::~morkStdioFile() // assert CloseStdioFile() executed earlier +{ + if (mStdioFile_File) CloseStdioFile(mMorkEnv); + MORK_ASSERT(mStdioFile_File == 0); +} + +/*public non-poly*/ void morkStdioFile::CloseStdioFile( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + if (mStdioFile_File && this->FileActive() && this->FileIoOpen()) { + this->CloseStdio(ev); + } + + mStdioFile_File = 0; + + this->CloseFile(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +// compatible with the morkFile::MakeFile() entry point + +/*static*/ morkStdioFile* morkStdioFile::OpenOldStdioFile( + morkEnv* ev, nsIMdbHeap* ioHeap, const PathChar* inFilePath, + mork_bool inFrozen) { + morkStdioFile* outFile = 0; + if (ioHeap && inFilePath) { + const char* mode = (inFrozen) ? "rb" : "rb+"; + outFile = new (*ioHeap, ev) + morkStdioFile(ev, morkUsage::kHeap, ioHeap, ioHeap, inFilePath, mode); + + if (outFile) { + outFile->SetFileFrozen(inFrozen); + } + } else + ev->NilPointerError(); + + return outFile; +} + +/*static*/ morkStdioFile* morkStdioFile::CreateNewStdioFile( + morkEnv* ev, nsIMdbHeap* ioHeap, const PathChar* inFilePath) { + morkStdioFile* outFile = 0; + if (ioHeap && inFilePath) { + const char* mode = "wb+"; + outFile = new (*ioHeap, ev) + morkStdioFile(ev, morkUsage::kHeap, ioHeap, ioHeap, inFilePath, mode); + } else + ev->NilPointerError(); + + return outFile; +} + +NS_IMETHODIMP +morkStdioFile::BecomeTrunk(nsIMdbEnv* ev) +// If this file is a file version branch created by calling AcquireBud(), +// BecomeTrunk() causes this file's content to replace the original +// file's content, typically by assuming the original file's identity. +{ + return Flush(ev); +} + +NS_IMETHODIMP +morkStdioFile::AcquireBud(nsIMdbEnv* mdbev, nsIMdbHeap* ioHeap, + nsIMdbFile** acquiredFile) +// AcquireBud() starts a new "branch" version of the file, empty of content, +// so that a new version of the file can be written. This new file +// can later be told to BecomeTrunk() the original file, so the branch +// created by budding the file will replace the original file. Some +// file subclasses might initially take the unsafe but expedient +// approach of simply truncating this file down to zero length, and +// then returning the same morkFile pointer as this, with an extra +// reference count increment. Note that the caller of AcquireBud() is +// expected to eventually call CutStrongRef() on the returned file +// in order to release the strong reference. High quality versions +// of morkFile subclasses will create entirely new files which later +// are renamed to become the old file, so that better transactional +// behavior is exhibited by the file, so crashes protect old files. +// Note that AcquireBud() is an illegal operation on readonly files. +{ + NS_ENSURE_ARG(acquiredFile); + MORK_USED_1(ioHeap); + nsresult rv = NS_OK; + morkFile* outFile = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mdbev); + + if (this->IsOpenAndActiveFile()) { + FILE* file = (FILE*)mStdioFile_File; + if (file) { + // #ifdef MORK_WIN + // truncate(file, /*eof*/ 0); + // #else /*MORK_WIN*/ + PathChar* name = mFile_Name; + if (name) { + if (MORK_FILECLOSE(file) >= 0) { + this->SetFileActive(morkBool_kFalse); + this->SetFileIoOpen(morkBool_kFalse); + mStdioFile_File = 0; + + file = MORK_FILEOPEN( + name, "wb+"); // open for write, discarding old content + if (file) { + mStdioFile_File = file; + this->SetFileActive(morkBool_kTrue); + this->SetFileIoOpen(morkBool_kTrue); + this->SetFileFrozen(morkBool_kFalse); + } else + this->new_stdio_file_fault(ev); + } else + this->new_stdio_file_fault(ev); + } else + this->NilFileNameError(ev); + + // #endif /*MORK_WIN*/ + + if (ev->Good() && this->AddStrongRef(ev->AsMdbEnv())) { + outFile = this; + AddRef(); + } + } else if (mFile_Thief) { + rv = mFile_Thief->AcquireBud(ev->AsMdbEnv(), ioHeap, acquiredFile); + } else + this->NewMissingIoError(ev); + } else + this->NewFileDownError(ev); + + *acquiredFile = outFile; + return rv; +} + +mork_pos morkStdioFile::Length(morkEnv* ev) const { + mork_pos outPos = 0; + + if (this->IsOpenAndActiveFile()) { + FILE* file = (FILE*)mStdioFile_File; + if (file) { + long start = MORK_FILETELL(file); + if (start >= 0) { + long fore = MORK_FILESEEK(file, 0, SEEK_END); + if (fore >= 0) { + long eof = MORK_FILETELL(file); + if (eof >= 0) { + long back = MORK_FILESEEK(file, start, SEEK_SET); + if (back >= 0) + outPos = eof; + else + this->new_stdio_file_fault(ev); + } else + this->new_stdio_file_fault(ev); + } else + this->new_stdio_file_fault(ev); + } else + this->new_stdio_file_fault(ev); + } else if (mFile_Thief) + mFile_Thief->Eof(ev->AsMdbEnv(), &outPos); + else + this->NewMissingIoError(ev); + } else + this->NewFileDownError(ev); + + return outPos; +} + +NS_IMETHODIMP +morkStdioFile::Tell(nsIMdbEnv* ev, mork_pos* outPos) const { + nsresult rv = NS_OK; + NS_ENSURE_ARG(outPos); + morkEnv* mev = morkEnv::FromMdbEnv(ev); + if (this->IsOpenAndActiveFile()) { + FILE* file = (FILE*)mStdioFile_File; + if (file) { + long where = MORK_FILETELL(file); + if (where >= 0) + *outPos = where; + else + this->new_stdio_file_fault(mev); + } else if (mFile_Thief) + mFile_Thief->Tell(ev, outPos); + else + this->NewMissingIoError(mev); + } else + this->NewFileDownError(mev); + + return rv; +} + +NS_IMETHODIMP +morkStdioFile::Read(nsIMdbEnv* ev, void* outBuf, mork_size inSize, + mork_num* outCount) { + nsresult rv = NS_OK; + morkEnv* mev = morkEnv::FromMdbEnv(ev); + if (this->IsOpenAndActiveFile()) { + FILE* file = (FILE*)mStdioFile_File; + if (file) { + long count = (long)MORK_FILEREAD(outBuf, inSize, file); + if (count >= 0) { + *outCount = (mork_num)count; + } else + this->new_stdio_file_fault(mev); + } else if (mFile_Thief) + mFile_Thief->Read(ev, outBuf, inSize, outCount); + else + this->NewMissingIoError(mev); + } else + this->NewFileDownError(mev); + + return rv; +} + +NS_IMETHODIMP +morkStdioFile::Seek(nsIMdbEnv* mdbev, mork_pos inPos, mork_pos* aOutPos) { + mork_pos outPos = 0; + nsresult rv = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mdbev); + + if (this->IsOpenOrClosingNode() && this->FileActive()) { + FILE* file = (FILE*)mStdioFile_File; + if (file) { + long where = MORK_FILESEEK(file, inPos, SEEK_SET); + if (where >= 0) + outPos = inPos; + else + this->new_stdio_file_fault(ev); + } else if (mFile_Thief) + mFile_Thief->Seek(mdbev, inPos, aOutPos); + else + this->NewMissingIoError(ev); + } else + this->NewFileDownError(ev); + + *aOutPos = outPos; + return rv; +} + +NS_IMETHODIMP +morkStdioFile::Write(nsIMdbEnv* mdbev, const void* inBuf, mork_size inSize, + mork_size* aOutSize) { + mork_num outCount = 0; + nsresult rv = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mdbev); + if (this->IsOpenActiveAndMutableFile()) { + FILE* file = (FILE*)mStdioFile_File; + if (file) { + mozilla::Unused << fwrite(inBuf, 1, inSize, file); + if (!ferror(file)) + outCount = inSize; + else + this->new_stdio_file_fault(ev); + } else if (mFile_Thief) + mFile_Thief->Write(mdbev, inBuf, inSize, &outCount); + else + this->NewMissingIoError(ev); + } else + this->NewFileDownError(ev); + + *aOutSize = outCount; + return rv; +} + +NS_IMETHODIMP +morkStdioFile::Flush(nsIMdbEnv* mdbev) { + morkEnv* ev = morkEnv::FromMdbEnv(mdbev); + if (this->IsOpenOrClosingNode() && this->FileActive()) { + FILE* file = (FILE*)mStdioFile_File; + if (file) { + MORK_FILEFLUSH(file); + + } else if (mFile_Thief) + mFile_Thief->Flush(mdbev); + else + this->NewMissingIoError(ev); + } else + this->NewFileDownError(ev); + return NS_OK; +} + +// ````` ````` ````` ````` ````` ````` ````` ````` +// protected: // protected non-poly morkStdioFile methods + +void morkStdioFile::new_stdio_file_fault(morkEnv* ev) const { + FILE* file = (FILE*)mStdioFile_File; + + int copyErrno = errno; // facilitate seeing error in debugger + + // bunch of stuff not ported here + if (!copyErrno && file) { + copyErrno = ferror(file); + errno = copyErrno; + } + + this->NewFileErrnoError(ev); +} + +// ````` ````` ````` ````` ````` ````` ````` ````` +// public: // public non-poly morkStdioFile methods + +/*public non-poly*/ +morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) + : morkFile(ev, inUsage, ioHeap, ioSlotHeap), mStdioFile_File(0) { + if (ev->Good()) mNode_Derived = morkDerived_kStdioFile; +} + +morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, + const PathChar* inName, const char* inMode) + // calls OpenStdio() after construction + : morkFile(ev, inUsage, ioHeap, ioSlotHeap), mStdioFile_File(0) { + if (ev->Good()) this->OpenStdio(ev, inName, inMode); +} + +morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, + void* ioFile, const PathChar* inName, + mork_bool inFrozen) + // calls UseStdio() after construction + : morkFile(ev, inUsage, ioHeap, ioSlotHeap), mStdioFile_File(0) { + if (ev->Good()) this->UseStdio(ev, ioFile, inName, inFrozen); +} + +void morkStdioFile::OpenStdio(morkEnv* ev, const PathChar* inName, + const char* inMode) +// Open a new FILE with name inName, using mode flags from inMode. +{ + if (ev->Good()) { + if (!inMode) inMode = ""; + + mork_bool frozen = (*inMode == 'r'); // cursory attempt to note readonly + + if (this->IsOpenNode()) { + if (!this->FileActive()) { + this->SetFileIoOpen(morkBool_kFalse); + if (inName && *inName) { + this->SetFileName(ev, inName); + if (ev->Good()) { + FILE* file = MORK_FILEOPEN(inName, inMode); + if (file) { + mStdioFile_File = file; + this->SetFileActive(morkBool_kTrue); + this->SetFileIoOpen(morkBool_kTrue); + this->SetFileFrozen(frozen); + } else + this->new_stdio_file_fault(ev); + } + } else + ev->NewError("no file name"); + } else + ev->NewError("file already active"); + } else + this->NewFileDownError(ev); + } +} + +void morkStdioFile::UseStdio(morkEnv* ev, void* ioFile, const PathChar* inName, + mork_bool inFrozen) +// Use an existing file, like stdin/stdout/stderr, which should not +// have the io stream closed when the file is closed. The ioFile +// parameter must actually be of type FILE (but we don't want to make +// this header file include the stdio.h header file). +{ + if (ev->Good()) { + if (this->IsOpenNode()) { + if (!this->FileActive()) { + if (ioFile) { + this->SetFileIoOpen(morkBool_kFalse); + this->SetFileName(ev, inName); + if (ev->Good()) { + mStdioFile_File = ioFile; + this->SetFileActive(morkBool_kTrue); + this->SetFileFrozen(inFrozen); + } + } else + ev->NilPointerError(); + } else + ev->NewError("file already active"); + } else + this->NewFileDownError(ev); + } +} + +void morkStdioFile::CloseStdio(morkEnv* ev) +// Close the stream io if both and FileActive() and FileIoOpen(), but +// this does not close this instances (like CloseStdioFile() does). +// If stream io was made active by means of calling UseStdio(), +// then this method does little beyond marking the stream inactive +// because FileIoOpen() is false. +{ + if (mStdioFile_File && this->FileActive() && this->FileIoOpen()) { + FILE* file = (FILE*)mStdioFile_File; + if (MORK_FILECLOSE(file) < 0) this->new_stdio_file_fault(ev); + + mStdioFile_File = 0; + this->SetFileActive(morkBool_kFalse); + this->SetFileIoOpen(morkBool_kFalse); + } +} + +NS_IMETHODIMP +morkStdioFile::Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) +// If this file is a file version branch created by calling AcquireBud(), +// BecomeTrunk() causes this file's content to replace the original +// file's content, typically by assuming the original file's identity. +{ + morkEnv* mev = morkEnv::FromMdbEnv(ev); + if (mStdioFile_File && FileActive() && FileIoOpen()) { + FILE* file = (FILE*)mStdioFile_File; + if (MORK_FILECLOSE(file) < 0) new_stdio_file_fault(mev); + + mStdioFile_File = 0; + } + SetThief(mev, ioThief); + return NS_OK; +} + +#if defined(MORK_WIN) + +void mork_fileflush(FILE* file) { fflush(file); } + +#endif /*MORK_WIN*/ + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkFile.h b/comm/mailnews/db/mork/morkFile.h new file mode 100644 index 0000000000..1a2933643f --- /dev/null +++ b/comm/mailnews/db/mork/morkFile.h @@ -0,0 +1,360 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MORKFILE_ +#define _MORKFILE_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKOBJECT_ +# include "morkObject.h" +#endif + +#include "mozilla/Path.h" + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*============================================================================= + * morkFile: abstract file interface + */ + +#define morkDerived_kFile /*i*/ 0x4669 /* ascii 'Fi' */ + +class morkFile /*d*/ : public morkObject, + public nsIMdbFile { /* ````` simple file API ````` */ + using PathChar = mozilla::filesystem::Path::value_type; + + // public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // public: // slots inherited from morkObject (meant to inform only) + + // mork_color mBead_Color; // ID for this bead + // morkHandle* mObject_Handle; // weak ref to handle for this object + + // ````` ````` ````` ````` ````` ````` ````` ````` + protected: // protected morkFile members (similar to public domain IronDoc) + virtual ~morkFile(); // assert that CloseFile() executed earlier + + mork_u1 mFile_Frozen; // 'F' => file allows only read access + mork_u1 mFile_DoTrace; // 'T' trace if ev->DoTrace() + mork_u1 mFile_IoOpen; // 'O' => io stream is open (& needs a close) + mork_u1 mFile_Active; // 'A' => file is active and usable + + nsIMdbHeap* mFile_SlotHeap; // heap for Name and other allocated slots + PathChar* mFile_Name; // can be nil if SetFileName() is never called + // mFile_Name convention: managed with morkEnv::CopyString()/FreeString() + + nsIMdbFile* mFile_Thief; // from a call to orkinFile::Steal() + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + NS_DECL_ISUPPORTS_INHERITED + virtual void CloseMorkNode(morkEnv* ev) override; // CloseFile() only if open + + public: // morkFile construction & destruction + morkFile(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + void CloseFile(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkFile(const morkFile& other); + morkFile& operator=(const morkFile& other); + + public: // dynamic type identification + mork_bool IsFile() const { + return IsNode() && mNode_Derived == morkDerived_kFile; + } + // } ===== end morkNode methods ===== + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // public static standard file creation entry point + static morkFile* OpenOldFile(morkEnv* ev, nsIMdbHeap* ioHeap, + const PathChar* inFilePath, mork_bool inFrozen); + // Choose some subclass of morkFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be open and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. + + static morkFile* CreateNewFile(morkEnv* ev, nsIMdbHeap* ioHeap, + const PathChar* inFilePath); + // Choose some subclass of morkFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be created and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. + + public: // non-poly morkFile methods + mork_bool FileFrozen() const { return mFile_Frozen == 'F'; } + mork_bool FileDoTrace() const { return mFile_DoTrace == 'T'; } + mork_bool FileIoOpen() const { return mFile_IoOpen == 'O'; } + mork_bool FileActive() const { return mFile_Active == 'A'; } + + void SetFileFrozen(mork_bool b) { mFile_Frozen = (mork_u1)((b) ? 'F' : 0); } + void SetFileDoTrace(mork_bool b) { mFile_DoTrace = (mork_u1)((b) ? 'T' : 0); } + void SetFileIoOpen(mork_bool b) { mFile_IoOpen = (mork_u1)((b) ? 'O' : 0); } + void SetFileActive(mork_bool b) { mFile_Active = (mork_u1)((b) ? 'A' : 0); } + + mork_bool IsOpenActiveAndMutableFile() const { + return (IsOpenNode() && FileActive() && !FileFrozen()); + } + // call IsOpenActiveAndMutableFile() before writing a file + + mork_bool IsOpenAndActiveFile() const { + return (this->IsOpenNode() && this->FileActive()); + } + // call IsOpenAndActiveFile() before using a file + + nsIMdbFile* GetThief() const { return mFile_Thief; } + void SetThief(morkEnv* ev, nsIMdbFile* ioThief); // ioThief can be nil + + const PathChar* GetFileNameString() const { return mFile_Name; } + void SetFileName(morkEnv* ev, const PathChar* inName); // inName can be nil + static void NilSlotHeapError(morkEnv* ev); + static void NilFileNameError(morkEnv* ev); + static void NonFileTypeError(morkEnv* ev); + + void NewMissingIoError(morkEnv* ev) const; + + void NewFileDownError(morkEnv* ev) const; + // call NewFileDownError() when either IsOpenAndActiveFile() + // is false, or when IsOpenActiveAndMutableFile() is false. + + void NewFileErrnoError(morkEnv* ev) const; + // call NewFileErrnoError() to convert std C errno into AB fault + + mork_size WriteNewlines(morkEnv* ev, mork_count inNewlines); + // WriteNewlines() returns the number of bytes written. + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakFile(morkFile* me, morkEnv* ev, morkFile** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongFile(morkFile* me, morkEnv* ev, morkFile** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + public: + virtual mork_pos Length(morkEnv* ev) const = 0; // eof + // nsIMdbFile methods + NS_IMETHOD Tell(nsIMdbEnv* ev, mdb_pos* outPos) const override = 0; + NS_IMETHOD Seek(nsIMdbEnv* ev, mdb_pos inPos, mdb_pos* outPos) override = 0; + NS_IMETHOD Eof(nsIMdbEnv* ev, mdb_pos* outPos) override; + // } ----- end pos methods ----- + + // { ----- begin read methods ----- + NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mdb_size inSize, + mdb_size* outActualSize) override = 0; + NS_IMETHOD Get(nsIMdbEnv* ev, void* outBuf, mdb_size inSize, mdb_pos inPos, + mdb_size* outActualSize) override; + // } ----- end read methods ----- + + // { ----- begin write methods ----- + NS_IMETHOD Write(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize, + mdb_size* outActualSize) override = 0; + NS_IMETHOD Put(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize, + mdb_pos inPos, mdb_size* outActualSize) override; + NS_IMETHOD Flush(nsIMdbEnv* ev) override = 0; + // } ----- end attribute methods ----- + + // { ----- begin path methods ----- + NS_IMETHOD Path(nsIMdbEnv* ev, mdbYarn* outFilePath) override; + // } ----- end path methods ----- + + // { ----- begin replacement methods ----- + NS_IMETHOD Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) override = 0; + NS_IMETHOD Thief(nsIMdbEnv* ev, nsIMdbFile** acqThief) override; + // } ----- end replacement methods ----- + + // { ----- begin versioning methods ----- + NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev) override = 0; + + NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + nsIMdbFile** acqBud) override = 0; + // } ----- end versioning methods ----- + + // } ===== end nsIMdbFile methods ===== +}; + +/*============================================================================= + * morkStdioFile: concrete file using standard C file io + */ + +#define morkDerived_kStdioFile /*i*/ 0x7346 /* ascii 'sF' */ + +class morkStdioFile /*d*/ : public morkFile { /* `` copied from IronDoc `` */ + using PathChar = mozilla::filesystem::Path::value_type; + + // ````` ````` ````` ````` ````` ````` ````` ````` + protected: // protected morkStdioFile members + void* mStdioFile_File; + // actually type FILE*, but using opaque void* type + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseStdioFile() only if open + virtual ~morkStdioFile(); // assert that CloseStdioFile() executed earlier + + public: // morkStdioFile construction & destruction + morkStdioFile(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + void CloseStdioFile(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkStdioFile(const morkStdioFile& other); + morkStdioFile& operator=(const morkStdioFile& other); + + public: // dynamic type identification + mork_bool IsStdioFile() const { + return IsNode() && mNode_Derived == morkDerived_kStdioFile; + } + // } ===== end morkNode methods ===== + + public: // typing + static void NonStdioFileTypeError(morkEnv* ev); + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // compatible with the morkFile::OpenOldFile() entry point + static morkStdioFile* OpenOldStdioFile(morkEnv* ev, nsIMdbHeap* ioHeap, + const PathChar* inFilePath, + mork_bool inFrozen); + + static morkStdioFile* CreateNewStdioFile(morkEnv* ev, nsIMdbHeap* ioHeap, + const PathChar* inFilePath); + + virtual mork_pos Length(morkEnv* ev) const override; // eof + + NS_IMETHOD Tell(nsIMdbEnv* ev, mdb_pos* outPos) const override; + NS_IMETHOD Seek(nsIMdbEnv* ev, mdb_pos inPos, mdb_pos* outPos) override; + // NS_IMETHOD Eof(nsIMdbEnv* ev, mdb_pos* outPos); + // } ----- end pos methods ----- + + // { ----- begin read methods ----- + NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mdb_size inSize, + mdb_size* outActualSize) override; + + // { ----- begin write methods ----- + NS_IMETHOD Write(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize, + mdb_size* outActualSize) override; + // NS_IMETHOD Put(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize, + // mdb_pos inPos, mdb_size* outActualSize); + NS_IMETHOD Flush(nsIMdbEnv* ev) override; + // } ----- end attribute methods ----- + + NS_IMETHOD Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) override; + + // { ----- begin versioning methods ----- + NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev) override; + + NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + nsIMdbFile** acqBud) override; + // } ----- end versioning methods ----- + + // } ===== end nsIMdbFile methods ===== + + // ````` ````` ````` ````` ````` ````` ````` ````` + + // ````` ````` ````` ````` ````` ````` ````` ````` + protected: // protected non-poly morkStdioFile methods + void new_stdio_file_fault(morkEnv* ev) const; + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // public non-poly morkStdioFile methods + morkStdioFile(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap, const PathChar* inName, + const char* inMode); + // calls OpenStdio() after construction + + morkStdioFile(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap, void* ioFile, const PathChar* inName, + mork_bool inFrozen); + // calls UseStdio() after construction + + void OpenStdio(morkEnv* ev, const PathChar* inName, const char* inMode); + // Open a new FILE with name inName, using mode flags from inMode. + + void UseStdio(morkEnv* ev, void* ioFile, const PathChar* inName, + mork_bool inFrozen); + // Use an existing file, like stdin/stdout/stderr, which should not + // have the io stream closed when the file is closed. The ioFile + // parameter must actually be of type FILE (but we don't want to make + // this header file include the stdio.h header file). + + void CloseStdio(morkEnv* ev); + // Close the stream io if both and FileActive() and FileIoOpen(), but + // this does not close this instances (like CloseStdioFile() does). + // If stream io was made active by means of calling UseStdio(), + // then this method does little beyond marking the stream inactive + // because FileIoOpen() is false. + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakStdioFile(morkStdioFile* me, morkEnv* ev, + morkStdioFile** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongStdioFile(morkStdioFile* me, morkEnv* ev, + morkStdioFile** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKFILE_ */ diff --git a/comm/mailnews/db/mork/morkHandle.cpp b/comm/mailnews/db/mork/morkHandle.cpp new file mode 100644 index 0000000000..e838f8b997 --- /dev/null +++ b/comm/mailnews/db/mork/morkHandle.cpp @@ -0,0 +1,357 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKFACTORY_ +# include "morkFactory.h" +#endif + +#ifndef _MORKPOOL_ +# include "morkPool.h" +#endif + +#ifndef _MORKHANDLE_ +# include "morkHandle.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkHandle::CloseMorkNode( + morkEnv* ev) // CloseHandle() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseHandle(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkHandle::~morkHandle() // assert CloseHandle() executed earlier +{ + MORK_ASSERT(mHandle_Env == 0); + MORK_ASSERT(mHandle_Face == 0); + MORK_ASSERT(mHandle_Object == 0); + MORK_ASSERT(mHandle_Magic == 0); + MORK_ASSERT(mHandle_Tag == morkHandle_kTag); // should still have correct tag +} + +/*public non-poly*/ +morkHandle::morkHandle( + morkEnv* ev, // note morkUsage is always morkUsage_kPool + morkHandleFace* ioFace, // must not be nil, cookie for this handle + morkObject* ioObject, // must not be nil, the object for this handle + mork_magic inMagic) // magic sig to denote specific subclass + : morkNode(ev, morkUsage::kPool, (nsIMdbHeap*)0L), + mHandle_Tag(0), + mHandle_Env(ev), + mHandle_Face(ioFace), + mHandle_Object(0), + mHandle_Magic(0) { + if (ioFace && ioObject) { + if (ev->Good()) { + mHandle_Tag = morkHandle_kTag; + morkObject::SlotStrongObject(ioObject, ev, &mHandle_Object); + morkHandle::SlotWeakHandle(this, ev, &ioObject->mObject_Handle); + if (ev->Good()) { + mHandle_Magic = inMagic; + mNode_Derived = morkDerived_kHandle; + } + } else + ev->CantMakeWhenBadError(); + } else + ev->NilPointerError(); +} + +/*public non-poly*/ void morkHandle::CloseHandle( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + morkObject* obj = mHandle_Object; + mork_bool objDidRefSelf = (obj && obj->mObject_Handle == this); + if (objDidRefSelf) obj->mObject_Handle = 0; // drop the reference + + morkObject::SlotStrongObject((morkObject*)0, ev, &mHandle_Object); + mHandle_Magic = 0; + // note mHandle_Tag MUST stay morkHandle_kTag for morkNode::ZapOld() + this->MarkShut(); + + if (objDidRefSelf) + this->CutWeakRef(ev); // do last, because it might self destroy + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +void morkHandle::NilFactoryError(morkEnv* ev) const { + ev->NewError("nil mHandle_Factory"); +} + +void morkHandle::NilHandleObjectError(morkEnv* ev) const { + ev->NewError("nil mHandle_Object"); +} + +void morkHandle::NonNodeObjectError(morkEnv* ev) const { + ev->NewError("non-node mHandle_Object"); +} + +void morkHandle::NonOpenObjectError(morkEnv* ev) const { + ev->NewError("non-open mHandle_Object"); +} + +void morkHandle::NewBadMagicHandleError(morkEnv* ev, mork_magic inMagic) const { + MORK_USED_1(inMagic); + ev->NewError("wrong mHandle_Magic"); +} + +void morkHandle::NewDownHandleError(morkEnv* ev) const { + if (this->IsHandle()) { + if (this->GoodHandleTag()) { + if (this->IsOpenNode()) + ev->NewError("unknown down morkHandle error"); + else + this->NonOpenNodeError(ev); + } else + ev->NewError("wrong morkHandle tag"); + } else + ev->NewError("non morkHandle"); +} + +morkObject* morkHandle::GetGoodHandleObject(morkEnv* ev, mork_bool inMutable, + mork_magic inMagicType, + mork_bool inClosedOkay) const { + morkObject* outObject = 0; + if (this->IsHandle() && this->GoodHandleTag() && + (inClosedOkay || this->IsOpenNode())) { + if (!inMagicType || mHandle_Magic == inMagicType) { + morkObject* obj = this->mHandle_Object; + if (obj) { + if (obj->IsNode()) { + if (inClosedOkay || obj->IsOpenNode()) { + if (this->IsMutable() || !inMutable) + outObject = obj; + else + this->NonMutableNodeError(ev); + } else + this->NonOpenObjectError(ev); + } else + this->NonNodeObjectError(ev); + } else if (!inClosedOkay) + this->NilHandleObjectError(ev); + } else + this->NewBadMagicHandleError(ev, inMagicType); + } else + this->NewDownHandleError(ev); + + MORK_ASSERT(outObject || inClosedOkay); + return outObject; +} + +morkEnv* morkHandle::CanUseHandle(nsIMdbEnv* mev, mork_bool inMutable, + mork_bool inClosedOkay, + nsresult* outErr) const { + morkEnv* outEnv = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + morkObject* obj = this->GetGoodHandleObject(ev, inMutable, + /*magic*/ 0, inClosedOkay); + if (obj) { + outEnv = ev; + } + *outErr = ev->AsErr(); + } + MORK_ASSERT(outEnv || inClosedOkay); + return outEnv; +} + +// { ===== begin nsIMdbObject methods ===== + +// { ----- begin attribute methods ----- +/*virtual*/ nsresult morkHandle::Handle_IsFrozenMdbObject( + nsIMdbEnv* mev, mdb_bool* outIsReadonly) { + nsresult outErr = NS_OK; + mdb_bool readOnly = mdbBool_kTrue; + + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if (ev) { + readOnly = mHandle_Object->IsFrozen(); + + outErr = ev->AsErr(); + } + MORK_ASSERT(outIsReadonly); + if (outIsReadonly) *outIsReadonly = readOnly; + + return outErr; +} +// same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port. +// } ----- end attribute methods ----- + +// { ----- begin factory methods ----- +/*virtual*/ nsresult morkHandle::Handle_GetMdbFactory( + nsIMdbEnv* mev, nsIMdbFactory** acqFactory) { + nsresult outErr = NS_OK; + nsIMdbFactory* handle = 0; + + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if (ev) { + morkFactory* factory = ev->mEnv_Factory; + if (factory) { + handle = factory; + NS_ADDREF(handle); + } else + this->NilFactoryError(ev); + + outErr = ev->AsErr(); + } + + MORK_ASSERT(acqFactory); + if (acqFactory) *acqFactory = handle; + + return outErr; +} +// } ----- end factory methods ----- + +// { ----- begin ref counting for well-behaved cyclic graphs ----- +/*virtual*/ nsresult morkHandle::Handle_GetWeakRefCount( + nsIMdbEnv* mev, // weak refs + mdb_count* outCount) { + nsresult outErr = NS_OK; + mdb_count count = 0; + + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if (ev) { + count = this->WeakRefsOnly(); + + outErr = ev->AsErr(); + } + MORK_ASSERT(outCount); + if (outCount) *outCount = count; + + return outErr; +} +/*virtual*/ nsresult morkHandle::Handle_GetStrongRefCount( + nsIMdbEnv* mev, // strong refs + mdb_count* outCount) { + nsresult outErr = NS_OK; + mdb_count count = 0; + + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if (ev) { + count = this->StrongRefsOnly(); + + outErr = ev->AsErr(); + } + MORK_ASSERT(outCount); + if (outCount) *outCount = count; + + return outErr; +} + +/*virtual*/ nsresult morkHandle::Handle_AddWeakRef(nsIMdbEnv* mev) { + nsresult outErr = NS_OK; + + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if (ev) { + this->AddWeakRef(ev); + outErr = ev->AsErr(); + } + + return outErr; +} +/*virtual*/ nsresult morkHandle::Handle_AddStrongRef(nsIMdbEnv* mev) { + nsresult outErr = NS_OK; + + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kFalse, &outErr); + if (ev) { + this->AddStrongRef(ev); + outErr = ev->AsErr(); + } + + return outErr; +} + +/*virtual*/ nsresult morkHandle::Handle_CutWeakRef(nsIMdbEnv* mev) { + nsresult outErr = NS_OK; + + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if (ev) { + this->CutWeakRef(ev); + outErr = ev->AsErr(); + } + + return outErr; +} +/*virtual*/ nsresult morkHandle::Handle_CutStrongRef(nsIMdbEnv* mev) { + nsresult outErr = NS_OK; + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if (ev) { + this->CutStrongRef(ev); + outErr = ev->AsErr(); + } + return outErr; +} + +/*virtual*/ nsresult morkHandle::Handle_CloseMdbObject(nsIMdbEnv* mev) +// called at strong refs zero +{ + // if only one ref, Handle_CutStrongRef will clean up better. + if (mNode_Uses == 1) return Handle_CutStrongRef(mev); + + nsresult outErr = NS_OK; + + if (this->IsNode() && this->IsOpenNode()) { + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if (ev) { + morkObject* object = mHandle_Object; + if (object && object->IsNode() && object->IsOpenNode()) + object->CloseMorkNode(ev); + + this->CloseMorkNode(ev); + outErr = ev->AsErr(); + } + } + return outErr; +} + +/*virtual*/ nsresult morkHandle::Handle_IsOpenMdbObject(nsIMdbEnv* mev, + mdb_bool* outOpen) { + MORK_USED_1(mev); + nsresult outErr = NS_OK; + + MORK_ASSERT(outOpen); + if (outOpen) *outOpen = this->IsOpenNode(); + + return outErr; +} +// } ----- end ref counting ----- + +// } ===== end nsIMdbObject methods ===== + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkHandle.h b/comm/mailnews/db/mork/morkHandle.h new file mode 100644 index 0000000000..5089e629c8 --- /dev/null +++ b/comm/mailnews/db/mork/morkHandle.h @@ -0,0 +1,183 @@ +/* -*- 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 _MORKHANDLE_ +#define _MORKHANDLE_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKDEQUE_ +# include "morkDeque.h" +#endif + +#ifndef _MORKPOOL_ +# include "morkPool.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class morkPool; +class morkObject; +class morkFactory; + +#define morkDerived_kHandle /*i*/ 0x486E /* ascii 'Hn' */ +#define morkHandle_kTag 0x68416E44 /* ascii 'hAnD' */ + +/*| morkHandle: +|*/ +class morkHandle : public morkNode { + // public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + public: // state is public because the entire Mork system is private + mork_u4 mHandle_Tag; // must equal morkHandle_kTag + morkEnv* mHandle_Env; // pool that allocated this handle + morkHandleFace* mHandle_Face; // cookie from pool containing this + morkObject* mHandle_Object; // object this handle wraps for MDB API + mork_magic mHandle_Magic; // magic sig different in each subclass + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseHandle() only if open + virtual ~morkHandle(); // assert that CloseHandle() executed earlier + + public: // morkHandle construction & destruction + morkHandle( + morkEnv* ev, // note morkUsage is always morkUsage_kPool + morkHandleFace* ioFace, // must not be nil, cookie for this handle + morkObject* ioObject, // must not be nil, the object for this handle + mork_magic inMagic); // magic sig to denote specific subclass + void CloseHandle(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkHandle(const morkHandle& other); + morkHandle& operator=(const morkHandle& other); + + protected: // special case empty construction for morkHandleFrame + friend class morkHandleFrame; + morkHandle() {} + + public: // dynamic type identification + mork_bool IsHandle() const { + return IsNode() && mNode_Derived == morkDerived_kHandle; + } + // } ===== end morkNode methods ===== + + public: // morkHandle memory management operators + void* operator new(size_t inSize, morkPool& ioPool, morkZone& ioZone, + morkEnv* ev) noexcept(true) { + return ioPool.NewHandle(ev, inSize, &ioZone); + } + + void* operator new(size_t inSize, morkPool& ioPool, + morkEnv* ev) noexcept(true) { + return ioPool.NewHandle(ev, inSize, (morkZone*)0); + } + + void* operator new(size_t inSize, morkHandleFace* ioFace) noexcept(true) { + MORK_USED_1(inSize); + return ioFace; + } + + public: // other handle methods + mork_bool GoodHandleTag() const { return mHandle_Tag == morkHandle_kTag; } + + void NewBadMagicHandleError(morkEnv* ev, mork_magic inMagic) const; + void NewDownHandleError(morkEnv* ev) const; + void NilFactoryError(morkEnv* ev) const; + void NilHandleObjectError(morkEnv* ev) const; + void NonNodeObjectError(morkEnv* ev) const; + void NonOpenObjectError(morkEnv* ev) const; + + morkObject* GetGoodHandleObject(morkEnv* ev, mork_bool inMutable, + mork_magic inMagicType, + mork_bool inClosedOkay) const; + + public: // interface supporting mdbObject methods + morkEnv* CanUseHandle(nsIMdbEnv* mev, mork_bool inMutable, + mork_bool inClosedOkay, nsresult* outErr) const; + + // { ----- begin mdbObject style methods ----- + nsresult Handle_IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly); + + nsresult Handle_GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory); + nsresult Handle_GetWeakRefCount(nsIMdbEnv* ev, mdb_count* outCount); + nsresult Handle_GetStrongRefCount(nsIMdbEnv* ev, mdb_count* outCount); + + nsresult Handle_AddWeakRef(nsIMdbEnv* ev); + nsresult Handle_AddStrongRef(nsIMdbEnv* ev); + + nsresult Handle_CutWeakRef(nsIMdbEnv* ev); + nsresult Handle_CutStrongRef(nsIMdbEnv* ev); + + nsresult Handle_CloseMdbObject(nsIMdbEnv* ev); + nsresult Handle_IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen); + // } ----- end mdbObject style methods ----- + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakHandle(morkHandle* me, morkEnv* ev, morkHandle** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongHandle(morkHandle* me, morkEnv* ev, + morkHandle** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +#define morkHandleFrame_kPadSlotCount 4 + +/*| morkHandleFrame: an object format used for allocating and maintaining +**| instances of morkHandle, with leading slots used to maintain this in a +**| linked list, and following slots to provide extra footprint that might +**| be needed by any morkHandle subclasses that include very little extra +**| space (by virtue of the fact that each morkHandle subclass is expected +**| to multiply inherit from another base class that has only abstract methods +**| for space overhead related only to some vtable representation). +|*/ +class morkHandleFrame { + public: + morkLink mHandleFrame_Link; // list storage without trampling Handle + morkHandle mHandleFrame_Handle; + mork_ip mHandleFrame_Padding[morkHandleFrame_kPadSlotCount]; + + public: + morkHandle* AsHandle() { return &mHandleFrame_Handle; } + + morkHandleFrame() {} // actually, morkHandleFrame never gets constructed + + private: // copying is not allowed + morkHandleFrame(const morkHandleFrame& other); + morkHandleFrame& operator=(const morkHandleFrame& other); +}; + +#define morkHandleFrame_kHandleOffset \ + mork_OffsetOf(morkHandleFrame, mHandleFrame_Handle) + +#define morkHandle_AsHandleFrame(h) \ + ((h)->mHandle_Block, \ + ((morkHandleFrame*)(((mork_u1*)(h)) - morkHandleFrame_kHandleOffset))) + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKHANDLE_ */ diff --git a/comm/mailnews/db/mork/morkIntMap.cpp b/comm/mailnews/db/mork/morkIntMap.cpp new file mode 100644 index 0000000000..69caa867a1 --- /dev/null +++ b/comm/mailnews/db/mork/morkIntMap.cpp @@ -0,0 +1,212 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +#ifndef _MORKINTMAP_ +# include "morkIntMap.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkIntMap::CloseMorkNode( + morkEnv* ev) // CloseIntMap() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseIntMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkIntMap::~morkIntMap() // assert CloseIntMap() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkIntMap::morkIntMap(morkEnv* ev, const morkUsage& inUsage, + mork_size inValSize, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges) + : morkMap(ev, inUsage, ioHeap, sizeof(mork_u4), inValSize, + morkIntMap_kStartSlotCount, ioSlotHeap, inHoldChanges) { + if (ev->Good()) mNode_Derived = morkDerived_kIntMap; +} + +/*public non-poly*/ void morkIntMap::CloseIntMap( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + this->CloseMap(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +// { ===== begin morkMap poly interface ===== +/*virtual*/ mork_bool // *((mork_u4*) inKeyA) == *((mork_u4*) inKeyB) +morkIntMap::Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const { + MORK_USED_1(ev); + return *((const mork_u4*)inKeyA) == *((const mork_u4*)inKeyB); +} + +/*virtual*/ mork_u4 // some integer function of *((mork_u4*) inKey) +morkIntMap::Hash(morkEnv* ev, const void* inKey) const { + MORK_USED_1(ev); + return *((const mork_u4*)inKey); +} +// } ===== end morkMap poly interface ===== + +mork_bool morkIntMap::AddInt(morkEnv* ev, mork_u4 inKey, void* ioAddress) +// the AddInt() method return value equals ev->Good(). +{ + if (ev->Good()) { + this->Put(ev, &inKey, &ioAddress, + /*key*/ (void*)0, /*val*/ (void*)0, (mork_change**)0); + } + + return ev->Good(); +} + +mork_bool morkIntMap::CutInt(morkEnv* ev, mork_u4 inKey) { + return this->Cut(ev, &inKey, /*key*/ (void*)0, /*val*/ (void*)0, + (mork_change**)0); +} + +void* morkIntMap::GetInt(morkEnv* ev, mork_u4 inKey) +// Note the returned val does NOT have an increase in refcount for this. +{ + void* val = 0; // old val in the map + this->Get(ev, &inKey, /*key*/ (void*)0, &val, (mork_change**)0); + + return val; +} + +mork_bool morkIntMap::HasInt(morkEnv* ev, mork_u4 inKey) +// Note the returned val does NOT have an increase in refcount for this. +{ + return this->Get(ev, &inKey, /*key*/ (void*)0, /*val*/ (void*)0, + (mork_change**)0); +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#ifdef MORK_POINTER_MAP_IMPL + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkPointerMap::CloseMorkNode( + morkEnv* ev) // ClosePointerMap() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->ClosePointerMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkPointerMap::~morkPointerMap() // assert ClosePointerMap() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkPointerMap::morkPointerMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) + : morkMap(ev, inUsage, ioHeap, sizeof(void*), sizeof(void*), + morkPointerMap_kStartSlotCount, ioSlotHeap, + /*inHoldChanges*/ morkBool_kFalse) { + if (ev->Good()) mNode_Derived = morkDerived_kPointerMap; +} + +/*public non-poly*/ void morkPointerMap::ClosePointerMap( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + this->CloseMap(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +// { ===== begin morkMap poly interface ===== +/*virtual*/ mork_bool // *((void**) inKeyA) == *((void**) inKeyB) +morkPointerMap::Equal(morkEnv* ev, const void* inKeyA, + const void* inKeyB) const { + MORK_USED_1(ev); + return *((const void**)inKeyA) == *((const void**)inKeyB); +} + +/*virtual*/ mork_u4 // some integer function of *((mork_u4*) inKey) +morkPointerMap::Hash(morkEnv* ev, const void* inKey) const { + MORK_USED_1(ev); + return *((const mork_u4*)inKey); +} +// } ===== end morkMap poly interface ===== + +mork_bool morkPointerMap::AddPointer(morkEnv* ev, void* inKey, void* ioAddress) +// the AddPointer() method return value equals ev->Good(). +{ + if (ev->Good()) { + this->Put(ev, &inKey, &ioAddress, + /*key*/ (void*)0, /*val*/ (void*)0, (mork_change**)0); + } + + return ev->Good(); +} + +mork_bool morkPointerMap::CutPointer(morkEnv* ev, void* inKey) { + return this->Cut(ev, &inKey, /*key*/ (void*)0, /*val*/ (void*)0, + (mork_change**)0); +} + +void* morkPointerMap::GetPointer(morkEnv* ev, void* inKey) +// Note the returned val does NOT have an increase in refcount for this. +{ + void* val = 0; // old val in the map + this->Get(ev, &inKey, /*key*/ (void*)0, &val, (mork_change**)0); + + return val; +} + +mork_bool morkPointerMap::HasPointer(morkEnv* ev, void* inKey) +// Note the returned val does NOT have an increase in refcount for this. +{ + return this->Get(ev, &inKey, /*key*/ (void*)0, /*val*/ (void*)0, + (mork_change**)0); +} +#endif /*MORK_POINTER_MAP_IMPL*/ + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkIntMap.h b/comm/mailnews/db/mork/morkIntMap.h new file mode 100644 index 0000000000..97f9c4b977 --- /dev/null +++ b/comm/mailnews/db/mork/morkIntMap.h @@ -0,0 +1,144 @@ +/* -*- 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 _MORKINTMAP_ +#define _MORKINTMAP_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kIntMap /*i*/ 0x694D /* ascii 'iM' */ + +#define morkIntMap_kStartSlotCount 256 + +/*| morkIntMap: maps mork_token -> morkNode +|*/ +class morkIntMap : public morkMap { // for mapping tokens to maps + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseIntMap() only if open + virtual ~morkIntMap(); // assert that CloseIntMap() executed earlier + + public: // morkMap construction & destruction + // keySize for morkIntMap equals sizeof(mork_u4) + morkIntMap(morkEnv* ev, const morkUsage& inUsage, mork_size inValSize, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, + mork_bool inHoldChanges); + void CloseIntMap(morkEnv* ev); // called by CloseMorkNode(); + + public: // dynamic type identification + mork_bool IsIntMap() const { + return IsNode() && mNode_Derived == morkDerived_kIntMap; + } + // } ===== end morkNode methods ===== + + // { ===== begin morkMap poly interface ===== + virtual mork_bool // *((mork_u4*) inKeyA) == *((mork_u4*) inKeyB) + Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override; + + virtual mork_u4 // some integer function of *((mork_u4*) inKey) + Hash(morkEnv* ev, const void* inKey) const override; + // } ===== end morkMap poly interface ===== + + public: // other map methods + mork_bool AddInt(morkEnv* ev, mork_u4 inKey, void* ioAddress); + // the AddInt() boolean return equals ev->Good(). + + mork_bool CutInt(morkEnv* ev, mork_u4 inKey); + // The CutInt() boolean return indicates whether removal happened. + + void* GetInt(morkEnv* ev, mork_u4 inKey); + // Note the returned node does NOT have an increase in refcount for this. + + mork_bool HasInt(morkEnv* ev, mork_u4 inKey); + // Note the returned node does NOT have an increase in refcount for this. +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#ifdef MORK_POINTER_MAP_IMPL + +# define morkDerived_kPointerMap /*i*/ 0x704D /* ascii 'pM' */ + +# define morkPointerMap_kStartSlotCount 256 + +/*| morkPointerMap: maps void* -> void* +**| +**| This pointer map class is equivalent to morkIntMap when sizeof(mork_u4) +**| equals sizeof(void*). However, when these two sizes are different, +**| then we cannot use the same hash table structure very easily without +**| being very careful about the size and usage assumptions of those +**| clients using the smaller data type. So we just go ahead and use +**| morkPointerMap for hash tables using pointer key types. +|*/ +class morkPointerMap : public morkMap { // for mapping tokens to maps + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // ClosePointerMap() only if open + virtual ~morkPointerMap(); // assert that ClosePointerMap() executed earlier + + public: // morkMap construction & destruction + // keySize for morkPointerMap equals sizeof(mork_u4) + morkPointerMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + void ClosePointerMap(morkEnv* ev); // called by CloseMorkNode(); + + public: // dynamic type identification + mork_bool IsPointerMap() const { + return IsNode() && mNode_Derived == morkDerived_kPointerMap; + } + // } ===== end morkNode methods ===== + + // { ===== begin morkMap poly interface ===== + virtual mork_bool // *((void**) inKeyA) == *((void**) inKeyB) + Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const; + + virtual mork_u4 // some integer function of *((mork_u4*) inKey) + Hash(morkEnv* ev, const void* inKey) const; + // } ===== end morkMap poly interface ===== + + public: // other map methods + mork_bool AddPointer(morkEnv* ev, void* inKey, void* ioAddress); + // the AddPointer() boolean return equals ev->Good(). + + mork_bool CutPointer(morkEnv* ev, void* inKey); + // The CutPointer() boolean return indicates whether removal happened. + + void* GetPointer(morkEnv* ev, void* inKey); + // Note the returned node does NOT have an increase in refcount for this. + + mork_bool HasPointer(morkEnv* ev, void* inKey); + // Note the returned node does NOT have an increase in refcount for this. + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakIntMap(morkIntMap* me, morkEnv* ev, morkIntMap** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongIntMap(morkIntMap* me, morkEnv* ev, + morkIntMap** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; +#endif /*MORK_POINTER_MAP_IMPL*/ + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKINTMAP_ */ diff --git a/comm/mailnews/db/mork/morkMap.cpp b/comm/mailnews/db/mork/morkMap.cpp new file mode 100644 index 0000000000..22e57b4df5 --- /dev/null +++ b/comm/mailnews/db/mork/morkMap.cpp @@ -0,0 +1,852 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// This code is mostly a port to C++ from public domain IronDoc C sources. +// Note many code comments here come verbatim from cut-and-pasted IronDoc. + +#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 + +class morkHashArrays { + public: + nsIMdbHeap* mHashArrays_Heap; // copy of mMap_Heap + mork_count mHashArrays_Slots; // copy of mMap_Slots + + mork_u1* mHashArrays_Keys; // copy of mMap_Keys + mork_u1* mHashArrays_Vals; // copy of mMap_Vals + morkAssoc* mHashArrays_Assocs; // copy of mMap_Assocs + mork_change* mHashArrays_Changes; // copy of mMap_Changes + morkAssoc** mHashArrays_Buckets; // copy of mMap_Buckets + morkAssoc* mHashArrays_FreeList; // copy of mMap_FreeList + + public: + void finalize(morkEnv* ev); +}; + +void morkHashArrays::finalize(morkEnv* ev) { + nsIMdbEnv* menv = ev->AsMdbEnv(); + nsIMdbHeap* heap = mHashArrays_Heap; + + if (heap) { + if (mHashArrays_Keys) heap->Free(menv, mHashArrays_Keys); + if (mHashArrays_Vals) heap->Free(menv, mHashArrays_Vals); + if (mHashArrays_Assocs) heap->Free(menv, mHashArrays_Assocs); + if (mHashArrays_Changes) heap->Free(menv, mHashArrays_Changes); + if (mHashArrays_Buckets) heap->Free(menv, mHashArrays_Buckets); + } +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkMap::CloseMorkNode( + morkEnv* ev) // CloseMap() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkMap::~morkMap() // assert CloseMap() executed earlier +{ + MORK_ASSERT(mMap_FreeList == 0); + MORK_ASSERT(mMap_Buckets == 0); + MORK_ASSERT(mMap_Keys == 0); + MORK_ASSERT(mMap_Vals == 0); + MORK_ASSERT(mMap_Changes == 0); + MORK_ASSERT(mMap_Assocs == 0); +} + +/*public non-poly*/ void morkMap::CloseMap( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + nsIMdbHeap* heap = mMap_Heap; + if (heap) /* need to free the arrays? */ + { + nsIMdbEnv* menv = ev->AsMdbEnv(); + + if (mMap_Keys) heap->Free(menv, mMap_Keys); + + if (mMap_Vals) heap->Free(menv, mMap_Vals); + + if (mMap_Assocs) heap->Free(menv, mMap_Assocs); + + if (mMap_Buckets) heap->Free(menv, mMap_Buckets); + + if (mMap_Changes) heap->Free(menv, mMap_Changes); + } + mMap_Keys = 0; + mMap_Vals = 0; + mMap_Buckets = 0; + mMap_Assocs = 0; + mMap_Changes = 0; + mMap_FreeList = 0; + MORK_MEMSET(&mMap_Form, 0, sizeof(morkMapForm)); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +void morkMap::clear_map(morkEnv* ev, nsIMdbHeap* ioSlotHeap) { + mMap_Tag = 0; + mMap_Seed = 0; + mMap_Slots = 0; + mMap_Fill = 0; + mMap_Keys = 0; + mMap_Vals = 0; + mMap_Assocs = 0; + mMap_Changes = 0; + mMap_Buckets = 0; + mMap_FreeList = 0; + MORK_MEMSET(&mMap_Form, 0, sizeof(morkMapForm)); + + mMap_Heap = 0; + if (ioSlotHeap) { + nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mMap_Heap); + } else + ev->NilPointerError(); +} + +morkMap::morkMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_size inKeySize, mork_size inValSize, mork_size inSlots, + nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges) + : morkNode(ev, inUsage, ioHeap), mMap_Heap(0) { + if (ev->Good()) { + this->clear_map(ev, ioSlotHeap); + if (ev->Good()) { + mMap_Form.mMapForm_HoldChanges = inHoldChanges; + + mMap_Form.mMapForm_KeySize = inKeySize; + mMap_Form.mMapForm_ValSize = inValSize; + mMap_Form.mMapForm_KeyIsIP = (inKeySize == sizeof(mork_ip)); + mMap_Form.mMapForm_ValIsIP = (inValSize == sizeof(mork_ip)); + + this->InitMap(ev, inSlots); + if (ev->Good()) mNode_Derived = morkDerived_kMap; + } + } +} + +void morkMap::NewIterOutOfSyncError(morkEnv* ev) { + ev->NewError("map iter out of sync"); +} + +void morkMap::NewBadMapError(morkEnv* ev) { ev->NewError("bad morkMap tag"); } + +void morkMap::NewSlotsUnderflowWarning(morkEnv* ev) { + ev->NewWarning("member count underflow"); +} + +void morkMap::InitMap(morkEnv* ev, mork_size inSlots) { + if (ev->Good()) { + morkHashArrays old; + // MORK_MEMCPY(&mMap_Form, &inForm, sizeof(morkMapForm)); + if (inSlots < 3) /* requested capacity absurdly small? */ + inSlots = 3; /* bump it up to a minimum practical level */ + else if (inSlots > (128 * 1024)) /* requested slots absurdly big? */ + inSlots = (128 * 1024); /* decrease it to a maximum practical level */ + + if (this->new_arrays(ev, &old, inSlots)) mMap_Tag = morkMap_kTag; + + MORK_MEMSET(&old, 0, sizeof(morkHashArrays)); // do NOT finalize + } +} + +morkAssoc** morkMap::find(morkEnv* ev, const void* inKey, + mork_u4 inHash) const { + mork_u1* keys = mMap_Keys; + mork_num keySize = this->FormKeySize(); + // morkMap_mEqual equal = this->FormEqual(); + + morkAssoc** ref = mMap_Buckets + (inHash % mMap_Slots); + morkAssoc* assoc = *ref; + while (assoc) /* look at another assoc in the bucket? */ + { + mork_pos i = assoc - mMap_Assocs; /* index of this assoc */ + if (this->Equal(ev, keys + (i * keySize), inKey)) /* found? */ + return ref; + + ref = &assoc->mAssoc_Next; /* consider next assoc slot in bucket */ + assoc = *ref; /* if this is null, then we are done */ + } + return 0; +} + +/*| get_assoc: read the key and/or value at index inPos +|*/ +void morkMap::get_assoc(void* outKey, void* outVal, mork_pos inPos) const { + mork_num valSize = this->FormValSize(); + if (valSize && outVal) /* map holds values? caller wants the value? */ + { + const mork_u1* value = mMap_Vals + (valSize * inPos); + if (valSize == sizeof(mork_ip) && this->FormValIsIP()) /* ip case? */ + *((mork_ip*)outVal) = *((const mork_ip*)value); + else + MORK_MEMCPY(outVal, value, valSize); + } + if (outKey) /* caller wants the key? */ + { + mork_num keySize = this->FormKeySize(); + const mork_u1* key = mMap_Keys + (keySize * inPos); + if (keySize == sizeof(mork_ip) && this->FormKeyIsIP()) /* ip case? */ + *((mork_ip*)outKey) = *((const mork_ip*)key); + else + MORK_MEMCPY(outKey, key, keySize); + } +} + +/*| put_assoc: write the key and/or value at index inPos +|*/ +void morkMap::put_assoc(const void* inKey, const void* inVal, + mork_pos inPos) const { + mork_num valSize = this->FormValSize(); + if (valSize && inVal) /* map holds values? caller sends a value? */ + { + mork_u1* value = mMap_Vals + (valSize * inPos); + if (valSize == sizeof(mork_ip) && this->FormValIsIP()) /* ip case? */ + *((mork_ip*)value) = *((const mork_ip*)inVal); + else + MORK_MEMCPY(value, inVal, valSize); + } + if (inKey) /* caller sends a key? */ + { + mork_num keySize = this->FormKeySize(); + mork_u1* key = mMap_Keys + (keySize * inPos); + if (keySize == sizeof(mork_ip) && this->FormKeyIsIP()) /* ip case? */ + *((mork_ip*)key) = *((const mork_ip*)inKey); + else + MORK_MEMCPY(key, inKey, keySize); + } +} + +void* morkMap::clear_alloc(morkEnv* ev, mork_size inSize) { + void* p = 0; + nsIMdbHeap* heap = mMap_Heap; + if (heap) { + if (NS_SUCCEEDED(heap->Alloc(ev->AsMdbEnv(), inSize, (void**)&p)) && p) { + MORK_MEMSET(p, 0, inSize); + return p; + } + } else + ev->NilPointerError(); + + return (void*)0; +} + +void* morkMap::alloc(morkEnv* ev, mork_size inSize) { + void* p = 0; + nsIMdbHeap* heap = mMap_Heap; + if (heap) { + if (NS_SUCCEEDED(heap->Alloc(ev->AsMdbEnv(), inSize, (void**)&p)) && p) + return p; + } else + ev->NilPointerError(); + + return (void*)0; +} + +/*| new_keys: allocate an array of inSlots new keys filled with zero. +|*/ +mork_u1* morkMap::new_keys(morkEnv* ev, mork_num inSlots) { + mork_num size = inSlots * this->FormKeySize(); + return (mork_u1*)this->clear_alloc(ev, size); +} + +/*| new_values: allocate an array of inSlots new values filled with zero. +**| When values are zero sized, we just return a null pointer. +|*/ +mork_u1* morkMap::new_values(morkEnv* ev, mork_num inSlots) { + mork_u1* values = 0; + mork_num size = inSlots * this->FormValSize(); + if (size) values = (mork_u1*)this->clear_alloc(ev, size); + return values; +} + +mork_change* morkMap::new_changes(morkEnv* ev, mork_num inSlots) { + mork_change* changes = 0; + mork_num size = inSlots * sizeof(mork_change); + if (size && mMap_Form.mMapForm_HoldChanges) + changes = (mork_change*)this->clear_alloc(ev, size); + return changes; +} + +/*| new_buckets: allocate an array of inSlots new buckets filled with zero. +|*/ +morkAssoc** morkMap::new_buckets(morkEnv* ev, mork_num inSlots) { + mork_num size = inSlots * sizeof(morkAssoc*); + return (morkAssoc**)this->clear_alloc(ev, size); +} + +/*| new_assocs: allocate an array of inSlots new assocs, with each assoc +**| linked together in a list with the first array element at the list head +**| and the last element at the list tail. (morkMap::grow() needs this.) +|*/ +morkAssoc* morkMap::new_assocs(morkEnv* ev, mork_num inSlots) { + mork_num size = inSlots * sizeof(morkAssoc); + morkAssoc* assocs = (morkAssoc*)this->alloc(ev, size); + if (assocs) /* able to allocate the array? */ + { + morkAssoc* a = assocs + (inSlots - 1); /* the last array element */ + a->mAssoc_Next = 0; /* terminate tail element of the list with null */ + while (--a >= assocs) /* another assoc to link into the list? */ + a->mAssoc_Next = a + 1; /* each points to the following assoc */ + } + return assocs; +} + +mork_bool morkMap::new_arrays(morkEnv* ev, morkHashArrays* old, + mork_num inSlots) { + mork_bool outNew = morkBool_kFalse; + + /* see if we can allocate all the new arrays before we go any further: */ + morkAssoc** newBuckets = this->new_buckets(ev, inSlots); + morkAssoc* newAssocs = this->new_assocs(ev, inSlots); + mork_u1* newKeys = this->new_keys(ev, inSlots); + mork_u1* newValues = this->new_values(ev, inSlots); + mork_change* newChanges = this->new_changes(ev, inSlots); + + /* it is okay for newChanges to be null when changes are not held: */ + mork_bool okayChanges = (newChanges || !this->FormHoldChanges()); + + /* it is okay for newValues to be null when values are zero sized: */ + mork_bool okayValues = (newValues || !this->FormValSize()); + + if (newBuckets && newAssocs && newKeys && okayChanges && okayValues) { + outNew = morkBool_kTrue; /* yes, we created all the arrays we need */ + + /* init the old hashArrays with slots from this hash table: */ + old->mHashArrays_Heap = mMap_Heap; + + old->mHashArrays_Slots = mMap_Slots; + old->mHashArrays_Keys = mMap_Keys; + old->mHashArrays_Vals = mMap_Vals; + old->mHashArrays_Assocs = mMap_Assocs; + old->mHashArrays_Buckets = mMap_Buckets; + old->mHashArrays_Changes = mMap_Changes; + + /* now replace all our array slots with the new structures: */ + ++mMap_Seed; /* note the map is now changed */ + mMap_Keys = newKeys; + mMap_Vals = newValues; + mMap_Buckets = newBuckets; + mMap_Assocs = newAssocs; + mMap_FreeList = newAssocs; /* all are free to start with */ + mMap_Changes = newChanges; + mMap_Slots = inSlots; + } else /* free the partial set of arrays that were actually allocated */ + { + nsIMdbEnv* menv = ev->AsMdbEnv(); + nsIMdbHeap* heap = mMap_Heap; + if (newBuckets) heap->Free(menv, newBuckets); + if (newAssocs) heap->Free(menv, newAssocs); + if (newKeys) heap->Free(menv, newKeys); + if (newValues) heap->Free(menv, newValues); + if (newChanges) heap->Free(menv, newChanges); + + MORK_MEMSET(old, 0, sizeof(morkHashArrays)); + } + + return outNew; +} + +/*| grow: make the map arrays bigger by 33%. The old map is completely +**| full, or else we would not have called grow() to get more space. This +**| means the free list is empty, and also means every old key and value is in +**| use in the old arrays. So every key and value must be copied to the new +**| arrays, and since they are contiguous in the old arrays, we can efficiently +**| bitwise copy them in bulk from the old arrays to the new arrays, without +**| paying any attention to the structure of the old arrays. +**| +**|| The content of the old bucket and assoc arrays need not be copied because +**| this information is going to be completely rebuilt by rehashing all the +**| keys into their new buckets, given the new larger map capacity. The new +**| bucket array from new_arrays() is assumed to contain all zeroes, which is +**| necessary to ensure the lists in each bucket stay null terminated as we +**| build the new linked lists. (Note no old bucket ordering is preserved.) +**| +**|| If the old capacity is N, then in the new arrays the assocs with indexes +**| from zero to N-1 are still allocated and must be rehashed into the map. +**| The new free list contains all the following assocs, starting with the new +**| assoc link at index N. (We call the links in the link array "assocs" +**| because allocating a link is the same as allocating the key/value pair +**| with the same index as the link.) +**| +**|| The new free list is initialized simply by pointing at the first new link +**| in the assoc array after the size of the old assoc array. This assumes +**| that FeHashTable_new_arrays() has already linked all the new assocs into a +**| list with the first at the head of the list and the last at the tail of the +**| list. So by making the free list point to the first of the new uncopied +**| assocs, the list is already well-formed. +|*/ +mork_bool morkMap::grow(morkEnv* ev) { + if (mMap_Heap) /* can we grow the map? */ + { + mork_num newSlots = (mMap_Slots * 2); /* +100% */ + morkHashArrays old; /* a place to temporarily hold all the old arrays */ + if (this->new_arrays(ev, &old, newSlots)) /* have more? */ + { + // morkMap_mHash hash = this->FormHash(); /* for terse loop use */ + + /* figure out the bulk volume sizes of old keys and values to move: */ + mork_num oldSlots = old.mHashArrays_Slots; /* number of old assocs */ + mork_num keyBulk = oldSlots * this->FormKeySize(); /* key volume */ + mork_num valBulk = oldSlots * this->FormValSize(); /* values */ + + /* convenient variables for new arrays that need to be rehashed: */ + morkAssoc** newBuckets = mMap_Buckets; /* new all zeroes */ + morkAssoc* newAssocs = mMap_Assocs; /* hash into buckets */ + morkAssoc* newFreeList = newAssocs + oldSlots; /* new room is free */ + mork_u1* key = mMap_Keys; /* the first key to rehash */ + --newAssocs; /* back up one before preincrement used in while loop */ + + /* move all old keys and values to the new arrays: */ + MORK_MEMCPY(mMap_Keys, old.mHashArrays_Keys, keyBulk); + if (valBulk) /* are values nonzero sized? */ + MORK_MEMCPY(mMap_Vals, old.mHashArrays_Vals, valBulk); + + mMap_FreeList = newFreeList; /* remaining assocs are free */ + + while (++newAssocs < newFreeList) /* rehash another old assoc? */ + { + morkAssoc** top = newBuckets + (this->Hash(ev, key) % newSlots); + key += this->FormKeySize(); /* the next key to rehash */ + newAssocs->mAssoc_Next = *top; /* link to prev bucket top */ + *top = newAssocs; /* push assoc on top of bucket */ + } + ++mMap_Seed; /* note the map has changed */ + old.finalize(ev); /* remember to free the old arrays */ + } + } else + ev->OutOfMemoryError(); + + return ev->Good(); +} + +mork_bool morkMap::Put(morkEnv* ev, const void* inKey, const void* inVal, + void* outKey, void* outVal, mork_change** outChange) { + mork_bool outPut = morkBool_kFalse; + + if (this->GoodMap()) /* looks good? */ + { + mork_u4 hash = this->Hash(ev, inKey); + morkAssoc** ref = this->find(ev, inKey, hash); + if (ref) /* assoc was found? reuse an existing assoc slot? */ + { + outPut = morkBool_kTrue; /* inKey was indeed already inside the map */ + } else /* assoc not found -- need to allocate a new assoc slot */ + { + morkAssoc* assoc = this->pop_free_assoc(); + if (!assoc) /* no slots remaining in free list? must grow map? */ + { + if (this->grow(ev)) /* successfully made map larger? */ + assoc = this->pop_free_assoc(); + } + if (assoc) /* allocated new assoc without error? */ + { + ref = mMap_Buckets + (hash % mMap_Slots); + assoc->mAssoc_Next = *ref; /* link to prev bucket top */ + *ref = assoc; /* push assoc on top of bucket */ + + ++mMap_Fill; /* one more member in the collection */ + ++mMap_Seed; /* note the map has changed */ + } + } + if (ref) /* did not have an error during possible growth? */ + { + mork_pos i = (*ref) - mMap_Assocs; /* index of assoc */ + if (outPut && (outKey || outVal)) /* copy old before cobbering? */ + this->get_assoc(outKey, outVal, i); + + this->put_assoc(inKey, inVal, i); + ++mMap_Seed; /* note the map has changed */ + + if (outChange) { + if (mMap_Changes) + *outChange = mMap_Changes + i; + else + *outChange = this->FormDummyChange(); + } + } + } else + this->NewBadMapError(ev); + + return outPut; +} + +mork_num morkMap::CutAll(morkEnv* ev) { + mork_num outCutAll = 0; + + if (this->GoodMap()) /* map looks good? */ + { + mork_num slots = mMap_Slots; + morkAssoc* before = mMap_Assocs - 1; /* before first member */ + morkAssoc* assoc = before + slots; /* the very last member */ + + ++mMap_Seed; /* note the map is changed */ + + /* make the assoc array a linked list headed by first & tailed by last: */ + assoc->mAssoc_Next = 0; /* null terminate the new free list */ + while (--assoc > before) /* another assoc to link into the list? */ + assoc->mAssoc_Next = assoc + 1; + mMap_FreeList = mMap_Assocs; /* all are free */ + + outCutAll = mMap_Fill; /* we'll cut all of them of course */ + + mMap_Fill = 0; /* the map is completely empty */ + } else + this->NewBadMapError(ev); + + return outCutAll; +} + +mork_bool morkMap::Cut(morkEnv* ev, const void* inKey, void* outKey, + void* outVal, mork_change** outChange) { + mork_bool outCut = morkBool_kFalse; + + if (this->GoodMap()) /* looks good? */ + { + mork_u4 hash = this->Hash(ev, inKey); + morkAssoc** ref = this->find(ev, inKey, hash); + if (ref) /* found an assoc for key? */ + { + outCut = morkBool_kTrue; + morkAssoc* assoc = *ref; + mork_pos i = assoc - mMap_Assocs; /* index of assoc */ + if (outKey || outVal) this->get_assoc(outKey, outVal, i); + + *ref = assoc->mAssoc_Next; /* unlink the found assoc */ + this->push_free_assoc(assoc); /* and put it in free list */ + + if (outChange) { + if (mMap_Changes) + *outChange = mMap_Changes + i; + else + *outChange = this->FormDummyChange(); + } + + ++mMap_Seed; /* note the map has changed */ + if (mMap_Fill) /* the count shows nonzero members? */ + --mMap_Fill; /* one less member in the collection */ + else + this->NewSlotsUnderflowWarning(ev); + } + } else + this->NewBadMapError(ev); + + return outCut; +} + +mork_bool morkMap::Get(morkEnv* ev, const void* inKey, void* outKey, + void* outVal, mork_change** outChange) { + mork_bool outGet = morkBool_kFalse; + + if (this->GoodMap()) /* looks good? */ + { + mork_u4 hash = this->Hash(ev, inKey); + morkAssoc** ref = this->find(ev, inKey, hash); + if (ref) /* found an assoc for inKey? */ + { + mork_pos i = (*ref) - mMap_Assocs; /* index of assoc */ + outGet = morkBool_kTrue; + this->get_assoc(outKey, outVal, i); + if (outChange) { + if (mMap_Changes) + *outChange = mMap_Changes + i; + else + *outChange = this->FormDummyChange(); + } + } + } else + this->NewBadMapError(ev); + + return outGet; +} + +morkMapIter::morkMapIter() + : mMapIter_Map(0), + mMapIter_Seed(0) + + , + mMapIter_Bucket(0), + mMapIter_AssocRef(0), + mMapIter_Assoc(0), + mMapIter_Next(0) {} + +void morkMapIter::InitMapIter(morkEnv* ev, morkMap* ioMap) { + mMapIter_Map = 0; + mMapIter_Seed = 0; + + mMapIter_Bucket = 0; + mMapIter_AssocRef = 0; + mMapIter_Assoc = 0; + mMapIter_Next = 0; + + if (ioMap) { + if (ioMap->GoodMap()) { + mMapIter_Map = ioMap; + mMapIter_Seed = ioMap->mMap_Seed; + } else + ioMap->NewBadMapError(ev); + } else + ev->NilPointerError(); +} + +morkMapIter::morkMapIter(morkEnv* ev, morkMap* ioMap) + : mMapIter_Map(0), + mMapIter_Seed(0) + + , + mMapIter_Bucket(0), + mMapIter_AssocRef(0), + mMapIter_Assoc(0), + mMapIter_Next(0) { + if (ioMap) { + if (ioMap->GoodMap()) { + mMapIter_Map = ioMap; + mMapIter_Seed = ioMap->mMap_Seed; + } else + ioMap->NewBadMapError(ev); + } else + ev->NilPointerError(); +} + +void morkMapIter::CloseMapIter(morkEnv* ev) { + MORK_USED_1(ev); + mMapIter_Map = 0; + mMapIter_Seed = 0; + + mMapIter_Bucket = 0; + mMapIter_AssocRef = 0; + mMapIter_Assoc = 0; + mMapIter_Next = 0; +} + +mork_change* morkMapIter::First(morkEnv* ev, void* outKey, void* outVal) { + mork_change* outFirst = 0; + + morkMap* map = mMapIter_Map; + + if (map && map->GoodMap()) /* map looks good? */ + { + morkAssoc** bucket = map->mMap_Buckets; + morkAssoc** end = bucket + map->mMap_Slots; /* one past last */ + + mMapIter_Seed = map->mMap_Seed; /* sync the seeds */ + + while (bucket < end) /* another bucket in which to look for assocs? */ + { + morkAssoc* assoc = *bucket++; + if (assoc) /* found the first map assoc in use? */ + { + mork_pos i = assoc - map->mMap_Assocs; + mork_change* c = map->mMap_Changes; + outFirst = (c) ? (c + i) : map->FormDummyChange(); + + mMapIter_Assoc = assoc; /* current assoc in iteration */ + mMapIter_Next = assoc->mAssoc_Next; /* more in bucket */ + mMapIter_Bucket = --bucket; /* bucket for this assoc */ + mMapIter_AssocRef = bucket; /* slot referencing assoc */ + + map->get_assoc(outKey, outVal, i); + + break; /* end while loop */ + } + } + } else + map->NewBadMapError(ev); + + return outFirst; +} + +mork_change* morkMapIter::Next(morkEnv* ev, void* outKey, void* outVal) { + mork_change* outNext = 0; + + morkMap* map = mMapIter_Map; + + if (map && map->GoodMap()) /* map looks good? */ + { + if (mMapIter_Seed == map->mMap_Seed) /* in sync? */ + { + morkAssoc* here = mMapIter_Assoc; /* current assoc */ + if (here) /* iteration is not yet concluded? */ + { + morkAssoc* next = mMapIter_Next; + morkAssoc* assoc = next; /* default new mMapIter_Assoc */ + if (next) /* there are more assocs in the same bucket after Here? */ + { + morkAssoc** ref = mMapIter_AssocRef; + + /* (*HereRef) equals Here, except when Here has been cut, after + ** which (*HereRef) always equals Next. So if (*HereRef) is not + ** equal to Next, then HereRef still needs to be updated to point + ** somewhere else other than Here. Otherwise it is fine. + */ + if (*ref != next) /* Here was not cut? must update HereRef? */ + mMapIter_AssocRef = &here->mAssoc_Next; + + mMapIter_Next = next->mAssoc_Next; + } else /* look for the next assoc in the next nonempty bucket */ + { + morkAssoc** bucket = map->mMap_Buckets; + morkAssoc** end = bucket + map->mMap_Slots; /* beyond */ + mMapIter_Assoc = 0; /* default to no more assocs */ + bucket = mMapIter_Bucket; /* last exhausted bucket */ + mMapIter_Assoc = 0; /* default to iteration ended */ + + while (++bucket < end) /* another bucket to search for assocs? */ + { + assoc = *bucket; + if (assoc) /* found another map assoc in use? */ + { + mMapIter_Bucket = bucket; + mMapIter_AssocRef = bucket; /* ref to assoc */ + mMapIter_Next = assoc->mAssoc_Next; /* more */ + + break; /* end while loop */ + } + } + } + if (assoc) /* did we find another assoc in the iteration? */ + { + mMapIter_Assoc = assoc; /* current assoc */ + mork_pos i = assoc - map->mMap_Assocs; + mork_change* c = map->mMap_Changes; + outNext = (c) ? (c + i) : map->FormDummyChange(); + + map->get_assoc(outKey, outVal, i); + } + } + } else + map->NewIterOutOfSyncError(ev); + } else + map->NewBadMapError(ev); + + return outNext; +} + +mork_change* morkMapIter::Here(morkEnv* ev, void* outKey, void* outVal) { + mork_change* outHere = 0; + + morkMap* map = mMapIter_Map; + + if (map && map->GoodMap()) /* map looks good? */ + { + if (mMapIter_Seed == map->mMap_Seed) /* in sync? */ + { + morkAssoc* here = mMapIter_Assoc; /* current assoc */ + if (here) /* iteration is not yet concluded? */ + { + mork_pos i = here - map->mMap_Assocs; + mork_change* c = map->mMap_Changes; + outHere = (c) ? (c + i) : map->FormDummyChange(); + + map->get_assoc(outKey, outVal, i); + } + } else + map->NewIterOutOfSyncError(ev); + } else + map->NewBadMapError(ev); + + return outHere; +} + +mork_change* morkMapIter::CutHere(morkEnv* ev, void* outKey, void* outVal) { + mork_change* outCutHere = 0; + morkMap* map = mMapIter_Map; + + if (map && map->GoodMap()) /* map looks good? */ + { + if (mMapIter_Seed == map->mMap_Seed) /* in sync? */ + { + morkAssoc* here = mMapIter_Assoc; /* current assoc */ + if (here) /* iteration is not yet concluded? */ + { + morkAssoc** ref = mMapIter_AssocRef; + if (*ref != mMapIter_Next) /* not already cut? */ + { + mork_pos i = here - map->mMap_Assocs; + mork_change* c = map->mMap_Changes; + outCutHere = (c) ? (c + i) : map->FormDummyChange(); + if (outKey || outVal) map->get_assoc(outKey, outVal, i); + + map->push_free_assoc(here); /* add to free list */ + *ref = mMapIter_Next; /* unlink here from bucket list */ + + /* note the map has changed, but we are still in sync: */ + mMapIter_Seed = ++map->mMap_Seed; /* sync */ + + if (map->mMap_Fill) /* still has nonzero value? */ + --map->mMap_Fill; /* one less member in the collection */ + else + map->NewSlotsUnderflowWarning(ev); + } + } + } else + map->NewIterOutOfSyncError(ev); + } else + map->NewBadMapError(ev); + + return outCutHere; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkMap.h b/comm/mailnews/db/mork/morkMap.h new file mode 100644 index 0000000000..0275755c4f --- /dev/null +++ b/comm/mailnews/db/mork/morkMap.h @@ -0,0 +1,379 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MORKMAP_ +#define _MORKMAP_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/* (These hash methods closely resemble those in public domain IronDoc.) */ + +/*| Equal: equal for hash table. Note equal(a,b) implies hash(a)==hash(b). +|*/ +typedef mork_bool (*morkMap_mEqual)(const morkMap* self, morkEnv* ev, + const void* inKeyA, const void* inKeyB); + +/*| Hash: hash for hash table. Note equal(a,b) implies hash(a)==hash(b). +|*/ +typedef mork_u4 (*morkMap_mHash)(const morkMap* self, morkEnv* ev, + const void* inKey); + +/*| IsNil: whether a key slot contains a "null" value denoting "no such key". +|*/ +typedef mork_bool (*morkMap_mIsNil)(const morkMap* self, morkEnv* ev, + const void* inKey); + +/*| Note: notify regarding a refcounting change for a key or a value. +|*/ +// typedef void (* morkMap_mNote) +//(morkMap* self, morkEnv* ev, void* inKeyOrVal); + +/*| morkMapForm: slots need to initialize a new dict. (This is very similar +**| to the config object for public domain IronDoc hash tables.) +|*/ +class morkMapForm { // a struct of callback method pointers for morkMap + public: + // const void* mMapForm_NilKey; // externally defined 'nil' bit pattern + + // void* mMapForm_NilBuf[ 8 ]; // potential place to put NilKey + // If keys are no larger than 8*sizeof(void*), NilKey can be put in NilBuf. + // Note this should be true for all Mork subclasses, and we plan usage so. + + // These three methods must always be provided, so zero will cause errors: + + // morkMap_mEqual mMapForm_Equal; // for comparing two keys for identity + // morkMap_mHash mMapForm_Hash; // deterministic key to hash method + // morkMap_mIsNil mMapForm_IsNil; // to query whether a key equals 'nil' + + // If any of these method slots are nonzero, then morkMap will call the + // appropriate one to notify dict users when a key or value is added or cut. + // Presumably a caller wants to know this in order to perform refcounting or + // some other kind of memory management. These methods are definitely only + // called when references to keys or values are inserted or removed, and are + // never called when the actual number of references does not change (such + // as when added keys are already present or cut keys are alreading missing). + // + // morkMap_mNote mMapForm_AddKey; // if nonzero, notify about add key + // morkMap_mNote mMapForm_CutKey; // if nonzero, notify about cut key + // morkMap_mNote mMapForm_AddVal; // if nonzero, notify about add val + // morkMap_mNote mMapForm_CutVal; // if nonzero, notify about cut val + // + // These note methods have been removed because it seems difficult to + // guarantee suitable alignment of objects passed to notification methods. + + // Note dict clients should pick key and val sizes that provide whatever + // alignment will be required for an array of such keys and values. + mork_size mMapForm_KeySize; // size of every key (cannot be zero) + mork_size mMapForm_ValSize; // size of every val (can indeed be zero) + + mork_bool mMapForm_HoldChanges; // support changes array in the map + mork_change mMapForm_DummyChange; // change used for false HoldChanges + mork_bool mMapForm_KeyIsIP; // key is mork_ip sized + mork_bool mMapForm_ValIsIP; // key is mork_ip sized +}; + +/*| morkAssoc: a canonical association slot in a morkMap. A single assoc +**| instance does nothing except point to the next assoc in the same bucket +**| of a hash table. Each assoc has only two interesting attributes: 1) the +**| address of the assoc, and 2) the next assoc in a bucket's list. The assoc +**| address is interesting because in the context of an array of such assocs, +**| one can determine the index of a particular assoc in the array by address +**| arithmetic, subtracting the array address from the assoc address. And the +**| index of each assoc is the same index as the associated key and val slots +**| in the associated arrays +**| +**|| Think of an assoc instance as really also containing a key slot and a val +**| slot, where each key is mMap_Form.mMapForm_KeySize bytes in size, and +**| each val is mMap_Form.mMapForm_ValSize in size. But the key and val +**| slots are stored in separate arrays with indexes that are parallel to the +**| indexes in the array of morkAssoc instances. We have taken the variable +**| sized slots out of the morkAssoc structure, and put them into parallel +**| arrays associated with each morkAssoc by array index. And this leaves us +**| with only the link field to the next assoc in each assoc instance. +|*/ +class morkAssoc { + public: + morkAssoc* mAssoc_Next; +}; + +#define morkDerived_kMap /*i*/ 0x4D70 /* ascii 'Mp' */ + +#define morkMap_kTag /*i*/ 0x6D4D6150 /* ascii 'mMaP' */ + +/*| morkMap: a hash table based on the public domain IronDoc hash table +**| (which is in turn rather like a similar OpenDoc hash table). +|*/ +class morkMap : public morkNode { + // public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + public: // state is public because the entire Mork system is private + nsIMdbHeap* mMap_Heap; // strong ref to heap allocating all space + mork_u4 mMap_Tag; // must equal morkMap_kTag + + // When a morkMap instance is constructed, the dict form slots must be + // provided in order to properly configure a dict with all runtime needs: + + morkMapForm mMap_Form; // construction time parameterization + + // Whenever the dict changes structure in a way that would affect any + // iteration of the dict associations, the seed increments to show this: + + mork_seed mMap_Seed; // counter for member and structural changes + + // The current total assoc capacity of the dict is mMap_Slots, where + // mMap_Fill of these slots are actually holding content, so mMap_Fill + // is the actual membership count, and mMap_Slots is how larger membership + // can become before the hash table must grow the buffers being used. + + mork_count mMap_Slots; // count of slots in the hash table + mork_fill mMap_Fill; // number of used slots in the hash table + + // Key and value slots are bound to corresponding mMap_Assocs array slots. + // Instead of having a single array like this: {key,val,next}[ mMap_Slots ] + // we have instead three parallel arrays with essentially the same meaning: + // {key}[ mMap_Slots ], {val}[ mMap_Slots ], {assocs}[ mMap_Slots ] + + mork_u1* mMap_Keys; // mMap_Slots * mMapForm_KeySize buffer + mork_u1* mMap_Vals; // mMap_Slots * mMapForm_ValSize buffer + + // An assoc is "used" when it appears in a bucket's linked list of assocs. + // Until an assoc is used, it appears in the FreeList linked list. Every + // assoc that becomes used goes into the bucket determined by hashing the + // key associated with a new assoc. The key associated with a new assoc + // goes in to the slot in mMap_Keys which occupies exactly the same array + // index as the array index of the used assoc in the mMap_Assocs array. + + morkAssoc* mMap_Assocs; // mMap_Slots * sizeof(morkAssoc) buffer + + // The changes array is only needed when the + + mork_change* mMap_Changes; // mMap_Slots * sizeof(mork_change) buffer + + // The Buckets array need not be the same length as the Assocs array, but we + // usually do it that way so the average bucket depth is no more than one. + // (We could pick a different policy, or make it parameterizable, but that's + // tuning we can do some other time.) + + morkAssoc** mMap_Buckets; // mMap_Slots * sizeof(morkAssoc*) buffer + + // The length of the mMap_FreeList should equal (mMap_Slots - mMap_Fill). + // We need a free list instead of a simpler representation because assocs + // can be cut and returned to availability in any kind of unknown pattern. + // (However, when assocs are first allocated, or when the dict is grown, we + // know all new assocs are contiguous and can chain together adjacently.) + + morkAssoc* mMap_FreeList; // list of unused mMap_Assocs array slots + + public: // getters (morkProbeMap compatibility) + mork_fill MapFill() const { return mMap_Fill; } + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseMap() only if open + virtual ~morkMap(); // assert that CloseMap() executed earlier + + public: // morkMap construction & destruction + morkMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap, + mork_size inKeySize, mork_size inValSize, mork_size inSlots, + nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges); + + void CloseMap(morkEnv* ev); // called by + + public: // dynamic type identification + mork_bool IsMap() const { + return IsNode() && mNode_Derived == morkDerived_kMap; + } + // } ===== end morkNode methods ===== + + public: // poly map hash table methods + // { ===== begin morkMap poly interface ===== + virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b) + Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const = 0; + + virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b) + Hash(morkEnv* ev, const void* inKey) const = 0; + // } ===== end morkMap poly interface ===== + + public: // open utility methods + mork_bool GoodMapTag() const { return mMap_Tag == morkMap_kTag; } + mork_bool GoodMap() const { return (IsNode() && GoodMapTag()); } + + void NewIterOutOfSyncError(morkEnv* ev); + void NewBadMapError(morkEnv* ev); + void NewSlotsUnderflowWarning(morkEnv* ev); + void InitMap(morkEnv* ev, mork_size inSlots); + + protected: // internal utility methods + friend class morkMapIter; + void clear_map(morkEnv* ev, nsIMdbHeap* ioHeap); + + void* alloc(morkEnv* ev, mork_size inSize); + void* clear_alloc(morkEnv* ev, mork_size inSize); + + void push_free_assoc(morkAssoc* ioAssoc) { + ioAssoc->mAssoc_Next = mMap_FreeList; + mMap_FreeList = ioAssoc; + } + + morkAssoc* pop_free_assoc() { + morkAssoc* assoc = mMap_FreeList; + if (assoc) mMap_FreeList = assoc->mAssoc_Next; + return assoc; + } + + morkAssoc** find(morkEnv* ev, const void* inKey, mork_u4 inHash) const; + + mork_u1* new_keys(morkEnv* ev, mork_num inSlots); + mork_u1* new_values(morkEnv* ev, mork_num inSlots); + mork_change* new_changes(morkEnv* ev, mork_num inSlots); + morkAssoc** new_buckets(morkEnv* ev, mork_num inSlots); + morkAssoc* new_assocs(morkEnv* ev, mork_num inSlots); + mork_bool new_arrays(morkEnv* ev, morkHashArrays* old, mork_num inSlots); + + mork_bool grow(morkEnv* ev); + + void get_assoc(void* outKey, void* outVal, mork_pos inPos) const; + void put_assoc(const void* inKey, const void* inVal, mork_pos inPos) const; + + public: // inlines to form slots + // const void* FormNilKey() const { return mMap_Form.mMapForm_NilKey; } + + // morkMap_mEqual FormEqual() const { return mMap_Form.mMapForm_Equal; } + // morkMap_mHash FormHash() const { return mMap_Form.mMapForm_Hash; } + // orkMap_mIsNil FormIsNil() const { return mMap_Form.mMapForm_IsNil; } + + // morkMap_mNote FormAddKey() const { return mMap_Form.mMapForm_AddKey; } + // morkMap_mNote FormCutKey() const { return mMap_Form.mMapForm_CutKey; } + // morkMap_mNote FormAddVal() const { return mMap_Form.mMapForm_AddVal; } + // morkMap_mNote FormCutVal() const { return mMap_Form.mMapForm_CutVal; } + + mork_size FormKeySize() const { return mMap_Form.mMapForm_KeySize; } + mork_size FormValSize() const { return mMap_Form.mMapForm_ValSize; } + + mork_bool FormKeyIsIP() const { return mMap_Form.mMapForm_KeyIsIP; } + mork_bool FormValIsIP() const { return mMap_Form.mMapForm_ValIsIP; } + + mork_bool FormHoldChanges() const { return mMap_Form.mMapForm_HoldChanges; } + + mork_change* FormDummyChange() { return &mMap_Form.mMapForm_DummyChange; } + + public: // other map methods + mork_bool Put(morkEnv* ev, const void* inKey, const void* inVal, void* outKey, + void* outVal, mork_change** outChange); + + mork_bool Cut(morkEnv* ev, const void* inKey, void* outKey, void* outVal, + mork_change** outChange); + + mork_bool Get(morkEnv* ev, const void* inKey, void* outKey, void* outVal, + mork_change** outChange); + + mork_num CutAll(morkEnv* ev); + + private: // copying is not allowed + morkMap(const morkMap& other); + morkMap& operator=(const morkMap& other); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakMap(morkMap* me, morkEnv* ev, morkMap** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongMap(morkMap* me, morkEnv* ev, morkMap** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +/*| morkMapIter: an iterator for morkMap and subclasses. This is not a node, +**| and expected usage is as a member of some other node subclass, such as in +**| a cursor subclass or a thumb subclass. Also, iters might be as temp stack +**| objects when scanning the content of a map. +|*/ +class morkMapIter { // iterator for hash table map + + protected: + morkMap* mMapIter_Map; // map to iterate, NOT refcounted + mork_seed mMapIter_Seed; // cached copy of map's seed + + morkAssoc** mMapIter_Bucket; // one bucket in mMap_Buckets array + morkAssoc** mMapIter_AssocRef; // usually *AtRef equals Here + morkAssoc* mMapIter_Assoc; // the current assoc in an iteration + morkAssoc* mMapIter_Next; // mMapIter_Assoc->mAssoc_Next */ + + public: + morkMapIter(morkEnv* ev, morkMap* ioMap); + void CloseMapIter(morkEnv* ev); + + morkMapIter(); // everything set to zero -- need to call InitMapIter() + + protected: // we want all subclasses to provide typesafe wrappers: + void InitMapIter(morkEnv* ev, morkMap* ioMap); + + // The morkAssoc returned below is always either mork_change* or + // else nil (when there is no such assoc). We return a pointer to + // the change rather than a simple bool, because callers might + // want to access change info associated with an assoc. + + mork_change* First(morkEnv* ev, void* outKey, void* outVal); + mork_change* Next(morkEnv* ev, void* outKey, void* outVal); + mork_change* Here(morkEnv* ev, void* outKey, void* outVal); + + mork_change* CutHere(morkEnv* ev, void* outKey, void* outVal); +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKMAP_ */ diff --git a/comm/mailnews/db/mork/morkNode.cpp b/comm/mailnews/db/mork/morkNode.cpp new file mode 100644 index 0000000000..c12fc6a0fd --- /dev/null +++ b/comm/mailnews/db/mork/morkNode.cpp @@ -0,0 +1,550 @@ +/* -*- 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 _MORKPOOL_ +# include "morkPool.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKHANDLE_ +# include "morkHandle.h" +#endif + +/*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/ + +/* ===== ===== ===== ===== morkUsage ===== ===== ===== ===== */ + +static morkUsage morkUsage_gHeap; // ensure EnsureReadyStaticUsage() +const morkUsage& morkUsage::kHeap = morkUsage_gHeap; + +static morkUsage morkUsage_gStack; // ensure EnsureReadyStaticUsage() +const morkUsage& morkUsage::kStack = morkUsage_gStack; + +static morkUsage morkUsage_gMember; // ensure EnsureReadyStaticUsage() +const morkUsage& morkUsage::kMember = morkUsage_gMember; + +static morkUsage morkUsage_gGlobal; // ensure EnsureReadyStaticUsage() +const morkUsage& morkUsage::kGlobal = morkUsage_gGlobal; + +static morkUsage morkUsage_gPool; // ensure EnsureReadyStaticUsage() +const morkUsage& morkUsage::kPool = morkUsage_gPool; + +static morkUsage morkUsage_gNone; // ensure EnsureReadyStaticUsage() +const morkUsage& morkUsage::kNone = morkUsage_gNone; + +// This must be structured to allow for non-zero values in global variables +// just before static init time. We can only safely check for whether a +// global has the address of some other global. Please, do not initialize +// either of the variables below to zero, because this could break when a zero +// is assigned at static init time, but after EnsureReadyStaticUsage() runs. + +static mork_u4 morkUsage_g_static_init_target; // only address of this matters +static mork_u4* morkUsage_g_static_init_done; // is address of target above? + +#define morkUsage_do_static_init() \ + (morkUsage_g_static_init_done = &morkUsage_g_static_init_target) + +#define morkUsage_need_static_init() \ + (morkUsage_g_static_init_done != &morkUsage_g_static_init_target) + +/*static*/ +void morkUsage::EnsureReadyStaticUsage() { + if (morkUsage_need_static_init()) { + morkUsage_do_static_init(); + + morkUsage_gHeap.InitUsage(morkUsage_kHeap); + morkUsage_gStack.InitUsage(morkUsage_kStack); + morkUsage_gMember.InitUsage(morkUsage_kMember); + morkUsage_gGlobal.InitUsage(morkUsage_kGlobal); + morkUsage_gPool.InitUsage(morkUsage_kPool); + morkUsage_gNone.InitUsage(morkUsage_kNone); + } +} + +/*static*/ +const morkUsage& morkUsage::GetHeap() // kHeap safe at static init time +{ + EnsureReadyStaticUsage(); + return morkUsage_gHeap; +} + +/*static*/ +const morkUsage& morkUsage::GetStack() // kStack safe at static init time +{ + EnsureReadyStaticUsage(); + return morkUsage_gStack; +} + +/*static*/ +const morkUsage& morkUsage::GetMember() // kMember safe at static init time +{ + EnsureReadyStaticUsage(); + return morkUsage_gMember; +} + +/*static*/ +const morkUsage& morkUsage::GetGlobal() // kGlobal safe at static init time +{ + EnsureReadyStaticUsage(); + return morkUsage_gGlobal; +} + +/*static*/ +const morkUsage& morkUsage::GetPool() // kPool safe at static init time +{ + EnsureReadyStaticUsage(); + return morkUsage_gPool; +} + +/*static*/ +const morkUsage& morkUsage::GetNone() // kNone safe at static init time +{ + EnsureReadyStaticUsage(); + return morkUsage_gNone; +} + +morkUsage::morkUsage() { + if (morkUsage_need_static_init()) { + morkUsage::EnsureReadyStaticUsage(); + } +} + +morkUsage::morkUsage(mork_usage code) : mUsage_Code(code) { + if (morkUsage_need_static_init()) { + morkUsage::EnsureReadyStaticUsage(); + } +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*static*/ void* morkNode::MakeNew(size_t inSize, nsIMdbHeap& ioHeap, + morkEnv* ev) { + void* node = 0; + ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void**)&node); + if (!node) ev->OutOfMemoryError(); + + return node; +} + +/*public non-poly*/ void morkNode::ZapOld(morkEnv* ev, nsIMdbHeap* ioHeap) { + if (this->IsNode()) { + mork_usage usage = mNode_Usage; // mNode_Usage before ~morkNode + this->morkNode::~morkNode(); // first call polymorphic destructor + if (ioHeap) // was this node heap allocated? + ioHeap->Free(ev->AsMdbEnv(), this); + else if (usage == morkUsage_kPool) // mNode_Usage before ~morkNode + { + morkHandle* h = (morkHandle*)this; + if (h->IsHandle() && h->GoodHandleTag()) { + if (h->mHandle_Face) { + if (ev->mEnv_HandlePool) + ev->mEnv_HandlePool->ZapHandle(ev, h->mHandle_Face); + else if (h->mHandle_Env && h->mHandle_Env->mEnv_HandlePool) + h->mHandle_Env->mEnv_HandlePool->ZapHandle(ev, h->mHandle_Face); + } else + ev->NilPointerError(); + } + } + } else + this->NonNodeError(ev); +} + +/*public virtual*/ void morkNode::CloseMorkNode( + morkEnv* ev) // CloseNode() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseNode(ev); + this->MarkShut(); + } +} +NS_IMETHODIMP +morkNode::CloseMdbObject(nsIMdbEnv* mev) { + return morkNode::CloseMdbObject((morkEnv*)mev); +} + +nsresult morkNode::CloseMdbObject(morkEnv* ev) { + // if only one ref, Handle_CutStrongRef will clean up better. + if (mNode_Uses == 1) + // XXX Casting mork_uses to nsresult + return static_cast<nsresult>(CutStrongRef(ev)); + + nsresult outErr = NS_OK; + + if (IsNode() && IsOpenNode()) { + if (ev) { + CloseMorkNode(ev); + outErr = ev->AsErr(); + } + } + return outErr; +} + +/*public virtual*/ +morkNode::~morkNode() // assert that CloseNode() executed earlier +{ + MORK_ASSERT(this->IsShutNode() || + IsDeadNode()); // sometimes we call destructor explicitly w/o + // freeing object. + mNode_Access = morkAccess_kDead; + mNode_Usage = morkUsage_kNone; +} + +/*public virtual*/ +// void CloseMorkNode(morkEnv* ev) = 0; // CloseNode() only if open +// CloseMorkNode() is the polymorphic close method called when uses==0, +// which must do NOTHING at all when IsOpenNode() is not true. Otherwise, +// CloseMorkNode() should call a static close method specific to an object. +// Each such static close method should either call inherited static close +// methods, or else perform the consolidated effect of calling them, where +// subclasses should closely track any changes in base classes with care. + +/*public non-poly*/ +morkNode::morkNode(mork_usage inCode) + : mNode_Heap(0), + mNode_Base(morkBase_kNode), + mNode_Derived(0) // until subclass sets appropriately + , + mNode_Access(morkAccess_kOpen), + mNode_Usage(inCode), + mNode_Mutable(morkAble_kEnabled), + mNode_Load(morkLoad_kClean), + mNode_Uses(1), + mNode_Refs(1) {} + +/*public non-poly*/ +morkNode::morkNode(const morkUsage& inUsage, nsIMdbHeap* ioHeap) + : mNode_Heap(ioHeap), + mNode_Base(morkBase_kNode), + mNode_Derived(0) // until subclass sets appropriately + , + mNode_Access(morkAccess_kOpen), + mNode_Usage(inUsage.Code()), + mNode_Mutable(morkAble_kEnabled), + mNode_Load(morkLoad_kClean), + mNode_Uses(1), + mNode_Refs(1) { + if (!ioHeap && mNode_Usage == morkUsage_kHeap) MORK_ASSERT(ioHeap); +} + +/*public non-poly*/ +morkNode::morkNode(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap) + : mNode_Heap(ioHeap), + mNode_Base(morkBase_kNode), + mNode_Derived(0) // until subclass sets appropriately + , + mNode_Access(morkAccess_kOpen), + mNode_Usage(inUsage.Code()), + mNode_Mutable(morkAble_kEnabled), + mNode_Load(morkLoad_kClean), + mNode_Uses(1), + mNode_Refs(1) { + if (!ioHeap && mNode_Usage == morkUsage_kHeap) { + this->NilHeapError(ev); + } +} + +/*protected non-poly*/ void morkNode::RefsUnderUsesWarning(morkEnv* ev) const { + ev->NewError("mNode_Refs < mNode_Uses"); +} + +/*protected non-poly*/ void morkNode::NonNodeError( + morkEnv* ev) const // called when IsNode() is false +{ + ev->NewError("non-morkNode"); +} + +/*protected non-poly*/ void morkNode::NonOpenNodeError( + morkEnv* ev) const // when IsOpenNode() is false +{ + ev->NewError("non-open-morkNode"); +} + +/*protected non-poly*/ void morkNode::NonMutableNodeError( + morkEnv* ev) const // when IsMutable() is false +{ + ev->NewError("non-mutable-morkNode"); +} + +/*protected non-poly*/ void morkNode::NilHeapError( + morkEnv* ev) const // zero mNode_Heap w/ kHeap usage +{ + ev->NewError("nil mNode_Heap"); +} + +/*protected non-poly*/ void morkNode::RefsOverflowWarning( + morkEnv* ev) const // mNode_Refs overflow +{ + ev->NewWarning("mNode_Refs overflow"); +} + +/*protected non-poly*/ void morkNode::UsesOverflowWarning( + morkEnv* ev) const // mNode_Uses overflow +{ + ev->NewWarning("mNode_Uses overflow"); +} + +/*protected non-poly*/ void morkNode::RefsUnderflowWarning( + morkEnv* ev) const // mNode_Refs underflow +{ + ev->NewWarning("mNode_Refs underflow"); +} + +/*protected non-poly*/ void morkNode::UsesUnderflowWarning( + morkEnv* ev) const // mNode_Uses underflow +{ + ev->NewWarning("mNode_Uses underflow"); +} + +/*public non-poly*/ void morkNode::CloseNode( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) + this->MarkShut(); + else + this->NonNodeError(ev); +} + +extern void // utility method very similar to morkNode::SlotStrongNode(): +nsIMdbFile_SlotStrongFile(nsIMdbFile* self, morkEnv* ev, nsIMdbFile** ioSlot) +// If *ioSlot is non-nil, that file is released by CutStrongRef() and +// then zeroed out. Then if self is non-nil, this is acquired by +// calling AddStrongRef(), and if the return value shows success, +// then self is put into slot *ioSlot. Note self can be nil, so we take +// expression 'nsIMdbFile_SlotStrongFile(0, ev, &slot)'. +{ + nsIMdbFile* file = *ioSlot; + if (self != file) { + if (file) { + *ioSlot = 0; + NS_RELEASE(file); + } + if (self && ev->Good()) NS_ADDREF(*ioSlot = self); + } +} + +void // utility method very similar to morkNode::SlotStrongNode(): +nsIMdbHeap_SlotStrongHeap(nsIMdbHeap* self, morkEnv* ev, nsIMdbHeap** ioSlot) +// If *ioSlot is non-nil, that heap is released by CutStrongRef() and +// then zeroed out. Then if self is non-nil, self is acquired by +// calling AddStrongRef(), and if the return value shows success, +// then self is put into slot *ioSlot. Note self can be nil, so we +// permit expression 'nsIMdbHeap_SlotStrongHeap(0, ev, &slot)'. +{ + nsIMdbHeap* heap = *ioSlot; + if (self != heap) { + if (heap) *ioSlot = 0; + + if (self && ev->Good()) *ioSlot = self; + } +} + +/*public static*/ void morkNode::SlotStrongNode(morkNode* me, morkEnv* ev, + morkNode** ioSlot) +// If *ioSlot is non-nil, that node is released by CutStrongRef() and +// then zeroed out. Then if me is non-nil, this is acquired by +// calling AddStrongRef(), and if positive is returned to show success, +// then me is put into slot *ioSlot. Note me can be nil, so we take +// expression 'morkNode::SlotStrongNode((morkNode*) 0, ev, &slot)'. +{ + morkNode* node = *ioSlot; + if (me != node) { + if (node) { + // what if this nulls out the ev and causes asserts? + // can we move this after the CutStrongRef()? + *ioSlot = 0; + node->CutStrongRef(ev); + } + if (me && me->AddStrongRef(ev)) *ioSlot = me; + } +} + +/*public static*/ void morkNode::SlotWeakNode(morkNode* me, morkEnv* ev, + morkNode** ioSlot) +// If *ioSlot is non-nil, that node is released by CutWeakRef() and +// then zeroed out. Then if me is non-nil, this is acquired by +// calling AddWeakRef(), and if positive is returned to show success, +// then me is put into slot *ioSlot. Note me can be nil, so we +// expression 'morkNode::SlotWeakNode((morkNode*) 0, ev, &slot)'. +{ + morkNode* node = *ioSlot; + if (me != node) { + if (node) { + *ioSlot = 0; + node->CutWeakRef(ev); + } + if (me && me->AddWeakRef(ev)) *ioSlot = me; + } +} + +/*public non-poly*/ mork_uses morkNode::AddStrongRef(morkEnv* ev) { + mork_uses outUses = 0; + if (this->IsNode()) { + mork_uses uses = mNode_Uses; + mork_refs refs = mNode_Refs; + if (refs < uses) // need to fix broken refs/uses relation? + { + this->RefsUnderUsesWarning(ev); + mNode_Refs = mNode_Uses = refs = uses; + } + if (refs < morkNode_kMaxRefCount) // not too great? + { + mNode_Refs = ++refs; + mNode_Uses = ++uses; + } else + this->RefsOverflowWarning(ev); + + outUses = uses; + } else + this->NonNodeError(ev); + return outUses; +} + +/*private non-poly*/ mork_bool morkNode::cut_use_count( + morkEnv* ev) // just one part of CutStrongRef() +{ + mork_bool didCut = morkBool_kFalse; + if (this->IsNode()) { + mork_uses uses = mNode_Uses; + if (uses) // not yet zero? + mNode_Uses = --uses; + else + this->UsesUnderflowWarning(ev); + + didCut = morkBool_kTrue; + if (!mNode_Uses) // last use gone? time to close node? + { + if (this->IsOpenNode()) { + if (!mNode_Refs) // no outstanding reference? + { + this->RefsUnderflowWarning(ev); + ++mNode_Refs; // prevent potential crash during close + } + this->CloseMorkNode(ev); // polymorphic self close + // (Note CutNode() is not polymorphic -- so don't call that.) + } + } + } else + this->NonNodeError(ev); + return didCut; +} + +/*public non-poly*/ mork_uses morkNode::CutStrongRef(morkEnv* ev) { + mork_refs outRefs = 0; + if (this->IsNode()) { + if (this->cut_use_count(ev)) outRefs = this->CutWeakRef(ev); + } else + this->NonNodeError(ev); + + return outRefs; +} + +/*public non-poly*/ mork_refs morkNode::AddWeakRef(morkEnv* ev) { + mork_refs outRefs = 0; + if (this->IsNode()) { + mork_refs refs = mNode_Refs; + if (refs < morkNode_kMaxRefCount) // not too great? + mNode_Refs = ++refs; + else + this->RefsOverflowWarning(ev); + + outRefs = refs; + } else + this->NonNodeError(ev); + + return outRefs; +} + +/*public non-poly*/ mork_refs morkNode::CutWeakRef(morkEnv* ev) { + mork_refs outRefs = 0; + if (this->IsNode()) { + mork_uses uses = mNode_Uses; + mork_refs refs = mNode_Refs; + if (refs) // not yet zero? + mNode_Refs = --refs; + else + this->RefsUnderflowWarning(ev); + + if (refs < uses) // need to fix broken refs/uses relation? + { + this->RefsUnderUsesWarning(ev); + mNode_Refs = mNode_Uses = refs = uses; + } + + outRefs = refs; + if (!refs) // last reference gone? time to destroy node? + this->ZapOld(ev, mNode_Heap); // self destroy, use this no longer + } else + this->NonNodeError(ev); + + return outRefs; +} + +static const char morkNode_kBroken[] = "broken"; + +/*public non-poly*/ const char* morkNode::GetNodeAccessAsString() + const // e.g. "open", "shut", etc. +{ + const char* outString = morkNode_kBroken; + switch (mNode_Access) { + case morkAccess_kOpen: + outString = "open"; + break; + case morkAccess_kClosing: + outString = "closing"; + break; + case morkAccess_kShut: + outString = "shut"; + break; + case morkAccess_kDead: + outString = "dead"; + break; + } + return outString; +} + +/*public non-poly*/ const char* morkNode::GetNodeUsageAsString() + const // e.g. "heap", "stack", etc. +{ + const char* outString = morkNode_kBroken; + switch (mNode_Usage) { + case morkUsage_kHeap: + outString = "heap"; + break; + case morkUsage_kStack: + outString = "stack"; + break; + case morkUsage_kMember: + outString = "member"; + break; + case morkUsage_kGlobal: + outString = "global"; + break; + case morkUsage_kPool: + outString = "pool"; + break; + case morkUsage_kNone: + outString = "none"; + break; + } + return outString; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkNode.h b/comm/mailnews/db/mork/morkNode.h new file mode 100644 index 0000000000..6518487a30 --- /dev/null +++ b/comm/mailnews/db/mork/morkNode.h @@ -0,0 +1,290 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MORKNODE_ +#define _MORKNODE_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkUsage_kHeap 'h' +#define morkUsage_kStack 's' +#define morkUsage_kMember 'm' +#define morkUsage_kGlobal 'g' +#define morkUsage_kPool 'p' +#define morkUsage_kNone 'n' + +class morkUsage { + public: + mork_usage mUsage_Code; // kHeap, kStack, kMember, or kGhost + + public: + explicit morkUsage(mork_usage inCode); + + morkUsage(); // does nothing except maybe call EnsureReadyStaticUsage() + void InitUsage(mork_usage inCode) { mUsage_Code = inCode; } + + ~morkUsage() {} + mork_usage Code() const { return mUsage_Code; } + + static void EnsureReadyStaticUsage(); + + public: + static const morkUsage& kHeap; // morkUsage_kHeap + static const morkUsage& kStack; // morkUsage_kStack + static const morkUsage& kMember; // morkUsage_kMember + static const morkUsage& kGlobal; // morkUsage_kGlobal + static const morkUsage& kPool; // morkUsage_kPool + static const morkUsage& kNone; // morkUsage_kNone + + static const morkUsage& GetHeap(); // kHeap, safe at static init time + static const morkUsage& GetStack(); // kStack, safe at static init time + static const morkUsage& GetMember(); // kMember, safe at static init time + static const morkUsage& GetGlobal(); // kGlobal, safe at static init time + static const morkUsage& GetPool(); // kPool, safe at static init time + static const morkUsage& GetNone(); // kNone, safe at static init time +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkNode_kMaxRefCount 0x0FFFF /* count sticks if it hits this */ + +#define morkBase_kNode /*i*/ 0x4E64 /* ascii 'Nd' */ + +/*| morkNode: several groups of two-byte integers that track the basic +**| status of an object that can be used to compose in-memory graphs. +**| This is the base class for nsIMdbObject (which adds members that fit +**| the needs of an nsIMdbObject subclass). The morkNode class is also used +**| as the base class for other Mork db classes with no strong relation to +**| the MDB class hierarchy. +**| +**|| Heap: the heap in which this node was allocated, when the usage equals +**| morkUsage_kHeap to show dynamic allocation. Note this heap is NOT ref- +**| counted, because that would be too great and complex a burden for all +**| the nodes allocated in that heap. So heap users should take care to +**| understand that nodes allocated in that heap are considered protected +**| by some inclusive context in which all those nodes are allocated, and +**| that context must maintain at least one strong refcount for the heap. +**| Occasionally a node subclass will indeed wish to hold a refcounted +**| reference to a heap, and possibly the same heap that is in mNode_Heap, +**| but this is always done in a separate slot that explicitly refcounts, +**| so we avoid confusing what is meant by the mNode_Heap slot. +|*/ +class morkNode /*: public nsISupports */ { // base class for constructing Mork + // object graphs + + public: // state is public because the entire Mork system is private + // NS_DECL_ISUPPORTS; + nsIMdbHeap* mNode_Heap; // NON-refcounted heap pointer + + 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_able mNode_Mutable; // can this node be modified? + mork_load mNode_Load; // is this node clean or dirty? + + mork_uses mNode_Uses; // refcount for strong refs + mork_refs mNode_Refs; // refcount for strong refs + weak refs + + protected: // special case empty construction for morkHandleFrame + friend class morkHandleFrame; + morkNode() {} + + public: // inlines for weird mNode_Mutable and mNode_Load constants + void SetFrozen() { mNode_Mutable = morkAble_kDisabled; } + void SetMutable() { mNode_Mutable = morkAble_kEnabled; } + void SetAsleep() { mNode_Mutable = morkAble_kAsleep; } + + mork_bool IsFrozen() const { return mNode_Mutable == morkAble_kDisabled; } + mork_bool IsMutable() const { return mNode_Mutable == morkAble_kEnabled; } + mork_bool IsAsleep() const { return mNode_Mutable == morkAble_kAsleep; } + + void SetNodeClean() { mNode_Load = morkLoad_kClean; } + void SetNodeDirty() { mNode_Load = morkLoad_kDirty; } + + mork_bool IsNodeClean() const { return mNode_Load == morkLoad_kClean; } + mork_bool IsNodeDirty() const { return mNode_Load == morkLoad_kDirty; } + + public: // morkNode memory management methods + static void* MakeNew(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev); + + void ZapOld(morkEnv* ev, nsIMdbHeap* ioHeap); // replaces operator delete() + // this->morkNode::~morkNode(); // first call polymorphic destructor + // if ( ioHeap ) // was this node heap allocated? + // ioHeap->Free(ev->AsMdbEnv(), this); + + public: // morkNode memory management operators + void* operator new(size_t inSize, nsIMdbHeap& ioHeap, + morkEnv* ev) noexcept(true) { + return morkNode::MakeNew(inSize, ioHeap, ev); + } + + protected: // construction without an anv needed for first env constructed: + morkNode(const morkUsage& inUsage, nsIMdbHeap* ioHeap); + + explicit morkNode(mork_usage inCode); // usage == inCode, heap == nil + + // { ===== begin basic node interface ===== + public: // morkNode virtual methods + // virtual FlushMorkNode(morkEnv* ev, morkStream* ioStream); + // virtual WriteMorkNode(morkEnv* ev, morkStream* ioStream); + + virtual ~morkNode(); // assert that CloseNode() executed earlier + virtual void CloseMorkNode(morkEnv* ev); // CloseNode() only if open + + // CloseMorkNode() is the polymorphic close method called when uses==0, + // which must do NOTHING at all when IsOpenNode() is not true. Otherwise, + // CloseMorkNode() should call a static close method specific to an object. + // Each such static close method should either call inherited static close + // methods, or else perform the consolidated effect of calling them, where + // subclasses should closely track any changes in base classes with care. + + public: // morkNode construction + morkNode(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap); + void CloseNode(morkEnv* ev); // called by CloseMorkNode(); + nsresult CloseMdbObject(morkEnv* ev); + NS_IMETHOD CloseMdbObject(nsIMdbEnv* ev); + + private: // copying is not allowed + morkNode(const morkNode& other); + morkNode& operator=(const morkNode& other); + + public: // dynamic type identification + mork_bool IsNode() const { return mNode_Base == morkBase_kNode; } + // } ===== end basic node methods ===== + + public: // public error & warning methods + void RefsUnderUsesWarning( + morkEnv* ev) const; // call if mNode_Refs < mNode_Uses + void NonNodeError(morkEnv* ev) const; // call when IsNode() is false + void NilHeapError(morkEnv* ev) const; // zero mNode_Heap when usage is kHeap + void NonOpenNodeError(morkEnv* ev) const; // call when IsOpenNode() is false + + void NonMutableNodeError(morkEnv* ev) const; // when IsMutable() is false + + void RefsOverflowWarning(morkEnv* ev) const; // call on mNode_Refs overflow + void UsesOverflowWarning(morkEnv* ev) const; // call on mNode_Uses overflow + void RefsUnderflowWarning(morkEnv* ev) const; // call on mNode_Refs underflow + void UsesUnderflowWarning(morkEnv* ev) const; // call on mNode_Uses underflow + + private: // private refcounting methods + mork_bool cut_use_count(morkEnv* ev); // just one part of CutStrongRef() + + public: // other morkNode methods + mork_bool GoodRefs() const { return mNode_Refs >= mNode_Uses; } + mork_bool BadRefs() const { return mNode_Refs < mNode_Uses; } + + mork_uses StrongRefsOnly() const { return mNode_Uses; } + mork_refs WeakRefsOnly() const { + return (mork_refs)(mNode_Refs - mNode_Uses); + } + + // (this refcounting derives from public domain IronDoc node refcounts) + virtual mork_uses AddStrongRef(morkEnv* ev); + virtual mork_uses CutStrongRef(morkEnv* ev); + mork_refs AddWeakRef(morkEnv* ev); + mork_refs CutWeakRef(morkEnv* ev); + + const char* GetNodeAccessAsString() const; // e.g. "open", "shut", etc. + const char* GetNodeUsageAsString() const; // e.g. "heap", "stack", etc. + + mork_usage NodeUsage() const { return mNode_Usage; } + + mork_bool IsHeapNode() const { return mNode_Usage == morkUsage_kHeap; } + + mork_bool IsOpenNode() const { return mNode_Access == morkAccess_kOpen; } + + mork_bool IsShutNode() const { return mNode_Access == morkAccess_kShut; } + + mork_bool IsDeadNode() const { return mNode_Access == morkAccess_kDead; } + + mork_bool IsClosingNode() const { + return mNode_Access == morkAccess_kClosing; + } + + mork_bool IsOpenOrClosingNode() const { + return IsOpenNode() || IsClosingNode(); + } + + mork_bool HasNodeAccess() const { + return (IsOpenNode() || IsShutNode() || IsClosingNode()); + } + + void MarkShut() { mNode_Access = morkAccess_kShut; } + void MarkClosing() { mNode_Access = morkAccess_kClosing; } + void MarkDead() { mNode_Access = morkAccess_kDead; } + + public: // refcounting for typesafe subclass inline methods + static void SlotWeakNode(morkNode* me, morkEnv* ev, morkNode** ioSlot); + // If *ioSlot is non-nil, that node is released by CutWeakRef() and + // then zeroed out. Then if me is non-nil, this is acquired by + // calling AddWeakRef(), and if positive is returned to show success, + // then this is put into slot *ioSlot. Note me can be nil, so we + // permit expression '((morkNode*) 0L)->SlotWeakNode(ev, &slot)'. + + static void SlotStrongNode(morkNode* me, morkEnv* ev, morkNode** ioSlot); + // If *ioSlot is non-nil, that node is released by CutStrongRef() and + // then zeroed out. Then if me is non-nil, this is acquired by + // calling AddStrongRef(), and if positive is returned to show success, + // then me is put into slot *ioSlot. Note me can be nil, so we take + // expression 'morkNode::SlotStrongNode((morkNode*) 0, ev, &slot)'. +}; + +extern void // utility method very similar to morkNode::SlotStrongNode(): +nsIMdbHeap_SlotStrongHeap(nsIMdbHeap* self, morkEnv* ev, nsIMdbHeap** ioSlot); +// If *ioSlot is non-nil, that heap is released by CutStrongRef() and +// then zeroed out. Then if self is non-nil, this is acquired by +// calling AddStrongRef(), and if the return value shows success, +// then self is put into slot *ioSlot. Note self can be nil, so we take +// expression 'nsIMdbHeap_SlotStrongHeap(0, ev, &slot)'. + +extern void // utility method very similar to morkNode::SlotStrongNode(): +nsIMdbFile_SlotStrongFile(nsIMdbFile* self, morkEnv* ev, nsIMdbFile** ioSlot); +// If *ioSlot is non-nil, that file is released by CutStrongRef() and +// then zeroed out. Then if self is non-nil, this is acquired by +// calling AddStrongRef(), and if the return value shows success, +// then self is put into slot *ioSlot. Note self can be nil, so we take +// expression 'nsIMdbFile_SlotStrongFile(0, ev, &slot)'. + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKNODE_ */ diff --git a/comm/mailnews/db/mork/morkNodeMap.cpp b/comm/mailnews/db/mork/morkNodeMap.cpp new file mode 100644 index 0000000000..a01b688b16 --- /dev/null +++ b/comm/mailnews/db/mork/morkNodeMap.cpp @@ -0,0 +1,139 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +#ifndef _MORKINTMAP_ +# include "morkIntMap.h" +#endif + +#ifndef _MORKNODEMAP_ +# include "morkNodeMap.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkNodeMap::CloseMorkNode( + morkEnv* ev) // CloseNodeMap() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseNodeMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkNodeMap::~morkNodeMap() // assert CloseNodeMap() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkNodeMap::morkNodeMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) + : morkIntMap(ev, inUsage, /*valsize*/ sizeof(morkNode*), ioHeap, ioSlotHeap, + /*inHoldChanges*/ morkBool_kTrue) { + if (ev->Good()) mNode_Derived = morkDerived_kNodeMap; +} + +/*public non-poly*/ void morkNodeMap::CloseNodeMap( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + this->CutAllNodes(ev); + this->CloseMap(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +mork_bool morkNodeMap::AddNode(morkEnv* ev, mork_token inToken, + morkNode* ioNode) +// the AddNode() method return value equals ev->Good(). +{ + if (ioNode && ev->Good()) { + morkNode* node = 0; // old val in the map + + mork_bool put = this->Put(ev, &inToken, &ioNode, + /*key*/ (void*)0, &node, (mork_change**)0); + + if (put) // replaced an existing value for key inToken? + { + if (node && node != ioNode) // need to release old node? + node->CutStrongRef(ev); + } + + if (ev->Bad() || !ioNode->AddStrongRef(ev)) { + // problems adding node or increasing refcount? + this->Cut(ev, &inToken, // make sure not in map + /*key*/ (void*)0, /*val*/ (void*)0, (mork_change**)0); + } + } else if (!ioNode) + ev->NilPointerError(); + + return ev->Good(); +} + +mork_bool morkNodeMap::CutNode(morkEnv* ev, mork_token inToken) { + morkNode* node = 0; // old val in the map + mork_bool outCutNode = this->Cut(ev, &inToken, + /*key*/ (void*)0, &node, (mork_change**)0); + if (node) node->CutStrongRef(ev); + + return outCutNode; +} + +morkNode* morkNodeMap::GetNode(morkEnv* ev, mork_token inToken) +// Note the returned node does NOT have an increase in refcount for this. +{ + morkNode* node = 0; // old val in the map + this->Get(ev, &inToken, /*key*/ (void*)0, &node, (mork_change**)0); + + return node; +} + +mork_num morkNodeMap::CutAllNodes(morkEnv* ev) +// CutAllNodes() releases all the reference node values. +{ + mork_num outSlots = mMap_Slots; + mork_token key = 0; // old key token in the map + morkNode* val = 0; // old val node in the map + + mork_change* c = 0; + morkNodeMapIter i(ev, this); + for (c = i.FirstNode(ev, &key, &val); c; c = i.NextNode(ev, &key, &val)) { + if (val) val->CutStrongRef(ev); + i.CutHereNode(ev, /*key*/ (mork_token*)0, /*val*/ (morkNode**)0); + } + + return outSlots; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkNodeMap.h b/comm/mailnews/db/mork/morkNodeMap.h new file mode 100644 index 0000000000..c2edc7007e --- /dev/null +++ b/comm/mailnews/db/mork/morkNodeMap.h @@ -0,0 +1,101 @@ +/* -*- 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 _MORKNODEMAP_ +#define _MORKNODEMAP_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +#ifndef _MORKINTMAP_ +# include "morkIntMap.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kNodeMap /*i*/ 0x6E4D /* ascii 'nM' */ + +#define morkNodeMap_kStartSlotCount 512 + +/*| morkNodeMap: maps mork_token -> morkNode +|*/ +class morkNodeMap : public morkIntMap { // for mapping tokens to nodes + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseNodeMap() only if open + virtual ~morkNodeMap(); // assert that CloseNodeMap() executed earlier + + public: // morkMap construction & destruction + morkNodeMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + void CloseNodeMap(morkEnv* ev); // called by CloseMorkNode(); + + public: // dynamic type identification + mork_bool IsNodeMap() const { + return IsNode() && mNode_Derived == morkDerived_kNodeMap; + } + // } ===== end morkNode methods ===== + + // { ===== begin morkMap poly interface ===== + // use the Equal() and Hash() for mork_u4 inherited from morkIntMap + // } ===== end morkMap poly interface ===== + + protected: // we want all subclasses to provide typesafe wrappers: + mork_bool AddNode(morkEnv* ev, mork_token inToken, morkNode* ioNode); + // the AddNode() boolean return equals ev->Good(). + + mork_bool CutNode(morkEnv* ev, mork_token inToken); + // The CutNode() boolean return indicates whether removal happened. + + morkNode* GetNode(morkEnv* ev, mork_token inToken); + // Note the returned node does NOT have an increase in refcount for this. + + mork_num CutAllNodes(morkEnv* ev); + // CutAllNodes() releases all the reference node values. +}; + +class morkNodeMapIter : public morkMapIter { // typesafe wrapper class + + public: + morkNodeMapIter(morkEnv* ev, morkNodeMap* ioMap) : morkMapIter(ev, ioMap) {} + + morkNodeMapIter() : morkMapIter() {} + void InitNodeMapIter(morkEnv* ev, morkNodeMap* ioMap) { + this->InitMapIter(ev, ioMap); + } + + mork_change* FirstNode(morkEnv* ev, mork_token* outToken, + morkNode** outNode) { + return this->First(ev, outToken, outNode); + } + + mork_change* NextNode(morkEnv* ev, mork_token* outToken, morkNode** outNode) { + return this->Next(ev, outToken, outNode); + } + + mork_change* HereNode(morkEnv* ev, mork_token* outToken, morkNode** outNode) { + return this->Here(ev, outToken, outNode); + } + + mork_change* CutHereNode(morkEnv* ev, mork_token* outToken, + morkNode** outNode) { + return this->CutHere(ev, outToken, outNode); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKNODEMAP_ */ diff --git a/comm/mailnews/db/mork/morkObject.cpp b/comm/mailnews/db/mork/morkObject.cpp new file mode 100644 index 0000000000..227fa81f08 --- /dev/null +++ b/comm/mailnews/db/mork/morkObject.cpp @@ -0,0 +1,176 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKOBJECT_ +# include "morkObject.h" +#endif + +#ifndef _MORKHANDLE_ +# include "morkHandle.h" +#endif + +#include "nsCOMPtr.h" + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +NS_IMPL_ISUPPORTS(morkObject, nsIMdbObject) + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkObject::CloseMorkNode( + morkEnv* ev) // CloseObject() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseObject(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkObject::~morkObject() // assert CloseObject() executed earlier +{ + if (!IsShutNode()) CloseMorkNode(this->mMorkEnv); + MORK_ASSERT(mObject_Handle == 0); +} + +/*public non-poly*/ +morkObject::morkObject(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_color inBeadColor) + : morkBead(inUsage, ioHeap, inBeadColor), mObject_Handle(0) { + mMorkEnv = nullptr; +} + +/*public non-poly*/ +morkObject::morkObject(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, mork_color inBeadColor, + morkHandle* ioHandle) + : morkBead(ev, inUsage, ioHeap, inBeadColor), mObject_Handle(0) { + mMorkEnv = ev; + if (ev->Good()) { + if (ioHandle) morkHandle::SlotWeakHandle(ioHandle, ev, &mObject_Handle); + + if (ev->Good()) mNode_Derived = morkDerived_kObject; + } +} + +/*public non-poly*/ void morkObject::CloseObject( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + if (!this->IsShutNode()) { + if (mObject_Handle) + morkHandle::SlotWeakHandle((morkHandle*)0L, ev, &mObject_Handle); + + mBead_Color = 0; // this->CloseBead(ev); + this->MarkShut(); + } + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +// { ----- begin factory methods ----- +NS_IMETHODIMP +morkObject::GetMdbFactory(nsIMdbEnv* mev, nsIMdbFactory** acqFactory) { + nsresult rv; + nsCOMPtr<nsIMdbObject> obj = do_QueryInterface(mev); + if (obj) + rv = obj->GetMdbFactory(mev, acqFactory); + else + return NS_ERROR_NO_INTERFACE; + + return rv; +} +// } ----- end factory methods ----- + +// { ----- begin ref counting for well-behaved cyclic graphs ----- +NS_IMETHODIMP +morkObject::GetWeakRefCount(nsIMdbEnv* mev, // weak refs + mdb_count* outCount) { + *outCount = WeakRefsOnly(); + return NS_OK; +} +NS_IMETHODIMP +morkObject::GetStrongRefCount(nsIMdbEnv* mev, // strong refs + mdb_count* outCount) { + *outCount = StrongRefsOnly(); + return NS_OK; +} +// ### TODO - clean up this cast, if required +NS_IMETHODIMP +morkObject::AddWeakRef(nsIMdbEnv* mev) { + // XXX Casting mork_refs to nsresult + return static_cast<nsresult>(morkNode::AddWeakRef((morkEnv*)mev)); +} + +#ifndef _MSC_VER +NS_IMETHODIMP_(mork_uses) +morkObject::AddStrongRef(morkEnv* mev) { return morkNode::AddStrongRef(mev); } +#endif + +NS_IMETHODIMP_(mork_uses) +morkObject::AddStrongRef(nsIMdbEnv* mev) { + return morkNode::AddStrongRef((morkEnv*)mev); +} + +NS_IMETHODIMP +morkObject::CutWeakRef(nsIMdbEnv* mev) { + // XXX Casting mork_refs to nsresult + return static_cast<nsresult>(morkNode::CutWeakRef((morkEnv*)mev)); +} + +#ifndef _MSC_VER +NS_IMETHODIMP_(mork_uses) +morkObject::CutStrongRef(morkEnv* mev) { return morkNode::CutStrongRef(mev); } +#endif + +NS_IMETHODIMP +morkObject::CutStrongRef(nsIMdbEnv* mev) { + // XXX Casting mork_refs to nsresult + return static_cast<nsresult>(morkNode::CutStrongRef((morkEnv*)mev)); +} + +NS_IMETHODIMP +morkObject::CloseMdbObject(nsIMdbEnv* mev) { + return morkNode::CloseMdbObject((morkEnv*)mev); +} + +NS_IMETHODIMP +morkObject::IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen) { + *outOpen = IsOpenNode(); + return NS_OK; +} +NS_IMETHODIMP +morkObject::IsFrozenMdbObject(nsIMdbEnv* mev, mdb_bool* outIsReadonly) { + *outIsReadonly = IsFrozen(); + return NS_OK; +} + +// void morkObject::NewNilHandleError(morkEnv* ev) // mObject_Handle is nil +//{ +// ev->NewError("nil mObject_Handle"); +//} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkObject.h b/comm/mailnews/db/mork/morkObject.h new file mode 100644 index 0000000000..9548c779d1 --- /dev/null +++ b/comm/mailnews/db/mork/morkObject.h @@ -0,0 +1,146 @@ +/* -*- 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 _MORKOBJECT_ +#define _MORKOBJECT_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKBEAD_ +# include "morkBead.h" +#endif + +#ifndef _MORKCONFIG_ +# include "morkConfig.h" +#endif + +#ifndef _ORKINHEAP_ +# include "orkinHeap.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kObject /*i*/ 0x6F42 /* ascii 'oB' */ + +/*| morkObject: subclass of morkNode that adds knowledge of db suite factory +**| and containing port to those objects that are exposed as instances of +**| nsIMdbObject in the public interface. +|*/ +class morkObject : public morkBead, public nsIMdbObject { + // public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // mork_color mBead_Color; // ID for this bead + + public: // state is public because the entire Mork system is private + morkHandle* mObject_Handle; // weak ref to handle for this object + + morkEnv* mMorkEnv; // weak ref to environment this object created in. + + // { ===== begin morkNode interface ===== + public: + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseObject() only if open +#ifdef MORK_DEBUG_HEAP_STATS + void operator delete(void* ioAddress, size_t size) { + mork_u4* array = (mork_u4*)ioAddress; + array -= 3; + orkinHeap* heap = (orkinHeap*)*array; + if (heap) heap->Free(nullptr, ioAddress); + } +#endif + + NS_DECL_ISUPPORTS + + // { ----- begin attribute methods ----- + NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly) override; + // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port. + // } ----- end attribute methods ----- + + // { ----- begin factory methods ----- + NS_IMETHOD GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory) override; + // } ----- 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 ----- + + protected: // special case construction of first env without preceding env + morkObject(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_color inBeadColor); + virtual ~morkObject(); // assert that CloseObject() executed earlier + + public: // morkEnv construction & destruction + morkObject(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_color inBeadColor, + morkHandle* ioHandle); // ioHandle can be nil + void CloseObject(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkObject(const morkObject& other); + morkObject& operator=(const morkObject& other); + + public: // dynamic type identification + mork_bool IsObject() const { + return IsNode() && mNode_Derived == morkDerived_kObject; + } + // } ===== end morkNode methods ===== + + // void NewNilHandleError(morkEnv* ev); // mObject_Handle is nil + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakObject(morkObject* me, morkEnv* ev, morkObject** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongObject(morkObject* me, morkEnv* ev, + morkObject** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKOBJECT_ */ diff --git a/comm/mailnews/db/mork/morkParser.cpp b/comm/mailnews/db/mork/morkParser.cpp new file mode 100644 index 0000000000..8ca635014f --- /dev/null +++ b/comm/mailnews/db/mork/morkParser.cpp @@ -0,0 +1,1331 @@ +/* -*- 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 _MORKPARSER_ +# include "morkParser.h" +#endif + +#ifndef _MORKSTREAM_ +# include "morkStream.h" +#endif + +#ifndef _MORKBLOB_ +# include "morkBlob.h" +#endif + +#ifndef _MORKSINK_ +# include "morkSink.h" +#endif + +#ifndef _MORKCH_ +# include "morkCh.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkParser::CloseMorkNode( + morkEnv* ev) // CloseParser() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseParser(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkParser::~morkParser() // assert CloseParser() executed earlier +{ + MORK_ASSERT(mParser_Heap == 0); + MORK_ASSERT(mParser_Stream == 0); +} + +/*public non-poly*/ +morkParser::morkParser(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkStream* ioStream, + mdb_count inBytesPerParseSegment, nsIMdbHeap* ioSlotHeap) + : morkNode(ev, inUsage, ioHeap), + mParser_Heap(0), + mParser_Stream(0), + mParser_MoreGranularity(inBytesPerParseSegment), + mParser_State(morkParser_kStartState) + + , + mParser_GroupContentStartPos(0) + + , + mParser_TableMid(), + mParser_RowMid(), + mParser_CellMid() + + , + mParser_InPort(morkBool_kFalse), + mParser_InDict(morkBool_kFalse), + mParser_InCell(morkBool_kFalse), + mParser_InMeta(morkBool_kFalse) + + , + mParser_InPortRow(morkBool_kFalse), + mParser_InRow(morkBool_kFalse), + mParser_InTable(morkBool_kFalse), + mParser_InGroup(morkBool_kFalse) + + , + mParser_AtomChange(morkChange_kNil), + mParser_CellChange(morkChange_kNil), + mParser_RowChange(morkChange_kNil), + mParser_TableChange(morkChange_kNil) + + , + mParser_Change(morkChange_kNil), + mParser_IsBroken(morkBool_kFalse), + mParser_IsDone(morkBool_kFalse), + mParser_DoMore(morkBool_kTrue) + + , + mParser_Mid() + + , + mParser_ScopeCoil(ev, ioSlotHeap), + mParser_ValueCoil(ev, ioSlotHeap), + mParser_ColumnCoil(ev, ioSlotHeap), + mParser_StringCoil(ev, ioSlotHeap) + + , + mParser_ScopeSpool(ev, &mParser_ScopeCoil), + mParser_ValueSpool(ev, &mParser_ValueCoil), + mParser_ColumnSpool(ev, &mParser_ColumnCoil), + mParser_StringSpool(ev, &mParser_StringCoil) + + , + mParser_MidYarn(ev, morkUsage(morkUsage_kMember), ioSlotHeap) { + if (inBytesPerParseSegment < morkParser_kMinGranularity) + inBytesPerParseSegment = morkParser_kMinGranularity; + else if (inBytesPerParseSegment > morkParser_kMaxGranularity) + inBytesPerParseSegment = morkParser_kMaxGranularity; + + mParser_MoreGranularity = inBytesPerParseSegment; + + if (ioSlotHeap && ioStream) { + nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mParser_Heap); + morkStream::SlotStrongStream(ioStream, ev, &mParser_Stream); + + if (ev->Good()) { + mParser_Tag = morkParser_kTag; + mNode_Derived = morkDerived_kParser; + } + } else + ev->NilPointerError(); +} + +/*public non-poly*/ void morkParser::CloseParser( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + if (!this->IsShutNode()) { + mParser_ScopeCoil.CloseCoil(ev); + mParser_ValueCoil.CloseCoil(ev); + mParser_ColumnCoil.CloseCoil(ev); + mParser_StringCoil.CloseCoil(ev); + nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*)0, ev, &mParser_Heap); + morkStream::SlotStrongStream((morkStream*)0, ev, &mParser_Stream); + this->MarkShut(); + } + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*protected non-poly*/ void morkParser::NonGoodParserError( + morkEnv* ev) // when GoodParserTag() is false +{ + ev->NewError("non-morkNode"); +} + +/*protected non-poly*/ void morkParser::NonUsableParserError(morkEnv* ev) // +{ + if (this->IsNode()) { + if (this->IsOpenNode()) { + if (this->GoodParserTag()) { + // okay + } else + this->NonGoodParserError(ev); + } else + this->NonOpenNodeError(ev); + } else + this->NonNodeError(ev); +} + +/*protected non-poly*/ void morkParser::StartParse(morkEnv* ev) { + MORK_USED_1(ev); + mParser_InCell = morkBool_kFalse; + mParser_InMeta = morkBool_kFalse; + mParser_InDict = morkBool_kFalse; + mParser_InPortRow = morkBool_kFalse; + + mParser_RowMid.ClearMid(); + mParser_TableMid.ClearMid(); + mParser_CellMid.ClearMid(); + + mParser_GroupId = 0; + mParser_InPort = morkBool_kTrue; + + mParser_GroupSpan.ClearSpan(); + mParser_DictSpan.ClearSpan(); + mParser_AliasSpan.ClearSpan(); + mParser_MetaSpan.ClearSpan(); + mParser_TableSpan.ClearSpan(); + mParser_RowSpan.ClearSpan(); + mParser_CellSpan.ClearSpan(); + mParser_ColumnSpan.ClearSpan(); + mParser_SlotSpan.ClearSpan(); + + mParser_PortSpan.ClearSpan(); +} + +/*protected non-poly*/ void morkParser::StopParse(morkEnv* ev) { + if (mParser_InCell) { + mParser_InCell = morkBool_kFalse; + mParser_CellSpan.SetEndWithEnd(mParser_PortSpan); + this->OnCellEnd(ev, mParser_CellSpan); + } + if (mParser_InMeta) { + mParser_InMeta = morkBool_kFalse; + mParser_MetaSpan.SetEndWithEnd(mParser_PortSpan); + this->OnMetaEnd(ev, mParser_MetaSpan); + } + if (mParser_InDict) { + mParser_InDict = morkBool_kFalse; + mParser_DictSpan.SetEndWithEnd(mParser_PortSpan); + this->OnDictEnd(ev, mParser_DictSpan); + } + if (mParser_InPortRow) { + mParser_InPortRow = morkBool_kFalse; + mParser_RowSpan.SetEndWithEnd(mParser_PortSpan); + this->OnPortRowEnd(ev, mParser_RowSpan); + } + if (mParser_InRow) { + mParser_InRow = morkBool_kFalse; + mParser_RowMid.ClearMid(); + mParser_RowSpan.SetEndWithEnd(mParser_PortSpan); + this->OnRowEnd(ev, mParser_RowSpan); + } + if (mParser_InTable) { + mParser_InTable = morkBool_kFalse; + mParser_TableMid.ClearMid(); + mParser_TableSpan.SetEndWithEnd(mParser_PortSpan); + this->OnTableEnd(ev, mParser_TableSpan); + } + if (mParser_GroupId) { + mParser_GroupId = 0; + mParser_GroupSpan.SetEndWithEnd(mParser_PortSpan); + this->OnGroupAbortEnd(ev, mParser_GroupSpan); + } + if (mParser_InPort) { + mParser_InPort = morkBool_kFalse; + this->OnPortEnd(ev, mParser_PortSpan); + } +} + +int morkParser::eat_comment(morkEnv* ev) // last char was '/' +{ + morkStream* s = mParser_Stream; + // Note morkStream::Getc() returns EOF when an error occurs, so + // we don't need to check for both c != EOF and ev->Good() below. + + int c = s->Getc(ev); + if (c == '/') // C++ style comment? + { + while ((c = s->Getc(ev)) != EOF && c != 0xA && c != 0xD) + ; /* empty */ + + if (c == 0xA || c == 0xD) c = this->eat_line_break(ev, c); + } else if (c == '*') /* C style comment? */ + { + int depth = 1; // count depth of comments until depth reaches zero + + while (depth > 0 && c != EOF) // still looking for comment end(s)? + { + while ((c = s->Getc(ev)) != EOF && c != '/' && c != '*') { + if (c == 0xA || c == 0xD) // need to count a line break? + { + c = this->eat_line_break(ev, c); + if (c == '/' || c == '*') break; // end while loop + } + } + + if (c == '*') // maybe end of a comment, if next char is '/'? + { + if ((c = s->Getc(ev)) == '/') // end of comment? + { + --depth; // depth of comments has decreased by one + if (!depth) // comments all done? + c = s->Getc(ev); // return the byte after end of comment + } else if (c != EOF) // need to put the char back? + s->Ungetc(c); // especially need to put back '*', 0xA, or 0xD + } else if (c == '/') // maybe nested comemnt, if next char is '*'? + { + if ((c = s->Getc(ev)) == '*') // nested comment? + ++depth; // depth of comments has increased by one + else if (c != EOF) // need to put the char back? + s->Ungetc(c); // especially need to put back '/', 0xA, or 0xD + } + + if (ev->Bad()) c = EOF; + } + if (c == EOF && depth > 0) ev->NewWarning("EOF before end of comment"); + } else + ev->NewWarning("expected / or *"); + + return c; +} + +int morkParser::eat_line_break(morkEnv* ev, int inLast) { + morkStream* s = mParser_Stream; + int c = s->Getc(ev); // get next char after 0xA or 0xD + this->CountLineBreak(); + if (c == 0xA || c == 0xD) // another line break character? + { + if (c != inLast) // not the same as the last one? + c = s->Getc(ev); // get next char after two-byte linebreak + } + return c; +} + +int morkParser::eat_line_continue(morkEnv* ev) // last char was '\' +{ + morkStream* s = mParser_Stream; + int c = s->Getc(ev); + if (c == 0xA || c == 0xD) // linebreak follows \ as expected? + { + c = this->eat_line_break(ev, c); + } else + ev->NewWarning("expected linebreak"); + + return c; +} + +int morkParser::NextChar(morkEnv* ev) // next non-white content +{ + morkStream* s = mParser_Stream; + int c = s->Getc(ev); + while (c > 0 && ev->Good()) { + if (c == '/') + c = this->eat_comment(ev); + else if (c == 0xA || c == 0xD) + c = this->eat_line_break(ev, c); + else if (c == '\\') + c = this->eat_line_continue(ev); + else if (morkCh_IsWhite(c)) + c = s->Getc(ev); + else + break; // end while loop when return c is acceptable + } + if (ev->Bad()) { + mParser_State = morkParser_kBrokenState; + mParser_DoMore = morkBool_kFalse; + mParser_IsDone = morkBool_kTrue; + mParser_IsBroken = morkBool_kTrue; + c = EOF; + } else if (c == EOF) { + mParser_DoMore = morkBool_kFalse; + mParser_IsDone = morkBool_kTrue; + } + return c; +} + +void morkParser::OnCellState(morkEnv* ev) { ev->StubMethodOnlyError(); } + +void morkParser::OnMetaState(morkEnv* ev) { ev->StubMethodOnlyError(); } + +void morkParser::OnRowState(morkEnv* ev) { ev->StubMethodOnlyError(); } + +void morkParser::OnTableState(morkEnv* ev) { ev->StubMethodOnlyError(); } + +void morkParser::OnDictState(morkEnv* ev) { ev->StubMethodOnlyError(); } + +morkBuf* morkParser::ReadName(morkEnv* ev, int c) { + morkBuf* outBuf = 0; + + if (!morkCh_IsName(c)) ev->NewError("not a name char"); + + morkCoil* coil = &mParser_ColumnCoil; + coil->ClearBufFill(); + + morkSpool* spool = &mParser_ColumnSpool; + spool->Seek(ev, /*pos*/ 0); + + if (ev->Good()) { + spool->Putc(ev, c); + + morkStream* s = mParser_Stream; + while ((c = s->Getc(ev)) != EOF && morkCh_IsMore(c) && ev->Good()) + spool->Putc(ev, c); + + if (ev->Good()) { + if (c != EOF) { + s->Ungetc(c); + spool->FlushSink(ev); // update coil->mBuf_Fill + } else + this->UnexpectedEofError(ev); + + if (ev->Good()) outBuf = coil; + } + } + return outBuf; +} + +mork_bool morkParser::ReadMid(morkEnv* ev, morkMid* outMid) { + outMid->ClearMid(); + + morkStream* s = mParser_Stream; + int next; + outMid->mMid_Oid.mOid_Id = this->ReadHex(ev, &next); + int c = next; + if (c == ':') { + if ((c = s->Getc(ev)) != EOF && ev->Good()) { + if (c == '^') { + outMid->mMid_Oid.mOid_Scope = this->ReadHex(ev, &next); + if (ev->Good()) s->Ungetc(next); + } else if (morkCh_IsName(c)) { + outMid->mMid_Buf = this->ReadName(ev, c); + } else + ev->NewError("expected name or hex after ':' following ID"); + } + + if (c == EOF && ev->Good()) this->UnexpectedEofError(ev); + } else + s->Ungetc(c); + + return ev->Good(); +} + +void morkParser::ReadCell(morkEnv* ev) { + mParser_CellMid.ClearMid(); + // this->StartSpanOnLastByte(ev, &mParser_CellSpan); + morkMid* cellMid = 0; // if mid syntax is used for column + morkBuf* cellBuf = 0; // if naked string is used for column + + morkStream* s = mParser_Stream; + int c; + if ((c = s->Getc(ev)) != EOF && ev->Good()) { + // this->StartSpanOnLastByte(ev, &mParser_ColumnSpan); + if (c == '^') { + cellMid = &mParser_CellMid; + this->ReadMid(ev, cellMid); + // if ( !mParser_CellMid.mMid_Oid.mOid_Scope ) + // mParser_CellMid.mMid_Oid.mOid_Scope = (mork_scope) 'c'; + } else { + if (mParser_InMeta && c == morkStore_kFormColumn) { + ReadCellForm(ev, c); + return; + } else + cellBuf = this->ReadName(ev, c); + } + if (ev->Good()) { + // this->EndSpanOnThisByte(ev, &mParser_ColumnSpan); + + mParser_InCell = morkBool_kTrue; + this->OnNewCell(ev, *mParser_CellSpan.AsPlace(), cellMid, + cellBuf); // , mParser_CellChange + + mParser_CellChange = morkChange_kNil; + if ((c = this->NextChar(ev)) != EOF && ev->Good()) { + // this->StartSpanOnLastByte(ev, &mParser_SlotSpan); + if (c == '=') { + morkBuf* buf = this->ReadValue(ev); + if (buf) { + // this->EndSpanOnThisByte(ev, &mParser_SlotSpan); + this->OnValue(ev, mParser_SlotSpan, *buf); + } + } else if (c == '^') { + if (this->ReadMid(ev, &mParser_Mid)) { + // this->EndSpanOnThisByte(ev, &mParser_SlotSpan); + if ((c = this->NextChar(ev)) != EOF && ev->Good()) { + if (c != ')') ev->NewError("expected ')' after cell ^ID value"); + } else if (c == EOF) + this->UnexpectedEofError(ev); + + if (ev->Good()) this->OnValueMid(ev, mParser_SlotSpan, mParser_Mid); + } + } else if (c == 'r' || c == 't' || c == '"' || c == '\'') { + ev->NewError("cell syntax not yet supported"); + } else { + ev->NewError("unknown cell syntax"); + } + } + + // this->EndSpanOnThisByte(ev, &mParser_CellSpan); + mParser_InCell = morkBool_kFalse; + this->OnCellEnd(ev, mParser_CellSpan); + } + } + mParser_CellChange = morkChange_kNil; + + if (c == EOF && ev->Good()) this->UnexpectedEofError(ev); +} + +void morkParser::ReadRowPos(morkEnv* ev) { + int c; // next character + mork_pos rowPos = this->ReadHex(ev, &c); + + if (ev->Good() && c != EOF) // should put back byte after hex? + mParser_Stream->Ungetc(c); + + this->OnRowPos(ev, rowPos); +} + +void morkParser::ReadRow(morkEnv* ev, int c) +// zm:Row ::= zm:S? '[' zm:S? zm:Id zm:RowItem* zm:S? ']' +// zm:RowItem ::= zm:MetaRow | zm:Cell +// zm:MetaRow ::= zm:S? '[' zm:S? zm:Cell* zm:S? ']' /* meta attributes */ +// zm:Cell ::= zm:S? '(' zm:Column zm:S? zm:Slot? ')' +{ + if (ev->Good()) { + // this->StartSpanOnLastByte(ev, &mParser_RowSpan); + if (mParser_Change) mParser_RowChange = mParser_Change; + + mork_bool cutAllRowCols = morkBool_kFalse; + + if (c == '[') { + if ((c = this->NextChar(ev)) == '-') + cutAllRowCols = morkBool_kTrue; + else if (ev->Good() && c != EOF) + mParser_Stream->Ungetc(c); + + if (this->ReadMid(ev, &mParser_RowMid)) { + mParser_InRow = morkBool_kTrue; + this->OnNewRow(ev, *mParser_RowSpan.AsPlace(), mParser_RowMid, + cutAllRowCols); + + mParser_Change = mParser_RowChange = morkChange_kNil; + + while ((c = this->NextChar(ev)) != EOF && ev->Good() && c != ']') { + switch (c) { + case '(': // cell + this->ReadCell(ev); + break; + + case '[': // meta + this->ReadMeta(ev, ']'); + break; + + // case '+': // plus + // mParser_CellChange = morkChange_kAdd; + // break; + + case '-': // minus + // mParser_CellChange = morkChange_kCut; + this->OnMinusCell(ev); + break; + + // case '!': // bang + // mParser_CellChange = morkChange_kSet; + // break; + + default: + ev->NewWarning("unexpected byte in row"); + break; + } // switch + } // while + + if (ev->Good()) { + if ((c = this->NextChar(ev)) == '!') + this->ReadRowPos(ev); + else if (c != EOF && ev->Good()) + mParser_Stream->Ungetc(c); + } + + // this->EndSpanOnThisByte(ev, &mParser_RowSpan); + mParser_InRow = morkBool_kFalse; + this->OnRowEnd(ev, mParser_RowSpan); + + } // if ReadMid + } // if '[' + + else // c != '[' + { + morkStream* s = mParser_Stream; + s->Ungetc(c); + if (this->ReadMid(ev, &mParser_RowMid)) { + mParser_InRow = morkBool_kTrue; + this->OnNewRow(ev, *mParser_RowSpan.AsPlace(), mParser_RowMid, + cutAllRowCols); + + mParser_Change = mParser_RowChange = morkChange_kNil; + + if (ev->Good()) { + if ((c = this->NextChar(ev)) == '!') + this->ReadRowPos(ev); + else if (c != EOF && ev->Good()) + s->Ungetc(c); + } + + // this->EndSpanOnThisByte(ev, &mParser_RowSpan); + mParser_InRow = morkBool_kFalse; + this->OnRowEnd(ev, mParser_RowSpan); + } + } + } + + if (ev->Bad()) + mParser_State = morkParser_kBrokenState; + else if (c == EOF) + mParser_State = morkParser_kDoneState; +} + +void morkParser::ReadTable(morkEnv* ev) +// zm:Table ::= zm:S? '{' zm:S? zm:Id zm:TableItem* zm:S? '}' +// zm:TableItem ::= zm:MetaTable | zm:RowRef | zm:Row +// zm:MetaTable ::= zm:S? '{' zm:S? zm:Cell* zm:S? '}' /* meta attributes */ +{ + // this->StartSpanOnLastByte(ev, &mParser_TableSpan); + + if (mParser_Change) mParser_TableChange = mParser_Change; + + mork_bool cutAllTableRows = morkBool_kFalse; + + int c = this->NextChar(ev); + if (c == '-') + cutAllTableRows = morkBool_kTrue; + else if (ev->Good() && c != EOF) + mParser_Stream->Ungetc(c); + + if (ev->Good() && this->ReadMid(ev, &mParser_TableMid)) { + mParser_InTable = morkBool_kTrue; + this->OnNewTable(ev, *mParser_TableSpan.AsPlace(), mParser_TableMid, + cutAllTableRows); + + mParser_Change = mParser_TableChange = morkChange_kNil; + + while ((c = this->NextChar(ev)) != EOF && ev->Good() && c != '}') { + if (morkCh_IsHex(c)) { + this->ReadRow(ev, c); + } else { + switch (c) { + case '[': // row + this->ReadRow(ev, '['); + break; + + case '{': // meta + this->ReadMeta(ev, '}'); + break; + + // case '+': // plus + // mParser_RowChange = morkChange_kAdd; + // break; + + case '-': // minus + // mParser_RowChange = morkChange_kCut; + this->OnMinusRow(ev); + break; + + // case '!': // bang + // mParser_RowChange = morkChange_kSet; + // break; + + default: + ev->NewWarning("unexpected byte in table"); + break; + } + } + } + + // this->EndSpanOnThisByte(ev, &mParser_TableSpan); + mParser_InTable = morkBool_kFalse; + this->OnTableEnd(ev, mParser_TableSpan); + + if (ev->Bad()) + mParser_State = morkParser_kBrokenState; + else if (c == EOF) + mParser_State = morkParser_kDoneState; + } +} + +mork_id morkParser::ReadHex(morkEnv* ev, int* outNextChar) +// zm:Hex ::= [0-9a-fA-F] /* a single hex digit */ +// zm:Hex+ ::= zm:Hex | zm:Hex zm:Hex+ +{ + mork_id hex = 0; + + morkStream* s = mParser_Stream; + int c = this->NextChar(ev); + + if (ev->Good()) { + if (c != EOF) { + if (morkCh_IsHex(c)) { + do { + if (morkCh_IsDigit(c)) // '0' through '9'? + c -= '0'; + else if (morkCh_IsUpper(c)) // 'A' through 'F'? + c -= ('A' - 10); // c = (c - 'A') + 10; + else // 'a' through 'f'? + c -= ('a' - 10); // c = (c - 'a') + 10; + + hex = (hex << 4) + c; + } while ((c = s->Getc(ev)) != EOF && ev->Good() && morkCh_IsHex(c)); + } else + this->ExpectedHexDigitError(ev, c); + } + } + if (c == EOF) this->EofInsteadOfHexError(ev); + + *outNextChar = c; + return hex; +} + +/*static*/ void morkParser::EofInsteadOfHexError(morkEnv* ev) { + ev->NewWarning("eof instead of hex"); +} + +/*static*/ void morkParser::ExpectedHexDigitError(morkEnv* ev, int c) { + MORK_USED_1(c); + ev->NewWarning("expected hex digit"); +} + +/*static*/ void morkParser::ExpectedEqualError(morkEnv* ev) { + ev->NewWarning("expected '='"); +} + +/*static*/ void morkParser::UnexpectedEofError(morkEnv* ev) { + ev->NewWarning("unexpected eof"); +} + +morkBuf* morkParser::ReadValue(morkEnv* ev) { + morkBuf* outBuf = 0; + + morkCoil* coil = &mParser_ValueCoil; + coil->ClearBufFill(); + + morkSpool* spool = &mParser_ValueSpool; + spool->Seek(ev, /*pos*/ 0); + + if (ev->Good()) { + morkStream* s = mParser_Stream; + int c; + while ((c = s->Getc(ev)) != EOF && c != ')' && ev->Good()) { + if (c == '\\') // next char is escaped by '\'? + { + if ((c = s->Getc(ev)) == 0xA || c == 0xD) // linebreak after \? + { + c = this->eat_line_break(ev, c); + if (c == ')' || c == '\\' || c == '$') { + s->Ungetc(c); // just let while loop test read this again + continue; // goto next iteration of while loop + } + } + if (c == EOF || ev->Bad()) break; // end while loop + } else if (c == '$') // "$" escapes next two hex digits? + { + if ((c = s->Getc(ev)) != EOF && ev->Good()) { + mork_ch first = (mork_ch)c; // first hex digit + if ((c = s->Getc(ev)) != EOF && ev->Good()) { + mork_ch second = (mork_ch)c; // second hex digit + c = ev->HexToByte(first, second); + } else + break; // end while loop + } else + break; // end while loop + } + spool->Putc(ev, c); + } + + if (ev->Good()) { + if (c != EOF) + spool->FlushSink(ev); // update coil->mBuf_Fill + else + this->UnexpectedEofError(ev); + + if (ev->Good()) outBuf = coil; + } + } + return outBuf; +} + +void morkParser::ReadDictForm(morkEnv* ev) { + int nextChar; + nextChar = this->NextChar(ev); + if (nextChar == '(') { + nextChar = this->NextChar(ev); + if (nextChar == morkStore_kFormColumn) { + int dictForm; + + nextChar = this->NextChar(ev); + if (nextChar == '=') { + dictForm = this->NextChar(ev); + nextChar = this->NextChar(ev); + } else if (nextChar == '^') { + dictForm = this->ReadHex(ev, &nextChar); + } else { + ev->NewWarning("unexpected byte in dict form"); + return; + } + mParser_ValueCoil.mText_Form = dictForm; + if (nextChar == ')') { + nextChar = this->NextChar(ev); + if (nextChar == '>') return; + } + } + } + ev->NewWarning("unexpected byte in dict form"); +} + +void morkParser::ReadCellForm(morkEnv* ev, int c) { + MORK_ASSERT(c == morkStore_kFormColumn); + int nextChar; + nextChar = this->NextChar(ev); + int cellForm; + + if (nextChar == '=') { + cellForm = this->NextChar(ev); + nextChar = this->NextChar(ev); + } else if (nextChar == '^') { + cellForm = this->ReadHex(ev, &nextChar); + } else { + ev->NewWarning("unexpected byte in cell form"); + return; + } + // ### not sure about this. Which form should we set? + // mBuilder_CellForm = mBuilder_RowForm = cellForm; + if (nextChar == ')') { + OnCellForm(ev, cellForm); + return; + } + ev->NewWarning("unexpected byte in cell form"); +} + +void morkParser::ReadAlias(morkEnv* ev) +// zm:Alias ::= zm:S? '(' ('#')? zm:Hex+ zm:S? zm:Value ')' +// zm:Value ::= '=' ([^)$\] | '\' zm:NonCRLF | zm:Continue | zm:Dollar)* +{ + // this->StartSpanOnLastByte(ev, &mParser_AliasSpan); + + int nextChar; + mork_id hex = this->ReadHex(ev, &nextChar); + int c = nextChar; + + mParser_Mid.ClearMid(); + mParser_Mid.mMid_Oid.mOid_Id = hex; + + if (morkCh_IsWhite(c) && ev->Good()) c = this->NextChar(ev); + + if (ev->Good()) { + if (c == '<') { + ReadDictForm(ev); + if (ev->Good()) c = this->NextChar(ev); + } + if (c == '=') { + mParser_Mid.mMid_Buf = this->ReadValue(ev); + if (mParser_Mid.mMid_Buf) { + // this->EndSpanOnThisByte(ev, &mParser_AliasSpan); + this->OnAlias(ev, mParser_AliasSpan, mParser_Mid); + // need to reset this somewhere. + mParser_ValueCoil.mText_Form = 0; + } + } else + this->ExpectedEqualError(ev); + } +} + +void morkParser::ReadMeta(morkEnv* ev, int inEndMeta) +// zm:MetaDict ::= zm:S? '<' zm:S? zm:Cell* zm:S? '>' /* meta attributes */ +// zm:MetaTable ::= zm:S? '{' zm:S? zm:Cell* zm:S? '}' /* meta attributes */ +// zm:MetaRow ::= zm:S? '[' zm:S? zm:Cell* zm:S? ']' /* meta attributes */ +{ + // this->StartSpanOnLastByte(ev, &mParser_MetaSpan); + mParser_InMeta = morkBool_kTrue; + this->OnNewMeta(ev, *mParser_MetaSpan.AsPlace()); + + mork_bool more = morkBool_kTrue; // until end meta + int c; + while (more && (c = this->NextChar(ev)) != EOF && ev->Good()) { + switch (c) { + case '(': // cell + this->ReadCell(ev); + break; + + case '>': // maybe end meta? + if (inEndMeta == '>') + more = morkBool_kFalse; // stop reading meta + else + this->UnexpectedByteInMetaWarning(ev); + break; + + case '}': // maybe end meta? + if (inEndMeta == '}') + more = morkBool_kFalse; // stop reading meta + else + this->UnexpectedByteInMetaWarning(ev); + break; + + case ']': // maybe end meta? + if (inEndMeta == ']') + more = morkBool_kFalse; // stop reading meta + else + this->UnexpectedByteInMetaWarning(ev); + break; + + case '[': // maybe table meta row? + if (mParser_InTable) + this->ReadRow(ev, '['); + else + this->UnexpectedByteInMetaWarning(ev); + break; + + default: + if (mParser_InTable && morkCh_IsHex(c)) + this->ReadRow(ev, c); + else + this->UnexpectedByteInMetaWarning(ev); + break; + } + } + + // this->EndSpanOnThisByte(ev, &mParser_MetaSpan); + mParser_InMeta = morkBool_kFalse; + this->OnMetaEnd(ev, mParser_MetaSpan); +} + +/*static*/ void morkParser::UnexpectedByteInMetaWarning(morkEnv* ev) { + ev->NewWarning("unexpected byte in meta"); +} + +/*static*/ void morkParser::NonParserTypeError(morkEnv* ev) { + ev->NewError("non morkParser"); +} + +mork_bool morkParser::MatchPattern(morkEnv* ev, const char* inPattern) { + // if an error occurs, we want original inPattern in the debugger: + const char* pattern = inPattern; // mutable copy of pointer + morkStream* s = mParser_Stream; + int c; + while (*pattern && ev->Good()) { + char byte = *pattern++; + if ((c = s->Getc(ev)) != byte) { + ev->NewError("byte not in expected pattern"); + } + } + return ev->Good(); +} + +mork_bool morkParser::FindGroupEnd(morkEnv* ev) { + mork_bool foundEnd = morkBool_kFalse; + + // char gidBuf[ 64 ]; // to hold hex pattern we want + // (void) ev->TokenAsHex(gidBuf, mParser_GroupId); + + morkStream* s = mParser_Stream; + int c; + + while ((c = s->Getc(ev)) != EOF && ev->Good() && !foundEnd) { + if (c == '@') // maybe start of group ending? + { + // this->EndSpanOnThisByte(ev, &mParser_GroupSpan); + if ((c = s->Getc(ev)) == '$') // '$' follows '@' ? + { + if ((c = s->Getc(ev)) == '$') // '$' follows "@$" ? + { + if ((c = s->Getc(ev)) == '}') { + foundEnd = this->ReadEndGroupId(ev); + // this->EndSpanOnThisByte(ev, &mParser_GroupSpan); + + } else + ev->NewError("expected '}' after @$$"); + } + } + if (!foundEnd && c == '@') s->Ungetc(c); + } + } + + return foundEnd && ev->Good(); +} + +void morkParser::ReadGroup(morkEnv* mev) { + nsIMdbEnv* ev = mev->AsMdbEnv(); + int next = 0; + mParser_GroupId = this->ReadHex(mev, &next); + if (next == '{') { + morkStream* s = mParser_Stream; + int c; + if ((c = s->Getc(mev)) == '@') { + // we really need the following span inside morkBuilder::OnNewGroup(): + this->StartSpanOnThisByte(mev, &mParser_GroupSpan); + mork_pos startPos = mParser_GroupSpan.mSpan_Start.mPlace_Pos; + + // if ( !store->mStore_FirstCommitGroupPos ) + // store->mStore_FirstCommitGroupPos = startPos; + // else if ( !store->mStore_SecondCommitGroupPos ) + // store->mStore_SecondCommitGroupPos = startPos; + + if (this->FindGroupEnd(mev)) { + mork_pos outPos; + s->Seek(ev, startPos, &outPos); + if (mev->Good()) { + this->OnNewGroup(mev, mParser_GroupSpan.mSpan_Start, mParser_GroupId); + + this->ReadContent(mev, /*inInsideGroup*/ morkBool_kTrue); + + this->OnGroupCommitEnd(mev, mParser_GroupSpan); + } + } + } else + mev->NewError("expected '@' after @$${id{"); + } else + mev->NewError("expected '{' after @$$id"); +} + +mork_bool morkParser::ReadAt(morkEnv* ev, mork_bool inInsideGroup) +/* groups must be ignored until properly terminated */ +// zm:Group ::= zm:GroupStart zm:Content zm:GroupEnd /* transaction */ +// zm:GroupStart ::= zm:S? '@$${' zm:Hex+ '{@' /* xaction id has own space */ +// zm:GroupEnd ::= zm:GroupCommit | zm:GroupAbort +// zm:GroupCommit ::= zm:S? '@$$}' zm:Hex+ '}@' /* id matches start id */ +// zm:GroupAbort ::= zm:S? '@$$}~~}@' /* id matches start id */ +/* We must allow started transactions to be aborted in summary files. */ +/* Note '$$' will never occur unescaped in values we will see in Mork. */ +{ + if (this->MatchPattern(ev, "$$")) { + morkStream* s = mParser_Stream; + int c; + if (((c = s->Getc(ev)) == '{' || c == '}') && ev->Good()) { + if (c == '{') // start of new group? + { + if (!inInsideGroup) + this->ReadGroup(ev); + else + ev->NewError("nested @$${ inside another group"); + } else // c == '}' // end of old group? + { + if (inInsideGroup) { + this->ReadEndGroupId(ev); + mParser_GroupId = 0; + } else + ev->NewError("unmatched @$$} outside any group"); + } + } else + ev->NewError("expected '{' or '}' after @$$"); + } + return ev->Good(); +} + +mork_bool morkParser::ReadEndGroupId(morkEnv* ev) { + mork_bool outSawGroupId = morkBool_kFalse; + morkStream* s = mParser_Stream; + int c; + if ((c = s->Getc(ev)) != EOF && ev->Good()) { + if (c == '~') // transaction is aborted? + { + this->MatchPattern(ev, "~}@"); // finish rest of pattern + } else // push back byte and read expected trailing hex id + { + s->Ungetc(c); + int next = 0; + mork_gid endGroupId = this->ReadHex(ev, &next); + if (ev->Good()) { + if (endGroupId == mParser_GroupId) // matches start? + { + if (next == '}') // '}' after @$$}id ? + { + if ((c = s->Getc(ev)) == '@') // '@' after @$$}id} ? + { + // looks good, so return with no error + outSawGroupId = morkBool_kTrue; + mParser_InGroup = false; + } else + ev->NewError("expected '@' after @$$}id}"); + } else + ev->NewError("expected '}' after @$$}id"); + } else + ev->NewError("end group id mismatch"); + } + } + } + return (outSawGroupId && ev->Good()); +} + +void morkParser::ReadDict(morkEnv* ev) +// zm:Dict ::= zm:S? '<' zm:DictItem* zm:S? '>' +// zm:DictItem ::= zm:MetaDict | zm:Alias +// zm:MetaDict ::= zm:S? '<' zm:S? zm:Cell* zm:S? '>' /* meta attributes */ +// zm:Alias ::= zm:S? '(' ('#')? zm:Hex+ zm:S? zm:Value ')' +{ + mParser_Change = morkChange_kNil; + mParser_AtomChange = morkChange_kNil; + + // this->StartSpanOnLastByte(ev, &mParser_DictSpan); + mParser_InDict = morkBool_kTrue; + this->OnNewDict(ev, *mParser_DictSpan.AsPlace()); + + int c; + while ((c = this->NextChar(ev)) != EOF && ev->Good() && c != '>') { + switch (c) { + case '(': // alias + this->ReadAlias(ev); + break; + + case '<': // meta + this->ReadMeta(ev, '>'); + break; + + default: + ev->NewWarning("unexpected byte in dict"); + break; + } + } + + // this->EndSpanOnThisByte(ev, &mParser_DictSpan); + mParser_InDict = morkBool_kFalse; + this->OnDictEnd(ev, mParser_DictSpan); + + if (ev->Bad()) + mParser_State = morkParser_kBrokenState; + else if (c == EOF) + mParser_State = morkParser_kDoneState; +} + +void morkParser::EndSpanOnThisByte(morkEnv* mev, morkSpan* ioSpan) { + mork_pos here; + nsIMdbEnv* ev = mev->AsMdbEnv(); + nsresult rv = mParser_Stream->Tell(ev, &here); + if (NS_SUCCEEDED(rv) && mev->Good()) { + this->SetHerePos(here); + ioSpan->SetEndWithEnd(mParser_PortSpan); + } +} + +void morkParser::EndSpanOnLastByte(morkEnv* mev, morkSpan* ioSpan) { + mork_pos here; + nsIMdbEnv* ev = mev->AsMdbEnv(); + nsresult rv = mParser_Stream->Tell(ev, &here); + if (NS_SUCCEEDED(rv) && mev->Good()) { + if (here > 0) + --here; + else + here = 0; + + this->SetHerePos(here); + ioSpan->SetEndWithEnd(mParser_PortSpan); + } +} + +void morkParser::StartSpanOnLastByte(morkEnv* mev, morkSpan* ioSpan) { + mork_pos here; + nsIMdbEnv* ev = mev->AsMdbEnv(); + nsresult rv = mParser_Stream->Tell(ev, &here); + if (NS_SUCCEEDED(rv) && mev->Good()) { + if (here > 0) + --here; + else + here = 0; + + this->SetHerePos(here); + ioSpan->SetStartWithEnd(mParser_PortSpan); + ioSpan->SetEndWithEnd(mParser_PortSpan); + } +} + +void morkParser::StartSpanOnThisByte(morkEnv* mev, morkSpan* ioSpan) { + mork_pos here; + nsIMdbEnv* ev = mev->AsMdbEnv(); + nsresult rv = mParser_Stream->Tell(ev, &here); + if (NS_SUCCEEDED(rv) && mev->Good()) { + this->SetHerePos(here); + ioSpan->SetStartWithEnd(mParser_PortSpan); + ioSpan->SetEndWithEnd(mParser_PortSpan); + } +} + +mork_bool morkParser::ReadContent(morkEnv* ev, mork_bool inInsideGroup) { + int c; + mork_bool keep_going = true; + while (keep_going && (c = this->NextChar(ev)) != EOF && ev->Good()) { + switch (c) { + case '[': // row + this->ReadRow(ev, '['); + keep_going = false; + break; + + case '{': // table + this->ReadTable(ev); + keep_going = false; + break; + + case '<': // dict + this->ReadDict(ev); + keep_going = false; + break; + + case '@': // group + return this->ReadAt(ev, inInsideGroup); + // break; + + // case '+': // plus + // mParser_Change = morkChange_kAdd; + // break; + + // case '-': // minus + // mParser_Change = morkChange_kCut; + // break; + + // case '!': // bang + // mParser_Change = morkChange_kSet; + // break; + + default: + ev->NewWarning("unexpected byte in ReadContent()"); + break; + } + } + if (ev->Bad()) + mParser_State = morkParser_kBrokenState; + else if (c == EOF) + mParser_State = morkParser_kDoneState; + + return (ev->Good() && c != EOF); +} + +void morkParser::OnPortState(morkEnv* ev) { + mork_bool firstTime = !mParser_InPort; + mParser_InPort = morkBool_kTrue; + if (firstTime) this->OnNewPort(ev, *mParser_PortSpan.AsPlace()); + + mork_bool done = !this->ReadContent(ev, mParser_InGroup /*inInsideGroup*/); + + if (done) { + mParser_InPort = morkBool_kFalse; + this->OnPortEnd(ev, mParser_PortSpan); + } + + if (ev->Bad()) mParser_State = morkParser_kBrokenState; +} + +void morkParser::OnStartState(morkEnv* mev) { + morkStream* s = mParser_Stream; + nsIMdbEnv* ev = mev->AsMdbEnv(); + if (s && s->IsNode() && s->IsOpenNode()) { + mork_pos outPos; + nsresult rv = s->Seek(ev, 0, &outPos); + if (NS_SUCCEEDED(rv) && mev->Good()) { + this->StartParse(mev); + mParser_State = morkParser_kPortState; + } + } else + mev->NilPointerError(); + + if (mev->Bad()) mParser_State = morkParser_kBrokenState; +} + +/*protected non-poly*/ void morkParser::ParseChunk(morkEnv* ev) { + mParser_Change = morkChange_kNil; + mParser_DoMore = morkBool_kTrue; + + switch (mParser_State) { + case morkParser_kCellState: // 0 + this->OnCellState(ev); + break; + + case morkParser_kMetaState: // 1 + this->OnMetaState(ev); + break; + + case morkParser_kRowState: // 2 + this->OnRowState(ev); + break; + + case morkParser_kTableState: // 3 + this->OnTableState(ev); + break; + + case morkParser_kDictState: // 4 + this->OnDictState(ev); + break; + + case morkParser_kPortState: // 5 + this->OnPortState(ev); + break; + + case morkParser_kStartState: // 6 + this->OnStartState(ev); + break; + + case morkParser_kDoneState: // 7 + mParser_DoMore = morkBool_kFalse; + mParser_IsDone = morkBool_kTrue; + this->StopParse(ev); + break; + case morkParser_kBrokenState: // 8 + mParser_DoMore = morkBool_kFalse; + mParser_IsBroken = morkBool_kTrue; + this->StopParse(ev); + break; + default: // ? + MORK_ASSERT(morkBool_kFalse); + mParser_State = morkParser_kBrokenState; + break; + } +} + +/*public non-poly*/ mdb_count +morkParser::ParseMore( // return count of bytes consumed now + morkEnv* ev, // context + mork_pos* outPos, // current byte pos in the stream afterwards + mork_bool* outDone, // is parsing finished? + mork_bool* outBroken // is parsing irreparably dead and broken? +) { + mdb_count outCount = 0; + if (this->IsNode() && this->GoodParserTag() && this->IsOpenNode()) { + mork_pos startPos = this->HerePos(); + + if (!mParser_IsDone && !mParser_IsBroken) this->ParseChunk(ev); + + // HerePos is only updated for groups. I'd like it to be more accurate. + + mork_pos here; + mParser_Stream->Tell(ev, &here); + + if (outDone) *outDone = mParser_IsDone; + if (outBroken) *outBroken = mParser_IsBroken; + if (outPos) *outPos = here; + + if (here > startPos) outCount = (mdb_count)(here - startPos); + } else { + this->NonUsableParserError(ev); + if (outDone) *outDone = morkBool_kTrue; + if (outBroken) *outBroken = morkBool_kTrue; + if (outPos) *outPos = 0; + } + return outCount; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkParser.h b/comm/mailnews/db/mork/morkParser.h new file mode 100644 index 0000000000..61184ee995 --- /dev/null +++ b/comm/mailnews/db/mork/morkParser.h @@ -0,0 +1,547 @@ +/* -*- 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 _MORKPARSER_ +#define _MORKPARSER_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKBLOB_ +# include "morkBlob.h" +#endif + +#ifndef _MORKSINK_ +# include "morkSink.h" +#endif + +#ifndef _MORKYARN_ +# include "morkYarn.h" +#endif + +#ifndef _MORKCELL_ +# include "morkCell.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*============================================================================= + * morkPlace: stream byte position and stream line count + */ + +class morkPlace { + public: + mork_pos mPlace_Pos; // byte offset in an input stream + mork_line mPlace_Line; // line count in an input stream + + void ClearPlace() { + mPlace_Pos = 0; + mPlace_Line = 0; + } + + void SetPlace(mork_pos inPos, mork_line inLine) { + mPlace_Pos = inPos; + mPlace_Line = inLine; + } + + morkPlace() { + mPlace_Pos = 0; + mPlace_Line = 0; + } + + morkPlace(mork_pos inPos, mork_line inLine) { + mPlace_Pos = inPos; + mPlace_Line = inLine; + } + + morkPlace(const morkPlace& inPlace) + : mPlace_Pos(inPlace.mPlace_Pos), mPlace_Line(inPlace.mPlace_Line) {} +}; + +/*============================================================================= + * morkGlitch: stream place and error comment describing a parsing error + */ + +class morkGlitch { + public: + morkPlace mGlitch_Place; // place in stream where problem happened + const char* mGlitch_Comment; // null-terminated ASCII C string + + morkGlitch() { mGlitch_Comment = 0; } + + morkGlitch(const morkPlace& inPlace, const char* inComment) + : mGlitch_Place(inPlace), mGlitch_Comment(inComment) {} +}; + +/*============================================================================= + * morkMid: all possible ways needed to express an alias ID in Mork syntax + */ + +/*| morkMid: an abstraction of all the variations we might need to support +**| in order to present an ID through the parser interface most cheaply and +**| with minimum transformation away from the original text format. +**| +**|| An ID can have one of four forms: +**| 1) idHex (mMid_Oid.mOid_Id <- idHex) +**| 2) idHex:^scopeHex (mMid_Oid.mOid_Id <- idHex, mOid_Scope <- scopeHex) +**| 3) idHex:scopeName (mMid_Oid.mOid_Id <- idHex, mMid_Buf <- scopeName) +**| 4) columnName (mMid_Buf <- columnName, for columns in cells only) +**| +**|| Typically, mMid_Oid.mOid_Id will hold a nonzero integer value for +**| an ID, but we might have an optional scope specified by either an integer +**| in hex format, or a string name. (Note that while the first ID can be +**| scoped variably, any integer ID for a scope is assumed always located in +**| the same scope, so the second ID need not be disambiguated.) +**| +**|| The only time mMid_Oid.mOid_Id is ever zero is when mMid_Buf alone +**| is nonzero, to indicate an explicit string instead of an alias appeared. +**| This case happens to make the representation of columns in cells somewhat +**| easier to represent, since columns can just appear as a string name; and +**| this unifies those interfaces with row and table APIs expecting IDs. +**| +**|| So when the parser passes an instance of morkMid to a subclass, the +**| mMid_Oid.mOid_Id slot should usually be nonzero. And the other two +**| slots, mMid_Oid.mOid_Scope and mMid_Buf, might both be zero, or at +**| most one of them will be nonzero to indicate an explicit scope; the +**| parser is responsible for ensuring at most one of these is nonzero. +|*/ +class morkMid { + public: + mdbOid mMid_Oid; // mOid_Scope is zero when not specified + const morkBuf* mMid_Buf; // points to some specific buf subclass + + morkMid() { + mMid_Oid.mOid_Scope = 0; + mMid_Oid.mOid_Id = morkId_kMinusOne; + mMid_Buf = 0; + } + + void InitMidWithCoil(morkCoil* ioCoil) { + mMid_Oid.mOid_Scope = 0; + mMid_Oid.mOid_Id = morkId_kMinusOne; + mMid_Buf = ioCoil; + } + + void ClearMid() { + mMid_Oid.mOid_Scope = 0; + mMid_Oid.mOid_Id = morkId_kMinusOne; + mMid_Buf = 0; + } + + morkMid(const morkMid& other) + : mMid_Oid(other.mMid_Oid), mMid_Buf(other.mMid_Buf) {} + + mork_bool HasNoId() const // ID is unspecified? + { + return (mMid_Oid.mOid_Id == morkId_kMinusOne); + } + + mork_bool HasSomeId() const // ID is specified? + { + return (mMid_Oid.mOid_Id != morkId_kMinusOne); + } +}; + +/*============================================================================= + * morkSpan: start and end stream byte position and stream line count + */ + +class morkSpan { + public: + morkPlace mSpan_Start; + morkPlace mSpan_End; + + public: // methods + public: // inlines + morkSpan() {} // use inline empty constructor for each place + + morkPlace* AsPlace() { return &mSpan_Start; } + const morkPlace* AsConstPlace() const { return &mSpan_Start; } + + void SetSpan(mork_pos inFromPos, mork_line inFromLine, mork_pos inToPos, + mork_line inToLine) { + mSpan_Start.SetPlace(inFromPos, inFromLine); + mSpan_End.SetPlace(inToPos, inToLine); + } + + // setting end, useful to terminate a span using current port span end: + void SetEndWithEnd(const morkSpan& inSpan) // end <- span.end + { + mSpan_End = inSpan.mSpan_End; + } + + // setting start, useful to initiate a span using current port span end: + void SetStartWithEnd(const morkSpan& inSpan) // start <- span.end + { + mSpan_Start = inSpan.mSpan_End; + } + + void ClearSpan() { + mSpan_Start.mPlace_Pos = 0; + mSpan_Start.mPlace_Line = 0; + mSpan_End.mPlace_Pos = 0; + mSpan_End.mPlace_Line = 0; + } + + morkSpan(mork_pos inFromPos, mork_line inFromLine, mork_pos inToPos, + mork_line inToLine) + : mSpan_Start(inFromPos, inFromLine), + mSpan_End(inToPos, inToLine) { /* empty implementation */ + } +}; + +/*============================================================================= + * morkParser: for parsing Mork text syntax + */ + +/* parse at least half 0.5K at once */ +#define morkParser_kMinGranularity 512 +/* parse at most 64 K at once */ +#define morkParser_kMaxGranularity (64 * 1024) + +#define morkDerived_kParser /*i*/ 0x5073 /* ascii 'Ps' */ +#define morkParser_kTag /*i*/ 0x70417253 /* ascii 'pArS' */ + +// These are states for the simple parsing virtual machine. Needless to say, +// these must be distinct, and preferably in a contiguous integer range. +// Don't change these constants without looking at switch statements in code. +#define morkParser_kCellState 0 /* cell is tightest scope */ +#define morkParser_kMetaState 1 /* meta is tightest scope */ +#define morkParser_kRowState 2 /* row is tightest scope */ +#define morkParser_kTableState 3 /* table is tightest scope */ +#define morkParser_kDictState 4 /* dict is tightest scope */ +#define morkParser_kPortState 5 /* port is tightest scope */ + +#define morkParser_kStartState 6 /* parsing has not yet begun */ +#define morkParser_kDoneState 7 /* parsing is complete */ +#define morkParser_kBrokenState 8 /* parsing is to broken to work */ + +class morkParser /*d*/ : public morkNode { + // public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // ````` ````` ````` ````` ````` ````` ````` ````` + protected: // protected morkParser members + nsIMdbHeap* mParser_Heap; // refcounted heap used for allocation + morkStream* mParser_Stream; // refcounted input stream + + mork_u4 mParser_Tag; // must equal morkParser_kTag + mork_count mParser_MoreGranularity; // constructor inBytesPerParseSegment + + mork_u4 mParser_State; // state where parser should resume + + // after finding ends of group transactions, we can re-seek the start: + mork_pos mParser_GroupContentStartPos; // start of this group + + morkMid mParser_TableMid; // table mid if inside a table + morkMid mParser_RowMid; // row mid if inside a row + morkMid mParser_CellMid; // cell mid if inside a row + mork_gid mParser_GroupId; // group ID if inside a group + + mork_bool mParser_InPort; // called OnNewPort but not OnPortEnd? + mork_bool mParser_InDict; // called OnNewDict but not OnDictEnd? + mork_bool mParser_InCell; // called OnNewCell but not OnCellEnd? + mork_bool mParser_InMeta; // called OnNewMeta but not OnMetaEnd? + + mork_bool mParser_InPortRow; // called OnNewPortRow but not OnPortRowEnd? + mork_bool mParser_InRow; // called OnNewRow but not OnNewRowEnd? + mork_bool mParser_InTable; // called OnNewMeta but not OnMetaEnd? + mork_bool mParser_InGroup; // called OnNewGroup but not OnGroupEnd? + + mork_change mParser_AtomChange; // driven by mParser_Change + mork_change mParser_CellChange; // driven by mParser_Change + mork_change mParser_RowChange; // driven by mParser_Change + mork_change mParser_TableChange; // driven by mParser_Change + + mork_change mParser_Change; // driven by modifier in text + mork_bool mParser_IsBroken; // has the parse become broken? + mork_bool mParser_IsDone; // has the parse finished? + mork_bool mParser_DoMore; // mParser_MoreGranularity not exhausted? + + morkMid mParser_Mid; // current alias being parsed + // note that mParser_Mid.mMid_Buf points at mParser_ScopeCoil below: + + // blob coils allocated in mParser_Heap + morkCoil mParser_ScopeCoil; // place to accumulate ID scope blobs + morkCoil mParser_ValueCoil; // place to accumulate value blobs + morkCoil mParser_ColumnCoil; // place to accumulate column blobs + morkCoil mParser_StringCoil; // place to accumulate string blobs + + morkSpool mParser_ScopeSpool; // writes to mParser_ScopeCoil + morkSpool mParser_ValueSpool; // writes to mParser_ValueCoil + morkSpool mParser_ColumnSpool; // writes to mParser_ColumnCoil + morkSpool mParser_StringSpool; // writes to mParser_StringCoil + + // yarns allocated in mParser_Heap + morkYarn mParser_MidYarn; // place to receive from MidToYarn() + + // span showing current ongoing file position status: + morkSpan mParser_PortSpan; // span of current db port file + + // various spans denoting nested subspaces inside the file's port span: + morkSpan mParser_GroupSpan; // span of current transaction group + morkSpan mParser_DictSpan; + morkSpan mParser_AliasSpan; + morkSpan mParser_MetaSpan; + morkSpan mParser_TableSpan; + morkSpan mParser_RowSpan; + morkSpan mParser_CellSpan; + morkSpan mParser_ColumnSpan; + morkSpan mParser_SlotSpan; + + private: // convenience inlines + mork_pos HerePos() const { return mParser_PortSpan.mSpan_End.mPlace_Pos; } + + void SetHerePos(mork_pos inPos) { + mParser_PortSpan.mSpan_End.mPlace_Pos = inPos; + } + + void CountLineBreak() { ++mParser_PortSpan.mSpan_End.mPlace_Line; } + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseParser() only if open + virtual ~morkParser(); // assert that CloseParser() executed earlier + + public: // morkYarn construction & destruction + morkParser(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkStream* ioStream, // the readonly stream for input bytes + mdb_count inBytesPerParseSegment, // target for ParseMore() + nsIMdbHeap* ioSlotHeap); + + void CloseParser(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkParser(const morkParser& other); + morkParser& operator=(const morkParser& other); + + public: // dynamic type identification + mork_bool IsParser() const { + return IsNode() && mNode_Derived == morkDerived_kParser; + } + + // } ===== end morkNode methods ===== + + public: // errors and warnings + static void UnexpectedEofError(morkEnv* ev); + static void EofInsteadOfHexError(morkEnv* ev); + static void ExpectedEqualError(morkEnv* ev); + static void ExpectedHexDigitError(morkEnv* ev, int c); + static void NonParserTypeError(morkEnv* ev); + static void UnexpectedByteInMetaWarning(morkEnv* ev); + + public: // other type methods + mork_bool GoodParserTag() const { return mParser_Tag == morkParser_kTag; } + void NonGoodParserError(morkEnv* ev); + void NonUsableParserError(morkEnv* ev); + // call when IsNode() or GoodParserTag() is false + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // in virtual morkParser methods, data flow subclass to parser + virtual void MidToYarn( + morkEnv* ev, + const morkMid& inMid, // typically an alias to concat with strings + mdbYarn* outYarn) = 0; + // The parser might ask that some aliases be turned into yarns, so they + // can be concatenated into longer blobs under some circumstances. This + // is an alternative to using a long and complex callback for many parts + // for a single cell value. + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // out virtual morkParser methods, data flow parser to subclass + // The virtual methods below will be called in a pattern corresponding + // to the following grammar isomorphic to the Mork grammar. There should + // be no exceptions, so subclasses can rely on seeing an appropriate "end" + // method whenever some "new" method has been seen earlier. In the event + // that some error occurs that causes content to be flushed, or sudden early + // termination of a larger containing entity, we will always call a more + // enclosed "end" method before we call an "end" method with greater scope. + + // Note the "mp" prefix stands for "Mork Parser": + + // mp:Start ::= OnNewPort mp:PortItem* OnPortEnd + // mp:PortItem ::= mp:Content | mp:Group | OnPortGlitch + // mp:Group ::= OnNewGroup mp:GroupItem* mp:GroupEnd + // mp:GroupItem ::= mp:Content | OnGroupGlitch + // mp:GroupEnd ::= OnGroupCommitEnd | OnGroupAbortEnd + // mp:Content ::= mp:PortRow | mp:Dict | mp:Table | mp:Row + // mp:PortRow ::= OnNewPortRow mp:RowItem* OnPortRowEnd + // mp:Dict ::= OnNewDict mp:DictItem* OnDictEnd + // mp:DictItem ::= OnAlias | OnAliasGlitch | mp:Meta | OnDictGlitch + // mp:Table ::= OnNewTable mp:TableItem* OnTableEnd + // mp:TableItem ::= mp:Row | mp:MetaTable | OnTableGlitch + // mp:MetaTable ::= OnNewMeta mp:MetaItem* mp:Row OnMetaEnd + // mp:Meta ::= OnNewMeta mp:MetaItem* OnMetaEnd + // mp:MetaItem ::= mp:Cell | OnMetaGlitch + // mp:Row ::= OnMinusRow? OnNewRow mp:RowItem* OnRowEnd + // mp:RowItem ::= mp:Cell | mp:Meta | OnRowGlitch + // mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd + // mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch + // mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid + + // Note that in interfaces below, mork_change parameters kAdd and kNil + // both mean about the same thing by default. Only kCut is interesting, + // because this usually means to remove members instead of adding them. + + virtual void OnNewPort(morkEnv* ev, const morkPlace& inPlace) = 0; + virtual void OnPortGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnPortEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnNewGroup(morkEnv* ev, const morkPlace& inPlace, + mork_gid inGid) = 0; + virtual void OnGroupGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnGroupCommitEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + virtual void OnGroupAbortEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnNewPortRow(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, mork_change inChange) = 0; + virtual void OnPortRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnPortRowEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnNewTable(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, mork_bool inCutAllRows) = 0; + virtual void OnTableGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnTableEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnNewMeta(morkEnv* ev, const morkPlace& inPlace) = 0; + virtual void OnMetaGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnMetaEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnMinusRow(morkEnv* ev) = 0; + virtual void OnNewRow(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, mork_bool inCutAllCols) = 0; + virtual void OnRowPos(morkEnv* ev, mork_pos inRowPos) = 0; + virtual void OnRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnRowEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnNewDict(morkEnv* ev, const morkPlace& inPlace) = 0; + virtual void OnDictGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnDictEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnAlias(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) = 0; + + virtual void OnAliasGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + + virtual void OnMinusCell(morkEnv* ev) = 0; + virtual void OnNewCell(morkEnv* ev, const morkPlace& inPlace, + const morkMid* inMid, const morkBuf* inBuf) = 0; + // Exactly one of inMid and inBuf is nil, and the other is non-nil. + // When hex ID syntax is used for a column, then inMid is not nil, and + // when a naked string names a column, then inBuf is not nil. + + virtual void OnCellGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnCellForm(morkEnv* ev, mork_cscode inCharsetFormat) = 0; + virtual void OnCellEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnValue(morkEnv* ev, const morkSpan& inSpan, + const morkBuf& inBuf) = 0; + + virtual void OnValueMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) = 0; + + virtual void OnRowMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) = 0; + + virtual void OnTableMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) = 0; + + // ````` ````` ````` ````` ````` ````` ````` ````` + protected: // protected parser helper methods + void ParseChunk(morkEnv* ev); // find parse continuation and resume + + void StartParse(morkEnv* ev); // prepare for parsing + void StopParse(morkEnv* ev); // terminate parsing & call needed methods + + int NextChar(morkEnv* ev); // next non-white content + + void OnCellState(morkEnv* ev); + void OnMetaState(morkEnv* ev); + void OnRowState(morkEnv* ev); + void OnTableState(morkEnv* ev); + void OnDictState(morkEnv* ev); + void OnPortState(morkEnv* ev); + void OnStartState(morkEnv* ev); + + void ReadCell(morkEnv* ev); + void ReadRow(morkEnv* ev, int c); + void ReadRowPos(morkEnv* ev); + void ReadTable(morkEnv* ev); + void ReadTableMeta(morkEnv* ev); + void ReadDict(morkEnv* ev); + mork_bool ReadContent(morkEnv* ev, mork_bool inInsideGroup); + void ReadGroup(morkEnv* ev); + mork_bool ReadEndGroupId(morkEnv* ev); + mork_bool ReadAt(morkEnv* ev, mork_bool inInsideGroup); + mork_bool FindGroupEnd(morkEnv* ev); + void ReadMeta(morkEnv* ev, int inEndMeta); + void ReadAlias(morkEnv* ev); + mork_id ReadHex(morkEnv* ev, int* outNextChar); + morkBuf* ReadValue(morkEnv* ev); + morkBuf* ReadName(morkEnv* ev, int c); + mork_bool ReadMid(morkEnv* ev, morkMid* outMid); + void ReadDictForm(morkEnv* ev); + void ReadCellForm(morkEnv* ev, int c); + + mork_bool MatchPattern(morkEnv* ev, const char* inPattern); + + void EndSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan); + void EndSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan); + void StartSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan); + + void StartSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan); + + // void EndSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan) + // { MORK_USED_2(ev,ioSpan); } + + // void EndSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan) + // { MORK_USED_2(ev,ioSpan); } + + // void StartSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan) + // { MORK_USED_2(ev,ioSpan); } + + // void StartSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan) + // { MORK_USED_2(ev,ioSpan); } + + int eat_line_break(morkEnv* ev, int inLast); + int eat_line_continue(morkEnv* ev); // last char was '\\' + int eat_comment(morkEnv* ev); // last char was '/' + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // public non-poly morkParser methods + mdb_count ParseMore( // return count of bytes consumed now + morkEnv* ev, // context + mork_pos* outPos, // current byte pos in the stream afterwards + mork_bool* outDone, // is parsing finished? + mork_bool* outBroken // is parsing irreparably dead and broken? + ); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakParser(morkParser* me, morkEnv* ev, morkParser** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongParser(morkParser* me, morkEnv* ev, + morkParser** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKPARSER_ */ diff --git a/comm/mailnews/db/mork/morkPool.cpp b/comm/mailnews/db/mork/morkPool.cpp new file mode 100644 index 0000000000..eb7d543395 --- /dev/null +++ b/comm/mailnews/db/mork/morkPool.cpp @@ -0,0 +1,483 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKPOOL_ +# include "morkPool.h" +#endif + +#ifndef _MORKATOM_ +# include "morkAtom.h" +#endif + +#ifndef _MORKHANDLE_ +# include "morkHandle.h" +#endif + +#ifndef _MORKCELL_ +# include "morkCell.h" +#endif + +#ifndef _MORKROW_ +# include "morkRow.h" +#endif + +#ifndef _MORKBLOB_ +# include "morkBlob.h" +#endif + +#ifndef _MORKDEQUE_ +# include "morkDeque.h" +#endif + +#ifndef _MORKZONE_ +# include "morkZone.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkPool::CloseMorkNode( + morkEnv* ev) // ClosePool() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->ClosePool(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkPool::~morkPool() // assert ClosePool() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkPool::morkPool(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap) + : morkNode(inUsage, ioHeap), + mPool_Heap(ioSlotHeap), + mPool_UsedFramesCount(0), + mPool_FreeFramesCount(0) { + // mPool_Heap is NOT refcounted + MORK_ASSERT(ioSlotHeap); + if (ioSlotHeap) mNode_Derived = morkDerived_kPool; +} + +/*public non-poly*/ +morkPool::morkPool(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap) + : morkNode(ev, inUsage, ioHeap), + mPool_Heap(ioSlotHeap), + mPool_UsedFramesCount(0), + mPool_FreeFramesCount(0) { + if (ioSlotHeap) { + // mPool_Heap is NOT refcounted: + // nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mPool_Heap); + if (ev->Good()) mNode_Derived = morkDerived_kPool; + } else + ev->NilPointerError(); +} + +/*public non-poly*/ void morkPool::ClosePool( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { +#ifdef morkZone_CONFIG_ARENA +#else /*morkZone_CONFIG_ARENA*/ + // MORK_USED_1(ioZone); +#endif /*morkZone_CONFIG_ARENA*/ + + nsIMdbHeap* heap = mPool_Heap; + nsIMdbEnv* mev = ev->AsMdbEnv(); + morkLink* aLink; + morkDeque* d = &mPool_FreeHandleFrames; + while ((aLink = d->RemoveFirst()) != 0) heap->Free(mev, aLink); + + // if the pool's closed, get rid of the frames in use too. + d = &mPool_UsedHandleFrames; + while ((aLink = d->RemoveFirst()) != 0) heap->Free(mev, aLink); + + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +// alloc and free individual instances of handles (inside hand frames): +morkHandleFace* morkPool::NewHandle(morkEnv* ev, mork_size inSize, + morkZone* ioZone) { + void* newBlock = 0; + if (inSize <= sizeof(morkHandleFrame)) { + morkLink* firstLink = mPool_FreeHandleFrames.RemoveFirst(); + if (firstLink) { + newBlock = firstLink; + if (mPool_FreeFramesCount) + --mPool_FreeFramesCount; + else + ev->NewWarning("mPool_FreeFramesCount underflow"); + } else + mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkHandleFrame), + (void**)&newBlock); + } else { + ev->NewWarning("inSize > sizeof(morkHandleFrame)"); + mPool_Heap->Alloc(ev->AsMdbEnv(), inSize, (void**)&newBlock); + } +#ifdef morkZone_CONFIG_ARENA +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); +#endif /*morkZone_CONFIG_ARENA*/ + + return (morkHandleFace*)newBlock; +} + +void morkPool::ZapHandle(morkEnv* ev, morkHandleFace* ioHandle) { + if (ioHandle) { + morkLink* handleLink = (morkLink*)ioHandle; + mPool_FreeHandleFrames.AddLast(handleLink); + ++mPool_FreeFramesCount; + // lets free all handles to track down leaks + // - uncomment out next 3 lines, comment out above 2 + // nsIMdbHeap* heap = mPool_Heap; + // nsIMdbEnv* mev = ev->AsMdbEnv(); + // heap->Free(mev, handleLink); + } +} + +// alloc and free individual instances of rows: +morkRow* morkPool::NewRow(morkEnv* ev, + morkZone* ioZone) // allocate a new row instance +{ + morkRow* newRow = 0; + +#ifdef morkZone_CONFIG_ARENA + // a zone 'chip' remembers no size, and so cannot be deallocated: + newRow = (morkRow*)ioZone->ZoneNewChip(ev, sizeof(morkRow)); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkRow), (void**)&newRow); +#endif /*morkZone_CONFIG_ARENA*/ + + if (newRow) MORK_MEMSET(newRow, 0, sizeof(morkRow)); + + return newRow; +} + +void morkPool::ZapRow(morkEnv* ev, morkRow* ioRow, + morkZone* ioZone) // free old row instance +{ +#ifdef morkZone_CONFIG_ARENA + if (!ioRow) ev->NilPointerWarning(); // a zone 'chip' cannot be freed +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + if (ioRow) mPool_Heap->Free(ev->AsMdbEnv(), ioRow); +#endif /*morkZone_CONFIG_ARENA*/ +} + +// alloc and free entire vectors of cells (not just one cell at a time) +morkCell* morkPool::NewCells(morkEnv* ev, mork_size inSize, morkZone* ioZone) { + morkCell* newCells = 0; + + mork_size size = inSize * sizeof(morkCell); + if (size) { +#ifdef morkZone_CONFIG_ARENA + // a zone 'run' knows its size, and can indeed be deallocated: + newCells = (morkCell*)ioZone->ZoneNewRun(ev, size); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**)&newCells); +#endif /*morkZone_CONFIG_ARENA*/ + } + + // note morkAtom depends on having nil stored in all new mCell_Atom slots: + if (newCells) MORK_MEMSET(newCells, 0, size); + return newCells; +} + +void morkPool::ZapCells(morkEnv* ev, morkCell* ioVector, mork_size inSize, + morkZone* ioZone) { + MORK_USED_1(inSize); + + if (ioVector) { +#ifdef morkZone_CONFIG_ARENA + // a zone 'run' knows its size, and can indeed be deallocated: + ioZone->ZoneZapRun(ev, ioVector); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Free(ev->AsMdbEnv(), ioVector); +#endif /*morkZone_CONFIG_ARENA*/ + } +} + +// resize (grow or trim) cell vectors inside a containing row instance +mork_bool morkPool::AddRowCells(morkEnv* ev, morkRow* ioRow, + mork_size inNewSize, morkZone* ioZone) { + // note strong implementation similarity to morkArray::Grow() + + MORK_USED_1(ioZone); +#ifdef morkZone_CONFIG_ARENA +#else /*morkZone_CONFIG_ARENA*/ +#endif /*morkZone_CONFIG_ARENA*/ + + mork_fill fill = ioRow->mRow_Length; + if (ev->Good() && fill < inNewSize) // need more cells? + { + morkCell* newCells = this->NewCells(ev, inNewSize, ioZone); + if (newCells) { + morkCell* c = newCells; // for iterating during copy + morkCell* oldCells = ioRow->mRow_Cells; + morkCell* end = oldCells + fill; // copy all the old cells + while (oldCells < end) { + *c++ = *oldCells++; // bitwise copy each old cell struct + } + oldCells = ioRow->mRow_Cells; + ioRow->mRow_Cells = newCells; + ioRow->mRow_Length = (mork_u2)inNewSize; + ++ioRow->mRow_Seed; + + if (oldCells) this->ZapCells(ev, oldCells, fill, ioZone); + } + } + return (ev->Good() && ioRow->mRow_Length >= inNewSize); +} + +mork_bool morkPool::CutRowCells(morkEnv* ev, morkRow* ioRow, + mork_size inNewSize, morkZone* ioZone) { + MORK_USED_1(ioZone); +#ifdef morkZone_CONFIG_ARENA +#else /*morkZone_CONFIG_ARENA*/ +#endif /*morkZone_CONFIG_ARENA*/ + + mork_fill fill = ioRow->mRow_Length; + if (ev->Good() && fill > inNewSize) // need fewer cells? + { + if (inNewSize) // want any row cells at all? + { + morkCell* newCells = this->NewCells(ev, inNewSize, ioZone); + if (newCells) { + morkCell* saveNewCells = newCells; // Keep newcell pos + morkCell* oldCells = ioRow->mRow_Cells; + morkCell* oldEnd = oldCells + fill; // one past all old cells + morkCell* newEnd = oldCells + inNewSize; // copy only kept old cells + while (oldCells < newEnd) { + *newCells++ = *oldCells++; // bitwise copy each old cell struct + } + while (oldCells < oldEnd) { + if (oldCells->mCell_Atom) // need to unref old cell atom? + oldCells->SetAtom(ev, (morkAtom*)0, this); // unref cell atom + ++oldCells; + } + oldCells = ioRow->mRow_Cells; + ioRow->mRow_Cells = saveNewCells; + ioRow->mRow_Length = (mork_u2)inNewSize; + ++ioRow->mRow_Seed; + + if (oldCells) this->ZapCells(ev, oldCells, fill, ioZone); + } + } else // get rid of all row cells + { + morkCell* oldCells = ioRow->mRow_Cells; + ioRow->mRow_Cells = 0; + ioRow->mRow_Length = 0; + ++ioRow->mRow_Seed; + + if (oldCells) this->ZapCells(ev, oldCells, fill, ioZone); + } + } + return (ev->Good() && ioRow->mRow_Length <= inNewSize); +} + +// alloc & free individual instances of atoms (lots of atom subclasses): +void morkPool::ZapAtom(morkEnv* ev, morkAtom* ioAtom, + morkZone* ioZone) // any subclass (by kind) +{ +#ifdef morkZone_CONFIG_ARENA + if (!ioAtom) ev->NilPointerWarning(); // a zone 'chip' cannot be freed +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + if (ioAtom) mPool_Heap->Free(ev->AsMdbEnv(), ioAtom); +#endif /*morkZone_CONFIG_ARENA*/ +} + +morkOidAtom* morkPool::NewRowOidAtom(morkEnv* ev, const mdbOid& inOid, + morkZone* ioZone) { + morkOidAtom* newAtom = 0; + +#ifdef morkZone_CONFIG_ARENA + // a zone 'chip' remembers no size, and so cannot be deallocated: + newAtom = (morkOidAtom*)ioZone->ZoneNewChip(ev, sizeof(morkOidAtom)); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkOidAtom), (void**)&newAtom); +#endif /*morkZone_CONFIG_ARENA*/ + + if (newAtom) newAtom->InitRowOidAtom(ev, inOid); + return newAtom; +} + +morkOidAtom* morkPool::NewTableOidAtom(morkEnv* ev, const mdbOid& inOid, + morkZone* ioZone) { + morkOidAtom* newAtom = 0; + +#ifdef morkZone_CONFIG_ARENA + // a zone 'chip' remembers no size, and so cannot be deallocated: + newAtom = (morkOidAtom*)ioZone->ZoneNewChip(ev, sizeof(morkOidAtom)); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkOidAtom), (void**)&newAtom); +#endif /*morkZone_CONFIG_ARENA*/ + if (newAtom) newAtom->InitTableOidAtom(ev, inOid); + return newAtom; +} + +morkAtom* morkPool::NewAnonAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm, morkZone* ioZone) +// if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee' +// anon atom will be created, and otherwise a 'big' anon atom. +{ + morkAtom* newAtom = 0; + + mork_bool needBig = (inForm || inBuf.mBuf_Fill > 255); + mork_size size = (needBig) ? morkBigAnonAtom::SizeForFill(inBuf.mBuf_Fill) + : morkWeeAnonAtom::SizeForFill(inBuf.mBuf_Fill); + +#ifdef morkZone_CONFIG_ARENA + // a zone 'chip' remembers no size, and so cannot be deallocated: + newAtom = (morkAtom*)ioZone->ZoneNewChip(ev, size); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**)&newAtom); +#endif /*morkZone_CONFIG_ARENA*/ + if (newAtom) { + if (needBig) + ((morkBigAnonAtom*)newAtom)->InitBigAnonAtom(ev, inBuf, inForm); + else + ((morkWeeAnonAtom*)newAtom)->InitWeeAnonAtom(ev, inBuf); + } + return newAtom; +} + +morkBookAtom* morkPool::NewBookAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm, morkAtomSpace* ioSpace, + mork_aid inAid, morkZone* ioZone) +// if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee' +// book atom will be created, and otherwise a 'big' book atom. +{ + morkBookAtom* newAtom = 0; + + mork_bool needBig = (inForm || inBuf.mBuf_Fill > 255); + mork_size size = (needBig) ? morkBigBookAtom::SizeForFill(inBuf.mBuf_Fill) + : morkWeeBookAtom::SizeForFill(inBuf.mBuf_Fill); + +#ifdef morkZone_CONFIG_ARENA + // a zone 'chip' remembers no size, and so cannot be deallocated: + newAtom = (morkBookAtom*)ioZone->ZoneNewChip(ev, size); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**)&newAtom); +#endif /*morkZone_CONFIG_ARENA*/ + if (newAtom) { + if (needBig) + ((morkBigBookAtom*)newAtom) + ->InitBigBookAtom(ev, inBuf, inForm, ioSpace, inAid); + else + ((morkWeeBookAtom*)newAtom)->InitWeeBookAtom(ev, inBuf, ioSpace, inAid); + } + return newAtom; +} + +morkBookAtom* morkPool::NewBookAtomCopy(morkEnv* ev, + const morkBigBookAtom& inAtom, + morkZone* ioZone) +// make the smallest kind of book atom that can hold content in inAtom. +// The inAtom parameter is often expected to be a staged book atom in +// the store, which was used to search an atom space for existing atoms. +{ + morkBookAtom* newAtom = 0; + + mork_cscode form = inAtom.mBigBookAtom_Form; + mork_fill fill = inAtom.mBigBookAtom_Size; + mork_bool needBig = (form || fill > 255); + mork_size size = (needBig) ? morkBigBookAtom::SizeForFill(fill) + : morkWeeBookAtom::SizeForFill(fill); + +#ifdef morkZone_CONFIG_ARENA + // a zone 'chip' remembers no size, and so cannot be deallocated: + newAtom = (morkBookAtom*)ioZone->ZoneNewChip(ev, size); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**)&newAtom); +#endif /*morkZone_CONFIG_ARENA*/ + if (newAtom) { + morkBuf buf(inAtom.mBigBookAtom_Body, fill); + if (needBig) + ((morkBigBookAtom*)newAtom) + ->InitBigBookAtom(ev, buf, form, inAtom.mBookAtom_Space, + inAtom.mBookAtom_Id); + else + ((morkWeeBookAtom*)newAtom) + ->InitWeeBookAtom(ev, buf, inAtom.mBookAtom_Space, + inAtom.mBookAtom_Id); + } + return newAtom; +} + +morkBookAtom* morkPool::NewFarBookAtomCopy(morkEnv* ev, + const morkFarBookAtom& inAtom, + morkZone* ioZone) +// make the smallest kind of book atom that can hold content in inAtom. +// The inAtom parameter is often expected to be a staged book atom in +// the store, which was used to search an atom space for existing atoms. +{ + morkBookAtom* newAtom = 0; + + mork_cscode form = inAtom.mFarBookAtom_Form; + mork_fill fill = inAtom.mFarBookAtom_Size; + mork_bool needBig = (form || fill > 255); + mork_size size = (needBig) ? morkBigBookAtom::SizeForFill(fill) + : morkWeeBookAtom::SizeForFill(fill); + +#ifdef morkZone_CONFIG_ARENA + // a zone 'chip' remembers no size, and so cannot be deallocated: + newAtom = (morkBookAtom*)ioZone->ZoneNewChip(ev, size); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**)&newAtom); +#endif /*morkZone_CONFIG_ARENA*/ + if (newAtom) { + morkBuf buf(inAtom.mFarBookAtom_Body, fill); + if (needBig) + ((morkBigBookAtom*)newAtom) + ->InitBigBookAtom(ev, buf, form, inAtom.mBookAtom_Space, + inAtom.mBookAtom_Id); + else + ((morkWeeBookAtom*)newAtom) + ->InitWeeBookAtom(ev, buf, inAtom.mBookAtom_Space, + inAtom.mBookAtom_Id); + } + return newAtom; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkPool.h b/comm/mailnews/db/mork/morkPool.h new file mode 100644 index 0000000000..dbad8593b7 --- /dev/null +++ b/comm/mailnews/db/mork/morkPool.h @@ -0,0 +1,162 @@ +/* -*- 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 _MORKPOOL_ +#define _MORKPOOL_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKDEQUE_ +# include "morkDeque.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class morkHandle; +class morkHandleFrame; +class morkHandleFace; // just an opaque cookie type +class morkBigBookAtom; +class morkFarBookAtom; + +#define morkDerived_kPool /*i*/ 0x706C /* ascii 'pl' */ + +/*| morkPool: a place to manage pools of non-node objects that are memory +**| managed out of large chunks of space, so that per-object management +**| space overhead has no significant cost. +|*/ +class morkPool : public morkNode { + // public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + public: // state is public because the entire Mork system is private + nsIMdbHeap* mPool_Heap; // NON-refcounted heap instance + + morkDeque mPool_Blocks; // linked list of large blocks from heap + + // These two lists contain instances of morkHandleFrame: + morkDeque mPool_UsedHandleFrames; // handle frames currently being used + morkDeque mPool_FreeHandleFrames; // handle frames currently in free list + + mork_count mPool_UsedFramesCount; // length of mPool_UsedHandleFrames + mork_count mPool_FreeFramesCount; // length of mPool_UsedHandleFrames + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev); // ClosePool() only if open + virtual ~morkPool(); // assert that ClosePool() executed earlier + + public: // morkPool construction & destruction + morkPool(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + morkPool(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + void ClosePool(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkPool(const morkPool& other); + morkPool& operator=(const morkPool& other); + + public: // dynamic type identification + mork_bool IsPool() const { + return IsNode() && mNode_Derived == morkDerived_kPool; + } + // } ===== end morkNode methods ===== + + public: // typing + void NonPoolTypeError(morkEnv* ev); + + public: // morkNode memory management operators + void* operator new(size_t inSize, nsIMdbHeap& ioHeap, + morkEnv* ev) noexcept(true) { + return morkNode::MakeNew(inSize, ioHeap, ev); + } + + void* operator new(size_t inSize) noexcept(true) { + return ::operator new(inSize); + } + + public: // other pool methods + // alloc and free individual instances of handles (inside hand frames): + morkHandleFace* NewHandle(morkEnv* ev, mork_size inSize, morkZone* ioZone); + void ZapHandle(morkEnv* ev, morkHandleFace* ioHandle); + + // alloc and free individual instances of rows: + morkRow* NewRow(morkEnv* ev, morkZone* ioZone); // alloc new row instance + void ZapRow(morkEnv* ev, morkRow* ioRow, + morkZone* ioZone); // free old row instance + + // alloc and free entire vectors of cells (not just one cell at a time) + morkCell* NewCells(morkEnv* ev, mork_size inSize, morkZone* ioZone); + void ZapCells(morkEnv* ev, morkCell* ioVector, mork_size inSize, + morkZone* ioZone); + + // resize (grow or trim) cell vectors inside a containing row instance + mork_bool AddRowCells(morkEnv* ev, morkRow* ioRow, mork_size inNewSize, + morkZone* ioZone); + mork_bool CutRowCells(morkEnv* ev, morkRow* ioRow, mork_size inNewSize, + morkZone* ioZone); + + // alloc & free individual instances of atoms (lots of atom subclasses): + void ZapAtom(morkEnv* ev, morkAtom* ioAtom, + morkZone* ioZone); // any subclass (by kind) + + morkOidAtom* NewRowOidAtom(morkEnv* ev, const mdbOid& inOid, + morkZone* ioZone); + morkOidAtom* NewTableOidAtom(morkEnv* ev, const mdbOid& inOid, + morkZone* ioZone); + + morkAtom* NewAnonAtom(morkEnv* ev, const morkBuf& inBuf, mork_cscode inForm, + morkZone* ioZone); + // if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee' + // anon atom will be created, and otherwise a 'big' anon atom. + + morkBookAtom* NewBookAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm, morkAtomSpace* ioSpace, + mork_aid inAid, morkZone* ioZone); + // if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee' + // book atom will be created, and otherwise a 'big' book atom. + + morkBookAtom* NewBookAtomCopy(morkEnv* ev, const morkBigBookAtom& inAtom, + morkZone* ioZone); + // make the smallest kind of book atom that can hold content in inAtom. + // The inAtom parameter is often expected to be a staged book atom in + // the store, which was used to search an atom space for existing atoms. + + morkBookAtom* NewFarBookAtomCopy(morkEnv* ev, const morkFarBookAtom& inAtom, + morkZone* ioZone); + // make the smallest kind of book atom that can hold content in inAtom. + // The inAtom parameter is often expected to be a staged book atom in + // the store, which was used to search an atom space for existing atoms. + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakPool(morkPool* me, morkEnv* ev, morkPool** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongPool(morkPool* me, morkEnv* ev, morkPool** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKPOOL_ */ diff --git a/comm/mailnews/db/mork/morkPortTableCursor.cpp b/comm/mailnews/db/mork/morkPortTableCursor.cpp new file mode 100644 index 0000000000..8f4f62411d --- /dev/null +++ b/comm/mailnews/db/mork/morkPortTableCursor.cpp @@ -0,0 +1,381 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKCURSOR_ +# include "morkCursor.h" +#endif + +#ifndef _MORKPORTTABLECURSOR_ +# include "morkPortTableCursor.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkPortTableCursor::CloseMorkNode( + morkEnv* ev) // ClosePortTableCursor() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->ClosePortTableCursor(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkPortTableCursor::~morkPortTableCursor() // ClosePortTableCursor() executed + // earlier +{ + CloseMorkNode(mMorkEnv); +} + +/*public non-poly*/ +morkPortTableCursor::morkPortTableCursor(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkStore* ioStore, + mdb_scope inRowScope, + mdb_kind inTableKind, + nsIMdbHeap* ioSlotHeap) + : morkCursor(ev, inUsage, ioHeap), + mPortTableCursor_Store(0), + mPortTableCursor_RowScope((mdb_scope)-1) // we want != inRowScope + , + mPortTableCursor_TableKind((mdb_kind)-1) // we want != inTableKind + , + mPortTableCursor_LastTable(0) // not refcounted + , + mPortTableCursor_RowSpace(0) // strong ref to row space + , + mPortTableCursor_TablesDidEnd(morkBool_kFalse), + mPortTableCursor_SpacesDidEnd(morkBool_kFalse) { + if (ev->Good()) { + if (ioStore && ioSlotHeap) { + mCursor_Pos = -1; + mCursor_Seed = 0; // let the iterator do its own seed handling + morkStore::SlotWeakStore(ioStore, ev, &mPortTableCursor_Store); + + if (this->SetRowScope(ev, inRowScope)) + this->SetTableKind(ev, inTableKind); + + if (ev->Good()) mNode_Derived = morkDerived_kPortTableCursor; + } else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkPortTableCursor, morkCursor, + nsIMdbPortTableCursor) + +morkEnv* morkPortTableCursor::CanUsePortTableCursor(nsIMdbEnv* mev, + mork_bool inMutable, + nsresult* outErr) const { + morkEnv* outEnv = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (IsPortTableCursor()) + outEnv = ev; + else + NonPortTableCursorTypeError(ev); + *outErr = ev->AsErr(); + } + MORK_ASSERT(outEnv); + return outEnv; +} + +/*public non-poly*/ void morkPortTableCursor::ClosePortTableCursor( + morkEnv* ev) { + if (this->IsNode()) { + mCursor_Pos = -1; + mCursor_Seed = 0; + mPortTableCursor_LastTable = 0; + morkStore::SlotWeakStore((morkStore*)0, ev, &mPortTableCursor_Store); + morkRowSpace::SlotStrongRowSpace((morkRowSpace*)0, ev, + &mPortTableCursor_RowSpace); + this->CloseCursor(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void morkPortTableCursor::NilCursorStoreError(morkEnv* ev) { + ev->NewError("nil mPortTableCursor_Store"); +} + +/*static*/ void morkPortTableCursor::NonPortTableCursorTypeError(morkEnv* ev) { + ev->NewError("non morkPortTableCursor"); +} + +mork_bool morkPortTableCursor::SetRowScope(morkEnv* ev, mork_scope inRowScope) { + mPortTableCursor_RowScope = inRowScope; + mPortTableCursor_LastTable = 0; // restart iteration of space + + mPortTableCursor_TableIter.CloseMapIter(ev); + mPortTableCursor_TablesDidEnd = morkBool_kTrue; + mPortTableCursor_SpacesDidEnd = morkBool_kTrue; + + morkStore* store = mPortTableCursor_Store; + if (store) { + morkRowSpace* space = mPortTableCursor_RowSpace; + + if (inRowScope) // intend to cover a specific scope only? + { + space = store->LazyGetRowSpace(ev, inRowScope); + morkRowSpace::SlotStrongRowSpace(space, ev, &mPortTableCursor_RowSpace); + + // We want mPortTableCursor_SpacesDidEnd == morkBool_kTrue + // to show this is the only space to be covered. + } else // prepare space map iter to cover all space scopes + { + morkRowSpaceMapIter* rsi = &mPortTableCursor_SpaceIter; + rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces); + + space = 0; + (void)rsi->FirstRowSpace(ev, (mork_scope*)0, &space); + morkRowSpace::SlotStrongRowSpace(space, ev, &mPortTableCursor_RowSpace); + + if (space) // found first space in store + mPortTableCursor_SpacesDidEnd = morkBool_kFalse; + } + + this->init_space_tables_map(ev); + } else + this->NilCursorStoreError(ev); + + return ev->Good(); +} + +void morkPortTableCursor::init_space_tables_map(morkEnv* ev) { + morkRowSpace* space = mPortTableCursor_RowSpace; + if (space && ev->Good()) { + morkTableMapIter* ti = &mPortTableCursor_TableIter; + ti->InitTableMapIter(ev, &space->mRowSpace_Tables); + if (ev->Good()) mPortTableCursor_TablesDidEnd = morkBool_kFalse; + } +} + +mork_bool morkPortTableCursor::SetTableKind(morkEnv* ev, + mork_kind inTableKind) { + mPortTableCursor_TableKind = inTableKind; + mPortTableCursor_LastTable = 0; // restart iteration of space + + mPortTableCursor_TablesDidEnd = morkBool_kTrue; + + morkRowSpace* space = mPortTableCursor_RowSpace; + if (!space && mPortTableCursor_RowScope == 0) { + this->SetRowScope(ev, 0); + } + this->init_space_tables_map(ev); + + return ev->Good(); +} + +morkRowSpace* morkPortTableCursor::NextSpace(morkEnv* ev) { + morkRowSpace* outSpace = 0; + mPortTableCursor_LastTable = 0; + mPortTableCursor_SpacesDidEnd = morkBool_kTrue; + mPortTableCursor_TablesDidEnd = morkBool_kTrue; + + if (!mPortTableCursor_RowScope) // not just one scope? + { + morkStore* store = mPortTableCursor_Store; + if (store) { + morkRowSpaceMapIter* rsi = &mPortTableCursor_SpaceIter; + + (void)rsi->NextRowSpace(ev, (mork_scope*)0, &outSpace); + morkRowSpace::SlotStrongRowSpace(outSpace, ev, + &mPortTableCursor_RowSpace); + + if (outSpace) // found next space in store + { + mPortTableCursor_SpacesDidEnd = morkBool_kFalse; + + this->init_space_tables_map(ev); + + if (ev->Bad()) outSpace = 0; + } + } else + this->NilCursorStoreError(ev); + } + + return outSpace; +} + +morkTable* morkPortTableCursor::NextTable(morkEnv* ev) { + mork_kind kind = mPortTableCursor_TableKind; + + do // until spaces end, or until we find a table in a space + { + morkRowSpace* space = mPortTableCursor_RowSpace; + if (mPortTableCursor_TablesDidEnd) // current space exhausted? + space = this->NextSpace(ev); // go on to the next space + + if (space) // have a space remaining that might hold tables? + { +#ifdef MORK_BEAD_OVER_NODE_MAPS + morkTableMapIter* ti = &mPortTableCursor_TableIter; + morkTable* table = + (mPortTableCursor_LastTable) ? ti->NextTable(ev) : ti->FirstTable(ev); + + for (; table && ev->Good(); table = ti->NextTable(ev)) +#else /*MORK_BEAD_OVER_NODE_MAPS*/ + mork_tid* key = 0; // ignore keys in table map + morkTable* table = 0; // old value table in the map + morkTableMapIter* ti = &mPortTableCursor_TableIter; + mork_change* c = (mPortTableCursor_LastTable) + ? ti->NextTable(ev, key, &table) + : ti->FirstTable(ev, key, &table); + + for (; c && ev->Good(); c = ti->NextTable(ev, key, &table)) +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ + { + if (table && table->IsTable()) { + if (!kind || kind == table->mTable_Kind) { + mPortTableCursor_LastTable = table; // ti->NextTable() hence + return table; + } + } else + table->NonTableTypeWarning(ev); + } + mPortTableCursor_TablesDidEnd = morkBool_kTrue; // space is done + mPortTableCursor_LastTable = 0; // make sure next space starts fresh + } + + } while (ev->Good() && !mPortTableCursor_SpacesDidEnd); + + return (morkTable*)0; +} + +// { ----- begin table iteration methods ----- + +// { ===== begin nsIMdbPortTableCursor methods ===== + +// { ----- begin attribute methods ----- +NS_IMETHODIMP +morkPortTableCursor::SetPort(nsIMdbEnv* mev, nsIMdbPort* ioPort) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkPortTableCursor::GetPort(nsIMdbEnv* mev, nsIMdbPort** acqPort) { + nsresult outErr = NS_OK; + nsIMdbPort* outPort = 0; + morkEnv* ev = + this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + if (mPortTableCursor_Store) + outPort = mPortTableCursor_Store->AcquireStoreHandle(ev); + outErr = ev->AsErr(); + } + if (acqPort) *acqPort = outPort; + return outErr; +} + +NS_IMETHODIMP +morkPortTableCursor::SetRowScope(nsIMdbEnv* mev, // sets pos to -1 + mdb_scope inRowScope) { + nsresult outErr = NS_OK; + morkEnv* ev = + this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + mCursor_Pos = -1; + + SetRowScope(ev, inRowScope); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkPortTableCursor::GetRowScope(nsIMdbEnv* mev, mdb_scope* outRowScope) { + nsresult outErr = NS_OK; + mdb_scope rowScope = 0; + morkEnv* ev = + this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + rowScope = mPortTableCursor_RowScope; + outErr = ev->AsErr(); + } + *outRowScope = rowScope; + return outErr; +} +// setting row scope to zero iterates over all row scopes in port + +NS_IMETHODIMP +morkPortTableCursor::SetTableKind(nsIMdbEnv* mev, // sets pos to -1 + mdb_kind inTableKind) { + nsresult outErr = NS_OK; + morkEnv* ev = + this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + mCursor_Pos = -1; + + SetTableKind(ev, inTableKind); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkPortTableCursor::GetTableKind(nsIMdbEnv* mev, mdb_kind* outTableKind) +// setting table kind to zero iterates over all table kinds in row scope +{ + nsresult outErr = NS_OK; + mdb_kind tableKind = 0; + morkEnv* ev = + this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + tableKind = mPortTableCursor_TableKind; + outErr = ev->AsErr(); + } + *outTableKind = tableKind; + return outErr; +} +// } ----- end attribute methods ----- + +// { ----- begin table iteration methods ----- +NS_IMETHODIMP +morkPortTableCursor::NextTable( // get table at next position in the db + nsIMdbEnv* mev, // context + nsIMdbTable** acqTable) { + nsresult outErr = NS_OK; + nsIMdbTable* outTable = 0; + morkEnv* ev = + CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkTable* table = NextTable(ev); + if (table && ev->Good()) outTable = table->AcquireTableHandle(ev); + + outErr = ev->AsErr(); + } + if (acqTable) *acqTable = outTable; + return outErr; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkPortTableCursor.h b/comm/mailnews/db/mork/morkPortTableCursor.h new file mode 100644 index 0000000000..2a7bf3d0e9 --- /dev/null +++ b/comm/mailnews/db/mork/morkPortTableCursor.h @@ -0,0 +1,142 @@ +/* -*- 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 _MORKPORTTABLECURSOR_ +#define _MORKPORTTABLECURSOR_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKCURSOR_ +# include "morkCursor.h" +#endif + +#ifndef _MORKROWSPACE_ +# include "morkRowSpace.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class orkinPortTableCursor; +#define morkDerived_kPortTableCursor /*i*/ 0x7443 /* ascii 'tC' */ + +class morkPortTableCursor : public morkCursor, + public nsIMdbPortTableCursor { // row iterator + public: + NS_DECL_ISUPPORTS_INHERITED + // public: // slots inherited from morkObject (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 + + // morkFactory* mObject_Factory; // weak ref to suite factory + + // mork_seed mCursor_Seed; + // mork_pos mCursor_Pos; + // mork_bool mCursor_DoFailOnSeedOutOfSync; + // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment + + public: // state is public because the entire Mork system is private + // { ----- begin attribute methods ----- + NS_IMETHOD SetPort(nsIMdbEnv* ev, + nsIMdbPort* ioPort) override; // sets pos to -1 + NS_IMETHOD GetPort(nsIMdbEnv* ev, nsIMdbPort** acqPort) override; + + NS_IMETHOD SetRowScope(nsIMdbEnv* ev, // sets pos to -1 + mdb_scope inRowScope) override; + NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) override; + // setting row scope to zero iterates over all row scopes in port + + NS_IMETHOD SetTableKind(nsIMdbEnv* ev, // sets pos to -1 + mdb_kind inTableKind) override; + NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) override; + // setting table kind to zero iterates over all table kinds in row scope + // } ----- end attribute methods ----- + + // { ----- begin table iteration methods ----- + NS_IMETHOD NextTable( // get table at next position in the db + nsIMdbEnv* ev, // context + nsIMdbTable** acqTable) override; // the next table in the iteration + // } ----- end table iteration methods ----- + morkStore* mPortTableCursor_Store; // weak ref to store + + mdb_scope mPortTableCursor_RowScope; + mdb_kind mPortTableCursor_TableKind; + + // We only care if LastTable is non-nil, so it is not refcounted; + // so you must never access table state or methods using LastTable: + + morkTable* mPortTableCursor_LastTable; // nil or last table (no refcount) + morkRowSpace* mPortTableCursor_RowSpace; // current space (strong ref) + + morkRowSpaceMapIter mPortTableCursor_SpaceIter; // iter over spaces + morkTableMapIter mPortTableCursor_TableIter; // iter over tables + + // these booleans indicate when the table or space iterator is exhausted: + + mork_bool mPortTableCursor_TablesDidEnd; // no more tables? + mork_bool mPortTableCursor_SpacesDidEnd; // no more spaces? + mork_u1 mPortTableCursor_Pad[2]; // for u4 alignment + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // ClosePortTableCursor() + + public: // morkPortTableCursor construction & destruction + morkPortTableCursor(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkStore* ioStore, mdb_scope inRowScope, + mdb_kind inTableKind, nsIMdbHeap* ioSlotHeap); + void ClosePortTableCursor(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkPortTableCursor(const morkPortTableCursor& other); + morkPortTableCursor& operator=(const morkPortTableCursor& other); + + public: // dynamic type identification + mork_bool IsPortTableCursor() const { + return IsNode() && mNode_Derived == morkDerived_kPortTableCursor; + } + // } ===== end morkNode methods ===== + + protected: // utilities + virtual ~morkPortTableCursor(); // assert that close executed earlier + + void init_space_tables_map(morkEnv* ev); + + public: // other cursor methods + static void NilCursorStoreError(morkEnv* ev); + static void NonPortTableCursorTypeError(morkEnv* ev); + + morkEnv* CanUsePortTableCursor(nsIMdbEnv* mev, mork_bool inMutable, + nsresult* outErr) const; + + morkRowSpace* NextSpace(morkEnv* ev); + morkTable* NextTable(morkEnv* ev); + + mork_bool SetRowScope(morkEnv* ev, mork_scope inRowScope); + mork_bool SetTableKind(morkEnv* ev, mork_kind inTableKind); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakPortTableCursor(morkPortTableCursor* me, morkEnv* ev, + morkPortTableCursor** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongPortTableCursor(morkPortTableCursor* me, morkEnv* ev, + morkPortTableCursor** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKPORTTABLECURSOR_ */ diff --git a/comm/mailnews/db/mork/morkProbeMap.cpp b/comm/mailnews/db/mork/morkProbeMap.cpp new file mode 100644 index 0000000000..6c9c3ecb6c --- /dev/null +++ b/comm/mailnews/db/mork/morkProbeMap.cpp @@ -0,0 +1,1107 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// This code is a port to NS Mork from public domain Mithril C++ sources. +// Note many code comments here come verbatim from cut-and-pasted Mithril. +// In many places, code is identical; Mithril versions stay public domain. +// Changes in porting are mainly class type and scalar type name changes. + +#include "nscore.h" + +#ifndef _MDB_ +# include "mdb.h" +#endif + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKPROBEMAP_ +# include "morkProbeMap.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +/*============================================================================*/ +/* morkMapScratch */ + +void morkMapScratch::halt_map_scratch(morkEnv* ev) { + nsIMdbHeap* heap = sMapScratch_Heap; + + if (heap) { + if (sMapScratch_Keys) heap->Free(ev->AsMdbEnv(), sMapScratch_Keys); + if (sMapScratch_Vals) heap->Free(ev->AsMdbEnv(), sMapScratch_Vals); + } +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*============================================================================*/ +/* morkProbeMap */ + +void morkProbeMap::ProbeMapBadTagError(morkEnv* ev) const { + ev->NewError("bad sProbeMap_Tag"); +} + +void morkProbeMap::WrapWithNoVoidSlotError(morkEnv* ev) const { + ev->NewError("wrap without void morkProbeMap slot"); +} + +void morkProbeMap::GrowFailsMaxFillError(morkEnv* ev) const { + ev->NewError("grow fails morkEnv > sMap_Fill"); +} + +void morkProbeMap::MapKeyIsNotIPError(morkEnv* ev) const { + ev->NewError("not sMap_KeyIsIP"); +} + +void morkProbeMap::MapValIsNotIPError(morkEnv* ev) const { + ev->NewError("not sMap_ValIsIP"); +} + +void morkProbeMap::rehash_old_map(morkEnv* ev, morkMapScratch* ioScratch) { + mork_size keySize = sMap_KeySize; // size of every key bucket + mork_size valSize = sMap_ValSize; // size of every associated value + + mork_count slots = sMap_Slots; // number of new buckets + mork_u1* keys = sMap_Keys; // destination for rehashed keys + mork_u1* vals = sMap_Vals; // destination for any copied values + + mork_bool keyIsIP = (keys && keySize == sizeof(mork_ip) && sMap_KeyIsIP); + mork_bool valIsIP = (vals && valSize == sizeof(mork_ip) && sMap_ValIsIP); + + mork_count oldSlots = ioScratch->sMapScratch_Slots; // sMap_Slots + mork_u1* oldKeys = ioScratch->sMapScratch_Keys; // sMap_Keys + mork_u1* oldVals = ioScratch->sMapScratch_Vals; // sMap_Vals + mork_u1* end = oldKeys + (keySize * oldSlots); // one byte past last key + + mork_fill fill = 0; // let's count the actual fill for a double check + + while (oldKeys < end) // another old key bucket to rehash if non-nil? + { + if (!this->ProbeMapIsKeyNil(ev, oldKeys)) // need to rehash? + { + ++fill; // this had better match sMap_Fill when we are all done + mork_u4 hash = this->ProbeMapHashMapKey(ev, oldKeys); + + mork_pos i = hash % slots; // target hash bucket + mork_pos startPos = i; // remember start to detect + + mork_u1* k = keys + (i * keySize); + while (!this->ProbeMapIsKeyNil(ev, k)) { + if (++i >= + (mork_pos)slots) // advanced past end? need to wrap around now? + i = 0; // wrap around to first slot in map's hash table + + if (i == startPos) // no void slots were found anywhere in map? + { + this->WrapWithNoVoidSlotError(ev); // should never happen + return; // this is bad, and we can't go on with the rehash + } + k = keys + (i * keySize); + } + if (keyIsIP) // int special case? + *((mork_ip*)k) = *((const mork_ip*)oldKeys); // fast bitwise copy + else + MORK_MEMCPY(k, oldKeys, keySize); // slow bitwise copy + + if (oldVals) // need to copy values as well? + { + mork_size valOffset = (i * valSize); + mork_u1* v = vals + valOffset; + mork_u1* ov = oldVals + valOffset; + if (valIsIP) // int special case? + *((mork_ip*)v) = *((const mork_ip*)ov); // fast bitwise copy + else + MORK_MEMCPY(v, ov, valSize); // slow bitwise copy + } + } + oldKeys += keySize; // advance to next key bucket in old map + } + if (fill != sMap_Fill) // is the recorded value of sMap_Fill wrong? + { + ev->NewWarning("fill != sMap_Fill"); + sMap_Fill = fill; + } +} + +mork_bool morkProbeMap::grow_probe_map(morkEnv* ev) { + if (sMap_Heap) // can we grow the map? + { + mork_num newSlots = ((sMap_Slots * 4) / 3) + 1; // +25% + morkMapScratch old; // a place to temporarily hold all the old arrays + if (this->new_slots(ev, &old, newSlots)) // have more? + { + ++sMap_Seed; // note the map has changed + this->rehash_old_map(ev, &old); + + if (ev->Good()) { + mork_count slots = sMap_Slots; + mork_num emptyReserve = (slots / 7) + 1; // keep this many empty + mork_fill maxFill = slots - emptyReserve; // new max occupancy + if (maxFill > sMap_Fill) // new max is bigger than old occupancy? + sProbeMap_MaxFill = maxFill; // we can install new max for fill + else + this->GrowFailsMaxFillError(ev); // we have invariant failure + } + + if (ev->Bad()) // rehash failed? need to revert map to last state? + this->revert_map(ev, &old); // swap the vectors back again + + old.halt_map_scratch(ev); // remember to free the old arrays + } + } else + ev->OutOfMemoryError(); + + return ev->Good(); +} + +void morkProbeMap::revert_map(morkEnv* ev, morkMapScratch* ioScratch) { + mork_count tempSlots = ioScratch->sMapScratch_Slots; // sMap_Slots + mork_u1* tempKeys = ioScratch->sMapScratch_Keys; // sMap_Keys + mork_u1* tempVals = ioScratch->sMapScratch_Vals; // sMap_Vals + + ioScratch->sMapScratch_Slots = sMap_Slots; + ioScratch->sMapScratch_Keys = sMap_Keys; + ioScratch->sMapScratch_Vals = sMap_Vals; + + sMap_Slots = tempSlots; + sMap_Keys = tempKeys; + sMap_Vals = tempVals; +} + +void morkProbeMap::put_probe_kv(morkEnv* ev, const void* inAppKey, + const void* inAppVal, mork_pos inPos) { + mork_u1* mapVal = 0; + mork_u1* mapKey = 0; + + mork_num valSize = sMap_ValSize; + if (valSize && inAppVal) // map holds values? caller sends value? + { + mork_u1* val = sMap_Vals + (valSize * inPos); + if (valSize == sizeof(mork_ip) && sMap_ValIsIP) // int special case? + *((mork_ip*)val) = *((const mork_ip*)inAppVal); + else + mapVal = val; // show possible need to call ProbeMapPushIn() + } + if (inAppKey) // caller sends the key? + { + mork_num keySize = sMap_KeySize; + mork_u1* key = sMap_Keys + (keySize * inPos); + if (keySize == sizeof(mork_ip) && sMap_KeyIsIP) // int special case? + *((mork_ip*)key) = *((const mork_ip*)inAppKey); + else + mapKey = key; // show possible need to call ProbeMapPushIn() + } else + ev->NilPointerError(); + + if ((inAppVal && mapVal) || (inAppKey && mapKey)) + this->ProbeMapPushIn(ev, inAppKey, inAppVal, mapKey, mapVal); + + if (sMap_Fill > sProbeMap_MaxFill) this->grow_probe_map(ev); +} + +void morkProbeMap::get_probe_kv(morkEnv* ev, void* outAppKey, void* outAppVal, + mork_pos inPos) const { + const mork_u1* mapVal = 0; + const mork_u1* mapKey = 0; + + mork_num valSize = sMap_ValSize; + if (valSize && outAppVal) // map holds values? caller wants value? + { + const mork_u1* val = sMap_Vals + (valSize * inPos); + if (valSize == sizeof(mork_ip) && sMap_ValIsIP) // int special case? + *((mork_ip*)outAppVal) = *((const mork_ip*)val); + else + mapVal = val; // show possible need to call ProbeMapPullOut() + } + if (outAppKey) // caller wants the key? + { + mork_num keySize = sMap_KeySize; + const mork_u1* key = sMap_Keys + (keySize * inPos); + if (keySize == sizeof(mork_ip) && sMap_KeyIsIP) // int special case? + *((mork_ip*)outAppKey) = *((const mork_ip*)key); + else + mapKey = key; // show possible need to call ProbeMapPullOut() + } + if ((outAppVal && mapVal) || (outAppKey && mapKey)) + this->ProbeMapPullOut(ev, mapKey, mapVal, outAppKey, outAppVal); +} + +mork_test morkProbeMap::find_key_pos(morkEnv* ev, const void* inAppKey, + mork_u4 inHash, mork_pos* outPos) const { + mork_u1* k = sMap_Keys; // array of keys, each of size sMap_KeySize + mork_num size = sMap_KeySize; // number of bytes in each key + mork_count slots = sMap_Slots; // total number of key buckets + mork_pos i = inHash % slots; // target hash bucket + mork_pos startPos = i; // remember start to detect + + mork_test outTest = this->MapTest(ev, k + (i * size), inAppKey); + while (outTest == morkTest_kMiss) { + if (++i >= + (mork_pos)slots) // advancing goes beyond end? need to wrap around now? + i = 0; // wrap around to first slot in map's hash table + + if (i == startPos) // no void slots were found anywhere in map? + { + this->WrapWithNoVoidSlotError(ev); // should never happen + break; // end loop on kMiss; note caller expects either kVoid or kHit + } + outTest = this->MapTest(ev, k + (i * size), inAppKey); + } + *outPos = i; + + return outTest; +} + +void morkProbeMap::probe_map_lazy_init(morkEnv* ev) { + if (this->need_lazy_init() && sMap_Fill == 0) // pending lazy action? + { + // The constructor cannot successfully call virtual ProbeMapClearKey(), + // so we lazily do so now, when we add the first member to the map. + + mork_u1* keys = sMap_Keys; + if (keys) // okay to call lazy virtual clear method on new map keys? + { + if (sProbeMap_ZeroIsClearKey) // zero is good enough to clear keys? + { + mork_num keyVolume = sMap_Slots * sMap_KeySize; + if (keyVolume) MORK_MEMSET(keys, 0, keyVolume); + } else + this->ProbeMapClearKey(ev, keys, sMap_Slots); + } else + this->MapNilKeysError(ev); + } + sProbeMap_LazyClearOnAdd = 0; // don't do this ever again +} + +mork_bool morkProbeMap::MapAtPut(morkEnv* ev, const void* inAppKey, + const void* inAppVal, void* outAppKey, + void* outAppVal) { + mork_bool outPut = morkBool_kFalse; + + if (this->GoodProbeMap()) /* looks good? */ + { + if (this->need_lazy_init() && sMap_Fill == 0) // pending lazy action? + this->probe_map_lazy_init(ev); + + if (ev->Good()) { + mork_pos slotPos = 0; + mork_u4 hash = this->MapHash(ev, inAppKey); + mork_test test = this->find_key_pos(ev, inAppKey, hash, &slotPos); + outPut = (test == morkTest_kHit); + + if (outPut) // replacing an old assoc? no change in member count? + { + if (outAppKey || outAppVal) /* copy old before cobber? */ + this->get_probe_kv(ev, outAppKey, outAppVal, slotPos); + } else // adding a new assoc increases membership by one + { + ++sMap_Fill; /* one more member in the collection */ + } + + if (test != morkTest_kMiss) /* found slot to hold new assoc? */ + { + ++sMap_Seed; /* note the map has changed */ + this->put_probe_kv(ev, inAppKey, inAppVal, slotPos); + } + } + } else + this->ProbeMapBadTagError(ev); + + return outPut; +} + +mork_bool morkProbeMap::MapAt(morkEnv* ev, const void* inAppKey, + void* outAppKey, void* outAppVal) { + if (this->GoodProbeMap()) /* looks good? */ + { + if (this->need_lazy_init() && sMap_Fill == 0) // pending lazy action? + this->probe_map_lazy_init(ev); + + mork_pos slotPos = 0; + mork_u4 hash = this->MapHash(ev, inAppKey); + mork_test test = this->find_key_pos(ev, inAppKey, hash, &slotPos); + if (test == morkTest_kHit) /* found an assoc pair for inAppKey? */ + { + this->get_probe_kv(ev, outAppKey, outAppVal, slotPos); + return morkBool_kTrue; + } + } else + this->ProbeMapBadTagError(ev); + + return morkBool_kFalse; +} + +mork_num morkProbeMap::MapCutAll(morkEnv* ev) { + mork_num outCutAll = 0; + + if (this->GoodProbeMap()) /* looks good? */ + { + outCutAll = sMap_Fill; /* number of members cut, which is all of them */ + + if (sMap_Keys && !sProbeMap_ZeroIsClearKey) + this->ProbeMapClearKey(ev, sMap_Keys, sMap_Slots); + + sMap_Fill = 0; /* map now has no members */ + } else + this->ProbeMapBadTagError(ev); + + return outCutAll; +} + +// { ===== node interface ===== + +/*virtual*/ +morkProbeMap::~morkProbeMap() // assert NodeStop() finished earlier +{ + MORK_ASSERT(sMap_Keys == 0); + MORK_ASSERT(sProbeMap_Tag == 0); +} + +/*public virtual*/ void morkProbeMap::CloseMorkNode( + morkEnv* ev) // CloseMap() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseProbeMap(ev); + this->MarkShut(); + } +} + +void morkProbeMap::CloseProbeMap(morkEnv* ev) { + if (this->IsNode()) { + nsIMdbHeap* heap = sMap_Heap; + if (heap) // able to free map arrays? + { + void* block = sMap_Keys; + if (block) { + heap->Free(ev->AsMdbEnv(), block); + sMap_Keys = 0; + } + + block = sMap_Vals; + if (block) { + heap->Free(ev->AsMdbEnv(), block); + sMap_Vals = 0; + } + } + sMap_Keys = 0; + sMap_Vals = 0; + + this->CloseNode(ev); + sProbeMap_Tag = 0; + sProbeMap_MaxFill = 0; + + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +void* morkProbeMap::clear_alloc(morkEnv* ev, mork_size inSize) { + void* p = 0; + nsIMdbHeap* heap = sMap_Heap; + if (heap) { + if (NS_SUCCEEDED(heap->Alloc(ev->AsMdbEnv(), inSize, (void**)&p)) && p) { + MORK_MEMSET(p, 0, inSize); + return p; + } + } else + ev->NilPointerError(); + + return (void*)0; +} + +/*| map_new_keys: allocate an array of inSlots new keys filled with zero. +**| (cf IronDoc's FeHashTable_new_keys()) +|*/ +mork_u1* morkProbeMap::map_new_keys(morkEnv* ev, mork_num inSlots) { + mork_num size = inSlots * sMap_KeySize; + return (mork_u1*)this->clear_alloc(ev, size); +} + +/*| map_new_vals: allocate an array of inSlots new values filled with zero. +**| When values are zero sized, we just return a null pointer. +**| +**| (cf IronDoc's FeHashTable_new_values()) +|*/ +mork_u1* morkProbeMap::map_new_vals(morkEnv* ev, mork_num inSlots) { + mork_u1* values = 0; + mork_num size = inSlots * sMap_ValSize; + if (size) values = (mork_u1*)this->clear_alloc(ev, size); + return values; +} + +void morkProbeMap::MapSeedOutOfSyncError(morkEnv* ev) { + ev->NewError("sMap_Seed out of sync"); +} + +void morkProbeMap::MapFillUnderflowWarning(morkEnv* ev) { + ev->NewWarning("sMap_Fill underflow"); +} + +void morkProbeMap::MapNilKeysError(morkEnv* ev) { + ev->NewError("nil sMap_Keys"); +} + +void morkProbeMap::MapZeroKeySizeError(morkEnv* ev) { + ev->NewError("zero sMap_KeySize"); +} + +/*static*/ +void morkProbeMap::ProbeMapCutError(morkEnv* ev) { + ev->NewError("morkProbeMap cannot cut"); +} + +void morkProbeMap::init_probe_map(morkEnv* ev, mork_size inSlots) { + // Note we cannot successfully call virtual ProbeMapClearKey() when we + // call init_probe_map() inside the constructor; so we leave this problem + // to the caller. (The constructor will call ProbeMapClearKey() later + // after setting a suitable lazy flag to show this action is pending.) + + if (ev->Good()) { + morkMapScratch old; + + if (inSlots < 7) // capacity too small? + inSlots = 7; // increase to reasonable minimum + else if (inSlots > (128 * 1024)) // requested capacity too big? + inSlots = (128 * 1024); // decrease to reasonable maximum + + if (this->new_slots(ev, &old, inSlots)) sProbeMap_Tag = morkProbeMap_kTag; + + mork_count slots = sMap_Slots; + mork_num emptyReserve = (slots / 7) + 1; // keep this many empty + sProbeMap_MaxFill = slots - emptyReserve; + + MORK_MEMSET(&old, 0, sizeof(morkMapScratch)); // don't bother halting + } +} + +mork_bool morkProbeMap::new_slots(morkEnv* ev, morkMapScratch* old, + mork_num inSlots) { + mork_bool outNew = morkBool_kFalse; + + // Note we cannot successfully call virtual ProbeMapClearKey() when we + // call new_slots() inside the constructor; so we leave this problem + // to the caller. (The constructor will call ProbeMapClearKey() later + // after setting a suitable lazy flag to show this action is pending.) + + // allocate every new array before we continue: + mork_u1* newKeys = this->map_new_keys(ev, inSlots); + mork_u1* newVals = this->map_new_vals(ev, inSlots); + + // okay for newVals to be null when values are zero sized? + mork_bool okayValues = (newVals || !sMap_ValSize); + + if (newKeys && okayValues) { + outNew = morkBool_kTrue; // we created every array needed + + // init mapScratch using slots from current map: + old->sMapScratch_Heap = sMap_Heap; + + old->sMapScratch_Slots = sMap_Slots; + old->sMapScratch_Keys = sMap_Keys; + old->sMapScratch_Vals = sMap_Vals; + + // replace all map array slots using the newly allocated members: + ++sMap_Seed; // the map has changed + sMap_Keys = newKeys; + sMap_Vals = newVals; + sMap_Slots = inSlots; + } else // free any allocations if only partially successful + { + nsIMdbHeap* heap = sMap_Heap; + if (newKeys) heap->Free(ev->AsMdbEnv(), newKeys); + if (newVals) heap->Free(ev->AsMdbEnv(), newVals); + + MORK_MEMSET(old, 0, sizeof(morkMapScratch)); // zap scratch space + } + + return outNew; +} + +void morkProbeMap::clear_probe_map(morkEnv* ev, nsIMdbHeap* ioMapHeap) { + sProbeMap_Tag = 0; + sMap_Seed = 0; + sMap_Slots = 0; + sMap_Fill = 0; + sMap_Keys = 0; + sMap_Vals = 0; + sProbeMap_MaxFill = 0; + + sMap_Heap = ioMapHeap; + if (!ioMapHeap) ev->NilPointerError(); +} + +morkProbeMap::morkProbeMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioNodeHeap, mork_size inKeySize, + mork_size inValSize, nsIMdbHeap* ioMapHeap, + mork_size inSlots, mork_bool inZeroIsClearKey) + + : morkNode(ev, inUsage, ioNodeHeap), + sMap_Heap(ioMapHeap) + + , + sMap_Keys(0), + sMap_Vals(0) + + , + sMap_Seed(0) // change count of members or structure + + , + sMap_Slots(0) // count of slots in the hash table + , + sMap_Fill(0) // number of used slots in the hash table + + , + sMap_KeySize(0) // size of each key (cannot be zero) + , + sMap_ValSize(0) // size of each val (zero allowed) + + , + sMap_KeyIsIP(morkBool_kFalse) // sMap_KeySize == sizeof(mork_ip) + , + sMap_ValIsIP(morkBool_kFalse) // sMap_ValSize == sizeof(mork_ip) + + , + sProbeMap_MaxFill(0), + sProbeMap_LazyClearOnAdd(0), + sProbeMap_ZeroIsClearKey(inZeroIsClearKey), + sProbeMap_Tag(0) { + // Note we cannot successfully call virtual ProbeMapClearKey() when we + // call init_probe_map() inside the constructor; so we leave this problem + // to the caller. (The constructor will call ProbeMapClearKey() later + // after setting a suitable lazy flag to show this action is pending.) + + if (ev->Good()) { + this->clear_probe_map(ev, ioMapHeap); + if (ev->Good()) { + sMap_KeySize = inKeySize; + sMap_ValSize = inValSize; + sMap_KeyIsIP = (inKeySize == sizeof(mork_ip)); + sMap_ValIsIP = (inValSize == sizeof(mork_ip)); + + this->init_probe_map(ev, inSlots); + if (ev->Good()) { + if (!inZeroIsClearKey) // must lazy clear later with virtual method? + sProbeMap_LazyClearOnAdd = morkProbeMap_kLazyClearOnAdd; + + mNode_Derived = morkDerived_kProbeMap; + } + } + } +} + +/*============================================================================*/ + +/*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b) +morkProbeMap::MapTest(morkEnv* ev, const void* inMapKey, + const void* inAppKey) const +// Note inMapKey is always a key already stored in the map, while inAppKey +// is always a method argument parameter from a client method call. +// This matters the most in morkProbeMap subclasses, which have the +// responsibility of putting 'app' keys into slots for 'map' keys, and +// the bit pattern representation might be different in such cases. +// morkTest_kHit means that inMapKey equals inAppKey (and this had better +// also imply that hash(inMapKey) == hash(inAppKey)). +// morkTest_kMiss means that inMapKey does NOT equal inAppKey (but this +// implies nothing at all about hash(inMapKey) and hash(inAppKey)). +// morkTest_kVoid means that inMapKey is not a valid key bit pattern, +// which means that key slot in the map is not being used. Note that +// kVoid is only expected as a return value in morkProbeMap subclasses, +// because morkProbeMap must ask whether a key slot is used or not. +// morkChainMap however, always knows when a key slot is used, so only +// key slots expected to have valid bit patterns will be presented to +// the MapTest() methods for morkChainMap subclasses. +// +// NOTE: it is very important that subclasses correctly return the value +// morkTest_kVoid whenever the slot for inMapKey contains a bit pattern +// that means the slot is not being used, because this is the only way a +// probe map can terminate an unsuccessful search for a key in the map. +{ + mork_size keySize = sMap_KeySize; + if (keySize == sizeof(mork_ip) && sMap_KeyIsIP) { + mork_ip mapKey = *((const mork_ip*)inMapKey); + if (mapKey == *((const mork_ip*)inAppKey)) + return morkTest_kHit; + else { + return (mapKey) ? morkTest_kMiss : morkTest_kVoid; + } + } else { + mork_bool allSame = morkBool_kTrue; + mork_bool allZero = morkBool_kTrue; + const mork_u1* ak = (const mork_u1*)inAppKey; + const mork_u1* mk = (const mork_u1*)inMapKey; + const mork_u1* end = mk + keySize; + --mk; // prepare for preincrement: + while (++mk < end) { + mork_u1 byte = *mk; + if (byte) // any nonzero byte in map key means slot is not nil? + allZero = morkBool_kFalse; + if (byte != *ak++) // bytes differ in map and app keys? + allSame = morkBool_kFalse; + } + if (allSame) + return morkTest_kHit; + else + return (allZero) ? morkTest_kVoid : morkTest_kMiss; + } +} + +/*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b) +morkProbeMap::MapHash(morkEnv* ev, const void* inAppKey) const { + mork_size keySize = sMap_KeySize; + if (keySize == sizeof(mork_ip) && sMap_KeyIsIP) { + return *((const mork_ip*)inAppKey); + } else { + const mork_u1* key = (const mork_u1*)inAppKey; + const mork_u1* end = key + keySize; + --key; // prepare for preincrement: + while (++key < end) { + if (*key) // any nonzero byte in map key means slot is not nil? + return morkBool_kFalse; + } + return morkBool_kTrue; + } + return (mork_u4)NS_PTR_TO_INT32(inAppKey); +} + +/*============================================================================*/ + +/*virtual*/ mork_u4 morkProbeMap::ProbeMapHashMapKey(morkEnv* ev, + const void* inMapKey) const +// ProbeMapHashMapKey() does logically the same thing as MapHash(), and +// the default implementation actually calls virtual MapHash(). However, +// Subclasses must override this method whenever the formats of keys in +// the map differ from app keys outside the map, because MapHash() only +// works on keys in 'app' format, while ProbeMapHashMapKey() only works +// on keys in 'map' format. This method is called in order to rehash all +// map keys when a map is grown, and this causes all old map members to +// move into new slot locations. +// +// Note it is absolutely imperative that a hash for a key in 'map' format +// be exactly the same the hash of the same key in 'app' format, or else +// maps will seem corrupt later when keys in 'app' format cannot be found. +{ + return this->MapHash(ev, inMapKey); +} + +/*virtual*/ mork_bool morkProbeMap::ProbeMapIsKeyNil(morkEnv* ev, + void* ioMapKey) +// ProbeMapIsKeyNil() must say whether the representation of logical 'nil' +// is currently found inside the key at ioMapKey, for a key found within +// the map. The the map iterator uses this method to find map keys that +// are actually being used for valid map associations; otherwise the +// iterator cannot determine which map slots actually denote used keys. +// The default method version returns true if all the bits equal zero. +{ + if (sMap_KeySize == sizeof(mork_ip) && sMap_KeyIsIP) { + return !*((const mork_ip*)ioMapKey); + } else { + const mork_u1* key = (const mork_u1*)ioMapKey; + const mork_u1* end = key + sMap_KeySize; + --key; // prepare for preincrement: + while (++key < end) { + if (*key) // any nonzero byte in map key means slot is not nil? + return morkBool_kFalse; + } + return morkBool_kTrue; + } +} + +/*virtual*/ void morkProbeMap::ProbeMapClearKey( + morkEnv* ev, // put 'nil' into all keys inside map + void* ioMapKey, mork_count inKeyCount) // array of keys inside map +// ProbeMapClearKey() must put some representation of logical 'nil' into +// every key slot in the map, such that MapTest() will later recognize +// that this bit pattern shows each key slot is not actually being used. +// +// This method is typically called whenever the map is either created or +// grown into a larger size, where ioMapKey is a pointer to an array of +// inKeyCount keys, where each key is this->MapKeySize() bytes in size. +// Note that keys are assumed immediately adjacent with no padding, so +// if any alignment requirements must be met, then subclasses should have +// already accounted for this when specifying a key size in the map. +// +// Since this method will be called when a map is being grown in size, +// nothing should be assumed about the state slots of the map, since the +// ioMapKey array might not yet live in sMap_Keys, and the array length +// inKeyCount might not yet live in sMap_Slots. However, the value kept +// in sMap_KeySize never changes, so this->MapKeySize() is always correct. +{ + if (ioMapKey && inKeyCount) { + MORK_MEMSET(ioMapKey, 0, (inKeyCount * sMap_KeySize)); + } else + ev->NilPointerWarning(); +} + +/*virtual*/ void morkProbeMap::ProbeMapPushIn( + morkEnv* ev, // move (key,val) into the map + const void* inAppKey, const void* inAppVal, // (key,val) outside map + void* outMapKey, void* outMapVal) // (key,val) inside map +// This method actually puts keys and vals in the map in suitable format. +// +// ProbeMapPushIn() must copy a caller key and value in 'app' format +// into the map slots provided, which are in 'map' format. When the +// 'app' and 'map' formats are identical, then this is just a bitwise +// copy of this->MapKeySize() key bytes and this->MapValSize() val bytes, +// and this is exactly what the default implementation performs. However, +// if 'app' and 'map' formats are different, and MapTest() depends on this +// difference in format, then subclasses must override this method to do +// whatever is necessary to store the input app key in output map format. +// +// Do NOT write more than this->MapKeySize() bytes of a map key, or more +// than this->MapValSize() bytes of a map val, or corruption might ensue. +// +// The inAppKey and inAppVal parameters are the same ones passed into a +// call to MapAtPut(), and the outMapKey and outMapVal parameters are ones +// determined by how the map currently positions key inAppKey in the map. +// +// Note any key or val parameter can be a null pointer, in which case +// this method must do nothing with those parameters. In particular, do +// no key move at all when either inAppKey or outMapKey is nil, and do +// no val move at all when either inAppVal or outMapVal is nil. Note that +// outMapVal should always be nil when this->MapValSize() is nil. +{} + +/*virtual*/ void morkProbeMap::ProbeMapPullOut( + morkEnv* ev, // move (key,val) out from the map + const void* inMapKey, const void* inMapVal, // (key,val) inside map + void* outAppKey, void* outAppVal) const // (key,val) outside map +// This method actually gets keys and vals from the map in suitable format. +// +// ProbeMapPullOut() must copy a key and val in 'map' format into the +// caller key and val slots provided, which are in 'app' format. When the +// 'app' and 'map' formats are identical, then this is just a bitwise +// copy of this->MapKeySize() key bytes and this->MapValSize() val bytes, +// and this is exactly what the default implementation performs. However, +// if 'app' and 'map' formats are different, and MapTest() depends on this +// difference in format, then subclasses must override this method to do +// whatever is necessary to store the input map key in output app format. +// +// The outAppKey and outAppVal parameters are the same ones passed into a +// call to either MapAtPut() or MapAt(), while inMapKey and inMapVal are +// determined by how the map currently positions the target key in the map. +// +// Note any key or val parameter can be a null pointer, in which case +// this method must do nothing with those parameters. In particular, do +// no key move at all when either inMapKey or outAppKey is nil, and do +// no val move at all when either inMapVal or outAppVal is nil. Note that +// inMapVal should always be nil when this->MapValSize() is nil. +{} + +/*============================================================================*/ +/* morkProbeMapIter */ + +morkProbeMapIter::morkProbeMapIter(morkEnv* ev, morkProbeMap* ioMap) + : sProbeMapIter_Map(0), + sProbeMapIter_Seed(0), + sProbeMapIter_HereIx(morkProbeMapIter_kBeforeIx) { + if (ioMap) { + if (ioMap->GoodProbeMap()) { + if (ioMap->need_lazy_init()) // pending lazy action? + ioMap->probe_map_lazy_init(ev); + + sProbeMapIter_Map = ioMap; + sProbeMapIter_Seed = ioMap->sMap_Seed; + } else + ioMap->ProbeMapBadTagError(ev); + } else + ev->NilPointerError(); +} + +void morkProbeMapIter::CloseMapIter(morkEnv* ev) { + MORK_USED_1(ev); + sProbeMapIter_Map = 0; + sProbeMapIter_Seed = 0; + + sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx; +} + +morkProbeMapIter::morkProbeMapIter() +// zero most slots; caller must call InitProbeMapIter() +{ + sProbeMapIter_Map = 0; + sProbeMapIter_Seed = 0; + + sProbeMapIter_HereIx = morkProbeMapIter_kBeforeIx; +} + +void morkProbeMapIter::InitProbeMapIter(morkEnv* ev, morkProbeMap* ioMap) { + sProbeMapIter_Map = 0; + sProbeMapIter_Seed = 0; + + sProbeMapIter_HereIx = morkProbeMapIter_kBeforeIx; + + if (ioMap) { + if (ioMap->GoodProbeMap()) { + if (ioMap->need_lazy_init()) // pending lazy action? + ioMap->probe_map_lazy_init(ev); + + sProbeMapIter_Map = ioMap; + sProbeMapIter_Seed = ioMap->sMap_Seed; + } else + ioMap->ProbeMapBadTagError(ev); + } else + ev->NilPointerError(); +} + +mork_bool morkProbeMapIter::IterFirst(morkEnv* ev, void* outAppKey, + void* outAppVal) { + sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx; // default to done + morkProbeMap* map = sProbeMapIter_Map; + + if (map && map->GoodProbeMap()) /* looks good? */ + { + sProbeMapIter_Seed = map->sMap_Seed; /* sync the seeds */ + + mork_u1* k = map->sMap_Keys; // array of keys, each of size sMap_KeySize + mork_num size = map->sMap_KeySize; // number of bytes in each key + mork_count slots = map->sMap_Slots; // total number of key buckets + mork_pos here = 0; // first hash bucket + + while (here < (mork_pos)slots) { + if (!map->ProbeMapIsKeyNil(ev, k + (here * size))) { + map->get_probe_kv(ev, outAppKey, outAppVal, here); + + sProbeMapIter_HereIx = (mork_i4)here; + return morkBool_kTrue; + } + ++here; // next bucket + } + } else + map->ProbeMapBadTagError(ev); + + return morkBool_kFalse; +} + +mork_bool morkProbeMapIter::IterNext(morkEnv* ev, void* outAppKey, + void* outAppVal) { + morkProbeMap* map = sProbeMapIter_Map; + + if (map && map->GoodProbeMap()) /* looks good? */ + { + if (sProbeMapIter_Seed == map->sMap_Seed) /* in sync? */ + { + if (sProbeMapIter_HereIx != morkProbeMapIter_kAfterIx) { + mork_pos here = (mork_pos)sProbeMapIter_HereIx; + if (sProbeMapIter_HereIx < 0) + here = 0; + else + ++here; + + sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx; // default to done + + mork_u1* k = map->sMap_Keys; // key array, each of size sMap_KeySize + mork_num size = map->sMap_KeySize; // number of bytes in each key + mork_count slots = map->sMap_Slots; // total number of key buckets + + while (here < (mork_pos)slots) { + if (!map->ProbeMapIsKeyNil(ev, k + (here * size))) { + map->get_probe_kv(ev, outAppKey, outAppVal, here); + + sProbeMapIter_HereIx = (mork_i4)here; + return morkBool_kTrue; + } + ++here; // next bucket + } + } + } else + map->MapSeedOutOfSyncError(ev); + } else + map->ProbeMapBadTagError(ev); + + return morkBool_kFalse; +} + +mork_bool morkProbeMapIter::IterHere(morkEnv* ev, void* outAppKey, + void* outAppVal) { + morkProbeMap* map = sProbeMapIter_Map; + + if (map && map->GoodProbeMap()) /* looks good? */ + { + if (sProbeMapIter_Seed == map->sMap_Seed) /* in sync? */ + { + mork_pos here = (mork_pos)sProbeMapIter_HereIx; + mork_count slots = map->sMap_Slots; // total number of key buckets + if (sProbeMapIter_HereIx >= 0 && (here < (mork_pos)slots)) { + mork_u1* k = map->sMap_Keys; // key array, each of size sMap_KeySize + mork_num size = map->sMap_KeySize; // number of bytes in each key + + if (!map->ProbeMapIsKeyNil(ev, k + (here * size))) { + map->get_probe_kv(ev, outAppKey, outAppVal, here); + return morkBool_kTrue; + } + } + } else + map->MapSeedOutOfSyncError(ev); + } else + map->ProbeMapBadTagError(ev); + + return morkBool_kFalse; +} + +mork_change* morkProbeMapIter::First(morkEnv* ev, void* outKey, void* outVal) { + if (this->IterFirst(ev, outKey, outVal)) return &sProbeMapIter_Change; + + return (mork_change*)0; +} + +mork_change* morkProbeMapIter::Next(morkEnv* ev, void* outKey, void* outVal) { + if (this->IterNext(ev, outKey, outVal)) return &sProbeMapIter_Change; + + return (mork_change*)0; +} + +mork_change* morkProbeMapIter::Here(morkEnv* ev, void* outKey, void* outVal) { + if (this->IterHere(ev, outKey, outVal)) return &sProbeMapIter_Change; + + return (mork_change*)0; +} + +mork_change* morkProbeMapIter::CutHere(morkEnv* ev, void* outKey, + void* outVal) { + morkProbeMap::ProbeMapCutError(ev); + + return (mork_change*)0; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// NOTE: the following methods ONLY work for sMap_ValIsIP pointer values. +// (Note the implied assumption that zero is never a good value pattern.) + +void* morkProbeMapIter::IterFirstVal(morkEnv* ev, void* outKey) +// equivalent to { void* v=0; this->IterFirst(ev, outKey, &v); return v; } +{ + morkProbeMap* map = sProbeMapIter_Map; + if (map) { + if (map->sMap_ValIsIP) { + void* v = 0; + this->IterFirst(ev, outKey, &v); + return v; + } else + map->MapValIsNotIPError(ev); + } + return (void*)0; +} + +void* morkProbeMapIter::IterNextVal(morkEnv* ev, void* outKey) +// equivalent to { void* v=0; this->IterNext(ev, outKey, &v); return v; } +{ + morkProbeMap* map = sProbeMapIter_Map; + if (map) { + if (map->sMap_ValIsIP) { + void* v = 0; + this->IterNext(ev, outKey, &v); + return v; + } else + map->MapValIsNotIPError(ev); + } + return (void*)0; +} + +void* morkProbeMapIter::IterHereVal(morkEnv* ev, void* outKey) +// equivalent to { void* v=0; this->IterHere(ev, outKey, &v); return v; } +{ + morkProbeMap* map = sProbeMapIter_Map; + if (map) { + if (map->sMap_ValIsIP) { + void* v = 0; + this->IterHere(ev, outKey, &v); + return v; + } else + map->MapValIsNotIPError(ev); + } + return (void*)0; +} + +// NOTE: the following methods ONLY work for sMap_KeyIsIP pointer values. +// (Note the implied assumption that zero is never a good key pattern.) + +void* morkProbeMapIter::IterFirstKey(morkEnv* ev) +// equivalent to { void* k=0; this->IterFirst(ev, &k, 0); return k; } +{ + morkProbeMap* map = sProbeMapIter_Map; + if (map) { + if (map->sMap_KeyIsIP) { + void* k = 0; + this->IterFirst(ev, &k, (void*)0); + return k; + } else + map->MapKeyIsNotIPError(ev); + } + return (void*)0; +} + +void* morkProbeMapIter::IterNextKey(morkEnv* ev) +// equivalent to { void* k=0; this->IterNext(ev, &k, 0); return k; } +{ + morkProbeMap* map = sProbeMapIter_Map; + if (map) { + if (map->sMap_KeyIsIP) { + void* k = 0; + this->IterNext(ev, &k, (void*)0); + return k; + } else + map->MapKeyIsNotIPError(ev); + } + return (void*)0; +} + +void* morkProbeMapIter::IterHereKey(morkEnv* ev) +// equivalent to { void* k=0; this->IterHere(ev, &k, 0); return k; } +{ + morkProbeMap* map = sProbeMapIter_Map; + if (map) { + if (map->sMap_KeyIsIP) { + void* k = 0; + this->IterHere(ev, &k, (void*)0); + return k; + } else + map->MapKeyIsNotIPError(ev); + } + return (void*)0; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkProbeMap.h b/comm/mailnews/db/mork/morkProbeMap.h new file mode 100644 index 0000000000..01068a1c82 --- /dev/null +++ b/comm/mailnews/db/mork/morkProbeMap.h @@ -0,0 +1,423 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// This code is a port to NS Mork from public domain Mithril C++ sources. +// Note many code comments here come verbatim from cut-and-pasted Mithril. +// In many places, code is identical; Mithril versions stay public domain. +// Changes in porting are mainly class type and scalar type name changes. + +#ifndef _MORKPROBEMAP_ +#define _MORKPROBEMAP_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class morkMapScratch { // utility class used by map subclasses + public: + nsIMdbHeap* sMapScratch_Heap; // cached sMap_Heap + mork_count sMapScratch_Slots; // cached sMap_Slots + + mork_u1* sMapScratch_Keys; // cached sMap_Keys + mork_u1* sMapScratch_Vals; // cached sMap_Vals + + public: + void halt_map_scratch(morkEnv* ev); +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kProbeMap 0x7072 /* ascii 'pr' */ +#define morkProbeMap_kTag 0x70724D50 /* ascii 'prMP' */ + +#define morkProbeMap_kLazyClearOnAdd ((mork_u1)'c') + +class morkProbeMap : public morkNode { + protected: + // public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + protected: + // { begin morkMap slots + nsIMdbHeap* sMap_Heap; // strong ref to heap allocating all space + + mork_u1* sMap_Keys; + mork_u1* sMap_Vals; + + mork_count sMap_Seed; // change count of members or structure + + mork_count sMap_Slots; // count of slots in the hash table + mork_fill sMap_Fill; // number of used slots in the hash table + + mork_size sMap_KeySize; // size of each key (cannot be zero) + mork_size sMap_ValSize; // size of each val (zero allowed) + + mork_bool sMap_KeyIsIP; // sMap_KeySize == sizeof(mork_ip) + mork_bool sMap_ValIsIP; // sMap_ValSize == sizeof(mork_ip) + mork_u1 sMap_Pad[2]; // for u4 alignment + // } end morkMap slots + + friend class morkProbeMapIter; // for access to protected slots + + public: // getters + mork_count MapSeed() const { return sMap_Seed; } + + mork_count MapSlots() const { return sMap_Slots; } + mork_fill MapFill() const { return sMap_Fill; } + + mork_size MapKeySize() const { return sMap_KeySize; } + mork_size MapValSize() const { return sMap_ValSize; } + + mork_bool MapKeyIsIP() const { return sMap_KeyIsIP; } + mork_bool MapValIsIP() const { return sMap_ValIsIP; } + + protected: // slots + // { begin morkProbeMap slots + + mork_fill sProbeMap_MaxFill; // max sMap_Fill before map must grow + + mork_u1 sProbeMap_LazyClearOnAdd; // true if kLazyClearOnAdd + mork_bool sProbeMap_ZeroIsClearKey; // zero is adequate to clear keys + mork_u1 sProbeMap_Pad[2]; // for u4 alignment + + mork_u4 sProbeMap_Tag; + + // } end morkProbeMap slots + + public: // lazy clear on add + mork_bool need_lazy_init() const { + return sProbeMap_LazyClearOnAdd == morkProbeMap_kLazyClearOnAdd; + } + + public: // typing + mork_bool GoodProbeMap() const { return sProbeMap_Tag == morkProbeMap_kTag; } + + protected: // utilities + void* clear_alloc(morkEnv* ev, mork_size inSize); + + mork_u1* map_new_vals(morkEnv* ev, mork_num inSlots); + mork_u1* map_new_keys(morkEnv* ev, mork_num inSlots); + + void clear_probe_map(morkEnv* ev, nsIMdbHeap* ioMapHeap); + void init_probe_map(morkEnv* ev, mork_size inSlots); + void probe_map_lazy_init(morkEnv* ev); + + mork_bool new_slots(morkEnv* ev, morkMapScratch* old, mork_num inSlots); + + mork_test find_key_pos(morkEnv* ev, const void* inAppKey, mork_u4 inHash, + mork_pos* outPos) const; + + void put_probe_kv(morkEnv* ev, const void* inAppKey, const void* inAppVal, + mork_pos inPos); + void get_probe_kv(morkEnv* ev, void* outAppKey, void* outAppVal, + mork_pos inPos) const; + + mork_bool grow_probe_map(morkEnv* ev); + void rehash_old_map(morkEnv* ev, morkMapScratch* ioScratch); + void revert_map(morkEnv* ev, morkMapScratch* ioScratch); + + public: // errors + void ProbeMapBadTagError(morkEnv* ev) const; + void WrapWithNoVoidSlotError(morkEnv* ev) const; + void GrowFailsMaxFillError(morkEnv* ev) const; + void MapKeyIsNotIPError(morkEnv* ev) const; + void MapValIsNotIPError(morkEnv* ev) const; + + void MapNilKeysError(morkEnv* ev); + void MapZeroKeySizeError(morkEnv* ev); + + void MapSeedOutOfSyncError(morkEnv* ev); + void MapFillUnderflowWarning(morkEnv* ev); + + static void ProbeMapCutError(morkEnv* ev); + + // { ===== begin morkMap methods ===== + public: + virtual mork_test // hit(a,b) implies hash(a) == hash(b) + MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const; + // Note inMapKey is always a key already stored in the map, while inAppKey + // is always a method argument parameter from a client method call. + // This matters the most in morkProbeMap subclasses, which have the + // responsibility of putting 'app' keys into slots for 'map' keys, and + // the bit pattern representation might be different in such cases. + // morkTest_kHit means that inMapKey equals inAppKey (and this had better + // also imply that hash(inMapKey) == hash(inAppKey)). + // morkTest_kMiss means that inMapKey does NOT equal inAppKey (but this + // implies nothing at all about hash(inMapKey) and hash(inAppKey)). + // morkTest_kVoid means that inMapKey is not a valid key bit pattern, + // which means that key slot in the map is not being used. Note that + // kVoid is only expected as a return value in morkProbeMap subclasses, + // because morkProbeMap must ask whether a key slot is used or not. + // morkChainMap however, always knows when a key slot is used, so only + // key slots expected to have valid bit patterns will be presented to + // the MapTest() methods for morkChainMap subclasses. + // + // NOTE: it is very important that subclasses correctly return the value + // morkTest_kVoid whenever the slot for inMapKey contains a bit pattern + // that means the slot is not being used, because this is the only way a + // probe map can terminate an unsuccessful search for a key in the map. + + virtual mork_u4 // hit(a,b) implies hash(a) == hash(b) + MapHash(morkEnv* ev, const void* inAppKey) const; + + virtual mork_bool MapAtPut(morkEnv* ev, const void* inAppKey, + const void* inAppVal, void* outAppKey, + void* outAppVal); + + virtual mork_bool MapAt(morkEnv* ev, const void* inAppKey, void* outAppKey, + void* outAppVal); + + virtual mork_num MapCutAll(morkEnv* ev); + // } ===== end morkMap methods ===== + + // { ===== begin morkProbeMap methods ===== + public: + virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const; + // ProbeMapHashMapKey() does logically the same thing as MapHash(), and + // the default implementation actually calls virtual MapHash(). However, + // Subclasses must override this method whenever the formats of keys in + // the map differ from app keys outside the map, because MapHash() only + // works on keys in 'app' format, while ProbeMapHashMapKey() only works + // on keys in 'map' format. This method is called in order to rehash all + // map keys when a map is grown, and this causes all old map members to + // move into new slot locations. + // + // Note it is absolutely imperative that a hash for a key in 'map' format + // be exactly the same the hash of the same key in 'app' format, or else + // maps will seem corrupt later when keys in 'app' format cannot be found. + + virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey); + // ProbeMapIsKeyNil() must say whether the representation of logical 'nil' + // is currently found inside the key at ioMapKey, for a key found within + // the map. The the map iterator uses this method to find map keys that + // are actually being used for valid map associations; otherwise the + // iterator cannot determine which map slots actually denote used keys. + // The default method version returns true if all the bits equal zero. + + virtual void ProbeMapClearKey( + morkEnv* ev, // put 'nil' into all keys inside map + void* ioMapKey, mork_count inKeyCount); // array of keys inside map + // ProbeMapClearKey() must put some representation of logical 'nil' into + // every key slot in the map, such that MapTest() will later recognize + // that this bit pattern shows each key slot is not actually being used. + // + // This method is typically called whenever the map is either created or + // grown into a larger size, where ioMapKey is a pointer to an array of + // inKeyCount keys, where each key is this->MapKeySize() bytes in size. + // Note that keys are assumed immediately adjacent with no padding, so + // if any alignment requirements must be met, then subclasses should have + // already accounted for this when specifying a key size in the map. + // + // Since this method will be called when a map is being grown in size, + // nothing should be assumed about the state slots of the map, since the + // ioMapKey array might not yet live in sMap_Keys, and the array length + // inKeyCount might not yet live in sMap_Slots. However, the value kept + // in sMap_KeySize never changes, so this->MapKeySize() is always correct. + + virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map + const void* inAppKey, + const void* inAppVal, // (key,val) outside map + void* outMapKey, + void* outMapVal); // (key,val) inside map + // This method actually puts keys and vals in the map in suitable format. + // + // ProbeMapPushIn() must copy a caller key and value in 'app' format + // into the map slots provided, which are in 'map' format. When the + // 'app' and 'map' formats are identical, then this is just a bitwise + // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes, + // and this is exactly what the default implementation performs. However, + // if 'app' and 'map' formats are different, and MapTest() depends on this + // difference in format, then subclasses must override this method to do + // whatever is necessary to store the input app key in output map format. + // + // Do NOT write more than this->MapKeySize() bytes of a map key, or more + // than this->MapValSize() bytes of a map val, or corruption might ensue. + // + // The inAppKey and inAppVal parameters are the same ones passed into a + // call to MapAtPut(), and the outMapKey and outMapVal parameters are ones + // determined by how the map currently positions key inAppKey in the map. + // + // Note any key or val parameter can be a null pointer, in which case + // this method must do nothing with those parameters. In particular, do + // no key move at all when either inAppKey or outMapKey is nil, and do + // no val move at all when either inAppVal or outMapVal is nil. Note that + // outMapVal should always be nil when this->MapValSize() is nil. + + virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map + const void* inMapKey, + const void* inMapVal, // (key,val) inside map + void* outAppKey, + void* outAppVal) const; // (key,val) outside map + // This method actually gets keys and vals from the map in suitable format. + // + // ProbeMapPullOut() must copy a key and val in 'map' format into the + // caller key and val slots provided, which are in 'app' format. When the + // 'app' and 'map' formats are identical, then this is just a bitwise + // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes, + // and this is exactly what the default implementation performs. However, + // if 'app' and 'map' formats are different, and MapTest() depends on this + // difference in format, then subclasses must override this method to do + // whatever is necessary to store the input map key in output app format. + // + // The outAppKey and outAppVal parameters are the same ones passed into a + // call to either MapAtPut() or MapAt(), while inMapKey and inMapVal are + // determined by how the map currently positions the target key in the map. + // + // Note any key or val parameter can be a null pointer, in which case + // this method must do nothing with those parameters. In particular, do + // no key move at all when either inMapKey or outAppKey is nil, and do + // no val move at all when either inMapVal or outAppVal is nil. Note that + // inMapVal should always be nil when this->MapValSize() is nil. + + // } ===== end morkProbeMap methods ===== + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseProbeMap() only if open + virtual ~morkProbeMap(); // assert that CloseProbeMap() executed earlier + + public: // morkProbeMap construction & destruction + morkProbeMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap, + mork_size inKeySize, mork_size inValSize, nsIMdbHeap* ioMapHeap, + mork_size inSlots, mork_bool inZeroIsClearKey); + + void CloseProbeMap(morkEnv* ev); // called by + + public: // dynamic type identification + mork_bool IsProbeMap() const { + return IsNode() && mNode_Derived == morkDerived_kProbeMap; + } + // } ===== end morkNode methods ===== + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakMap(morkMap* me, morkEnv* ev, morkMap** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongMap(morkMap* me, morkEnv* ev, morkMap** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +/*============================================================================*/ +/* morkProbeMapIter */ + +#define morkProbeMapIter_kBeforeIx ((mork_i4)-1) /* before first member */ +#define morkProbeMapIter_kAfterIx ((mork_i4)-2) /* after last member */ + +class morkProbeMapIter { + protected: + morkProbeMap* sProbeMapIter_Map; // nonref + mork_num sProbeMapIter_Seed; // iter's cached copy of map's seed + + mork_i4 sProbeMapIter_HereIx; + + mork_change sProbeMapIter_Change; // morkMapIter API simulation dummy + mork_u1 sProbeMapIter_Pad[3]; // for u4 alignment + + public: + morkProbeMapIter(morkEnv* ev, morkProbeMap* ioMap); + void CloseMapIter(morkEnv* ev); + + morkProbeMapIter(); // zero most slots; caller must call InitProbeMapIter() + + protected: // protected so subclasses must provide suitable typesafe inlines: + void InitProbeMapIter(morkEnv* ev, morkProbeMap* ioMap); + + void InitMapIter(morkEnv* ev, + morkProbeMap* ioMap) // morkMapIter compatibility + { + this->InitProbeMapIter(ev, ioMap); + } + + mork_bool IterFirst(morkEnv* ev, void* outKey, void* outVal); + mork_bool IterNext(morkEnv* ev, void* outKey, void* outVal); + mork_bool IterHere(morkEnv* ev, void* outKey, void* outVal); + + // NOTE: the following methods ONLY work for sMap_ValIsIP pointer values. + // (Note the implied assumption that zero is never a good value pattern.) + + void* IterFirstVal(morkEnv* ev, void* outKey); + // equivalent to { void* v=0; this->IterFirst(ev, outKey, &v); return v; } + + void* IterNextVal(morkEnv* ev, void* outKey); + // equivalent to { void* v=0; this->IterNext(ev, outKey, &v); return v; } + + void* IterHereVal(morkEnv* ev, void* outKey); + // equivalent to { void* v=0; this->IterHere(ev, outKey, &v); return v; } + + // NOTE: the following methods ONLY work for sMap_KeyIsIP pointer values. + // (Note the implied assumption that zero is never a good key pattern.) + + void* IterFirstKey(morkEnv* ev); + // equivalent to { void* k=0; this->IterFirst(ev, &k, 0); return k; } + + void* IterNextKey(morkEnv* ev); + // equivalent to { void* k=0; this->IterNext(ev, &k, 0); return k; } + + void* IterHereKey(morkEnv* ev); + // equivalent to { void* k=0; this->IterHere(ev, &k, 0); return k; } + + public: // simulation of the morkMapIter API for morkMap compatibility: + mork_change* First(morkEnv* ev, void* outKey, void* outVal); + mork_change* Next(morkEnv* ev, void* outKey, void* outVal); + mork_change* Here(morkEnv* ev, void* outKey, void* outVal); + + mork_change* CutHere(morkEnv* ev, void* outKey, void* outVal); +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKPROBEMAP_ */ diff --git a/comm/mailnews/db/mork/morkQuickSort.cpp b/comm/mailnews/db/mork/morkQuickSort.cpp new file mode 100644 index 0000000000..6fd211ee5c --- /dev/null +++ b/comm/mailnews/db/mork/morkQuickSort.cpp @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _MDB_ +# include "mdb.h" +#endif + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKQUICKSORT_ +# include "morkQuickSort.h" +#endif + +#if !defined(DEBUG) && (defined(__cplusplus) || defined(__gcc)) +# ifndef INLINE +# define INLINE inline +# endif +#else +# define INLINE +#endif + +static INLINE mork_u1* morkQS_med3(mork_u1*, mork_u1*, mork_u1*, mdbAny_Order, + void*); + +static INLINE void morkQS_swapfunc(mork_u1*, mork_u1*, int, int); + +/* + * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". + */ +#define morkQS_swapcode(TYPE, parmi, parmj, n) \ + { \ + long i = (n) / sizeof(TYPE); \ + TYPE* pi = (TYPE*)(parmi); \ + TYPE* pj = (TYPE*)(parmj); \ + do { \ + TYPE t = *pi; \ + *pi++ = *pj; \ + *pj++ = t; \ + } while (--i > 0); \ + } + +#define morkQS_SwapInit(a, es) \ + swaptype = (a - (mork_u1*)0) % sizeof(long) || es % sizeof(long) ? 2 \ + : es == sizeof(long) ? 0 \ + : 1; + +static INLINE void morkQS_swapfunc(mork_u1* a, mork_u1* b, int n, + int swaptype) { + if (swaptype <= 1) + morkQS_swapcode(long, a, b, n) else morkQS_swapcode(mork_u1, a, b, n) +} + +#define morkQS_swap(a, b) \ + if (swaptype == 0) { \ + long t = *(long*)(a); \ + *(long*)(a) = *(long*)(b); \ + *(long*)(b) = t; \ + } else \ + morkQS_swapfunc(a, b, (int)inSize, swaptype) + +#define morkQS_vecswap(a, b, n) \ + if ((n) > 0) morkQS_swapfunc(a, b, (int)n, swaptype) + +static INLINE mork_u1* morkQS_med3(mork_u1* a, mork_u1* b, mork_u1* c, + mdbAny_Order cmp, void* closure) { + return (*cmp)(a, b, closure) < 0 + ? ((*cmp)(b, c, closure) < 0 ? b + : ((*cmp)(a, c, closure) < 0 ? c : a)) + : ((*cmp)(b, c, closure) > 0 + ? b + : ((*cmp)(a, c, closure) < 0 ? a : c)); +} + +#define morkQS_MIN(x, y) ((x) < (y) ? (x) : (y)) + +void morkQuickSort(mork_u1* ioVec, mork_u4 inCount, mork_u4 inSize, + mdbAny_Order inOrder, void* ioClosure) { + mork_u1 *pa, *pb, *pc, *pd, *pl, *pm, *pn; + int d, r, swaptype, swap_cnt; + +tailCall: + morkQS_SwapInit(ioVec, inSize); + swap_cnt = 0; + if (inCount < 7) { + for (pm = ioVec + inSize; pm < ioVec + inCount * inSize; pm += inSize) + for (pl = pm; pl > ioVec && (*inOrder)(pl - inSize, pl, ioClosure) > 0; + pl -= inSize) + morkQS_swap(pl, pl - inSize); + return; + } + pm = ioVec + (inCount / 2) * inSize; + if (inCount > 7) { + pl = ioVec; + pn = ioVec + (inCount - 1) * inSize; + if (inCount > 40) { + d = (inCount / 8) * inSize; + pl = morkQS_med3(pl, pl + d, pl + 2 * d, inOrder, ioClosure); + pm = morkQS_med3(pm - d, pm, pm + d, inOrder, ioClosure); + pn = morkQS_med3(pn - 2 * d, pn - d, pn, inOrder, ioClosure); + } + pm = morkQS_med3(pl, pm, pn, inOrder, ioClosure); + } + morkQS_swap(ioVec, pm); + pa = pb = ioVec + inSize; + + pc = pd = ioVec + (inCount - 1) * inSize; + for (;;) { + while (pb <= pc && (r = (*inOrder)(pb, ioVec, ioClosure)) <= 0) { + if (r == 0) { + swap_cnt = 1; + morkQS_swap(pa, pb); + pa += inSize; + } + pb += inSize; + } + while (pb <= pc && (r = (*inOrder)(pc, ioVec, ioClosure)) >= 0) { + if (r == 0) { + swap_cnt = 1; + morkQS_swap(pc, pd); + pd -= inSize; + } + pc -= inSize; + } + if (pb > pc) break; + morkQS_swap(pb, pc); + swap_cnt = 1; + pb += inSize; + pc -= inSize; + } + if (swap_cnt == 0) { /* Switch to insertion sort */ + for (pm = ioVec + inSize; pm < ioVec + inCount * inSize; pm += inSize) + for (pl = pm; pl > ioVec && (*inOrder)(pl - inSize, pl, ioClosure) > 0; + pl -= inSize) + morkQS_swap(pl, pl - inSize); + return; + } + + pn = ioVec + inCount * inSize; + r = morkQS_MIN(pa - ioVec, pb - pa); + morkQS_vecswap(ioVec, pb - r, r); + r = morkQS_MIN(pd - pc, (int)(pn - pd - inSize)); + morkQS_vecswap(pb, pn - r, r); + if ((r = pb - pa) > (int)inSize) + morkQuickSort(ioVec, r / inSize, inSize, inOrder, ioClosure); + if ((r = pd - pc) > (int)inSize) { + /* Iterate rather than recurse to save stack space */ + ioVec = pn - r; + inCount = r / inSize; + goto tailCall; + } + /* morkQuickSort(pn - r, r / inSize, inSize, inOrder, ioClosure);*/ +} diff --git a/comm/mailnews/db/mork/morkQuickSort.h b/comm/mailnews/db/mork/morkQuickSort.h new file mode 100644 index 0000000000..98561d5758 --- /dev/null +++ b/comm/mailnews/db/mork/morkQuickSort.h @@ -0,0 +1,24 @@ +/* -*- 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 _MORKQUICKSORT_ +#define _MORKQUICKSORT_ 1 + +#ifndef _MDB_ +# include "mdb.h" +#endif + +#ifndef _MORK_ +# include "mork.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +extern void morkQuickSort(mork_u1* ioVec, mork_u4 inCount, mork_u4 inSize, + mdbAny_Order inOrder, void* ioClosure); + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKQUICKSORT_ */ diff --git a/comm/mailnews/db/mork/morkRow.cpp b/comm/mailnews/db/mork/morkRow.cpp new file mode 100644 index 0000000000..2af5e9adcd --- /dev/null +++ b/comm/mailnews/db/mork/morkRow.cpp @@ -0,0 +1,769 @@ +/* -*- 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 _MORKROW_ +# include "morkRow.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKROWSPACE_ +# include "morkRowSpace.h" +#endif + +#ifndef _MORKPOOL_ +# include "morkPool.h" +#endif + +#ifndef _MORKROWOBJECT_ +# include "morkRowObject.h" +#endif + +#ifndef _MORKCELLOBJECT_ +# include "morkCellObject.h" +#endif + +#ifndef _MORKCELL_ +# include "morkCell.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +#ifndef _MORKROWCELLCURSOR_ +# include "morkRowCellCursor.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// notifications regarding row changes: + +void morkRow::NoteRowAddCol(morkEnv* ev, mork_column inColumn) { + if (!this->IsRowRewrite()) { + mork_delta newDelta; + morkDelta_Init(newDelta, inColumn, morkChange_kAdd); + + if (newDelta != mRow_Delta) // not repeating existing data? + { + if (this->HasRowDelta()) // already have one change recorded? + this->SetRowRewrite(); // just plan to write all row cells + else + this->SetRowDelta(inColumn, morkChange_kAdd); + } + } else + this->ClearRowDelta(); +} + +void morkRow::NoteRowCutCol(morkEnv* ev, mork_column inColumn) { + if (!this->IsRowRewrite()) { + mork_delta newDelta; + morkDelta_Init(newDelta, inColumn, morkChange_kCut); + + if (newDelta != mRow_Delta) // not repeating existing data? + { + if (this->HasRowDelta()) // already have one change recorded? + this->SetRowRewrite(); // just plan to write all row cells + else + this->SetRowDelta(inColumn, morkChange_kCut); + } + } else + this->ClearRowDelta(); +} + +void morkRow::NoteRowSetCol(morkEnv* ev, mork_column inColumn) { + if (!this->IsRowRewrite()) { + if (this->HasRowDelta()) // already have one change recorded? + this->SetRowRewrite(); // just plan to write all row cells + else + this->SetRowDelta(inColumn, morkChange_kSet); + } else + this->ClearRowDelta(); +} + +void morkRow::NoteRowSetAll(morkEnv* ev) { + this->SetRowRewrite(); // just plan to write all row cells + this->ClearRowDelta(); +} + +mork_u2 morkRow::AddRowGcUse(morkEnv* ev) { + if (this->IsRow()) { + if (mRow_GcUses < morkRow_kMaxGcUses) // not already maxed out? + ++mRow_GcUses; + } else + this->NonRowTypeError(ev); + + return mRow_GcUses; +} + +mork_u2 morkRow::CutRowGcUse(morkEnv* ev) { + if (this->IsRow()) { + if (mRow_GcUses) // any outstanding uses to cut? + { + if (mRow_GcUses < morkRow_kMaxGcUses) // not frozen at max? + --mRow_GcUses; + } else + this->GcUsesUnderflowWarning(ev); + } else + this->NonRowTypeError(ev); + + return mRow_GcUses; +} + +/*static*/ void morkRow::GcUsesUnderflowWarning(morkEnv* ev) { + ev->NewWarning("mRow_GcUses underflow"); +} + +/*static*/ void morkRow::NonRowTypeError(morkEnv* ev) { + ev->NewError("non morkRow"); +} + +/*static*/ void morkRow::NonRowTypeWarning(morkEnv* ev) { + ev->NewWarning("non morkRow"); +} + +/*static*/ void morkRow::LengthBeyondMaxError(morkEnv* ev) { + ev->NewError("mRow_Length over max"); +} + +/*static*/ void morkRow::ZeroColumnError(morkEnv* ev) { + ev->NewError(" zero mork_column"); +} + +/*static*/ void morkRow::NilCellsError(morkEnv* ev) { + ev->NewError("nil mRow_Cells"); +} + +void morkRow::InitRow(morkEnv* ev, const mdbOid* inOid, morkRowSpace* ioSpace, + mork_size inLength, morkPool* ioPool) +// if inLength is nonzero, cells will be allocated from ioPool +{ + if (ioSpace && ioPool && inOid) { + if (inLength <= morkRow_kMaxLength) { + if (inOid->mOid_Id != morkRow_kMinusOneRid) { + mRow_Space = ioSpace; + mRow_Object = 0; + mRow_Cells = 0; + mRow_Oid = *inOid; + + mRow_Length = (mork_u2)inLength; + mRow_Seed = (mork_u2)(mork_ip)this; // "random" assignment + + mRow_GcUses = 0; + mRow_Pad = 0; + mRow_Flags = 0; + mRow_Tag = morkRow_kTag; + + morkZone* zone = &ioSpace->mSpace_Store->mStore_Zone; + + if (inLength) mRow_Cells = ioPool->NewCells(ev, inLength, zone); + + if (this->MaybeDirtySpaceStoreAndRow()) // new row might dirty store + { + this->SetRowRewrite(); + this->NoteRowSetAll(ev); + } + } else + ioSpace->MinusOneRidError(ev); + } else + this->LengthBeyondMaxError(ev); + } else + ev->NilPointerError(); +} + +morkRowObject* morkRow::AcquireRowObject(morkEnv* ev, morkStore* ioStore) { + morkRowObject* ro = mRow_Object; + if (ro) // need new row object? + ro->AddRef(); + else { + nsIMdbHeap* heap = ioStore->mPort_Heap; + ro = new (*heap, ev) + morkRowObject(ev, morkUsage::kHeap, heap, this, ioStore); + if (!ro) return (morkRowObject*)0; + + morkRowObject::SlotWeakRowObject(ro, ev, &mRow_Object); + ro->AddRef(); + } + return ro; +} + +nsIMdbRow* morkRow::AcquireRowHandle(morkEnv* ev, morkStore* ioStore) { + return AcquireRowObject(ev, ioStore); +} + +nsIMdbCell* morkRow::AcquireCellHandle(morkEnv* ev, morkCell* ioCell, + mdb_column inCol, mork_pos inPos) { + nsIMdbHeap* heap = ev->mEnv_Heap; + morkCellObject* cellObj = new (*heap, ev) + morkCellObject(ev, morkUsage::kHeap, heap, this, ioCell, inCol, inPos); + if (cellObj) { + nsIMdbCell* cellHandle = cellObj->AcquireCellHandle(ev); + // cellObj->CutStrongRef(ev->AsMdbEnv()); + return cellHandle; + } + return (nsIMdbCell*)0; +} + +mork_count morkRow::CountOverlap(morkEnv* ev, morkCell* ioVector, + mork_fill inFill) +// Count cells in ioVector that change existing cells in this row when +// ioVector is added to the row (as in TakeCells()). This is the set +// of cells with the same columns in ioVector and mRow_Cells, which do +// not have exactly the same value in mCell_Atom, and which do not both +// have change status equal to morkChange_kCut (because cutting a cut +// cell still yields a cell that has been cut). CountOverlap() also +// modifies the change attribute of any cell in ioVector to kDup when +// the change was previously kCut and the same column cell was found +// in this row with change also equal to kCut; this tells callers later +// they need not look for that cell in the row again on a second pass. +{ + mork_count outCount = 0; + mork_pos pos = 0; // needed by GetCell() + morkCell* cells = ioVector; + morkCell* end = cells + inFill; + --cells; // prepare for preincrement + while (++cells < end && ev->Good()) { + mork_column col = cells->GetColumn(); + + morkCell* old = this->GetCell(ev, col, &pos); + if (old) // same column? + { + mork_change newChg = cells->GetChange(); + mork_change oldChg = old->GetChange(); + if (newChg != morkChange_kCut || oldChg != newChg) // not cut+cut? + { + if (cells->mCell_Atom != old->mCell_Atom) // not same atom? + ++outCount; // cells will replace old significantly when added + } else + cells->SetColumnAndChange(col, morkChange_kDup); // note dup status + } + } + return outCount; +} + +void morkRow::MergeCells(morkEnv* ev, morkCell* ioVector, mork_fill inVecLength, + mork_fill inOldRowFill, mork_fill inOverlap) +// MergeCells() is the part of TakeCells() that does the insertion. +// inOldRowFill is the old value of mRow_Length, and inOverlap is the +// number of cells in the intersection that must be updated. +{ + morkCell* newCells = mRow_Cells + inOldRowFill; // 1st new cell in row + morkCell* newEnd = newCells + mRow_Length; // one past last cell + + morkCell* srcCells = ioVector; + morkCell* srcEnd = srcCells + inVecLength; + + --srcCells; // prepare for preincrement + while (++srcCells < srcEnd && ev->Good()) { + mork_change srcChg = srcCells->GetChange(); + if (srcChg != morkChange_kDup) // anything to be done? + { + morkCell* dstCell = 0; + if (inOverlap) { + mork_pos pos = 0; // needed by GetCell() + dstCell = this->GetCell(ev, srcCells->GetColumn(), &pos); + } + if (dstCell) { + --inOverlap; // one fewer intersections to resolve + // swap the atoms in the cells to avoid ref counting here: + morkAtom* dstAtom = dstCell->mCell_Atom; + *dstCell = *srcCells; // bitwise copy, taking src atom + srcCells->mCell_Atom = dstAtom; // forget cell ref, if any + } else if (newCells < newEnd) // another new cell exists? + { + dstCell = newCells++; // alloc another new cell + // take atom from source cell, transferring ref to this row: + *dstCell = *srcCells; // bitwise copy, taking src atom + srcCells->mCell_Atom = 0; // forget cell ref, if any + } else // oops, we ran out... + ev->NewError("out of new cells"); + } + } +} + +void morkRow::TakeCells(morkEnv* ev, morkCell* ioVector, mork_fill inVecLength, + morkStore* ioStore) { + if (ioVector && inVecLength && ev->Good()) { + ++mRow_Seed; // intend to change structure of mRow_Cells + mork_size length = (mork_size)mRow_Length; + + mork_count overlap = this->CountOverlap(ev, ioVector, inVecLength); + + mork_size growth = inVecLength - overlap; // cells to add + mork_size newLength = length + growth; + + if (growth && ev->Good()) // need to add any cells? + { + morkZone* zone = &ioStore->mStore_Zone; + morkPool* pool = ioStore->StorePool(); + if (!pool->AddRowCells(ev, this, length + growth, zone)) + ev->NewError("cannot take cells"); + } + if (ev->Good()) { + if (mRow_Length >= newLength) + this->MergeCells(ev, ioVector, inVecLength, length, overlap); + else + ev->NewError("not enough new cells"); + } + } +} + +mork_bool morkRow::MaybeDirtySpaceStoreAndRow() { + morkRowSpace* rowSpace = mRow_Space; + if (rowSpace) { + morkStore* store = rowSpace->mSpace_Store; + if (store && store->mStore_CanDirty) { + store->SetStoreDirty(); + rowSpace->mSpace_CanDirty = morkBool_kTrue; + } + + if (rowSpace->mSpace_CanDirty) { + this->SetRowDirty(); + rowSpace->SetRowSpaceDirty(); + return morkBool_kTrue; + } + } + return morkBool_kFalse; +} + +morkCell* morkRow::NewCell(morkEnv* ev, mdb_column inColumn, mork_pos* outPos, + morkStore* ioStore) { + ++mRow_Seed; // intend to change structure of mRow_Cells + mork_size length = (mork_size)mRow_Length; + *outPos = (mork_pos)length; + morkPool* pool = ioStore->StorePool(); + morkZone* zone = &ioStore->mStore_Zone; + + mork_bool canDirty = this->MaybeDirtySpaceStoreAndRow(); + + if (pool->AddRowCells(ev, this, length + 1, zone)) { + morkCell* cell = mRow_Cells + length; + // next line equivalent to inline morkCell::SetCellDirty(): + if (canDirty) + cell->SetCellColumnDirty(inColumn); + else + cell->SetCellColumnClean(inColumn); + + if (canDirty && !this->IsRowRewrite()) this->NoteRowAddCol(ev, inColumn); + + return cell; + } + + return (morkCell*)0; +} + +void morkRow::SeekColumn(morkEnv* ev, mdb_pos inPos, mdb_column* outColumn, + mdbYarn* outYarn) { + morkCell* cells = mRow_Cells; + if (cells && inPos < mRow_Length && inPos >= 0) { + morkCell* c = cells + inPos; + if (outColumn) *outColumn = c->GetColumn(); + if (outYarn) morkAtom::GetYarn(c->mCell_Atom, outYarn); + } else { + if (outColumn) *outColumn = 0; + if (outYarn) morkAtom::GetYarn((morkAtom*)0, outYarn); + } +} + +void morkRow::NextColumn(morkEnv* ev, mdb_column* ioColumn, mdbYarn* outYarn) { + morkCell* cells = mRow_Cells; + if (cells) { + mork_column last = 0; + mork_column inCol = *ioColumn; + morkCell* end = cells + mRow_Length; + while (cells < end) { + if (inCol == last) // found column? + { + if (outYarn) morkAtom::GetYarn(cells->mCell_Atom, outYarn); + *ioColumn = cells->GetColumn(); + return; // stop, we are done + } else { + last = cells->GetColumn(); + ++cells; + } + } + } + *ioColumn = 0; + if (outYarn) morkAtom::GetYarn((morkAtom*)0, outYarn); +} + +morkCell* morkRow::CellAt(morkEnv* ev, mork_pos inPos) const { + MORK_USED_1(ev); + morkCell* cells = mRow_Cells; + if (cells && inPos < mRow_Length && inPos >= 0) { + return cells + inPos; + } + return (morkCell*)0; +} + +morkCell* morkRow::GetCell(morkEnv* ev, mdb_column inColumn, + mork_pos* outPos) const { + MORK_USED_1(ev); + morkCell* cells = mRow_Cells; + if (cells) { + morkCell* end = cells + mRow_Length; + while (cells < end) { + mork_column col = cells->GetColumn(); + if (col == inColumn) // found the desired column? + { + *outPos = cells - mRow_Cells; + return cells; + } else + ++cells; + } + } + *outPos = -1; + return (morkCell*)0; +} + +mork_aid morkRow::GetCellAtomAid(morkEnv* ev, mdb_column inColumn) const +// GetCellAtomAid() finds the cell with column inColumn, and sees if the +// atom has a token ID, and returns the atom's ID if there is one. Or +// else zero is returned if there is no such column, or no atom, or if +// the atom has no ID to return. This method is intended to support +// efficient updating of column indexes for rows in a row space. +{ + if (this->IsRow()) { + morkCell* cells = mRow_Cells; + if (cells) { + morkCell* end = cells + mRow_Length; + while (cells < end) { + mork_column col = cells->GetColumn(); + if (col == inColumn) // found desired column? + { + morkAtom* atom = cells->mCell_Atom; + if (atom && atom->IsBook()) + return ((morkBookAtom*)atom)->mBookAtom_Id; + else + return 0; + } else + ++cells; + } + } + } else + this->NonRowTypeError(ev); + + return 0; +} + +void morkRow::EmptyAllCells(morkEnv* ev) { + morkCell* cells = mRow_Cells; + if (cells) { + morkStore* store = this->GetRowSpaceStore(ev); + if (store) { + if (this->MaybeDirtySpaceStoreAndRow()) { + this->SetRowRewrite(); + this->NoteRowSetAll(ev); + } + morkPool* pool = store->StorePool(); + morkCell* end = cells + mRow_Length; + --cells; // prepare for preincrement: + while (++cells < end) { + if (cells->mCell_Atom) cells->SetAtom(ev, (morkAtom*)0, pool); + } + } + } +} + +void morkRow::cut_all_index_entries(morkEnv* ev) { + morkRowSpace* rowSpace = mRow_Space; + if (rowSpace->mRowSpace_IndexCount) // any indexes? + { + morkCell* cells = mRow_Cells; + if (cells) { + morkCell* end = cells + mRow_Length; + --cells; // prepare for preincrement: + while (++cells < end) { + morkAtom* atom = cells->mCell_Atom; + if (atom) { + mork_aid atomAid = atom->GetBookAtomAid(); + if (atomAid) { + mork_column col = cells->GetColumn(); + morkAtomRowMap* map = rowSpace->FindMap(ev, col); + if (map) // cut row from index for this column? + map->CutAid(ev, atomAid); + } + } + } + } + } +} + +void morkRow::CutAllColumns(morkEnv* ev) { + morkStore* store = this->GetRowSpaceStore(ev); + if (store) { + if (this->MaybeDirtySpaceStoreAndRow()) { + this->SetRowRewrite(); + this->NoteRowSetAll(ev); + } + morkRowSpace* rowSpace = mRow_Space; + if (rowSpace->mRowSpace_IndexCount) // any indexes? + this->cut_all_index_entries(ev); + + morkPool* pool = store->StorePool(); + pool->CutRowCells(ev, this, /*newSize*/ 0, &store->mStore_Zone); + } +} + +void morkRow::SetRow(morkEnv* ev, const morkRow* inSourceRow) { + // note inSourceRow might be in another DB, with a different store... + morkStore* store = this->GetRowSpaceStore(ev); + morkStore* srcStore = inSourceRow->GetRowSpaceStore(ev); + if (store && srcStore) { + if (this->MaybeDirtySpaceStoreAndRow()) { + this->SetRowRewrite(); + this->NoteRowSetAll(ev); + } + morkRowSpace* rowSpace = mRow_Space; + mork_count indexes = rowSpace->mRowSpace_IndexCount; // any indexes? + + mork_bool sameStore = (store == srcStore); // identical stores? + morkPool* pool = store->StorePool(); + if (pool->CutRowCells(ev, this, /*newSize*/ 0, &store->mStore_Zone)) { + mork_fill fill = inSourceRow->mRow_Length; + if (pool->AddRowCells(ev, this, fill, &store->mStore_Zone)) { + morkCell* dst = mRow_Cells; + morkCell* dstEnd = dst + mRow_Length; + + const morkCell* src = inSourceRow->mRow_Cells; + const morkCell* srcEnd = src + fill; + --dst; + --src; // prepare both for preincrement: + + while (++dst < dstEnd && ++src < srcEnd && ev->Good()) { + morkAtom* atom = src->mCell_Atom; + mork_column dstCol = src->GetColumn(); + // Note we modify the mCell_Atom slot directly instead of using + // morkCell::SetAtom(), because we know it starts equal to nil. + + if (sameStore) // source and dest in same store? + { + // next line equivalent to inline morkCell::SetCellDirty(): + dst->SetCellColumnDirty(dstCol); + dst->mCell_Atom = atom; + if (atom) // another ref to non-nil atom? + atom->AddCellUse(ev); + } else // need to dup items from src store in a dest store + { + dstCol = store->CopyToken(ev, dstCol, srcStore); + if (dstCol) { + // next line equivalent to inline morkCell::SetCellDirty(): + dst->SetCellColumnDirty(dstCol); + atom = store->CopyAtom(ev, atom); + dst->mCell_Atom = atom; + if (atom) // another ref? + atom->AddCellUse(ev); + } + } + if (indexes && atom) { + mork_aid atomAid = atom->GetBookAtomAid(); + if (atomAid) { + morkAtomRowMap* map = rowSpace->FindMap(ev, dstCol); + if (map) map->AddAid(ev, atomAid, this); + } + } + } + } + } + } +} + +void morkRow::AddRow(morkEnv* ev, const morkRow* inSourceRow) { + if (mRow_Length) // any existing cells we might need to keep? + { + ev->StubMethodOnlyError(); + } else + this->SetRow(ev, inSourceRow); // just exactly duplicate inSourceRow +} + +void morkRow::OnZeroRowGcUse(morkEnv* ev) +// OnZeroRowGcUse() is called when CutRowGcUse() returns zero. +{ + MORK_USED_1(ev); + // ev->NewWarning("need to implement OnZeroRowGcUse"); +} + +void morkRow::DirtyAllRowContent(morkEnv* ev) { + MORK_USED_1(ev); + + if (this->MaybeDirtySpaceStoreAndRow()) { + this->SetRowRewrite(); + this->NoteRowSetAll(ev); + } + morkCell* cells = mRow_Cells; + if (cells) { + morkCell* end = cells + mRow_Length; + --cells; // prepare for preincrement: + while (++cells < end) { + cells->SetCellDirty(); + } + } +} + +morkStore* morkRow::GetRowSpaceStore(morkEnv* ev) const { + morkRowSpace* rowSpace = mRow_Space; + if (rowSpace) { + morkStore* store = rowSpace->mSpace_Store; + if (store) { + if (store->IsStore()) { + return store; + } else + store->NonStoreTypeError(ev); + } else + ev->NilPointerError(); + } else + ev->NilPointerError(); + + return (morkStore*)0; +} + +void morkRow::CutColumn(morkEnv* ev, mdb_column inColumn) { + mork_pos pos = -1; + morkCell* cell = this->GetCell(ev, inColumn, &pos); + if (cell) { + morkStore* store = this->GetRowSpaceStore(ev); + if (store) { + if (this->MaybeDirtySpaceStoreAndRow() && !this->IsRowRewrite()) + this->NoteRowCutCol(ev, inColumn); + + morkRowSpace* rowSpace = mRow_Space; + morkAtomRowMap* map = (rowSpace->mRowSpace_IndexCount) + ? rowSpace->FindMap(ev, inColumn) + : (morkAtomRowMap*)0; + if (map) // this row attribute is indexed by row space? + { + morkAtom* oldAtom = cell->mCell_Atom; + if (oldAtom) // need to cut an entry from the index? + { + mork_aid oldAid = oldAtom->GetBookAtomAid(); + if (oldAid) // cut old row attribute from row index in space? + map->CutAid(ev, oldAid); + } + } + + morkPool* pool = store->StorePool(); + cell->SetAtom(ev, (morkAtom*)0, pool); + + mork_fill fill = mRow_Length; // should not be zero + MORK_ASSERT(fill); + if (fill) // index < fill for last cell exists? + { + mork_fill last = fill - 1; // index of last cell in row + + if (pos < (mork_pos)last) // need to move cells following cut cell? + { + morkCell* lastCell = mRow_Cells + last; + mork_count after = last - pos; // cell count after cut cell + morkCell* next = cell + 1; // next cell after cut cell + MORK_MEMMOVE(cell, next, after * sizeof(morkCell)); + lastCell->SetColumnAndChange(0, 0); + lastCell->mCell_Atom = 0; + } + + if (ev->Good()) + pool->CutRowCells(ev, this, fill - 1, &store->mStore_Zone); + } + } + } +} + +morkAtom* morkRow::GetColumnAtom(morkEnv* ev, mdb_column inColumn) { + if (ev->Good()) { + mork_pos pos = -1; + morkCell* cell = this->GetCell(ev, inColumn, &pos); + if (cell) return cell->mCell_Atom; + } + return (morkAtom*)0; +} + +void morkRow::AddColumn(morkEnv* ev, mdb_column inColumn, const mdbYarn* inYarn, + morkStore* ioStore) { + if (ev->Good()) { + mork_pos pos = -1; + morkCell* cell = this->GetCell(ev, inColumn, &pos); + morkCell* oldCell = cell; // need to know later whether new + if (!cell) // column does not yet exist? + cell = this->NewCell(ev, inColumn, &pos, ioStore); + + if (cell) { + morkAtom* oldAtom = cell->mCell_Atom; + + morkAtom* atom = ioStore->YarnToAtom(ev, inYarn, true /* create */); + if (atom && atom != oldAtom) { + morkRowSpace* rowSpace = mRow_Space; + morkAtomRowMap* map = (rowSpace->mRowSpace_IndexCount) + ? rowSpace->FindMap(ev, inColumn) + : (morkAtomRowMap*)0; + + if (map) // inColumn is indexed by row space? + { + if (oldAtom && oldAtom != atom) // cut old cell from index? + { + mork_aid oldAid = oldAtom->GetBookAtomAid(); + if (oldAid) // cut old row attribute from row index in space? + map->CutAid(ev, oldAid); + } + } + + cell->SetAtom(ev, atom, ioStore->StorePool()); // refcounts atom + + if (oldCell) // we changed a pre-existing cell in the row? + { + ++mRow_Seed; + if (this->MaybeDirtySpaceStoreAndRow() && !this->IsRowRewrite()) + this->NoteRowAddCol(ev, inColumn); + } + + if (map) // inColumn is indexed by row space? + { + mork_aid newAid = atom->GetBookAtomAid(); + if (newAid) // add new row attribute to row index in space? + map->AddAid(ev, newAid, this); + } + } + } + } +} + +morkRowCellCursor* morkRow::NewRowCellCursor(morkEnv* ev, mdb_pos inPos) { + morkRowCellCursor* outCursor = 0; + if (ev->Good()) { + morkStore* store = this->GetRowSpaceStore(ev); + if (store) { + morkRowObject* rowObj = this->AcquireRowObject(ev, store); + if (rowObj) { + nsIMdbHeap* heap = store->mPort_Heap; + morkRowCellCursor* cursor = new (*heap, ev) + morkRowCellCursor(ev, morkUsage::kHeap, heap, rowObj); + + if (cursor) { + if (ev->Good()) { + cursor->mRowCellCursor_Col = inPos; + outCursor = cursor; + } else + cursor->CutStrongRef(ev->mEnv_SelfAsMdbEnv); + } + rowObj->Release(); // always cut ref (cursor has its own) + } + } + } + return outCursor; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkRow.h b/comm/mailnews/db/mork/morkRow.h new file mode 100644 index 0000000000..e8a8c728ac --- /dev/null +++ b/comm/mailnews/db/mork/morkRow.h @@ -0,0 +1,208 @@ +/* -*- 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 _MORKROW_ +#define _MORKROW_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKCELL_ +# include "morkCell.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class nsIMdbRow; +class nsIMdbCell; +#define morkDerived_kRow /*i*/ 0x5277 /* ascii 'Rw' */ + +#define morkRow_kMaxGcUses 0x0FF /* max for 8-bit unsigned int */ +#define morkRow_kMaxLength 0x0FFFF /* max for 16-bit unsigned int */ +#define morkRow_kMinusOneRid ((mork_rid)-1) + +#define morkRow_kTag 'r' /* magic signature for mRow_Tag */ + +#define morkRow_kNotedBit ((mork_u1)(1 << 0)) /* space has change notes */ +#define morkRow_kRewriteBit ((mork_u1)(1 << 1)) /* must rewrite all cells */ +#define morkRow_kDirtyBit ((mork_u1)(1 << 2)) /* row has been changed */ + +class morkRow { // row of cells + + public: // state is public because the entire Mork system is private + morkRowSpace* mRow_Space; // mRow_Space->SpaceScope() is the row scope + morkRowObject* mRow_Object; // refcount & other state for object sharing + morkCell* mRow_Cells; + mdbOid mRow_Oid; + + mork_delta mRow_Delta; // space to note a single column change + + mork_u2 mRow_Length; // physical count of cells in mRow_Cells + mork_u2 mRow_Seed; // count changes in mRow_Cells structure + + mork_u1 mRow_GcUses; // persistent references from tables + mork_u1 mRow_Pad; // for u1 alignment + mork_u1 mRow_Flags; // one-byte flags slot + mork_u1 mRow_Tag; // one-byte tag (need u4 alignment pad) + + public: // interpreting mRow_Delta + mork_bool HasRowDelta() const { return (mRow_Delta != 0); } + + void ClearRowDelta() { mRow_Delta = 0; } + + void SetRowDelta(mork_column inCol, mork_change inChange) { + morkDelta_Init(mRow_Delta, inCol, inChange); + } + + mork_column GetDeltaColumn() const { return morkDelta_Column(mRow_Delta); } + mork_change GetDeltaChange() const { return morkDelta_Change(mRow_Delta); } + + public: // noting row changes + void NoteRowSetAll(morkEnv* ev); + void NoteRowSetCol(morkEnv* ev, mork_column inCol); + void NoteRowAddCol(morkEnv* ev, mork_column inCol); + void NoteRowCutCol(morkEnv* ev, mork_column inCol); + + public: // flags bit twiddling + void SetRowNoted() { mRow_Flags |= morkRow_kNotedBit; } + void SetRowRewrite() { mRow_Flags |= morkRow_kRewriteBit; } + void SetRowDirty() { mRow_Flags |= morkRow_kDirtyBit; } + + void ClearRowNoted() { mRow_Flags &= (mork_u1)~morkRow_kNotedBit; } + void ClearRowRewrite() { mRow_Flags &= (mork_u1)~morkRow_kRewriteBit; } + void SetRowClean() { + mRow_Flags = 0; + mRow_Delta = 0; + } + + mork_bool IsRowNoted() const { return (mRow_Flags & morkRow_kNotedBit) != 0; } + + mork_bool IsRowRewrite() const { + return (mRow_Flags & morkRow_kRewriteBit) != 0; + } + + mork_bool IsRowClean() const { return (mRow_Flags & morkRow_kDirtyBit) == 0; } + + mork_bool IsRowDirty() const { return (mRow_Flags & morkRow_kDirtyBit) != 0; } + + mork_bool IsRowUsed() const { return mRow_GcUses != 0; } + + public: // other row methods + morkRow() {} + explicit morkRow(const mdbOid* inOid) : mRow_Oid(*inOid) {} + void InitRow(morkEnv* ev, const mdbOid* inOid, morkRowSpace* ioSpace, + mork_size inLength, morkPool* ioPool); + // if inLength is nonzero, cells will be allocated from ioPool + + morkRowObject* AcquireRowObject(morkEnv* ev, morkStore* ioStore); + nsIMdbRow* AcquireRowHandle(morkEnv* ev, morkStore* ioStore); + nsIMdbCell* AcquireCellHandle(morkEnv* ev, morkCell* ioCell, + mdb_column inColumn, mork_pos inPos); + + mork_u2 AddRowGcUse(morkEnv* ev); + mork_u2 CutRowGcUse(morkEnv* ev); + + mork_bool MaybeDirtySpaceStoreAndRow(); + + public: // internal row methods + void cut_all_index_entries(morkEnv* ev); + + // void cut_cell_from_space_index(morkEnv* ev, morkCell* ioCell); + + mork_count CountOverlap(morkEnv* ev, morkCell* ioVector, mork_fill inFill); + // Count cells in ioVector that change existing cells in this row when + // ioVector is added to the row (as in TakeCells()). This is the set + // of cells with the same columns in ioVector and mRow_Cells, which do + // not have exactly the same value in mCell_Atom, and which do not both + // have change status equal to morkChange_kCut (because cutting a cut + // cell still yields a cell that has been cut). CountOverlap() also + // modifies the change attribute of any cell in ioVector to kDup when + // the change was previously kCut and the same column cell was found + // in this row with change also equal to kCut; this tells callers later + // they need not look for that cell in the row again on a second pass. + + void MergeCells(morkEnv* ev, morkCell* ioVector, mork_fill inVecLength, + mork_fill inOldRowFill, mork_fill inOverlap); + // MergeCells() is the part of TakeCells() that does the insertion. + // inOldRowFill is the old value of mRow_Length, and inOverlap is the + // number of cells in the intersection that must be updated. + + void TakeCells(morkEnv* ev, morkCell* ioVector, mork_fill inVecLength, + morkStore* ioStore); + + morkCell* NewCell(morkEnv* ev, mdb_column inColumn, mork_pos* outPos, + morkStore* ioStore); + morkCell* GetCell(morkEnv* ev, mdb_column inColumn, mork_pos* outPos) const; + morkCell* CellAt(morkEnv* ev, mork_pos inPos) const; + + mork_aid GetCellAtomAid(morkEnv* ev, mdb_column inColumn) const; + // GetCellAtomAid() finds the cell with column inColumn, and sees if the + // atom has a token ID, and returns the atom's ID if there is one. Or + // else zero is returned if there is no such column, or no atom, or if + // the atom has no ID to return. This method is intended to support + // efficient updating of column indexes for rows in a row space. + + public: // external row methods + void DirtyAllRowContent(morkEnv* ev); + + morkStore* GetRowSpaceStore(morkEnv* ev) const; + + void AddColumn(morkEnv* ev, mdb_column inColumn, const mdbYarn* inYarn, + morkStore* ioStore); + + morkAtom* GetColumnAtom(morkEnv* ev, mdb_column inColumn); + + void NextColumn(morkEnv* ev, mdb_column* ioColumn, mdbYarn* outYarn); + + void SeekColumn(morkEnv* ev, mdb_pos inPos, mdb_column* outColumn, + mdbYarn* outYarn); + + void CutColumn(morkEnv* ev, mdb_column inColumn); + + morkRowCellCursor* NewRowCellCursor(morkEnv* ev, mdb_pos inPos); + + void EmptyAllCells(morkEnv* ev); + void AddRow(morkEnv* ev, const morkRow* inSourceRow); + void SetRow(morkEnv* ev, const morkRow* inSourceRow); + void CutAllColumns(morkEnv* ev); + + void OnZeroRowGcUse(morkEnv* ev); + // OnZeroRowGcUse() is called when CutRowGcUse() returns zero. + + public: // dynamic typing + mork_bool IsRow() const { return mRow_Tag == morkRow_kTag; } + + public: // hash and equal + mork_u4 HashRow() const { + return (mRow_Oid.mOid_Scope << 16) ^ mRow_Oid.mOid_Id; + } + + mork_bool EqualRow(const morkRow* ioRow) const { + return ((mRow_Oid.mOid_Scope == ioRow->mRow_Oid.mOid_Scope) && + (mRow_Oid.mOid_Id == ioRow->mRow_Oid.mOid_Id)); + } + + mork_bool EqualOid(const mdbOid* ioOid) const { + return ((mRow_Oid.mOid_Scope == ioOid->mOid_Scope) && + (mRow_Oid.mOid_Id == ioOid->mOid_Id)); + } + + public: // errors + static void ZeroColumnError(morkEnv* ev); + static void LengthBeyondMaxError(morkEnv* ev); + static void NilCellsError(morkEnv* ev); + static void NonRowTypeError(morkEnv* ev); + static void NonRowTypeWarning(morkEnv* ev); + static void GcUsesUnderflowWarning(morkEnv* ev); + + private: // copying is not allowed + morkRow(const morkRow& other); + morkRow& operator=(const morkRow& other); +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKROW_ */ diff --git a/comm/mailnews/db/mork/morkRowCellCursor.cpp b/comm/mailnews/db/mork/morkRowCellCursor.cpp new file mode 100644 index 0000000000..edd6ebfd19 --- /dev/null +++ b/comm/mailnews/db/mork/morkRowCellCursor.cpp @@ -0,0 +1,220 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKCURSOR_ +# include "morkCursor.h" +#endif + +#ifndef _MORKROWCELLCURSOR_ +# include "morkRowCellCursor.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +#ifndef _MORKROWOBJECT_ +# include "morkRowObject.h" +#endif + +#ifndef _MORKROW_ +# include "morkRow.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkRowCellCursor::CloseMorkNode( + morkEnv* ev) // CloseRowCellCursor() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseRowCellCursor(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkRowCellCursor::~morkRowCellCursor() // CloseRowCellCursor() executed + // earlier +{ + CloseMorkNode(mMorkEnv); + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkRowCellCursor::morkRowCellCursor(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, + morkRowObject* ioRowObject) + : morkCursor(ev, inUsage, ioHeap), + mRowCellCursor_RowObject(0), + mRowCellCursor_Col(0) { + if (ev->Good()) { + if (ioRowObject) { + morkRow* row = ioRowObject->mRowObject_Row; + if (row) { + if (row->IsRow()) { + mCursor_Pos = -1; + mCursor_Seed = row->mRow_Seed; + + morkRowObject::SlotStrongRowObject(ioRowObject, ev, + &mRowCellCursor_RowObject); + if (ev->Good()) mNode_Derived = morkDerived_kRowCellCursor; + } else + row->NonRowTypeError(ev); + } else + ioRowObject->NilRowError(ev); + } else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkRowCellCursor, morkCursor, nsIMdbRowCellCursor) + +/*public non-poly*/ void morkRowCellCursor::CloseRowCellCursor(morkEnv* ev) { + if (this->IsNode()) { + mCursor_Pos = -1; + mCursor_Seed = 0; + morkRowObject::SlotStrongRowObject((morkRowObject*)0, ev, + &mRowCellCursor_RowObject); + this->CloseCursor(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void morkRowCellCursor::NilRowObjectError(morkEnv* ev) { + ev->NewError("nil mRowCellCursor_RowObject"); +} + +/*static*/ void morkRowCellCursor::NonRowCellCursorTypeError(morkEnv* ev) { + ev->NewError("non morkRowCellCursor"); +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 +// { ----- begin attribute methods ----- +NS_IMETHODIMP +morkRowCellCursor::SetRow(nsIMdbEnv* mev, nsIMdbRow* ioRow) { + nsresult outErr = NS_OK; + morkRow* row = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + row = (morkRow*)ioRow; + morkStore* store = row->GetRowSpaceStore(ev); + if (store) { + morkRowObject* rowObj = row->AcquireRowObject(ev, store); + if (rowObj) { + morkRowObject::SlotStrongRowObject((morkRowObject*)0, ev, + &mRowCellCursor_RowObject); + + mRowCellCursor_RowObject = rowObj; // take this strong ref + mCursor_Seed = row->mRow_Seed; + + row->GetCell(ev, mRowCellCursor_Col, &mCursor_Pos); + } + } + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowCellCursor::GetRow(nsIMdbEnv* mev, nsIMdbRow** acqRow) { + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + morkRowObject* rowObj = mRowCellCursor_RowObject; + if (rowObj) outRow = rowObj->AcquireRowHandle(ev); + + outErr = ev->AsErr(); + } + if (acqRow) *acqRow = outRow; + return outErr; +} +// } ----- end attribute methods ----- + +// { ----- begin cell seeking methods ----- +NS_IMETHODIMP +morkRowCellCursor::SeekCell( + nsIMdbEnv* mev, // context + mdb_pos inPos, // position of cell in row sequence + mdb_column* outColumn, // column for this particular cell + nsIMdbCell** acqCell) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} +// } ----- end cell seeking methods ----- + +// { ----- begin cell iteration methods ----- +NS_IMETHODIMP +morkRowCellCursor::NextCell( // get next cell in the row + nsIMdbEnv* mev, // context + nsIMdbCell** acqCell, // changes to the next cell in the iteration + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos) { + morkEnv* ev = morkEnv::FromMdbEnv(mev); + mdb_column col = 0; + mdb_pos pos = mRowCellCursor_Col; + if (pos < 0) + pos = 0; + else + ++pos; + + morkCell* cell = mRowCellCursor_RowObject->mRowObject_Row->CellAt(ev, pos); + if (cell) { + col = cell->GetColumn(); + *acqCell = mRowCellCursor_RowObject->mRowObject_Row->AcquireCellHandle( + ev, cell, col, pos); + } else { + *acqCell = nullptr; + pos = -1; + } + if (outPos) *outPos = pos; + if (outColumn) *outColumn = col; + + mRowCellCursor_Col = pos; + return NS_OK; +} + +NS_IMETHODIMP +morkRowCellCursor::PickNextCell( // get next cell in row within filter set + nsIMdbEnv* mev, // context + nsIMdbCell* ioCell, // changes to the next cell in the iteration + const mdbColumnSet* inFilterSet, // col set of actual caller interest + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos) +// Note that inFilterSet should not have too many (many more than 10?) +// cols, since this might imply a potential excessive consumption of time +// over many cursor calls when looking for column and filter intersection. +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +// } ----- end cell iteration methods ----- + +// } ===== end nsIMdbRowCellCursor methods ===== diff --git a/comm/mailnews/db/mork/morkRowCellCursor.h b/comm/mailnews/db/mork/morkRowCellCursor.h new file mode 100644 index 0000000000..91e032d2bc --- /dev/null +++ b/comm/mailnews/db/mork/morkRowCellCursor.h @@ -0,0 +1,118 @@ +/* -*- 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 _MORKROWCELLCURSOR_ +#define _MORKROWCELLCURSOR_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKCURSOR_ +# include "morkCursor.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class orkinRowCellCursor; +#define morkDerived_kRowCellCursor /*i*/ 0x6343 /* ascii 'cC' */ + +class morkRowCellCursor : public morkCursor, + public nsIMdbRowCellCursor { // row iterator + + // public: // slots inherited from morkObject (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 + + // morkFactory* mObject_Factory; // weak ref to suite factory + + // mork_seed mCursor_Seed; + // mork_pos mCursor_Pos; + // mork_bool mCursor_DoFailOnSeedOutOfSync; + // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment + + public: // state is public because the entire Mork system is private + NS_DECL_ISUPPORTS_INHERITED + morkRowObject* mRowCellCursor_RowObject; // strong ref to row + mork_column mRowCellCursor_Col; // col of cell last at mCursor_Pos + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseRowCellCursor() + + public: // morkRowCellCursor construction & destruction + morkRowCellCursor(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkRowObject* ioRowObject); + void CloseRowCellCursor(morkEnv* ev); // called by CloseMorkNode(); + + // { ----- begin attribute methods ----- + NS_IMETHOD SetRow(nsIMdbEnv* ev, + nsIMdbRow* ioRow) override; // sets pos to -1 + NS_IMETHOD GetRow(nsIMdbEnv* ev, nsIMdbRow** acqRow) override; + // } ----- end attribute methods ----- + + // { ----- begin cell seeking methods ----- + NS_IMETHOD SeekCell(nsIMdbEnv* ev, // context + mdb_pos inPos, // position of cell in row sequence + mdb_column* outColumn, // column for this particular cell + nsIMdbCell** acqCell) override; // the cell at inPos + // } ----- end cell seeking methods ----- + + // { ----- begin cell iteration methods ----- + NS_IMETHOD NextCell( // get next cell in the row + nsIMdbEnv* ev, // context + nsIMdbCell** acqCell, // changes to the next cell in the iteration + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos) override; // position of cell in row sequence + + NS_IMETHOD PickNextCell( // get next cell in row within filter set + nsIMdbEnv* ev, // context + nsIMdbCell* ioCell, // changes to the next cell in the iteration + const mdbColumnSet* inFilterSet, // col set of actual caller interest + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos) override; // position of cell in row sequence + + // Note that inFilterSet should not have too many (many more than 10?) + // cols, since this might imply a potential excessive consumption of time + // over many cursor calls when looking for column and filter intersection. + // } ----- end cell iteration methods ----- + + private: // copying is not allowed + morkRowCellCursor(const morkRowCellCursor& other); + morkRowCellCursor& operator=(const morkRowCellCursor& other); + virtual ~morkRowCellCursor(); // assert that close executed earlier + + public: // dynamic type identification + mork_bool IsRowCellCursor() const { + return IsNode() && mNode_Derived == morkDerived_kRowCellCursor; + } + // } ===== end morkNode methods ===== + + public: // errors + static void NilRowObjectError(morkEnv* ev); + static void NonRowCellCursorTypeError(morkEnv* ev); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakRowCellCursor(morkRowCellCursor* me, morkEnv* ev, + morkRowCellCursor** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongRowCellCursor(morkRowCellCursor* me, morkEnv* ev, + morkRowCellCursor** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKROWCELLCURSOR_ */ diff --git a/comm/mailnews/db/mork/morkRowMap.cpp b/comm/mailnews/db/mork/morkRowMap.cpp new file mode 100644 index 0000000000..a1d415f8d2 --- /dev/null +++ b/comm/mailnews/db/mork/morkRowMap.cpp @@ -0,0 +1,250 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +#ifndef _MORKROWMAP_ +# include "morkRowMap.h" +#endif + +#ifndef _MORKROW_ +# include "morkRow.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkRowMap::CloseMorkNode( + morkEnv* ev) // CloseRowMap() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseRowMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkRowMap::~morkRowMap() // assert CloseRowMap() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkRowMap::morkRowMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, + mork_size inSlots) + : morkMap(ev, inUsage, ioHeap, + /*inKeySize*/ sizeof(morkRow*), /*inValSize*/ 0, inSlots, + ioSlotHeap, /*inHoldChanges*/ morkBool_kFalse) { + if (ev->Good()) mNode_Derived = morkDerived_kRowMap; +} + +/*public non-poly*/ void morkRowMap::CloseRowMap( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + this->CloseMap(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +// { ===== begin morkMap poly interface ===== +/*virtual*/ mork_bool // +morkRowMap::Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const { + MORK_USED_1(ev); + return (*(const morkRow**)inKeyA)->EqualRow(*(const morkRow**)inKeyB); +} + +/*virtual*/ mork_u4 // +morkRowMap::Hash(morkEnv* ev, const void* inKey) const { + MORK_USED_1(ev); + return (*(const morkRow**)inKey)->HashRow(); +} +// } ===== end morkMap poly interface ===== + +mork_bool morkRowMap::AddRow(morkEnv* ev, morkRow* ioRow) { + if (ev->Good()) { + this->Put(ev, &ioRow, /*val*/ (void*)0, + /*key*/ (void*)0, /*val*/ (void*)0, (mork_change**)0); + } + return ev->Good(); +} + +morkRow* morkRowMap::CutOid(morkEnv* ev, const mdbOid* inOid) { + morkRow row(inOid); + morkRow* key = &row; + morkRow* oldKey = 0; + this->Cut(ev, &key, &oldKey, /*val*/ (void*)0, (mork_change**)0); + + return oldKey; +} + +morkRow* morkRowMap::CutRow(morkEnv* ev, const morkRow* ioRow) { + morkRow* oldKey = 0; + this->Cut(ev, &ioRow, &oldKey, /*val*/ (void*)0, (mork_change**)0); + + return oldKey; +} + +morkRow* morkRowMap::GetOid(morkEnv* ev, const mdbOid* inOid) { + morkRow row(inOid); + morkRow* key = &row; + morkRow* oldKey = 0; + this->Get(ev, &key, &oldKey, /*val*/ (void*)0, (mork_change**)0); + + return oldKey; +} + +morkRow* morkRowMap::GetRow(morkEnv* ev, const morkRow* ioRow) { + morkRow* oldKey = 0; + this->Get(ev, &ioRow, &oldKey, /*val*/ (void*)0, (mork_change**)0); + + return oldKey; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkRowProbeMap::CloseMorkNode( + morkEnv* ev) // CloseRowProbeMap() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseRowProbeMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkRowProbeMap::~morkRowProbeMap() // assert CloseRowProbeMap() executed + // earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkRowProbeMap::morkRowProbeMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, + mork_size inSlots) + : morkProbeMap(ev, inUsage, ioHeap, + /*inKeySize*/ sizeof(morkRow*), /*inValSize*/ 0, ioSlotHeap, + inSlots, + /*inHoldChanges*/ morkBool_kTrue) { + if (ev->Good()) mNode_Derived = morkDerived_kRowProbeMap; +} + +/*public non-poly*/ void morkRowProbeMap::CloseRowProbeMap( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + this->CloseProbeMap(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b) +morkRowProbeMap::MapTest(morkEnv* ev, const void* inMapKey, + const void* inAppKey) const { + MORK_USED_1(ev); + const morkRow* key = *(const morkRow**)inMapKey; + if (key) { + mork_bool hit = key->EqualRow(*(const morkRow**)inAppKey); + return (hit) ? morkTest_kHit : morkTest_kMiss; + } else + return morkTest_kVoid; +} + +/*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b) +morkRowProbeMap::MapHash(morkEnv* ev, const void* inAppKey) const { + const morkRow* key = *(const morkRow**)inAppKey; + if (key) + return key->HashRow(); + else { + ev->NilPointerWarning(); + return 0; + } +} + +/*virtual*/ mork_u4 morkRowProbeMap::ProbeMapHashMapKey( + morkEnv* ev, const void* inMapKey) const { + const morkRow* key = *(const morkRow**)inMapKey; + if (key) + return key->HashRow(); + else { + ev->NilPointerWarning(); + return 0; + } +} + +mork_bool morkRowProbeMap::AddRow(morkEnv* ev, morkRow* ioRow) { + if (ev->Good()) { + this->MapAtPut(ev, &ioRow, /*val*/ (void*)0, + /*key*/ (void*)0, /*val*/ (void*)0); + } + return ev->Good(); +} + +morkRow* morkRowProbeMap::CutOid(morkEnv* ev, const mdbOid* inOid) { + MORK_USED_1(inOid); + morkProbeMap::ProbeMapCutError(ev); + + return 0; +} + +morkRow* morkRowProbeMap::CutRow(morkEnv* ev, const morkRow* ioRow) { + MORK_USED_1(ioRow); + morkProbeMap::ProbeMapCutError(ev); + + return 0; +} + +morkRow* morkRowProbeMap::GetOid(morkEnv* ev, const mdbOid* inOid) { + morkRow row(inOid); + morkRow* key = &row; + morkRow* oldKey = 0; + this->MapAt(ev, &key, &oldKey, /*val*/ (void*)0); + + return oldKey; +} + +morkRow* morkRowProbeMap::GetRow(morkEnv* ev, const morkRow* ioRow) { + morkRow* oldKey = 0; + this->MapAt(ev, &ioRow, &oldKey, /*val*/ (void*)0); + + return oldKey; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkRowMap.h b/comm/mailnews/db/mork/morkRowMap.h new file mode 100644 index 0000000000..d58515d4db --- /dev/null +++ b/comm/mailnews/db/mork/morkRowMap.h @@ -0,0 +1,228 @@ +/* -*- 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 _MORKROWMAP_ +#define _MORKROWMAP_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +#ifndef _MORKPROBEMAP_ +# include "morkProbeMap.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kRowMap /*i*/ 0x724D /* ascii 'rM' */ + +/*| morkRowMap: maps a set of morkRow by contained Oid +|*/ +class morkRowMap : public morkMap { // for mapping row IDs to rows + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseRowMap() only if open + virtual ~morkRowMap(); // assert that CloseRowMap() executed earlier + + public: // morkMap construction & destruction + morkRowMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap, mork_size inSlots); + void CloseRowMap(morkEnv* ev); // called by CloseMorkNode(); + + public: // dynamic type identification + mork_bool IsRowMap() const { + return IsNode() && mNode_Derived == morkDerived_kRowMap; + } + // } ===== end morkNode methods ===== + + // { ===== begin morkMap poly interface ===== + virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b) + Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override; + // implemented using morkRow::EqualRow() + + virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b) + Hash(morkEnv* ev, const void* inKey) const override; + // implemented using morkRow::HashRow() + // } ===== end morkMap poly interface ===== + + public: // other map methods + mork_bool AddRow(morkEnv* ev, morkRow* ioRow); + // AddRow() returns ev->Good() + + morkRow* CutOid(morkEnv* ev, const mdbOid* inOid); + // CutRid() returns the row removed equal to inRid, if there was one + + morkRow* CutRow(morkEnv* ev, const morkRow* ioRow); + // CutRow() returns the row removed equal to ioRow, if there was one + + morkRow* GetOid(morkEnv* ev, const mdbOid* inOid); + // GetOid() returns the row equal to inRid, or else nil + + morkRow* GetRow(morkEnv* ev, const morkRow* ioRow); + // GetRow() returns the row equal to ioRow, or else nil + + // note the rows are owned elsewhere, usually by morkRowSpace + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakRowMap(morkRowMap* me, morkEnv* ev, morkRowMap** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongRowMap(morkRowMap* me, morkEnv* ev, + morkRowMap** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +class morkRowMapIter : public morkMapIter { // typesafe wrapper class + + public: + morkRowMapIter(morkEnv* ev, morkRowMap* ioMap) : morkMapIter(ev, ioMap) {} + + morkRowMapIter() : morkMapIter() {} + void InitRowMapIter(morkEnv* ev, morkRowMap* ioMap) { + this->InitMapIter(ev, ioMap); + } + + mork_change* FirstRow(morkEnv* ev, morkRow** outRowPtr) { + return this->First(ev, outRowPtr, /*val*/ (void*)0); + } + + mork_change* NextRow(morkEnv* ev, morkRow** outRowPtr) { + return this->Next(ev, outRowPtr, /*val*/ (void*)0); + } + + mork_change* HereRow(morkEnv* ev, morkRow** outRowPtr) { + return this->Here(ev, outRowPtr, /*val*/ (void*)0); + } + + mork_change* CutHereRow(morkEnv* ev, morkRow** outRowPtr) { + return this->CutHere(ev, outRowPtr, /*val*/ (void*)0); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kRowProbeMap /*i*/ 0x726D /* ascii 'rm' */ + +/*| morkRowProbeMap: maps a set of morkRow by contained Oid +|*/ +class morkRowProbeMap : public morkProbeMap { // for mapping row IDs to rows + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseRowProbeMap() only if open + virtual ~morkRowProbeMap(); // assert CloseRowProbeMap() executed earlier + + public: // morkMap construction & destruction + morkRowProbeMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap, mork_size inSlots); + void CloseRowProbeMap(morkEnv* ev); // called by CloseMorkNode(); + + public: // dynamic type identification + mork_bool IsRowMap() const { + return IsNode() && mNode_Derived == morkDerived_kRowMap; + } + // } ===== end morkNode methods ===== + + // { ===== begin morkProbeMap methods ===== + virtual mork_test // hit(a,b) implies hash(a) == hash(b) + MapTest(morkEnv* ev, const void* inMapKey, + const void* inAppKey) const override; + + virtual mork_u4 // hit(a,b) implies hash(a) == hash(b) + MapHash(morkEnv* ev, const void* inAppKey) const override; + + virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, + const void* inMapKey) const override; + + // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey); + + // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' into all keys + // inside map + // void* ioMapKey, mork_count inKeyCount); // array of keys inside map + + // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map + // const void* inAppKey, const void* inAppVal, // (key,val) outside map + // void* outMapKey, void* outMapVal); // (key,val) inside map + + // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the + // map + // const void* inMapKey, const void* inMapVal, // (key,val) inside map + // void* outAppKey, void* outAppVal) const; // (key,val) outside map + // } ===== end morkProbeMap methods ===== + + public: // other map methods + mork_bool AddRow(morkEnv* ev, morkRow* ioRow); + // AddRow() returns ev->Good() + + morkRow* CutOid(morkEnv* ev, const mdbOid* inOid); + // CutRid() returns the row removed equal to inRid, if there was one + + morkRow* CutRow(morkEnv* ev, const morkRow* ioRow); + // CutRow() returns the row removed equal to ioRow, if there was one + + morkRow* GetOid(morkEnv* ev, const mdbOid* inOid); + // GetOid() returns the row equal to inRid, or else nil + + morkRow* GetRow(morkEnv* ev, const morkRow* ioRow); + // GetRow() returns the row equal to ioRow, or else nil + + // note the rows are owned elsewhere, usually by morkRowSpace + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakRowProbeMap(morkRowProbeMap* me, morkEnv* ev, + morkRowProbeMap** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongRowProbeMap(morkRowProbeMap* me, morkEnv* ev, + morkRowProbeMap** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +class morkRowProbeMapIter : public morkProbeMapIter { // typesafe wrapper class + + public: + morkRowProbeMapIter(morkEnv* ev, morkRowProbeMap* ioMap) + : morkProbeMapIter(ev, ioMap) {} + + morkRowProbeMapIter() : morkProbeMapIter() {} + void InitRowMapIter(morkEnv* ev, morkRowProbeMap* ioMap) { + this->InitMapIter(ev, ioMap); + } + + mork_change* FirstRow(morkEnv* ev, morkRow** outRowPtr) { + return this->First(ev, outRowPtr, /*val*/ (void*)0); + } + + mork_change* NextRow(morkEnv* ev, morkRow** outRowPtr) { + return this->Next(ev, outRowPtr, /*val*/ (void*)0); + } + + mork_change* HereRow(morkEnv* ev, morkRow** outRowPtr) { + return this->Here(ev, outRowPtr, /*val*/ (void*)0); + } + + mork_change* CutHereRow(morkEnv* ev, morkRow** outRowPtr) { + return this->CutHere(ev, outRowPtr, /*val*/ (void*)0); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKROWMAP_ */ diff --git a/comm/mailnews/db/mork/morkRowObject.cpp b/comm/mailnews/db/mork/morkRowObject.cpp new file mode 100644 index 0000000000..39844172b8 --- /dev/null +++ b/comm/mailnews/db/mork/morkRowObject.cpp @@ -0,0 +1,530 @@ +/* -*- 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 _MORKROWOBJECT_ +# include "morkRowObject.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +#ifndef _MORKROWCELLCURSOR_ +# include "morkRowCellCursor.h" +#endif + +#ifndef _MORKCELLOBJECT_ +# include "morkCellObject.h" +#endif + +#ifndef _MORKROW_ +# include "morkRow.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkRowObject::CloseMorkNode( + morkEnv* ev) // CloseRowObject() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseRowObject(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkRowObject::~morkRowObject() // assert CloseRowObject() executed earlier +{ + CloseMorkNode(mMorkEnv); + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkRowObject::morkRowObject(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkRow* ioRow, + morkStore* ioStore) + : morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*)0), + mRowObject_Row(0), + mRowObject_Store(0) { + if (ev->Good()) { + if (ioRow && ioStore) { + mRowObject_Row = ioRow; + mRowObject_Store = + ioStore; // morkRowObjects don't ref-cnt the owning store. + + if (ev->Good()) mNode_Derived = morkDerived_kRowObject; + } else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkRowObject, morkObject, nsIMdbRow) +// { ===== begin nsIMdbCollection methods ===== + +// { ----- begin attribute methods ----- +NS_IMETHODIMP +morkRowObject::GetSeed(nsIMdbEnv* mev, mdb_seed* outSeed) { + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + *outSeed = (mdb_seed)mRowObject_Row->mRow_Seed; + outErr = ev->AsErr(); + } + return outErr; +} +NS_IMETHODIMP +morkRowObject::GetCount(nsIMdbEnv* mev, mdb_count* outCount) { + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + *outCount = (mdb_count)mRowObject_Row->mRow_Length; + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::GetPort(nsIMdbEnv* mev, nsIMdbPort** acqPort) { + nsresult outErr = NS_OK; + nsIMdbPort* outPort = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + morkRowSpace* rowSpace = mRowObject_Row->mRow_Space; + if (rowSpace && rowSpace->mSpace_Store) { + morkStore* store = mRowObject_Row->GetRowSpaceStore(ev); + if (store) outPort = store->AcquireStoreHandle(ev); + } else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if (acqPort) *acqPort = outPort; + + return outErr; +} +// } ----- end attribute methods ----- + +// { ----- begin cursor methods ----- +NS_IMETHODIMP +morkRowObject::GetCursor( // make a cursor starting iter at inMemberPos + nsIMdbEnv* mev, // context + mdb_pos inMemberPos, // zero-based ordinal pos of member in collection + nsIMdbCursor** acqCursor) { + return this->GetRowCellCursor(mev, inMemberPos, + (nsIMdbRowCellCursor**)acqCursor); +} +// } ----- end cursor methods ----- + +// { ----- begin ID methods ----- +NS_IMETHODIMP +morkRowObject::GetOid(nsIMdbEnv* mev, mdbOid* outOid) { + *outOid = mRowObject_Row->mRow_Oid; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + return (ev) ? ev->AsErr() : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +morkRowObject::BecomeContent(nsIMdbEnv* mev, const mdbOid* inOid) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; + // remember row->MaybeDirtySpaceStoreAndRow(); +} +// } ----- end ID methods ----- + +// { ----- begin activity dropping methods ----- +NS_IMETHODIMP +morkRowObject::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 nsIMdbRow methods ===== + +// { ----- begin cursor methods ----- +NS_IMETHODIMP +morkRowObject::GetRowCellCursor( // make a cursor starting iteration at + // inCellPos + nsIMdbEnv* mev, // context + mdb_pos inPos, // zero-based ordinal position of cell in row + nsIMdbRowCellCursor** acqCursor) { + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + nsIMdbRowCellCursor* outCursor = 0; + if (ev) { + morkRowCellCursor* cursor = mRowObject_Row->NewRowCellCursor(ev, inPos); + if (cursor) { + if (ev->Good()) { + cursor->mCursor_Seed = (mork_seed)inPos; + outCursor = cursor; + NS_ADDREF(cursor); + } + } + outErr = ev->AsErr(); + } + if (acqCursor) *acqCursor = outCursor; + return outErr; +} +// } ----- end cursor methods ----- + +// { ----- begin column methods ----- +NS_IMETHODIMP +morkRowObject::AddColumn( // make sure a particular column is inside row + nsIMdbEnv* mev, // context + mdb_column inColumn, // column to add + const mdbYarn* inYarn) { + nsresult outErr = NS_ERROR_FAILURE; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (mRowObject_Store && mRowObject_Row) + mRowObject_Row->AddColumn(ev, inColumn, inYarn, mRowObject_Store); + + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::CutColumn( // make sure a column is absent from the row + nsIMdbEnv* mev, // context + mdb_column inColumn) { + nsresult outErr = NS_ERROR_FAILURE; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + mRowObject_Row->CutColumn(ev, inColumn); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::CutAllColumns( // remove all columns from the row + nsIMdbEnv* mev) { + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + mRowObject_Row->CutAllColumns(ev); + outErr = ev->AsErr(); + } + return outErr; +} +// } ----- end column methods ----- + +// { ----- begin cell methods ----- +NS_IMETHODIMP +morkRowObject::NewCell( // get cell for specified column, or add new one + nsIMdbEnv* mev, // context + mdb_column inColumn, // column to add + nsIMdbCell** acqCell) { + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + GetCell(mev, inColumn, acqCell); + if (!*acqCell) { + if (mRowObject_Store) { + mdbYarn yarn; // to pass empty yarn into morkRowObject::AddColumn() + yarn.mYarn_Buf = 0; + yarn.mYarn_Fill = 0; + yarn.mYarn_Size = 0; + yarn.mYarn_More = 0; + yarn.mYarn_Form = 0; + yarn.mYarn_Grow = 0; + AddColumn(ev, inColumn, &yarn); + GetCell(mev, inColumn, acqCell); + } + } + + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::AddCell( // copy a cell from another row to this row + nsIMdbEnv* mev, // context + const nsIMdbCell* inCell) { + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + morkCell* cell = 0; + morkCellObject* cellObj = (morkCellObject*)inCell; + if (cellObj->CanUseCell(mev, morkBool_kFalse, &outErr, &cell)) { + morkRow* cellRow = cellObj->mCellObject_Row; + if (cellRow) { + if (mRowObject_Row != cellRow) { + morkStore* store = mRowObject_Row->GetRowSpaceStore(ev); + morkStore* cellStore = cellRow->GetRowSpaceStore(ev); + if (store && cellStore) { + mork_column col = cell->GetColumn(); + morkAtom* atom = cell->mCell_Atom; + mdbYarn yarn; + morkAtom::AliasYarn(atom, &yarn); // works even when atom is nil + + if (store != cellStore) col = store->CopyToken(ev, col, cellStore); + if (ev->Good()) AddColumn(ev, col, &yarn); + } else + ev->NilPointerError(); + } + } else + ev->NilPointerError(); + } + + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::GetCell( // find a cell in this row + nsIMdbEnv* mev, // context + mdb_column inColumn, // column to find + nsIMdbCell** acqCell) { + nsresult outErr = NS_OK; + nsIMdbCell* outCell = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + + if (ev) { + if (inColumn) { + mork_pos pos = 0; + morkCell* cell = mRowObject_Row->GetCell(ev, inColumn, &pos); + if (cell) { + outCell = mRowObject_Row->AcquireCellHandle(ev, cell, inColumn, pos); + } + } else + mRowObject_Row->ZeroColumnError(ev); + + outErr = ev->AsErr(); + } + if (acqCell) *acqCell = outCell; + return outErr; +} + +NS_IMETHODIMP +morkRowObject::EmptyAllCells( // make all cells in row empty of content + nsIMdbEnv* mev) { + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + EmptyAllCells(ev); + outErr = ev->AsErr(); + } + return outErr; +} +// } ----- end cell methods ----- + +// { ----- begin row methods ----- +NS_IMETHODIMP +morkRowObject::AddRow( // add all cells in another row to this one + nsIMdbEnv* mev, // context + nsIMdbRow* ioSourceRow) { + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + morkRow* unsafeSource = (morkRow*)ioSourceRow; // unsafe cast + // if ( unsafeSource->CanUseRow(mev, morkBool_kFalse, &outErr, &source) ) + { mRowObject_Row->AddRow(ev, unsafeSource); } + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::SetRow( // make exact duplicate of another row + nsIMdbEnv* mev, // context + nsIMdbRow* ioSourceRow) { + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + morkRowObject* sourceObject = (morkRowObject*)ioSourceRow; // unsafe cast + morkRow* unsafeSource = sourceObject->mRowObject_Row; + // if ( unsafeSource->CanUseRow(mev, morkBool_kFalse, &outErr, &source) ) + { mRowObject_Row->SetRow(ev, unsafeSource); } + outErr = ev->AsErr(); + } + return outErr; +} +// } ----- end row methods ----- + +// { ----- begin blob methods ----- +NS_IMETHODIMP +morkRowObject::SetCellYarn( // synonym for AddColumn() + nsIMdbEnv* mev, // context + mdb_column inColumn, // column to add + const mdbYarn* inYarn) { + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (mRowObject_Store) AddColumn(ev, inColumn, inYarn); + + outErr = ev->AsErr(); + } + return outErr; +} +NS_IMETHODIMP +morkRowObject::GetCellYarn(nsIMdbEnv* mev, // context + mdb_column inColumn, // column to read + mdbYarn* outYarn) // writes some yarn slots +// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (mRowObject_Store && mRowObject_Row) { + morkAtom* atom = mRowObject_Row->GetColumnAtom(ev, inColumn); + morkAtom::GetYarn(atom, outYarn); + } + + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::AliasCellYarn(nsIMdbEnv* mev, // context + mdb_column inColumn, // column to alias + mdbYarn* outYarn) // writes ALL yarn slots +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (mRowObject_Store && mRowObject_Row) { + morkAtom* atom = mRowObject_Row->GetColumnAtom(ev, inColumn); + morkAtom::AliasYarn(atom, outYarn); + // note nil atom works and sets yarn correctly + } + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::NextCellYarn( + nsIMdbEnv* mev, // iterative version of GetCellYarn() + mdb_column* ioColumn, // next column to read + mdbYarn* outYarn) // writes some yarn slots +// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form +// +// The ioColumn argument is an inout parameter which initially contains the +// last column accessed and returns the next column corresponding to the +// content read into the yarn. Callers should start with a zero column +// value to say 'no previous column', which causes the first column to be +// read. Then the value returned in ioColumn is perfect for the next call +// to NextCellYarn(), since it will then be the previous column accessed. +// Callers need only examine the column token returned to see which cell +// in the row is being read into the yarn. When no more columns remain, +// and the iteration has ended, ioColumn will return a zero token again. +// So iterating over cells starts and ends with a zero column token. +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (mRowObject_Store && mRowObject_Row) + mRowObject_Row->NextColumn(ev, ioColumn, outYarn); + + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::SeekCellYarn( // resembles nsIMdbRowCellCursor::SeekCell() + nsIMdbEnv* mev, // context + mdb_pos inPos, // position of cell in row sequence + mdb_column* outColumn, // column for this particular cell + mdbYarn* outYarn) // writes some yarn slots +// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form +// Callers can pass nil for outYarn to indicate no interest in content, so +// only the outColumn value is returned. NOTE to subclasses: you must be +// able to ignore outYarn when the pointer is nil; please do not crash. + +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (mRowObject_Store && mRowObject_Row) + mRowObject_Row->SeekColumn(ev, inPos, outColumn, outYarn); + + outErr = ev->AsErr(); + } + return outErr; +} + +// } ----- end blob methods ----- + +// } ===== end nsIMdbRow methods ===== + +/*public non-poly*/ void morkRowObject::CloseRowObject( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + morkRow* row = mRowObject_Row; + mRowObject_Row = 0; + this->CloseObject(ev); + this->MarkShut(); + + if (row) { + MORK_ASSERT(row->mRow_Object == this); + if (row->mRow_Object == this) { + row->mRow_Object = 0; // just nil this slot -- cut ref down below + + mRowObject_Store = 0; // morkRowObjects don't ref-cnt the owning store. + + this->CutWeakRef( + ev->AsMdbEnv()); // do last, because it might self destroy + } + } + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void morkRowObject::NonRowObjectTypeError(morkEnv* ev) { + ev->NewError("non morkRowObject"); +} + +/*static*/ void morkRowObject::NilRowError(morkEnv* ev) { + ev->NewError("nil mRowObject_Row"); +} + +/*static*/ void morkRowObject::NilStoreError(morkEnv* ev) { + ev->NewError("nil mRowObject_Store"); +} + +/*static*/ void morkRowObject::RowObjectRowNotSelfError(morkEnv* ev) { + ev->NewError("mRowObject_Row->mRow_Object != self"); +} + +nsIMdbRow* morkRowObject::AcquireRowHandle(morkEnv* ev) // mObject_Handle +{ + AddRef(); + return this; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkRowObject.h b/comm/mailnews/db/mork/morkRowObject.h new file mode 100644 index 0000000000..7af5642a3f --- /dev/null +++ b/comm/mailnews/db/mork/morkRowObject.h @@ -0,0 +1,204 @@ +/* -*- 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 _MORKROWOBJECT_ +#define _MORKROWOBJECT_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKOBJECT_ +# include "morkObject.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class nsIMdbRow; +#define morkDerived_kRowObject /*i*/ 0x724F /* ascii 'rO' */ + +class morkRowObject : public morkObject, public nsIMdbRow { // + + public: // state is public because the entire Mork system is private + NS_DECL_ISUPPORTS_INHERITED + + morkRow* mRowObject_Row; // non-refcounted alias to morkRow + morkStore* mRowObject_Store; // non-refcounted ptr to store containing row + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseRowObject() only if open + + public: // morkRowObject construction & destruction + morkRowObject(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkRow* ioRow, morkStore* ioStore); + void CloseRowObject(morkEnv* ev); // called by CloseMorkNode(); + + // { ===== begin nsIMdbCollection methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD GetSeed(nsIMdbEnv* ev, + mdb_seed* outSeed) override; // member change count + NS_IMETHOD GetCount(nsIMdbEnv* ev, + mdb_count* outCount) override; // member count + + NS_IMETHOD GetPort(nsIMdbEnv* ev, + nsIMdbPort** acqPort) override; // collection container + // } ----- end attribute methods ----- + + // { ----- begin cursor methods ----- + NS_IMETHOD GetCursor( // make a cursor starting iter at inMemberPos + nsIMdbEnv* ev, // context + mdb_pos inMemberPos, // zero-based ordinal pos of member in collection + nsIMdbCursor** acqCursor) override; // acquire new cursor instance + // } ----- end cursor methods ----- + + // { ----- begin ID methods ----- + NS_IMETHOD GetOid(nsIMdbEnv* ev, + mdbOid* outOid) override; // read object identity + NS_IMETHOD BecomeContent(nsIMdbEnv* ev, + const mdbOid* inOid) override; // exchange content + // } ----- end ID methods ----- + + // { ----- begin activity dropping methods ----- + NS_IMETHOD DropActivity( // tell collection usage no longer expected + nsIMdbEnv* ev) override; + // } ----- end activity dropping methods ----- + + // } ===== end nsIMdbCollection methods ===== + // { ===== begin nsIMdbRow methods ===== + + // { ----- begin cursor methods ----- + NS_IMETHOD GetRowCellCursor( // make a cursor starting iteration at inRowPos + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbRowCellCursor** acqCursor) override; // acquire new cursor instance + // } ----- end cursor methods ----- + + // { ----- begin column methods ----- + NS_IMETHOD AddColumn( // make sure a particular column is inside row + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to add + const mdbYarn* inYarn) override; // cell value to install + + NS_IMETHOD CutColumn( // make sure a column is absent from the row + nsIMdbEnv* ev, // context + mdb_column inColumn) override; // column to ensure absent from row + + NS_IMETHOD CutAllColumns( // remove all columns from the row + nsIMdbEnv* ev) override; // context + // } ----- end column methods ----- + + // { ----- begin cell methods ----- + NS_IMETHOD NewCell( // get cell for specified column, or add new one + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to add + nsIMdbCell** acqCell) override; // cell column and value + + NS_IMETHOD AddCell( // copy a cell from another row to this row + nsIMdbEnv* ev, // context + const nsIMdbCell* inCell) override; // cell column and value + + NS_IMETHOD GetCell( // find a cell in this row + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to find + nsIMdbCell** acqCell) override; // cell for specified column, or null + + NS_IMETHOD EmptyAllCells( // make all cells in row empty of content + nsIMdbEnv* ev) override; // context + // } ----- end cell methods ----- + + // { ----- begin row methods ----- + NS_IMETHOD AddRow( // add all cells in another row to this one + nsIMdbEnv* ev, // context + nsIMdbRow* ioSourceRow) override; // row to union with + + NS_IMETHOD SetRow( // make exact duplicate of another row + nsIMdbEnv* ev, // context + nsIMdbRow* ioSourceRow) override; // row to duplicate + // } ----- end row methods ----- + + // { ----- begin blob methods ----- + NS_IMETHOD SetCellYarn( + nsIMdbEnv* ev, // synonym for AddColumn() + mdb_column inColumn, // column to write + const mdbYarn* inYarn) override; // reads from yarn slots + // make this text object contain content from the yarn's buffer + + NS_IMETHOD GetCellYarn(nsIMdbEnv* ev, + mdb_column inColumn, // column to read + mdbYarn* outYarn) override; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + + NS_IMETHOD AliasCellYarn(nsIMdbEnv* ev, + mdb_column inColumn, // column to alias + mdbYarn* outYarn) override; // writes ALL yarn slots + + NS_IMETHOD NextCellYarn(nsIMdbEnv* ev, // iterative version of GetCellYarn() + mdb_column* ioColumn, // next column to read + mdbYarn* outYarn) override; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + // + // The ioColumn argument is an inout parameter which initially contains the + // last column accessed and returns the next column corresponding to the + // content read into the yarn. Callers should start with a zero column + // value to say 'no previous column', which causes the first column to be + // read. Then the value returned in ioColumn is perfect for the next call + // to NextCellYarn(), since it will then be the previous column accessed. + // Callers need only examine the column token returned to see which cell + // in the row is being read into the yarn. When no more columns remain, + // and the iteration has ended, ioColumn will return a zero token again. + // So iterating over cells starts and ends with a zero column token. + + NS_IMETHOD SeekCellYarn( // resembles nsIMdbRowCellCursor::SeekCell() + nsIMdbEnv* ev, // context + mdb_pos inPos, // position of cell in row sequence + mdb_column* outColumn, // column for this particular cell + mdbYarn* outYarn) override; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + // Callers can pass nil for outYarn to indicate no interest in content, so + // only the outColumn value is returned. NOTE to subclasses: you must be + // able to ignore outYarn when the pointer is nil; please do not crash. + + // } ----- end blob methods ----- + + // } ===== end nsIMdbRow methods ===== + + private: // copying is not allowed + morkRowObject(const morkRowObject& other); + morkRowObject& operator=(const morkRowObject& other); + virtual ~morkRowObject(); // assert that CloseRowObject() executed earlier + + public: // dynamic type identification + mork_bool IsRowObject() const { + return IsNode() && mNode_Derived == morkDerived_kRowObject; + } + // } ===== end morkNode methods ===== + + public: // typing + static void NonRowObjectTypeError(morkEnv* ev); + static void NilRowError(morkEnv* ev); + static void NilStoreError(morkEnv* ev); + static void RowObjectRowNotSelfError(morkEnv* ev); + + public: // other row node methods + nsIMdbRow* AcquireRowHandle(morkEnv* ev); // mObject_Handle + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakRowObject(morkRowObject* me, morkEnv* ev, + morkRowObject** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongRowObject(morkRowObject* me, morkEnv* ev, + morkRowObject** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKROWOBJECT_ */ diff --git a/comm/mailnews/db/mork/morkRowSpace.cpp b/comm/mailnews/db/mork/morkRowSpace.cpp new file mode 100644 index 0000000000..4ee9d5aead --- /dev/null +++ b/comm/mailnews/db/mork/morkRowSpace.cpp @@ -0,0 +1,540 @@ +/* -*- 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 _MORKSPACE_ +# include "morkSpace.h" +#endif + +#ifndef _MORKNODEMAP_ +# include "morkNodeMap.h" +#endif + +#ifndef _MORKROWMAP_ +# include "morkRowMap.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKROWSPACE_ +# include "morkRowSpace.h" +#endif + +#ifndef _MORKPOOL_ +# include "morkPool.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +#ifndef _MORKTABLE_ +# include "morkTable.h" +#endif + +#ifndef _MORKROW_ +# include "morkRow.h" +#endif + +#ifndef _MORKATOMMAP_ +# include "morkAtomMap.h" +#endif + +#ifndef _MORKROWOBJECT_ +# include "morkRowObject.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkRowSpace::CloseMorkNode( + morkEnv* ev) // CloseRowSpace() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseRowSpace(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkRowSpace::~morkRowSpace() // assert CloseRowSpace() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkRowSpace::morkRowSpace(morkEnv* ev, const morkUsage& inUsage, + mork_scope inScope, morkStore* ioStore, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) + : morkSpace(ev, inUsage, inScope, ioStore, ioHeap, ioSlotHeap), + mRowSpace_SlotHeap(ioSlotHeap), + mRowSpace_Rows(ev, morkUsage::kMember, (nsIMdbHeap*)0, ioSlotHeap, + morkRowSpace_kStartRowMapSlotCount), + mRowSpace_Tables(ev, morkUsage::kMember, (nsIMdbHeap*)0, ioSlotHeap), + mRowSpace_NextTableId(1), + mRowSpace_NextRowId(1) + + , + mRowSpace_IndexCount(0) { + morkAtomRowMap** cache = mRowSpace_IndexCache; + morkAtomRowMap** cacheEnd = cache + morkRowSpace_kPrimeCacheSize; + while (cache < cacheEnd) + *cache++ = 0; // put nil into every slot of cache table + + if (ev->Good()) { + if (ioSlotHeap) { + mNode_Derived = morkDerived_kRowSpace; + + // the morkSpace base constructor handles any dirty propagation + } else + ev->NilPointerError(); + } +} + +/*public non-poly*/ void morkRowSpace::CloseRowSpace( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + morkAtomRowMap** cache = mRowSpace_IndexCache; + morkAtomRowMap** cacheEnd = cache + morkRowSpace_kPrimeCacheSize; + --cache; // prepare for preincrement: + while (++cache < cacheEnd) { + if (*cache) morkAtomRowMap::SlotStrongAtomRowMap(0, ev, cache); + } + + mRowSpace_Tables.CloseMorkNode(ev); + + morkStore* store = mSpace_Store; + if (store) this->CutAllRows(ev, &store->mStore_Pool); + + mRowSpace_Rows.CloseMorkNode(ev); + this->CloseSpace(ev); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void morkRowSpace::NonRowSpaceTypeError(morkEnv* ev) { + ev->NewError("non morkRowSpace"); +} + +/*static*/ void morkRowSpace::ZeroKindError(morkEnv* ev) { + ev->NewError("zero table kind"); +} + +/*static*/ void morkRowSpace::ZeroScopeError(morkEnv* ev) { + ev->NewError("zero row scope"); +} + +/*static*/ void morkRowSpace::ZeroTidError(morkEnv* ev) { + ev->NewError("zero table ID"); +} + +/*static*/ void morkRowSpace::MinusOneRidError(morkEnv* ev) { + ev->NewError("row ID is -1"); +} + +///*static*/ void +// morkRowSpace::ExpectAutoIdOnlyError(morkEnv* ev) +//{ +// ev->NewError("zero row ID"); +//} + +///*static*/ void +// morkRowSpace::ExpectAutoIdNeverError(morkEnv* ev) +//{ +//} + +mork_num morkRowSpace::CutAllRows(morkEnv* ev, morkPool* ioPool) { + if (this->IsRowSpaceClean()) this->MaybeDirtyStoreAndSpace(); + +#ifdef MORK_ENABLE_ZONE_ARENAS + MORK_USED_2(ev, ioPool); + return 0; +#else /*MORK_ENABLE_ZONE_ARENAS*/ + mork_num outSlots = mRowSpace_Rows.MapFill(); + morkZone* zone = &mSpace_Store->mStore_Zone; + morkRow* r = 0; // old key row in the map + mork_change* c = 0; + +# ifdef MORK_ENABLE_PROBE_MAPS + morkRowProbeMapIter i(ev, &mRowSpace_Rows); +# else /*MORK_ENABLE_PROBE_MAPS*/ + morkRowMapIter i(ev, &mRowSpace_Rows); +# endif /*MORK_ENABLE_PROBE_MAPS*/ + + for (c = i.FirstRow(ev, &r); c && ev->Good(); c = i.NextRow(ev, &r)) { + if (r) { + if (r->IsRow()) { + if (r->mRow_Object) { + morkRowObject::SlotWeakRowObject((morkRowObject*)0, ev, + &r->mRow_Object); + } + ioPool->ZapRow(ev, r, zone); + } else + r->NonRowTypeWarning(ev); + } else + ev->NilPointerError(); + +# ifdef MORK_ENABLE_PROBE_MAPS + // cut nothing from the map +# else /*MORK_ENABLE_PROBE_MAPS*/ + i.CutHereRow(ev, /*key*/ (morkRow**)0); +# endif /*MORK_ENABLE_PROBE_MAPS*/ + } + + return outSlots; +#endif /*MORK_ENABLE_ZONE_ARENAS*/ +} + +morkTable* morkRowSpace::FindTableByKind(morkEnv* ev, mork_kind inTableKind) { + if (inTableKind) { +#ifdef MORK_BEAD_OVER_NODE_MAPS + + morkTableMapIter i(ev, &mRowSpace_Tables); + morkTable* table = i.FirstTable(ev); + for (; table && ev->Good(); table = i.NextTable(ev)) +#else /*MORK_BEAD_OVER_NODE_MAPS*/ + mork_tid* key = 0; // nil pointer to suppress key access + morkTable* table = 0; // old table in the map + + mork_change* c = 0; + morkTableMapIter i(ev, &mRowSpace_Tables); + for (c = i.FirstTable(ev, key, &table); c && ev->Good(); + c = i.NextTable(ev, key, &table)) +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ + { + if (table->mTable_Kind == inTableKind) return table; + } + } else + this->ZeroKindError(ev); + + return (morkTable*)0; +} + +morkTable* morkRowSpace::NewTableWithTid( + morkEnv* ev, mork_tid inTid, mork_kind inTableKind, + const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying +{ + morkTable* outTable = 0; + morkStore* store = mSpace_Store; + + if (inTableKind && store) { + mdb_bool mustBeUnique = morkBool_kFalse; + nsIMdbHeap* heap = store->mPort_Heap; + morkTable* table = new (*heap, ev) + morkTable(ev, morkUsage::kHeap, heap, store, heap, this, + inOptionalMetaRowOid, inTid, inTableKind, mustBeUnique); + if (table) { + if (mRowSpace_Tables.AddTable(ev, table)) { + outTable = table; + if (mRowSpace_NextTableId <= inTid) mRowSpace_NextTableId = inTid + 1; + } + + if (this->IsRowSpaceClean() && store->mStore_CanDirty) + this->MaybeDirtyStoreAndSpace(); // morkTable does already + } + } else if (store) + this->ZeroKindError(ev); + else + this->NilSpaceStoreError(ev); + + return outTable; +} + +morkTable* morkRowSpace::NewTable( + morkEnv* ev, mork_kind inTableKind, mdb_bool inMustBeUnique, + const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying +{ + morkTable* outTable = 0; + morkStore* store = mSpace_Store; + + if (inTableKind && store) { + if (inMustBeUnique) // need to look for existing table first? + outTable = this->FindTableByKind(ev, inTableKind); + + if (!outTable && ev->Good()) { + mork_tid id = this->MakeNewTableId(ev); + if (id) { + nsIMdbHeap* heap = mSpace_Store->mPort_Heap; + morkTable* table = new (*heap, ev) + morkTable(ev, morkUsage::kHeap, heap, mSpace_Store, heap, this, + inOptionalMetaRowOid, id, inTableKind, inMustBeUnique); + if (table) { + if (mRowSpace_Tables.AddTable(ev, table)) + outTable = table; + else + table->Release(); + + if (this->IsRowSpaceClean() && store->mStore_CanDirty) + this->MaybeDirtyStoreAndSpace(); // morkTable does already + } + } + } + } else if (store) + this->ZeroKindError(ev); + else + this->NilSpaceStoreError(ev); + + return outTable; +} + +mork_tid morkRowSpace::MakeNewTableId(morkEnv* ev) { + mork_tid outTid = 0; + mork_tid id = mRowSpace_NextTableId; + mork_num count = 9; // try up to eight times + + while (!outTid && --count) // still trying to find an unused table ID? + { + if (!mRowSpace_Tables.GetTable(ev, id)) + outTid = id; + else { + MORK_ASSERT(morkBool_kFalse); // alert developer about ID problems + ++id; + } + } + + mRowSpace_NextTableId = id + 1; + return outTid; +} + +#define MAX_AUTO_ID (morkRow_kMinusOneRid - 2) +mork_rid morkRowSpace::MakeNewRowId(morkEnv* ev) { + mork_rid outRid = 0; + mork_rid id = mRowSpace_NextRowId; + mork_num count = 9; // try up to eight times + mdbOid oid; + oid.mOid_Scope = this->SpaceScope(); + + // still trying to find an unused row ID? + while (!outRid && --count && id <= MAX_AUTO_ID) { + oid.mOid_Id = id; + if (!mRowSpace_Rows.GetOid(ev, &oid)) + outRid = id; + else { + MORK_ASSERT(morkBool_kFalse); // alert developer about ID problems + ++id; + } + } + + if (id < MAX_AUTO_ID) mRowSpace_NextRowId = id + 1; + return outRid; +} + +morkAtomRowMap* morkRowSpace::make_index(morkEnv* ev, mork_column inCol) { + morkAtomRowMap* outMap = 0; + nsIMdbHeap* heap = mRowSpace_SlotHeap; + if (heap) // have expected heap for allocations? + { + morkAtomRowMap* map = + new (*heap, ev) morkAtomRowMap(ev, morkUsage::kHeap, heap, heap, inCol); + + if (map) // able to create new map index? + { + if (ev->Good()) // no errors during construction? + { +#ifdef MORK_ENABLE_PROBE_MAPS + morkRowProbeMapIter i(ev, &mRowSpace_Rows); +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkRowMapIter i(ev, &mRowSpace_Rows); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + mork_change* c = 0; + morkRow* row = 0; + mork_aid aidKey = 0; + + for (c = i.FirstRow(ev, &row); c && ev->Good(); + c = i.NextRow(ev, &row)) // another row in space? + { + aidKey = row->GetCellAtomAid(ev, inCol); + if (aidKey) // row has indexed attribute? + map->AddAid(ev, aidKey, row); // include in map + } + } + if (ev->Good()) // no errors constructing index? + outMap = map; // return from function + else + map->CutStrongRef(ev); // discard map on error + } + } else + ev->NilPointerError(); + + return outMap; +} + +morkAtomRowMap* morkRowSpace::ForceMap(morkEnv* ev, mork_column inCol) { + morkAtomRowMap* outMap = this->FindMap(ev, inCol); + + if (!outMap && ev->Good()) // no such existing index? + { + if (mRowSpace_IndexCount < morkRowSpace_kMaxIndexCount) { + morkAtomRowMap* map = this->make_index(ev, inCol); + if (map) // created a new index for col? + { + mork_count wrap = 0; // count times wrap-around occurs + morkAtomRowMap** slot = mRowSpace_IndexCache; // table + morkAtomRowMap** end = slot + morkRowSpace_kPrimeCacheSize; + slot += (inCol % morkRowSpace_kPrimeCacheSize); // hash + while (*slot) // empty slot not yet found? + { + if (++slot >= end) // wrap around? + { + slot = mRowSpace_IndexCache; // back to table start + if (++wrap > 1) // wrapped more than once? + { + ev->NewError("no free cache slots"); // disaster + break; // end while loop + } + } + } + if (ev->Good()) // everything went just fine? + { + ++mRowSpace_IndexCount; // note another new map + *slot = map; // install map in the hash table + outMap = map; // return the new map from function + } else + map->CutStrongRef(ev); // discard map on error + } + } else + ev->NewError("too many indexes"); // why so many indexes? + } + return outMap; +} + +morkAtomRowMap* morkRowSpace::FindMap(morkEnv* ev, mork_column inCol) { + if (mRowSpace_IndexCount && ev->Good()) { + mork_count wrap = 0; // count times wrap-around occurs + morkAtomRowMap** slot = mRowSpace_IndexCache; // table + morkAtomRowMap** end = slot + morkRowSpace_kPrimeCacheSize; + slot += (inCol % morkRowSpace_kPrimeCacheSize); // hash + morkAtomRowMap* map = *slot; + while (map) // another used slot to examine? + { + if (inCol == map->mAtomRowMap_IndexColumn) // found col? + return map; + if (++slot >= end) // wrap around? + { + slot = mRowSpace_IndexCache; + if (++wrap > 1) // wrapped more than once? + return (morkAtomRowMap*)0; // stop searching + } + map = *slot; + } + } + return (morkAtomRowMap*)0; +} + +morkRow* morkRowSpace::FindRow(morkEnv* ev, mork_column inCol, + const mdbYarn* inYarn) { + morkRow* outRow = 0; + + // if yarn hasn't been atomized, there can't be a corresponding row, + // so pass in false to not create the row - should help history bloat + morkAtom* atom = mSpace_Store->YarnToAtom(ev, inYarn, false); + if (atom) // have or created an atom corresponding to input yarn? + { + mork_aid atomAid = atom->GetBookAtomAid(); + if (atomAid) // atom has an identity for use in hash table? + { + morkAtomRowMap* map = this->ForceMap(ev, inCol); + if (map) // able to find or create index for col? + { + outRow = map->GetAid(ev, atomAid); // search for row + } + } + } + + return outRow; +} + +morkRow* morkRowSpace::NewRowWithOid(morkEnv* ev, const mdbOid* inOid) { + morkRow* outRow = mRowSpace_Rows.GetOid(ev, inOid); + MORK_ASSERT(outRow == 0); + if (!outRow && ev->Good()) { + morkStore* store = mSpace_Store; + if (store) { + morkPool* pool = this->GetSpaceStorePool(); + morkRow* row = pool->NewRow(ev, &store->mStore_Zone); + if (row) { + row->InitRow(ev, inOid, this, /*length*/ 0, pool); + + if (ev->Good() && mRowSpace_Rows.AddRow(ev, row)) { + outRow = row; + mork_rid rid = inOid->mOid_Id; + if (mRowSpace_NextRowId <= rid) mRowSpace_NextRowId = rid + 1; + } else + pool->ZapRow(ev, row, &store->mStore_Zone); + + if (this->IsRowSpaceClean() && store->mStore_CanDirty) + this->MaybeDirtyStoreAndSpace(); // InitRow() does already + } + } else + this->NilSpaceStoreError(ev); + } + return outRow; +} + +morkRow* morkRowSpace::NewRow(morkEnv* ev) { + morkRow* outRow = 0; + if (ev->Good()) { + mork_rid id = this->MakeNewRowId(ev); + if (id) { + morkStore* store = mSpace_Store; + if (store) { + mdbOid oid; + oid.mOid_Scope = this->SpaceScope(); + oid.mOid_Id = id; + morkPool* pool = this->GetSpaceStorePool(); + morkRow* row = pool->NewRow(ev, &store->mStore_Zone); + if (row) { + row->InitRow(ev, &oid, this, /*length*/ 0, pool); + + if (ev->Good() && mRowSpace_Rows.AddRow(ev, row)) + outRow = row; + else + pool->ZapRow(ev, row, &store->mStore_Zone); + + if (this->IsRowSpaceClean() && store->mStore_CanDirty) + this->MaybeDirtyStoreAndSpace(); // InitRow() does already + } + } else + this->NilSpaceStoreError(ev); + } + } + return outRow; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +morkRowSpaceMap::~morkRowSpaceMap() {} + +morkRowSpaceMap::morkRowSpaceMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) + : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap) { + if (ev->Good()) mNode_Derived = morkDerived_kRowSpaceMap; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkRowSpace.h b/comm/mailnews/db/mork/morkRowSpace.h new file mode 100644 index 0000000000..81386576a1 --- /dev/null +++ b/comm/mailnews/db/mork/morkRowSpace.h @@ -0,0 +1,243 @@ +/* -*- 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 _MORKROWSPACE_ +#define _MORKROWSPACE_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKSPACE_ +# include "morkSpace.h" +#endif + +#ifndef _MORKNODEMAP_ +# include "morkNodeMap.h" +#endif + +#ifndef _MORKROWMAP_ +# include "morkRowMap.h" +#endif + +#ifndef _MORKTABLE_ +# include "morkTable.h" +#endif + +#ifndef _MORKARRAY_ +# include "morkArray.h" +#endif + +#ifndef _MORKDEQUE_ +# include "morkDeque.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kRowSpace /*i*/ 0x7253 /* ascii 'rS' */ + +#define morkRowSpace_kStartRowMapSlotCount 11 + +#define morkRowSpace_kMaxIndexCount 8 /* no more indexes than this */ +#define morkRowSpace_kPrimeCacheSize 17 /* should be prime number */ + +class morkAtomRowMap; + +/*| morkRowSpace: +|*/ +class morkRowSpace : public morkSpace { // + + // public: // slots inherited from morkSpace (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // morkStore* mSpace_Store; // weak ref to containing store + + // mork_bool mSpace_DoAutoIDs; // whether db should assign member IDs + // mork_bool mSpace_HaveDoneAutoIDs; // whether actually auto assigned IDs + // mork_u1 mSpace_Pad[ 2 ]; // pad to u4 alignment + + public: // state is public because the entire Mork system is private + nsIMdbHeap* mRowSpace_SlotHeap; + +#ifdef MORK_ENABLE_PROBE_MAPS + morkRowProbeMap mRowSpace_Rows; // hash table of morkRow instances +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkRowMap mRowSpace_Rows; // hash table of morkRow instances +#endif /*MORK_ENABLE_PROBE_MAPS*/ + morkTableMap mRowSpace_Tables; // all the tables in this row scope + + mork_tid mRowSpace_NextTableId; // for auto-assigning table IDs + mork_rid mRowSpace_NextRowId; // for auto-assigning row IDs + + mork_count mRowSpace_IndexCount; // if nonzero, row indexes exist + + // every nonzero slot in IndexCache is a strong ref to a morkAtomRowMap: + morkAtomRowMap* mRowSpace_IndexCache[morkRowSpace_kPrimeCacheSize]; + + morkDeque mRowSpace_TablesByPriority[morkPriority_kCount]; + + public: // more specific dirty methods for row space: + void SetRowSpaceDirty() { this->SetNodeDirty(); } + void SetRowSpaceClean() { this->SetNodeClean(); } + + mork_bool IsRowSpaceClean() const { return this->IsNodeClean(); } + mork_bool IsRowSpaceDirty() const { return this->IsNodeDirty(); } + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseRowSpace() only if open + virtual ~morkRowSpace(); // assert that CloseRowSpace() executed earlier + + public: // morkMap construction & destruction + morkRowSpace(morkEnv* ev, const morkUsage& inUsage, mork_scope inScope, + morkStore* ioStore, nsIMdbHeap* ioNodeHeap, + nsIMdbHeap* ioSlotHeap); + void CloseRowSpace(morkEnv* ev); // called by CloseMorkNode(); + + public: // dynamic type identification + mork_bool IsRowSpace() const { + return IsNode() && mNode_Derived == morkDerived_kRowSpace; + } + // } ===== end morkNode methods ===== + + public: // typing + static void NonRowSpaceTypeError(morkEnv* ev); + static void ZeroScopeError(morkEnv* ev); + static void ZeroKindError(morkEnv* ev); + static void ZeroTidError(morkEnv* ev); + static void MinusOneRidError(morkEnv* ev); + + // static void ExpectAutoIdOnlyError(morkEnv* ev); + // static void ExpectAutoIdNeverError(morkEnv* ev); + + public: // other space methods + mork_num CutAllRows(morkEnv* ev, morkPool* ioPool); + // CutAllRows() puts all rows and cells back into the pool. + + morkTable* NewTable(morkEnv* ev, mork_kind inTableKind, + mdb_bool inMustBeUnique, + const mdbOid* inOptionalMetaRowOid); + + morkTable* NewTableWithTid(morkEnv* ev, mork_tid inTid, mork_kind inTableKind, + const mdbOid* inOptionalMetaRowOid); + + morkTable* FindTableByKind(morkEnv* ev, mork_kind inTableKind); + morkTable* FindTableByTid(morkEnv* ev, mork_tid inTid) { + return mRowSpace_Tables.GetTable(ev, inTid); + } + + mork_tid MakeNewTableId(morkEnv* ev); + mork_rid MakeNewRowId(morkEnv* ev); + + // morkRow* FindRowByRid(morkEnv* ev, mork_rid inRid) + // { return (morkRow*) mRowSpace_Rows.GetRow(ev, inRid); } + + morkRow* NewRowWithOid(morkEnv* ev, const mdbOid* inOid); + morkRow* NewRow(morkEnv* ev); + + morkRow* FindRow(morkEnv* ev, mork_column inColumn, const mdbYarn* inYarn); + + morkAtomRowMap* ForceMap(morkEnv* ev, mork_column inColumn); + morkAtomRowMap* FindMap(morkEnv* ev, mork_column inColumn); + + protected: // internal utilities + morkAtomRowMap* make_index(morkEnv* ev, mork_column inColumn); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakRowSpace(morkRowSpace* me, morkEnv* ev, + morkRowSpace** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongRowSpace(morkRowSpace* me, morkEnv* ev, + morkRowSpace** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kRowSpaceMap /*i*/ 0x725A /* ascii 'rZ' */ + +/*| morkRowSpaceMap: maps mork_scope -> morkRowSpace +|*/ +class morkRowSpaceMap : public morkNodeMap { // for mapping tokens to tables + + public: + virtual ~morkRowSpaceMap(); + morkRowSpaceMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + + public: // other map methods + mork_bool AddRowSpace(morkEnv* ev, morkRowSpace* ioRowSpace) { + return this->AddNode(ev, ioRowSpace->SpaceScope(), ioRowSpace); + } + // the AddRowSpace() boolean return equals ev->Good(). + + mork_bool CutRowSpace(morkEnv* ev, mork_scope inScope) { + return this->CutNode(ev, inScope); + } + // The CutRowSpace() boolean return indicates whether removal happened. + + morkRowSpace* GetRowSpace(morkEnv* ev, mork_scope inScope) { + return (morkRowSpace*)this->GetNode(ev, inScope); + } + // Note the returned space does NOT have an increase in refcount for this. + + mork_num CutAllRowSpaces(morkEnv* ev) { return this->CutAllNodes(ev); } + // CutAllRowSpaces() releases all the referenced table values. +}; + +class morkRowSpaceMapIter : public morkMapIter { // typesafe wrapper class + + public: + morkRowSpaceMapIter(morkEnv* ev, morkRowSpaceMap* ioMap) + : morkMapIter(ev, ioMap) {} + + morkRowSpaceMapIter() : morkMapIter() {} + void InitRowSpaceMapIter(morkEnv* ev, morkRowSpaceMap* ioMap) { + this->InitMapIter(ev, ioMap); + } + + mork_change* FirstRowSpace(morkEnv* ev, mork_scope* outScope, + morkRowSpace** outRowSpace) { + return this->First(ev, outScope, outRowSpace); + } + + mork_change* NextRowSpace(morkEnv* ev, mork_scope* outScope, + morkRowSpace** outRowSpace) { + return this->Next(ev, outScope, outRowSpace); + } + + mork_change* HereRowSpace(morkEnv* ev, mork_scope* outScope, + morkRowSpace** outRowSpace) { + return this->Here(ev, outScope, outRowSpace); + } + + mork_change* CutHereRowSpace(morkEnv* ev, mork_scope* outScope, + morkRowSpace** outRowSpace) { + return this->CutHere(ev, outScope, outRowSpace); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKROWSPACE_ */ diff --git a/comm/mailnews/db/mork/morkSearchRowCursor.cpp b/comm/mailnews/db/mork/morkSearchRowCursor.cpp new file mode 100644 index 0000000000..f7cc4c56d6 --- /dev/null +++ b/comm/mailnews/db/mork/morkSearchRowCursor.cpp @@ -0,0 +1,153 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKCURSOR_ +# include "morkCursor.h" +#endif + +#ifndef _MORKSEARCHROWCURSOR_ +# include "morkSearchRowCursor.h" +#endif + +#ifndef _MORKUNIQROWCURSOR_ +# include "morkUniqRowCursor.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +#ifndef _MORKTABLE_ +# include "morkTable.h" +#endif + +#ifndef _MORKROW_ +# include "morkRow.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkSearchRowCursor::CloseMorkNode( + morkEnv* ev) // CloseSearchRowCursor() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseSearchRowCursor(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkSearchRowCursor::~morkSearchRowCursor() // CloseSearchRowCursor() executed + // earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkSearchRowCursor::morkSearchRowCursor(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkTable* ioTable, + mork_pos inRowPos) + : morkTableRowCursor(ev, inUsage, ioHeap, ioTable, inRowPos) +// , mSortingRowCursor_Sorting( 0 ) +{ + if (ev->Good()) { + if (ioTable) { + // morkSorting::SlotWeakSorting(ioSorting, ev, + // &mSortingRowCursor_Sorting); + if (ev->Good()) { + // mNode_Derived = morkDerived_kTableRowCursor; + // mNode_Derived must stay equal to kTableRowCursor + } + } else + ev->NilPointerError(); + } +} + +/*public non-poly*/ void morkSearchRowCursor::CloseSearchRowCursor( + morkEnv* ev) { + if (this->IsNode()) { + // morkSorting::SlotWeakSorting((morkSorting*) 0, ev, + // &mSortingRowCursor_Sorting); + this->CloseTableRowCursor(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void morkSearchRowCursor::NonSearchRowCursorTypeError(morkEnv* ev) { + ev->NewError("non morkSearchRowCursor"); +} + +morkUniqRowCursor* morkSearchRowCursor::MakeUniqCursor(morkEnv* ev) { + morkUniqRowCursor* outCursor = 0; + + return outCursor; +} + +#if 0 +orkinTableRowCursor* +morkSearchRowCursor::AcquireUniqueRowCursorHandle(morkEnv* ev) +{ + orkinTableRowCursor* outCursor = 0; + + morkUniqRowCursor* uniqCursor = this->MakeUniqCursor(ev); + if ( uniqCursor ) + { + outCursor = uniqCursor->AcquireTableRowCursorHandle(ev); + uniqCursor->CutStrongRef(ev); + } + return outCursor; +} +#endif +mork_bool morkSearchRowCursor::CanHaveDupRowMembers(morkEnv* ev) { + return morkBool_kTrue; // true is correct +} + +mork_count morkSearchRowCursor::GetMemberCount(morkEnv* ev) { + morkTable* table = mTableRowCursor_Table; + if (table) + return table->mTable_RowArray.mArray_Fill; + else + return 0; +} + +morkRow* morkSearchRowCursor::NextRow(morkEnv* ev, mdbOid* outOid, + mdb_pos* outPos) { + morkRow* outRow = 0; + mork_pos pos = -1; + + morkTable* table = mTableRowCursor_Table; + if (table) { + } else + ev->NilPointerError(); + + *outPos = pos; + return outRow; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkSearchRowCursor.h b/comm/mailnews/db/mork/morkSearchRowCursor.h new file mode 100644 index 0000000000..c267f35bf6 --- /dev/null +++ b/comm/mailnews/db/mork/morkSearchRowCursor.h @@ -0,0 +1,100 @@ +/* -*- 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 _MORKSEARCHROWCURSOR_ +#define _MORKSEARCHROWCURSOR_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKCURSOR_ +# include "morkCursor.h" +#endif + +#ifndef _MORKTABLEROWCURSOR_ +# include "morkTableRowCursor.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class morkUniqRowCursor; +class orkinTableRowCursor; +// #define morkDerived_kSearchRowCursor /*i*/ 0x7352 /* ascii 'sR' */ + +class morkSearchRowCursor : public morkTableRowCursor { // row iterator + + // public: // slots inherited from morkObject (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 + + // morkFactory* mObject_Factory; // weak ref to suite factory + + // mork_seed mCursor_Seed; + // mork_pos mCursor_Pos; + // mork_bool mCursor_DoFailOnSeedOutOfSync; + // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment + + // morkTable* mTableRowCursor_Table; // weak ref to table + + // { ===== begin morkNode interface ===== + public: + virtual void CloseMorkNode(morkEnv* ev); // CloseSearchRowCursor() + virtual ~morkSearchRowCursor(); // assert that close executed earlier + + public: // morkSearchRowCursor construction & destruction + morkSearchRowCursor(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkTable* ioTable, mork_pos inRowPos); + void CloseSearchRowCursor(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkSearchRowCursor(const morkSearchRowCursor& other); + morkSearchRowCursor& operator=(const morkSearchRowCursor& other); + + // } ===== end morkNode methods ===== + + public: // typing + static void NonSearchRowCursorTypeError(morkEnv* ev); + + public: // uniquify + morkUniqRowCursor* MakeUniqCursor(morkEnv* ev); + + public: // other search row cursor methods + virtual mork_bool CanHaveDupRowMembers(morkEnv* ev); + virtual mork_count GetMemberCount(morkEnv* ev); + +#if 0 + virtual orkinTableRowCursor* AcquireUniqueRowCursorHandle(morkEnv* ev); +#endif + + // virtual mdb_pos NextRowOid(morkEnv* ev, mdbOid* outOid); + virtual morkRow* NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakSearchRowCursor(morkSearchRowCursor* me, morkEnv* ev, + morkSearchRowCursor** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongSearchRowCursor(morkSearchRowCursor* me, morkEnv* ev, + morkSearchRowCursor** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKSEARCHROWCURSOR_ */ diff --git a/comm/mailnews/db/mork/morkSink.cpp b/comm/mailnews/db/mork/morkSink.cpp new file mode 100644 index 0000000000..daf1bc1b9c --- /dev/null +++ b/comm/mailnews/db/mork/morkSink.cpp @@ -0,0 +1,247 @@ +/* -*- 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 _MORKSINK_ +# include "morkSink.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKBLOB_ +# include "morkBlob.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*virtual*/ morkSink::~morkSink() { + mSink_At = 0; + mSink_End = 0; +} + +/*virtual*/ void morkSpool::FlushSink( + morkEnv* ev) // sync mSpool_Coil->mBuf_Fill +{ + morkCoil* coil = mSpool_Coil; + if (coil) { + mork_u1* body = (mork_u1*)coil->mBuf_Body; + if (body) { + mork_u1* at = mSink_At; + mork_u1* end = mSink_End; + if (at >= body && at <= end) // expected cursor order? + { + mork_fill fill = (mork_fill)(at - body); // current content size + if (fill <= coil->mBlob_Size) + coil->mBuf_Fill = fill; + else { + coil->BlobFillOverSizeError(ev); + coil->mBuf_Fill = coil->mBlob_Size; // make it safe + } + } else + this->BadSpoolCursorOrderError(ev); + } else + coil->NilBufBodyError(ev); + } else + this->NilSpoolCoilError(ev); +} + +/*virtual*/ void morkSpool::SpillPutc(morkEnv* ev, + int c) // grow coil and write byte +{ + morkCoil* coil = mSpool_Coil; + if (coil) { + mork_u1* body = (mork_u1*)coil->mBuf_Body; + if (body) { + mork_u1* at = mSink_At; + mork_u1* end = mSink_End; + if (at >= body && at <= end) // expected cursor order? + { + mork_size size = coil->mBlob_Size; + mork_fill fill = (mork_fill)(at - body); // current content size + if (fill <= size) // less content than medium size? + { + coil->mBuf_Fill = fill; + if (at >= end) // need to grow the coil? + { + if (size > 2048) // grow slower over 2K? + size += 512; + else { + mork_size growth = (size * 4) / 3; // grow by 33% + if (growth < 64) // grow faster under (64 * 3)? + growth = 64; + size += growth; + } + if (coil->GrowCoil(ev, size)) // made coil bigger? + { + body = (mork_u1*)coil->mBuf_Body; + if (body) // have a coil body? + { + mSink_At = at = body + fill; + mSink_End = end = body + coil->mBlob_Size; + } else + coil->NilBufBodyError(ev); + } + } + if (ev->Good()) // seem ready to write byte c? + { + if (at < end) // morkSink::Putc() would succeed? + { + *at++ = (mork_u1)c; + mSink_At = at; + coil->mBuf_Fill = fill + 1; + } else + this->BadSpoolCursorOrderError(ev); + } + } else // fill exceeds size + { + coil->BlobFillOverSizeError(ev); + coil->mBuf_Fill = coil->mBlob_Size; // make it safe + } + } else + this->BadSpoolCursorOrderError(ev); + } else + coil->NilBufBodyError(ev); + } else + this->NilSpoolCoilError(ev); +} + +// ````` ````` ````` ````` ````` ````` ````` ````` +// public: // public non-poly morkSink methods + +/*virtual*/ +morkSpool::~morkSpool() +// Zero all slots to show this sink is disabled, but destroy no memory. +// Note it is typically unnecessary to flush this coil sink, since all +// content is written directly to the coil without any buffering. +{ + mSink_At = 0; + mSink_End = 0; + mSpool_Coil = 0; +} + +morkSpool::morkSpool(morkEnv* ev, morkCoil* ioCoil) + // After installing the coil, calls Seek(ev, 0) to prepare for writing. + : morkSink(), mSpool_Coil(0) { + mSink_At = 0; // set correctly later in Seek() + mSink_End = 0; // set correctly later in Seek() + + if (ev->Good()) { + if (ioCoil) { + mSpool_Coil = ioCoil; + this->Seek(ev, /*pos*/ 0); + } else + ev->NilPointerError(); + } +} + +// ----- All boolean return values below are equal to ev->Good(): ----- + +/*static*/ void morkSpool::BadSpoolCursorOrderError(morkEnv* ev) { + ev->NewError("bad morkSpool cursor order"); +} + +/*static*/ void morkSpool::NilSpoolCoilError(morkEnv* ev) { + ev->NewError("nil mSpool_Coil"); +} + +mork_bool morkSpool::Seek(morkEnv* ev, mork_pos inPos) +// Changed the current write position in coil's buffer to inPos. +// For example, to start writing the coil from scratch, use inPos==0. +{ + morkCoil* coil = mSpool_Coil; + if (coil) { + mork_size minSize = (mork_size)(inPos + 64); + + if (coil->mBlob_Size < minSize) coil->GrowCoil(ev, minSize); + + if (ev->Good()) { + coil->mBuf_Fill = (mork_fill)inPos; + mork_u1* body = (mork_u1*)coil->mBuf_Body; + if (body) { + mSink_At = body + inPos; + mSink_End = body + coil->mBlob_Size; + } else + coil->NilBufBodyError(ev); + } + } else + this->NilSpoolCoilError(ev); + + return ev->Good(); +} + +mork_bool morkSpool::Write(morkEnv* ev, const void* inBuf, mork_size inSize) +// write inSize bytes of inBuf to current position inside coil's buffer +{ + // This method is conceptually very similar to morkStream::Write(), + // and this code was written while looking at that method for clues. + + morkCoil* coil = mSpool_Coil; + if (coil) { + mork_u1* body = (mork_u1*)coil->mBuf_Body; + if (body) { + if (inBuf && inSize) // anything to write? + { + mork_u1* at = mSink_At; + mork_u1* end = mSink_End; + if (at >= body && at <= end) // expected cursor order? + { + // note coil->mBuf_Fill can be stale after morkSink::Putc(): + mork_pos fill = at - body; // current content size + mork_num space = (mork_num)(end - at); // space left in body + if (space < inSize) // not enough to hold write? + { + mork_size minGrowth = space + 16; + mork_size minSize = coil->mBlob_Size + minGrowth; + if (coil->GrowCoil(ev, minSize)) { + body = (mork_u1*)coil->mBuf_Body; + if (body) { + mSink_At = at = body + fill; + mSink_End = end = body + coil->mBlob_Size; + space = (mork_num)(end - at); // space left in body + } else + coil->NilBufBodyError(ev); + } + } + if (ev->Good()) { + if (space >= inSize) // enough room to hold write? + { + MORK_MEMCPY(at, inBuf, inSize); // into body + mSink_At = at + inSize; // advance past written bytes + coil->mBuf_Fill = fill + inSize; // "flush" to fix fill + } else + ev->NewError("insufficient morkSpool space"); + } + } else + this->BadSpoolCursorOrderError(ev); + } + } else + coil->NilBufBodyError(ev); + } else + this->NilSpoolCoilError(ev); + + return ev->Good(); +} + +mork_bool morkSpool::PutString(morkEnv* ev, const char* inString) +// call Write() with inBuf=inString and inSize=strlen(inString), +// unless inString is null, in which case we then do nothing at all. +{ + if (inString) { + mork_size size = strlen(inString); + this->Write(ev, inString, size); + } + return ev->Good(); +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkSink.h b/comm/mailnews/db/mork/morkSink.h new file mode 100644 index 0000000000..9803b5c6da --- /dev/null +++ b/comm/mailnews/db/mork/morkSink.h @@ -0,0 +1,155 @@ +/* -*- 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 _MORKSINK_ +#define _MORKSINK_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKBLOB_ +# include "morkBlob.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*| morkSink is intended to be a very cheap buffered i/o sink which +**| writes to bufs and other strings a single byte at a time. The +**| basic idea is that writing a single byte has a very cheap average +**| cost, because a polymophic function call need only occur when the +**| space between At and End is exhausted. The rest of the time a +**| very cheap inline method will write a byte, and then bump a pointer. +**| +**|| At: the current position in some sequence of bytes at which to +**| write the next byte put into the sink. Presumably At points into +**| the private storage of some space which is not yet filled (except +**| when At reaches End, and the overflow must then spill). Note both +**| At and End are zeroed in the destructor to help show that a sink +**| is no longer usable; this is safe because At==End causes the case +**| where SpillPutc() is called to handled an exhausted buffer space. +**| +**|| End: an address one byte past the last byte which can be written +**| without needing to make a buffer larger than previously. When At +**| and End are equal, this means there is no space to write a byte, +**| and that some underlying buffer space must be grown before another +**| byte can be written. Note At must always be less than or equal to +**| End, and otherwise an important invariant has failed severely. +**| +**|| Buf: this original class slot has been commented out in the new +**| and more abstract version of this sink class, but the general idea +**| behind this slot should be explained to help design subclasses. +**| Each subclass should provide space into which At and End can point, +**| where End is beyond the last writable byte, and At is less than or +**| equal to this point inside the same buffer. With some kinds of +**| medium, such as writing to an instance of morkBlob, it is feasible +**| to point directly into the final resting place for all the content +**| written to the medium. Other mediums such as files, which write +**| only through function calls, will typically need a local buffer +**| to efficiently accumulate many bytes between such function calls. +**| +**|| FlushSink: this flush method should move any buffered content to +**| its final destination. For example, for buffered writes to a +**| string medium, where string methods are function calls and not just +**| inline macros, it is faster to accumulate many bytes in a small +**| local buffer and then move these en masse later in a single call. +**| +**|| SpillPutc: when At is greater than or equal to End, this means an +**| underlying buffer has become full, so the buffer must be flushed +**| before a new byte can be written. The intention is that SpillPutc() +**| will be equivalent to calling FlushSink() followed by another call +**| to Putc(), where the flush is expected to make At less then End once +**| again. Except that FlushSink() need not make the underlying buffer +**| any larger, and SpillPutc() typically must make room for more bytes. +**| Note subclasses might want to guard against the case that both At +**| and End are null, which happens when a sink is destroyed, which sets +**| both these pointers to null as an indication the sink is disabled. +|*/ +class morkSink { + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // public sink virtual methods + virtual void FlushSink(morkEnv* ev) = 0; + virtual void SpillPutc(morkEnv* ev, int c) = 0; + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // member variables + mork_u1* mSink_At; // pointer into mSink_Buf + mork_u1* mSink_End; // one byte past last content byte + + // define morkSink_kBufSize 256 /* small enough to go on stack */ + + // mork_u1 mSink_Buf[ morkSink_kBufSize + 4 ]; + // want plus one for any needed end null byte; use plus 4 for alignment + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // public non-poly morkSink methods + virtual ~morkSink(); // zero both At and End; virtual for subclasses + morkSink() {} // does nothing; subclasses must set At and End suitably + + void Putc(morkEnv* ev, int c) { + if (mSink_At < mSink_End) + *mSink_At++ = (mork_u1)c; + else + this->SpillPutc(ev, c); + } +}; + +/*| morkSpool: an output sink that efficiently writes individual bytes +**| or entire byte sequences to a coil instance, which grows as needed by +**| using the heap instance in the coil to grow the internal buffer. +**| +**|| Note we do not "own" the coil referenced by mSpool_Coil, and +**| the lifetime of the coil is expected to equal or exceed that of this +**| sink by some external means. Typical usage might involve keeping an +**| instance of morkCoil and an instance of morkSpool in the same +**| owning parent object, which uses the spool with the associated coil. +|*/ +class morkSpool : public morkSink { // for buffered i/o to a morkCoil + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // public sink virtual methods + // when morkSink::Putc() moves mSink_At, mSpool_Coil->mBuf_Fill is wrong: + + virtual void FlushSink(morkEnv* ev); // sync mSpool_Coil->mBuf_Fill + virtual void SpillPutc(morkEnv* ev, int c); // grow coil and write byte + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // member variables + morkCoil* mSpool_Coil; // destination medium for written bytes + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // public non-poly morkSink methods + static void BadSpoolCursorOrderError(morkEnv* ev); + static void NilSpoolCoilError(morkEnv* ev); + + virtual ~morkSpool(); + // Zero all slots to show this sink is disabled, but destroy no memory. + // Note it is typically unnecessary to flush this coil sink, since all + // content is written directly to the coil without any buffering. + + morkSpool(morkEnv* ev, morkCoil* ioCoil); + // After installing the coil, calls Seek(ev, 0) to prepare for writing. + + // ----- All boolean return values below are equal to ev->Good(): ----- + + mork_bool Seek(morkEnv* ev, mork_pos inPos); + // Changed the current write position in coil's buffer to inPos. + // For example, to start writing the coil from scratch, use inPos==0. + + mork_bool Write(morkEnv* ev, const void* inBuf, mork_size inSize); + // write inSize bytes of inBuf to current position inside coil's buffer + + mork_bool PutBuf(morkEnv* ev, const morkBuf& inBuffer) { + return this->Write(ev, inBuffer.mBuf_Body, inBuffer.mBuf_Fill); + } + + mork_bool PutString(morkEnv* ev, const char* inString); + // call Write() with inBuf=inString and inSize=strlen(inString), + // unless inString is null, in which case we then do nothing at all. +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKSINK_ */ diff --git a/comm/mailnews/db/mork/morkSpace.cpp b/comm/mailnews/db/mork/morkSpace.cpp new file mode 100644 index 0000000000..d3b1980089 --- /dev/null +++ b/comm/mailnews/db/mork/morkSpace.cpp @@ -0,0 +1,136 @@ +/* -*- 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 _MORKSPACE_ +# include "morkSpace.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkSpace::CloseMorkNode( + morkEnv* ev) // CloseSpace() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseSpace(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkSpace::~morkSpace() // assert CloseSpace() executed earlier +{ + MORK_ASSERT(SpaceScope() == 0); + MORK_ASSERT(mSpace_Store == 0); + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +// morkSpace::morkSpace(morkEnv* ev, const morkUsage& inUsage, +// nsIMdbHeap* ioNodeHeap, const morkMapForm& inForm, +// nsIMdbHeap* ioSlotHeap) +//: morkNode(ev, inUsage, ioNodeHeap) +//, mSpace_Map(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap) +//{ +// ev->StubMethodOnlyError(); +//} + +/*public non-poly*/ +morkSpace::morkSpace(morkEnv* ev, const morkUsage& inUsage, mork_scope inScope, + morkStore* ioStore, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap) + : morkBead(ev, inUsage, ioHeap, inScope), + mSpace_Store(0), + mSpace_DoAutoIDs(morkBool_kFalse), + mSpace_HaveDoneAutoIDs(morkBool_kFalse), + mSpace_CanDirty(morkBool_kFalse) // only when store can be dirtied +{ + if (ev->Good()) { + if (ioStore && ioSlotHeap) { + morkStore::SlotWeakStore(ioStore, ev, &mSpace_Store); + + mSpace_CanDirty = ioStore->mStore_CanDirty; + if (mSpace_CanDirty) // this new space dirties the store? + this->MaybeDirtyStoreAndSpace(); + + if (ev->Good()) mNode_Derived = morkDerived_kSpace; + } else + ev->NilPointerError(); + } +} + +/*public non-poly*/ void morkSpace::CloseSpace( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + morkStore::SlotWeakStore((morkStore*)0, ev, &mSpace_Store); + mBead_Color = 0; // this->CloseBead(); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void morkSpace::NonAsciiSpaceScopeName(morkEnv* ev) { + ev->NewError("SpaceScope() > 0x7F"); +} + +/*static*/ void morkSpace::NilSpaceStoreError(morkEnv* ev) { + ev->NewError("nil mSpace_Store"); +} + +morkPool* morkSpace::GetSpaceStorePool() const { + return &mSpace_Store->mStore_Pool; +} + +mork_bool morkSpace::MaybeDirtyStoreAndSpace() { + morkStore* store = mSpace_Store; + if (store && store->mStore_CanDirty) { + store->SetStoreDirty(); + mSpace_CanDirty = morkBool_kTrue; + } + + if (mSpace_CanDirty) { + this->SetSpaceDirty(); + return morkBool_kTrue; + } + + return morkBool_kFalse; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkSpace.h b/comm/mailnews/db/mork/morkSpace.h new file mode 100644 index 0000000000..baf74ee677 --- /dev/null +++ b/comm/mailnews/db/mork/morkSpace.h @@ -0,0 +1,108 @@ +/* -*- 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 _MORKSPACE_ +#define _MORKSPACE_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKBEAD_ +# include "morkBead.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkSpace_kInitialSpaceSlots /*i*/ 1024 /* default */ +#define morkDerived_kSpace /*i*/ 0x5370 /* ascii 'Sp' */ + +/*| morkSpace: +|*/ +class morkSpace : public morkBead { // + + // public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // mork_color mBead_Color; // ID for this bead + + public: // bead color setter & getter replace obsolete member mTable_Id: + mork_tid SpaceScope() const { return mBead_Color; } + void SetSpaceScope(mork_scope inScope) { mBead_Color = inScope; } + + public: // state is public because the entire Mork system is private + morkStore* mSpace_Store; // weak ref to containing store + + mork_bool mSpace_DoAutoIDs; // whether db should assign member IDs + mork_bool mSpace_HaveDoneAutoIDs; // whether actually auto assigned IDs + mork_bool mSpace_CanDirty; // changes imply the store becomes dirty? + mork_u1 mSpace_Pad; // pad to u4 alignment + + public: // more specific dirty methods for space: + void SetSpaceDirty() { this->SetNodeDirty(); } + void SetSpaceClean() { this->SetNodeClean(); } + + mork_bool IsSpaceClean() const { return this->IsNodeClean(); } + mork_bool IsSpaceDirty() const { return this->IsNodeDirty(); } + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev); // CloseSpace() only if open + virtual ~morkSpace(); // assert that CloseSpace() executed earlier + + public: // morkMap construction & destruction + // morkSpace(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap, + // const morkMapForm& inForm, nsIMdbHeap* ioSlotHeap); + + morkSpace(morkEnv* ev, const morkUsage& inUsage, mork_scope inScope, + morkStore* ioStore, nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioSlotHeap); + void CloseSpace(morkEnv* ev); // called by CloseMorkNode(); + + public: // dynamic type identification + mork_bool IsSpace() const { + return IsNode() && mNode_Derived == morkDerived_kSpace; + } + // } ===== end morkNode methods ===== + + public: // other space methods + mork_bool MaybeDirtyStoreAndSpace(); + + static void NonAsciiSpaceScopeName(morkEnv* ev); + static void NilSpaceStoreError(morkEnv* ev); + + morkPool* GetSpaceStorePool() const; + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakSpace(morkSpace* me, morkEnv* ev, morkSpace** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongSpace(morkSpace* me, morkEnv* ev, morkSpace** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKSPACE_ */ diff --git a/comm/mailnews/db/mork/morkStore.cpp b/comm/mailnews/db/mork/morkStore.cpp new file mode 100644 index 0000000000..9356864c35 --- /dev/null +++ b/comm/mailnews/db/mork/morkStore.cpp @@ -0,0 +1,1981 @@ +/* -*- 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 _MORKBLOB_ +# include "morkBlob.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +#ifndef _MORKFACTORY_ +# include "morkFactory.h" +#endif + +#ifndef _MORKNODEMAP_ +# include "morkNodeMap.h" +#endif + +#ifndef _MORKROW_ +# include "morkRow.h" +#endif + +#ifndef _MORKTHUMB_ +# include "morkThumb.h" +#endif +// #ifndef _MORKFILE_ +// #include "morkFile.h" +// #endif + +#ifndef _MORKBUILDER_ +# include "morkBuilder.h" +#endif + +#ifndef _MORKATOMSPACE_ +# include "morkAtomSpace.h" +#endif + +#ifndef _MORKSTREAM_ +# include "morkStream.h" +#endif + +#ifndef _MORKATOMSPACE_ +# include "morkAtomSpace.h" +#endif + +#ifndef _MORKROWSPACE_ +# include "morkRowSpace.h" +#endif + +#ifndef _MORKPORTTABLECURSOR_ +# include "morkPortTableCursor.h" +#endif + +#ifndef _MORKTABLE_ +# include "morkTable.h" +#endif + +#ifndef _MORKROWMAP_ +# include "morkRowMap.h" +#endif + +#ifndef _MORKPARSER_ +# include "morkParser.h" +#endif + +#include "nsCOMPtr.h" + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkStore::CloseMorkNode( + morkEnv* ev) // ClosePort() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseStore(ev); + this->MarkShut(); + } +} + +/*public non-poly*/ void morkStore::ClosePort( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + morkFactory::SlotWeakFactory((morkFactory*)0, ev, &mPort_Factory); + nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*)0, ev, &mPort_Heap); + this->CloseObject(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +/*public virtual*/ +morkStore::~morkStore() // assert CloseStore() executed earlier +{ + if (IsOpenNode()) CloseMorkNode(mMorkEnv); + MORK_ASSERT(this->IsShutNode()); + MORK_ASSERT(mStore_File == 0); + MORK_ASSERT(mStore_InStream == 0); + MORK_ASSERT(mStore_OutStream == 0); + MORK_ASSERT(mStore_Builder == 0); + MORK_ASSERT(mStore_OidAtomSpace == 0); + MORK_ASSERT(mStore_GroundAtomSpace == 0); + MORK_ASSERT(mStore_GroundColumnSpace == 0); + MORK_ASSERT(mStore_RowSpaces.IsShutNode()); + MORK_ASSERT(mStore_AtomSpaces.IsShutNode()); + MORK_ASSERT(mStore_Pool.IsShutNode()); +} + +/*public non-poly*/ +morkStore::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 + ) + : morkObject(ev, inUsage, ioNodeHeap, morkColor_kNone, (morkHandle*)0), + mPort_Env(ev), + mPort_Factory(0), + mPort_Heap(0), + mStore_OidAtomSpace(0), + mStore_GroundAtomSpace(0), + mStore_GroundColumnSpace(0) + + , + mStore_File(0), + mStore_InStream(0), + mStore_Builder(0), + mStore_OutStream(0) + + , + mStore_RowSpaces(ev, morkUsage::kMember, (nsIMdbHeap*)0, ioPortHeap), + mStore_AtomSpaces(ev, morkUsage::kMember, (nsIMdbHeap*)0, ioPortHeap), + mStore_Zone(ev, morkUsage::kMember, (nsIMdbHeap*)0, ioPortHeap), + mStore_Pool(ev, morkUsage::kMember, (nsIMdbHeap*)0, ioPortHeap) + + , + mStore_CommitGroupIdentity(0) + + , + mStore_FirstCommitGroupPos(0), + mStore_SecondCommitGroupPos(0) + + // disable auto-assignment of atom IDs until someone knows it is okay: + , + mStore_CanAutoAssignAtomIdentity(morkBool_kFalse), + mStore_CanDirty(morkBool_kFalse) // not until the store is open + , + mStore_CanWriteIncremental(morkBool_kTrue) // always with few exceptions +{ + if (ev->Good()) { + if (inFactory && ioPortHeap) { + morkFactory::SlotWeakFactory(inFactory, ev, &mPort_Factory); + nsIMdbHeap_SlotStrongHeap(ioPortHeap, ev, &mPort_Heap); + if (ev->Good()) mNode_Derived = morkDerived_kPort; + } else + ev->NilPointerError(); + } + if (ev->Good()) { + mNode_Derived = morkDerived_kStore; + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkStore, morkObject, nsIMdbStore) + +/*public non-poly*/ void morkStore::CloseStore( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + nsIMdbFile* file = mStore_File; + file->AddRef(); + + morkFactory::SlotWeakFactory((morkFactory*)0, ev, &mPort_Factory); + nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*)0, ev, &mPort_Heap); + morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*)0, ev, + &mStore_OidAtomSpace); + morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*)0, ev, + &mStore_GroundAtomSpace); + morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*)0, ev, + &mStore_GroundColumnSpace); + mStore_RowSpaces.CloseMorkNode(ev); + mStore_AtomSpaces.CloseMorkNode(ev); + morkBuilder::SlotStrongBuilder((morkBuilder*)0, ev, &mStore_Builder); + + nsIMdbFile_SlotStrongFile((nsIMdbFile*)0, ev, &mStore_File); + + file->Release(); + + morkStream::SlotStrongStream((morkStream*)0, ev, &mStore_InStream); + morkStream::SlotStrongStream((morkStream*)0, ev, &mStore_OutStream); + + mStore_Pool.CloseMorkNode(ev); + mStore_Zone.CloseMorkNode(ev); + this->ClosePort(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +mork_bool morkStore::DoPreferLargeOverCompressCommit(morkEnv* ev) +// true when mStore_CanWriteIncremental && store has file large enough +{ + nsIMdbFile* file = mStore_File; + if (file && mStore_CanWriteIncremental) { + mdb_pos fileEof = 0; + file->Eof(ev->AsMdbEnv(), &fileEof); + if (ev->Good() && fileEof > 128) return morkBool_kTrue; + } + return morkBool_kFalse; +} + +mork_percent morkStore::PercentOfStoreWasted(morkEnv* ev) { + mork_percent outPercent = 0; + nsIMdbFile* file = mStore_File; + + if (file) { + mork_pos firstPos = mStore_FirstCommitGroupPos; + mork_pos secondPos = mStore_SecondCommitGroupPos; + if (firstPos || secondPos) { + if (firstPos < 512 && secondPos > firstPos) + firstPos = secondPos; // better approximation of first commit + + mork_pos fileLength = 0; + file->Eof(ev->AsMdbEnv(), &fileLength); // end of file + if (ev->Good() && fileLength > firstPos) { + mork_size groupContent = fileLength - firstPos; + outPercent = (groupContent * 100) / fileLength; + } + } + } else + this->NilStoreFileError(ev); + + return outPercent; +} + +void morkStore::SetStoreAndAllSpacesCanDirty(morkEnv* ev, + mork_bool inCanDirty) { + mStore_CanDirty = inCanDirty; + + mork_change* c = 0; + mork_scope* key = 0; // ignore keys in maps + + if (ev->Good()) { + morkAtomSpaceMapIter asi(ev, &mStore_AtomSpaces); + + morkAtomSpace* atomSpace = 0; // old val node in the map + + for (c = asi.FirstAtomSpace(ev, key, &atomSpace); c && ev->Good(); + c = asi.NextAtomSpace(ev, key, &atomSpace)) { + if (atomSpace) { + if (atomSpace->IsAtomSpace()) + atomSpace->mSpace_CanDirty = inCanDirty; + else + atomSpace->NonAtomSpaceTypeError(ev); + } else + ev->NilPointerError(); + } + } + + if (ev->Good()) { + morkRowSpaceMapIter rsi(ev, &mStore_RowSpaces); + morkRowSpace* rowSpace = 0; // old val node in the map + + for (c = rsi.FirstRowSpace(ev, key, &rowSpace); c && ev->Good(); + c = rsi.NextRowSpace(ev, key, &rowSpace)) { + if (rowSpace) { + if (rowSpace->IsRowSpace()) + rowSpace->mSpace_CanDirty = inCanDirty; + else + rowSpace->NonRowSpaceTypeError(ev); + } + } + } +} + +void morkStore::RenumberAllCollectableContent(morkEnv* ev) { + MORK_USED_1(ev); + // do nothing currently +} + +nsIMdbStore* morkStore::AcquireStoreHandle(morkEnv* ev) { return this; } + +morkFarBookAtom* morkStore::StageAliasAsFarBookAtom(morkEnv* ev, + const morkMid* inMid, + morkAtomSpace* ioSpace, + mork_cscode inForm) { + if (inMid && inMid->mMid_Buf) { + const morkBuf* buf = inMid->mMid_Buf; + mork_size length = buf->mBuf_Fill; + if (length <= morkBookAtom_kMaxBodySize) { + mork_aid dummyAid = 1; + // mStore_BookAtom.InitMaxBookAtom(ev, *buf, + // inForm, ioSpace, dummyAid); + + mStore_FarBookAtom.InitFarBookAtom(ev, *buf, inForm, ioSpace, dummyAid); + return &mStore_FarBookAtom; + } + } else + ev->NilPointerError(); + + return (morkFarBookAtom*)0; +} + +morkFarBookAtom* morkStore::StageYarnAsFarBookAtom(morkEnv* ev, + const mdbYarn* inYarn, + morkAtomSpace* ioSpace) { + if (inYarn && inYarn->mYarn_Buf) { + mork_size length = inYarn->mYarn_Fill; + if (length <= morkBookAtom_kMaxBodySize) { + morkBuf buf(inYarn->mYarn_Buf, length); + mork_aid dummyAid = 1; + // mStore_BookAtom.InitMaxBookAtom(ev, buf, + // inYarn->mYarn_Form, ioSpace, dummyAid); + mStore_FarBookAtom.InitFarBookAtom(ev, buf, inYarn->mYarn_Form, ioSpace, + dummyAid); + return &mStore_FarBookAtom; + } + } else + ev->NilPointerError(); + + return (morkFarBookAtom*)0; +} + +morkFarBookAtom* morkStore::StageStringAsFarBookAtom(morkEnv* ev, + const char* inString, + mork_cscode inForm, + morkAtomSpace* ioSpace) { + if (inString) { + mork_size length = strlen(inString); + if (length <= morkBookAtom_kMaxBodySize) { + morkBuf buf(inString, length); + mork_aid dummyAid = 1; + // mStore_BookAtom.InitMaxBookAtom(ev, buf, inForm, ioSpace, dummyAid); + mStore_FarBookAtom.InitFarBookAtom(ev, buf, inForm, ioSpace, dummyAid); + return &mStore_FarBookAtom; + } + } else + ev->NilPointerError(); + + return (morkFarBookAtom*)0; +} + +morkAtomSpace* morkStore::LazyGetOidAtomSpace(morkEnv* ev) { + MORK_USED_1(ev); + if (!mStore_OidAtomSpace) { + } + return mStore_OidAtomSpace; +} + +morkAtomSpace* morkStore::LazyGetGroundAtomSpace(morkEnv* ev) { + if (!mStore_GroundAtomSpace) { + mork_scope atomScope = morkStore_kValueSpaceScope; + nsIMdbHeap* heap = mPort_Heap; + morkAtomSpace* space = new (*heap, ev) + morkAtomSpace(ev, morkUsage::kHeap, atomScope, this, heap, heap); + + if (space) // successful space creation? + { + this->MaybeDirtyStore(); + + mStore_GroundAtomSpace = space; // transfer strong ref to this slot + mStore_AtomSpaces.AddAtomSpace(ev, space); + } + } + return mStore_GroundAtomSpace; +} + +morkAtomSpace* morkStore::LazyGetGroundColumnSpace(morkEnv* ev) { + if (!mStore_GroundColumnSpace) { + mork_scope atomScope = morkStore_kGroundColumnSpace; + nsIMdbHeap* heap = mPort_Heap; + morkAtomSpace* space = new (*heap, ev) + morkAtomSpace(ev, morkUsage::kHeap, atomScope, this, heap, heap); + + if (space) // successful space creation? + { + this->MaybeDirtyStore(); + + mStore_GroundColumnSpace = space; // transfer strong ref to this slot + mStore_AtomSpaces.AddAtomSpace(ev, space); + } + } + return mStore_GroundColumnSpace; +} + +morkStream* morkStore::LazyGetInStream(morkEnv* ev) { + if (!mStore_InStream) { + nsIMdbFile* file = mStore_File; + if (file) { + morkStream* stream = new (*mPort_Heap, ev) + morkStream(ev, morkUsage::kHeap, mPort_Heap, file, + morkStore_kStreamBufSize, /*frozen*/ morkBool_kTrue); + if (stream) { + this->MaybeDirtyStore(); + mStore_InStream = stream; // transfer strong ref to this slot + } + } else + this->NilStoreFileError(ev); + } + return mStore_InStream; +} + +morkStream* morkStore::LazyGetOutStream(morkEnv* ev) { + if (!mStore_OutStream) { + nsIMdbFile* file = mStore_File; + if (file) { + morkStream* stream = new (*mPort_Heap, ev) + morkStream(ev, morkUsage::kHeap, mPort_Heap, file, + morkStore_kStreamBufSize, /*frozen*/ morkBool_kFalse); + if (stream) { + this->MaybeDirtyStore(); + mStore_InStream = stream; // transfer strong ref to this slot + } + } else + this->NilStoreFileError(ev); + } + return mStore_OutStream; +} + +void morkStore::ForgetBuilder(morkEnv* ev) { + if (mStore_Builder) + morkBuilder::SlotStrongBuilder((morkBuilder*)0, ev, &mStore_Builder); + if (mStore_InStream) + morkStream::SlotStrongStream((morkStream*)0, ev, &mStore_InStream); +} + +morkBuilder* morkStore::LazyGetBuilder(morkEnv* ev) { + if (!mStore_Builder) { + morkStream* stream = this->LazyGetInStream(ev); + if (stream) { + nsIMdbHeap* heap = mPort_Heap; + morkBuilder* builder = new (*heap, ev) + morkBuilder(ev, morkUsage::kHeap, heap, stream, + morkBuilder_kDefaultBytesPerParseSegment, heap, this); + if (builder) { + mStore_Builder = builder; // transfer strong ref to this slot + } + } + } + return mStore_Builder; +} + +morkRowSpace* morkStore::LazyGetRowSpace(morkEnv* ev, mdb_scope inRowScope) { + morkRowSpace* outSpace = mStore_RowSpaces.GetRowSpace(ev, inRowScope); + if (!outSpace && ev->Good()) // try to make new space? + { + nsIMdbHeap* heap = mPort_Heap; + outSpace = new (*heap, ev) + morkRowSpace(ev, morkUsage::kHeap, inRowScope, this, heap, heap); + + if (outSpace) // successful space creation? + { + this->MaybeDirtyStore(); + + // note adding to node map creates its own strong ref... + if (mStore_RowSpaces.AddRowSpace(ev, outSpace)) + outSpace->CutStrongRef(ev); // ...so we can drop our strong ref + } + } + return outSpace; +} + +morkAtomSpace* morkStore::LazyGetAtomSpace(morkEnv* ev, mdb_scope inAtomScope) { + morkAtomSpace* outSpace = mStore_AtomSpaces.GetAtomSpace(ev, inAtomScope); + if (!outSpace && ev->Good()) // try to make new space? + { + if (inAtomScope == morkStore_kValueSpaceScope) + outSpace = this->LazyGetGroundAtomSpace(ev); + + else if (inAtomScope == morkStore_kGroundColumnSpace) + outSpace = this->LazyGetGroundColumnSpace(ev); + else { + nsIMdbHeap* heap = mPort_Heap; + outSpace = new (*heap, ev) + morkAtomSpace(ev, morkUsage::kHeap, inAtomScope, this, heap, heap); + + if (outSpace) // successful space creation? + { + this->MaybeDirtyStore(); + + // note adding to node map creates its own strong ref... + if (mStore_AtomSpaces.AddAtomSpace(ev, outSpace)) + outSpace->CutStrongRef(ev); // ...so we can drop our strong ref + } + } + } + return outSpace; +} + +/*static*/ void morkStore::NonStoreTypeError(morkEnv* ev) { + ev->NewError("non morkStore"); +} + +/*static*/ void morkStore::NilStoreFileError(morkEnv* ev) { + ev->NewError("nil mStore_File"); +} + +/*static*/ void morkStore::CannotAutoAssignAtomIdentityError(morkEnv* ev) { + ev->NewError("false mStore_CanAutoAssignAtomIdentity"); +} + +mork_bool morkStore::OpenStoreFile( + morkEnv* ev, mork_bool inFrozen, + // const char* inFilePath, + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy) { + MORK_USED_2(inOpenPolicy, inFrozen); + nsIMdbFile_SlotStrongFile(ioFile, ev, &mStore_File); + + // if ( ev->Good() ) + // { + // morkFile* file = morkFile::OpenOldFile(ev, mPort_Heap, + // inFilePath, inFrozen); + // if ( ioFile ) + // { + // if ( ev->Good() ) + // morkFile::SlotStrongFile(file, ev, &mStore_File); + // else + // file->CutStrongRef(ev); + // + // } + // } + return ev->Good(); +} + +mork_bool morkStore::CreateStoreFile( + morkEnv* ev, + // const char* inFilePath, + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy) { + MORK_USED_1(inOpenPolicy); + nsIMdbFile_SlotStrongFile(ioFile, ev, &mStore_File); + + return ev->Good(); +} + +morkAtom* morkStore::CopyAtom(morkEnv* ev, const morkAtom* inAtom) +// copy inAtom (from some other store) over to this store +{ + morkAtom* outAtom = 0; + if (inAtom) { + mdbYarn yarn; + if (morkAtom::AliasYarn(inAtom, &yarn)) + outAtom = this->YarnToAtom(ev, &yarn, true /* create */); + } + return outAtom; +} + +morkAtom* morkStore::YarnToAtom(morkEnv* ev, const mdbYarn* inYarn, + bool createIfMissing /* = true */) { + morkAtom* outAtom = 0; + if (ev->Good()) { + morkAtomSpace* groundSpace = this->LazyGetGroundAtomSpace(ev); + if (groundSpace) { + morkFarBookAtom* keyAtom = + this->StageYarnAsFarBookAtom(ev, inYarn, groundSpace); + + if (keyAtom) { + morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies; + outAtom = map->GetAtom(ev, keyAtom); + if (!outAtom && createIfMissing) { + this->MaybeDirtyStore(); + outAtom = groundSpace->MakeBookAtomCopy(ev, *keyAtom); + } + } else if (ev->Good()) { + morkBuf b(inYarn->mYarn_Buf, inYarn->mYarn_Fill); + morkZone* z = &mStore_Zone; + outAtom = mStore_Pool.NewAnonAtom(ev, b, inYarn->mYarn_Form, z); + } + } + } + return outAtom; +} + +mork_bool morkStore::MidToOid(morkEnv* ev, const morkMid& inMid, + mdbOid* outOid) { + *outOid = inMid.mMid_Oid; + const morkBuf* buf = inMid.mMid_Buf; + if (buf && !outOid->mOid_Scope) { + if (buf->mBuf_Fill <= morkBookAtom_kMaxBodySize) { + if (buf->mBuf_Fill == 1) { + mork_u1* name = (mork_u1*)buf->mBuf_Body; + if (name) { + outOid->mOid_Scope = (mork_scope)*name; + return ev->Good(); + } + } + morkAtomSpace* groundSpace = this->LazyGetGroundColumnSpace(ev); + if (groundSpace) { + mork_cscode form = 0; // default + mork_aid aid = 1; // dummy + mStore_FarBookAtom.InitFarBookAtom(ev, *buf, form, groundSpace, aid); + morkFarBookAtom* keyAtom = &mStore_FarBookAtom; + morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies; + morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom); + if (bookAtom) + outOid->mOid_Scope = bookAtom->mBookAtom_Id; + else { + this->MaybeDirtyStore(); + bookAtom = groundSpace->MakeBookAtomCopy(ev, *keyAtom); + if (bookAtom) { + outOid->mOid_Scope = bookAtom->mBookAtom_Id; + bookAtom->MakeCellUseForever(ev); + } + } + } + } + } + return ev->Good(); +} + +morkRow* morkStore::MidToRow(morkEnv* ev, const morkMid& inMid) { + mdbOid tempOid; + this->MidToOid(ev, inMid, &tempOid); + return this->OidToRow(ev, &tempOid); +} + +morkTable* morkStore::MidToTable(morkEnv* ev, const morkMid& inMid) { + mdbOid tempOid; + this->MidToOid(ev, inMid, &tempOid); + return this->OidToTable(ev, &tempOid, /*metarow*/ (mdbOid*)0); +} + +mork_bool morkStore::MidToYarn(morkEnv* ev, const morkMid& inMid, + mdbYarn* outYarn) { + mdbOid tempOid; + this->MidToOid(ev, inMid, &tempOid); + return this->OidToYarn(ev, tempOid, outYarn); +} + +mork_bool morkStore::OidToYarn(morkEnv* ev, const mdbOid& inOid, + mdbYarn* outYarn) { + morkBookAtom* atom = 0; + + morkAtomSpace* atomSpace = + mStore_AtomSpaces.GetAtomSpace(ev, inOid.mOid_Scope); + if (atomSpace) { + morkAtomAidMap* map = &atomSpace->mAtomSpace_AtomAids; + atom = map->GetAid(ev, (mork_aid)inOid.mOid_Id); + } + morkAtom::GetYarn(atom, outYarn); + + return ev->Good(); +} + +morkBookAtom* morkStore::MidToAtom(morkEnv* ev, const morkMid& inMid) { + morkBookAtom* outAtom = 0; + mdbOid oid; + if (this->MidToOid(ev, inMid, &oid)) { + morkAtomSpace* atomSpace = + mStore_AtomSpaces.GetAtomSpace(ev, oid.mOid_Scope); + if (atomSpace) { + morkAtomAidMap* map = &atomSpace->mAtomSpace_AtomAids; + outAtom = map->GetAid(ev, (mork_aid)oid.mOid_Id); + } + } + return outAtom; +} + +/*static*/ void morkStore::SmallTokenToOneByteYarn(morkEnv* ev, + mdb_token inToken, + mdbYarn* outYarn) { + MORK_USED_1(ev); + if (outYarn->mYarn_Buf && outYarn->mYarn_Size) // any space in yarn at all? + { + mork_u1* buf = (mork_u1*)outYarn->mYarn_Buf; // for byte arithmetic + buf[0] = (mork_u1)inToken; // write the single byte + outYarn->mYarn_Fill = 1; + outYarn->mYarn_More = 0; + } else // just record we could not write the single byte + { + outYarn->mYarn_More = 1; + outYarn->mYarn_Fill = 0; + } +} + +void morkStore::TokenToString(morkEnv* ev, mdb_token inToken, + mdbYarn* outTokenName) { + if (inToken > morkAtomSpace_kMaxSevenBitAid) { + morkBookAtom* atom = 0; + morkAtomSpace* space = mStore_GroundColumnSpace; + if (space) atom = space->mAtomSpace_AtomAids.GetAid(ev, (mork_aid)inToken); + + morkAtom::GetYarn(atom, outTokenName); + } else // token is an "immediate" single byte string representation? + this->SmallTokenToOneByteYarn(ev, inToken, outTokenName); +} + +// void +// morkStore::SyncTokenIdChange(morkEnv* ev, const morkBookAtom* inAtom, +// const mdbOid* inOid) +// { +// mork_token mStore_MorkNoneToken; // token for "mork:none" // fill=9 +// mork_column mStore_CharsetToken; // token for "charset" // fill=7 +// mork_column mStore_AtomScopeToken; // token for "atomScope" // fill=9 +// mork_column mStore_RowScopeToken; // token for "rowScope" // fill=8 +// mork_column mStore_TableScopeToken; // token for "tableScope" // fill=10 +// mork_column mStore_ColumnScopeToken; // token for "columnScope" // fill=11 +// mork_kind mStore_TableKindToken; // token for "tableKind" // fill=9 +// ---------------------ruler-for-token-length-above---123456789012 +// +// if ( inOid->mOid_Scope == morkStore_kColumnSpaceScope && +// inAtom->IsWeeBook() ) +// { +// const mork_u1* body = ((const morkWeeBookAtom*) +// inAtom)->mWeeBookAtom_Body; mork_size size = inAtom->mAtom_Size; +// +// if ( size >= 7 && size <= 11 ) +// { +// if ( size == 9 ) +// { +// if ( *body == 'm' ) +// { +// if ( MORK_MEMCMP(body, "mork:none", 9) == 0 ) +// mStore_MorkNoneToken = inAtom->mBookAtom_Id; +// } +// else if ( *body == 'a' ) +// { +// if ( MORK_MEMCMP(body, "atomScope", 9) == 0 ) +// mStore_AtomScopeToken = inAtom->mBookAtom_Id; +// } +// else if ( *body == 't' ) +// { +// if ( MORK_MEMCMP(body, "tableKind", 9) == 0 ) +// mStore_TableKindToken = inAtom->mBookAtom_Id; +// } +// } +// else if ( size == 7 && *body == 'c' ) +// { +// if ( MORK_MEMCMP(body, "charset", 7) == 0 ) +// mStore_CharsetToken = inAtom->mBookAtom_Id; +// } +// else if ( size == 8 && *body == 'r' ) +// { +// if ( MORK_MEMCMP(body, "rowScope", 8) == 0 ) +// mStore_RowScopeToken = inAtom->mBookAtom_Id; +// } +// else if ( size == 10 && *body == 't' ) +// { +// if ( MORK_MEMCMP(body, "tableScope", 10) == 0 ) +// mStore_TableScopeToken = inAtom->mBookAtom_Id; +// } +// else if ( size == 11 && *body == 'c' ) +// { +// if ( MORK_MEMCMP(body, "columnScope", 11) == 0 ) +// mStore_ColumnScopeToken = inAtom->mBookAtom_Id; +// } +// } +// } +// } + +morkAtom* morkStore::AddAlias(morkEnv* ev, const morkMid& inMid, + mork_cscode inForm) { + morkBookAtom* outAtom = 0; + if (ev->Good()) { + const mdbOid* oid = &inMid.mMid_Oid; + morkAtomSpace* atomSpace = this->LazyGetAtomSpace(ev, oid->mOid_Scope); + if (atomSpace) { + morkFarBookAtom* keyAtom = + this->StageAliasAsFarBookAtom(ev, &inMid, atomSpace, inForm); + if (keyAtom) { + morkAtomAidMap* map = &atomSpace->mAtomSpace_AtomAids; + outAtom = map->GetAid(ev, (mork_aid)oid->mOid_Id); + if (outAtom) { + if (!outAtom->EqualFormAndBody(ev, keyAtom)) + ev->NewError("duplicate alias ID"); + } else { + this->MaybeDirtyStore(); + keyAtom->mBookAtom_Id = oid->mOid_Id; + outAtom = atomSpace->MakeBookAtomCopyWithAid(ev, *keyAtom, + (mork_aid)oid->mOid_Id); + + // if ( outAtom && outAtom->IsWeeBook() ) + // { + // if ( oid->mOid_Scope == morkStore_kColumnSpaceScope ) + // { + // mork_size size = outAtom->mAtom_Size; + // if ( size >= 7 && size <= 11 ) + // this->SyncTokenIdChange(ev, outAtom, oid); + // } + // } + } + } + } + } + return outAtom; +} + +#define morkStore_kMaxCopyTokenSize 512 /* if larger, cannot be copied */ + +mork_token morkStore::CopyToken(morkEnv* ev, mdb_token inToken, + morkStore* inStore) +// copy inToken from inStore over to this store +{ + mork_token outToken = 0; + if (inStore == this) // same store? + outToken = inToken; // just return token unchanged + else { + char yarnBuf[morkStore_kMaxCopyTokenSize]; + mdbYarn yarn; + yarn.mYarn_Buf = yarnBuf; + yarn.mYarn_Fill = 0; + yarn.mYarn_Size = morkStore_kMaxCopyTokenSize; + yarn.mYarn_More = 0; + yarn.mYarn_Form = 0; + yarn.mYarn_Grow = 0; + + inStore->TokenToString(ev, inToken, &yarn); + if (ev->Good()) { + morkBuf buf(yarn.mYarn_Buf, yarn.mYarn_Fill); + outToken = this->BufToToken(ev, &buf); + } + } + return outToken; +} + +mork_token morkStore::BufToToken(morkEnv* ev, const morkBuf* inBuf) { + mork_token outToken = 0; + if (ev->Good()) { + const mork_u1* s = (const mork_u1*)inBuf->mBuf_Body; + mork_bool nonAscii = (*s > 0x7F); + mork_size length = inBuf->mBuf_Fill; + if (nonAscii || length > 1) // more than one byte? + { + mork_cscode form = 0; // default charset + morkAtomSpace* space = this->LazyGetGroundColumnSpace(ev); + if (space) { + morkFarBookAtom* keyAtom = 0; + if (length <= morkBookAtom_kMaxBodySize) { + mork_aid aid = 1; // dummy + // mStore_BookAtom.InitMaxBookAtom(ev, *inBuf, form, space, aid); + mStore_FarBookAtom.InitFarBookAtom(ev, *inBuf, form, space, aid); + keyAtom = &mStore_FarBookAtom; + } + if (keyAtom) { + morkAtomBodyMap* map = &space->mAtomSpace_AtomBodies; + morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom); + if (bookAtom) + outToken = bookAtom->mBookAtom_Id; + else { + this->MaybeDirtyStore(); + bookAtom = space->MakeBookAtomCopy(ev, *keyAtom); + if (bookAtom) { + outToken = bookAtom->mBookAtom_Id; + bookAtom->MakeCellUseForever(ev); + } + } + } + } + } else // only a single byte in inTokenName string: + outToken = *s; + } + + return outToken; +} + +mork_token morkStore::StringToToken(morkEnv* ev, const char* inTokenName) { + mork_token outToken = 0; + if (ev->Good()) { + const mork_u1* s = (const mork_u1*)inTokenName; + mork_bool nonAscii = (*s > 0x7F); + if (nonAscii || (*s && s[1])) // more than one byte? + { + mork_cscode form = 0; // default charset + morkAtomSpace* groundSpace = this->LazyGetGroundColumnSpace(ev); + if (groundSpace) { + morkFarBookAtom* keyAtom = + this->StageStringAsFarBookAtom(ev, inTokenName, form, groundSpace); + if (keyAtom) { + morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies; + morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom); + if (bookAtom) + outToken = bookAtom->mBookAtom_Id; + else { + this->MaybeDirtyStore(); + bookAtom = groundSpace->MakeBookAtomCopy(ev, *keyAtom); + if (bookAtom) { + outToken = bookAtom->mBookAtom_Id; + bookAtom->MakeCellUseForever(ev); + } + } + } + } + } else // only a single byte in inTokenName string: + outToken = *s; + } + + return outToken; +} + +mork_token morkStore::QueryToken(morkEnv* ev, const char* inTokenName) { + mork_token outToken = 0; + if (ev->Good()) { + const mork_u1* s = (const mork_u1*)inTokenName; + mork_bool nonAscii = (*s > 0x7F); + if (nonAscii || (*s && s[1])) // more than one byte? + { + mork_cscode form = 0; // default charset + morkAtomSpace* groundSpace = this->LazyGetGroundColumnSpace(ev); + if (groundSpace) { + morkFarBookAtom* keyAtom = + this->StageStringAsFarBookAtom(ev, inTokenName, form, groundSpace); + if (keyAtom) { + morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies; + morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom); + if (bookAtom) { + outToken = bookAtom->mBookAtom_Id; + bookAtom->MakeCellUseForever(ev); + } + } + } + } else // only a single byte in inTokenName string: + outToken = *s; + } + + return outToken; +} + +mork_bool morkStore::HasTableKind(morkEnv* ev, mdb_scope inRowScope, + mdb_kind inTableKind, + mdb_count* outTableCount) { + MORK_USED_2(inRowScope, inTableKind); + mork_bool outBool = morkBool_kFalse; + mdb_count tableCount = 0; + + ev->StubMethodOnlyError(); + + if (outTableCount) *outTableCount = tableCount; + return outBool; +} + +morkTable* morkStore::GetTableKind(morkEnv* ev, mdb_scope inRowScope, + mdb_kind inTableKind, + mdb_count* outTableCount, + mdb_bool* outMustBeUnique) { + morkTable* outTable = 0; + if (ev->Good()) { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inRowScope); + if (rowSpace) { + outTable = rowSpace->FindTableByKind(ev, inTableKind); + if (outTable) { + if (outTableCount) *outTableCount = outTable->GetRowCount(); + if (outMustBeUnique) *outMustBeUnique = outTable->IsTableUnique(); + } + } + } + return outTable; +} + +morkRow* morkStore::FindRow(morkEnv* ev, mdb_scope inScope, mdb_column inColumn, + const mdbYarn* inYarn) { + morkRow* outRow = 0; + if (ev->Good()) { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inScope); + if (rowSpace) { + outRow = rowSpace->FindRow(ev, inColumn, inYarn); + } + } + return outRow; +} + +morkRow* morkStore::GetRow(morkEnv* ev, const mdbOid* inOid) { + morkRow* outRow = 0; + if (ev->Good()) { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope); + if (rowSpace) { + outRow = rowSpace->mRowSpace_Rows.GetOid(ev, inOid); + } + } + return outRow; +} + +morkTable* morkStore::GetTable(morkEnv* ev, const mdbOid* inOid) { + morkTable* outTable = 0; + if (ev->Good()) { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope); + if (rowSpace) { + outTable = rowSpace->FindTableByTid(ev, inOid->mOid_Id); + } + } + return outTable; +} + +morkTable* morkStore::NewTable( + morkEnv* ev, mdb_scope inRowScope, mdb_kind inTableKind, + mdb_bool inMustBeUnique, + const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying +{ + morkTable* outTable = 0; + if (ev->Good()) { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inRowScope); + if (rowSpace) + outTable = rowSpace->NewTable(ev, inTableKind, inMustBeUnique, + inOptionalMetaRowOid); + } + return outTable; +} + +morkPortTableCursor* morkStore::GetPortTableCursor(morkEnv* ev, + mdb_scope inRowScope, + mdb_kind inTableKind) { + morkPortTableCursor* outCursor = 0; + if (ev->Good()) { + nsIMdbHeap* heap = mPort_Heap; + outCursor = new (*heap, ev) morkPortTableCursor( + ev, morkUsage::kHeap, heap, this, inRowScope, inTableKind, heap); + } + NS_IF_ADDREF(outCursor); + return outCursor; +} + +morkRow* morkStore::NewRow(morkEnv* ev, mdb_scope inRowScope) { + morkRow* outRow = 0; + if (ev->Good()) { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inRowScope); + if (rowSpace) outRow = rowSpace->NewRow(ev); + } + return outRow; +} + +morkRow* morkStore::NewRowWithOid(morkEnv* ev, const mdbOid* inOid) { + morkRow* outRow = 0; + if (ev->Good()) { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope); + if (rowSpace) outRow = rowSpace->NewRowWithOid(ev, inOid); + } + return outRow; +} + +morkRow* morkStore::OidToRow(morkEnv* ev, const mdbOid* inOid) +// OidToRow() finds old row with oid, or makes new one if not found. +{ + morkRow* outRow = 0; + if (ev->Good()) { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope); + if (rowSpace) { + outRow = rowSpace->mRowSpace_Rows.GetOid(ev, inOid); + if (!outRow && ev->Good()) outRow = rowSpace->NewRowWithOid(ev, inOid); + } + } + return outRow; +} + +morkTable* morkStore::OidToTable( + morkEnv* ev, const mdbOid* inOid, + const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying +// OidToTable() finds old table with oid, or makes new one if not found. +{ + morkTable* outTable = 0; + if (ev->Good()) { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope); + if (rowSpace) { + outTable = rowSpace->mRowSpace_Tables.GetTable(ev, inOid->mOid_Id); + if (!outTable && ev->Good()) { + mork_kind tableKind = morkStore_kNoneToken; + outTable = rowSpace->NewTableWithTid(ev, inOid->mOid_Id, tableKind, + inOptionalMetaRowOid); + } + } + } + return outTable; +} + +// { ===== begin nsIMdbObject methods ===== + +// { ----- begin ref counting for well-behaved cyclic graphs ----- +NS_IMETHODIMP +morkStore::GetWeakRefCount(nsIMdbEnv* mev, // weak refs + mdb_count* outCount) { + *outCount = WeakRefsOnly(); + return NS_OK; +} +NS_IMETHODIMP +morkStore::GetStrongRefCount(nsIMdbEnv* mev, // strong refs + mdb_count* outCount) { + *outCount = StrongRefsOnly(); + return NS_OK; +} +// ### TODO - clean up this cast, if required +NS_IMETHODIMP +morkStore::AddWeakRef(nsIMdbEnv* mev) { + morkEnv* ev = morkEnv::FromMdbEnv(mev); + // XXX Casting mork_refs to nsresult + return static_cast<nsresult>(morkNode::AddWeakRef(ev)); +} +#ifndef _MSC_VER +NS_IMETHODIMP_(mork_uses) +morkStore::AddStrongRef(morkEnv* mev) { return AddRef(); } +#endif +NS_IMETHODIMP_(mork_uses) +morkStore::AddStrongRef(nsIMdbEnv* mev) { return AddRef(); } +NS_IMETHODIMP +morkStore::CutWeakRef(nsIMdbEnv* mev) { + morkEnv* ev = morkEnv::FromMdbEnv(mev); + // XXX Casting mork_refs to nsresult + return static_cast<nsresult>(morkNode::CutWeakRef(ev)); +} +#ifndef _MSC_VER +NS_IMETHODIMP_(mork_uses) +morkStore::CutStrongRef(morkEnv* mev) { return Release(); } +#endif +NS_IMETHODIMP +morkStore::CutStrongRef(nsIMdbEnv* mev) { + // XXX Casting nsrefcnt to nsresult + return static_cast<nsresult>(Release()); +} + +NS_IMETHODIMP +morkStore::CloseMdbObject(nsIMdbEnv* mev) { + morkEnv* ev = morkEnv::FromMdbEnv(mev); + CloseMorkNode(ev); + Release(); + return NS_OK; +} + +NS_IMETHODIMP +morkStore::IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen) { + *outOpen = IsOpenNode(); + return NS_OK; +} +// } ----- end ref counting ----- + +// } ===== end nsIMdbObject methods ===== + +// { ===== begin nsIMdbPort methods ===== + +// { ----- begin attribute methods ----- +NS_IMETHODIMP +morkStore::GetIsPortReadonly(nsIMdbEnv* mev, mdb_bool* outBool) { + nsresult outErr = NS_OK; + mdb_bool isReadOnly = morkBool_kFalse; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + ev->StubMethodOnlyError(); + outErr = ev->AsErr(); + } + if (outBool) *outBool = isReadOnly; + return outErr; +} + +morkEnv* morkStore::CanUseStore(nsIMdbEnv* mev, mork_bool inMutable, + nsresult* outErr) const { + morkEnv* outEnv = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (IsStore()) + outEnv = ev; + else + NonStoreTypeError(ev); + *outErr = ev->AsErr(); + } + MORK_ASSERT(outEnv); + return outEnv; +} + +NS_IMETHODIMP +morkStore::GetIsStore(nsIMdbEnv* mev, mdb_bool* outBool) { + MORK_USED_1(mev); + if (outBool) *outBool = morkBool_kTrue; + return NS_OK; +} + +NS_IMETHODIMP +morkStore::GetIsStoreAndDirty(nsIMdbEnv* mev, mdb_bool* outBool) { + nsresult outErr = NS_OK; + mdb_bool isStoreAndDirty = morkBool_kFalse; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + ev->StubMethodOnlyError(); + outErr = ev->AsErr(); + } + if (outBool) *outBool = isStoreAndDirty; + return outErr; +} + +NS_IMETHODIMP +morkStore::GetUsagePolicy(nsIMdbEnv* mev, mdbUsagePolicy* ioUsagePolicy) { + MORK_USED_1(ioUsagePolicy); + nsresult outErr = NS_OK; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + ev->StubMethodOnlyError(); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkStore::SetUsagePolicy(nsIMdbEnv* mev, const mdbUsagePolicy* inUsagePolicy) { + MORK_USED_1(inUsagePolicy); + nsresult outErr = NS_OK; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + // ev->StubMethodOnlyError(); // okay to do nothing? + outErr = ev->AsErr(); + } + return outErr; +} +// } ----- end attribute methods ----- + +// { ----- begin memory policy methods ----- +NS_IMETHODIMP +morkStore::IdleMemoryPurge( // do memory management already scheduled + nsIMdbEnv* mev, // context + mdb_size* outEstimatedBytesFreed) // approximate bytes actually freed +{ + nsresult outErr = NS_OK; + mdb_size estimatedBytesFreed = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + // ev->StubMethodOnlyError(); // okay to do nothing? + outErr = ev->AsErr(); + } + if (outEstimatedBytesFreed) *outEstimatedBytesFreed = estimatedBytesFreed; + return outErr; +} + +NS_IMETHODIMP +morkStore::SessionMemoryPurge( // request specific footprint decrease + nsIMdbEnv* mev, // context + mdb_size inDesiredBytesFreed, // approximate number of bytes wanted + mdb_size* outEstimatedBytesFreed) // approximate bytes actually freed +{ + MORK_USED_1(inDesiredBytesFreed); + nsresult outErr = NS_OK; + mdb_size estimate = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + // ev->StubMethodOnlyError(); // okay to do nothing? + outErr = ev->AsErr(); + } + if (outEstimatedBytesFreed) *outEstimatedBytesFreed = estimate; + return outErr; +} + +NS_IMETHODIMP +morkStore::PanicMemoryPurge( // desperately free all possible memory + nsIMdbEnv* mev, // context + mdb_size* outEstimatedBytesFreed) // approximate bytes actually freed +{ + nsresult outErr = NS_OK; + mdb_size estimate = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + // ev->StubMethodOnlyError(); // okay to do nothing? + outErr = ev->AsErr(); + } + if (outEstimatedBytesFreed) *outEstimatedBytesFreed = estimate; + return outErr; +} +// } ----- end memory policy methods ----- + +// { ----- begin filepath methods ----- +NS_IMETHODIMP +morkStore::GetPortFilePath( + nsIMdbEnv* mev, // context + mdbYarn* outFilePath, // name of file holding port content + mdbYarn* outFormatVersion) // file format description +{ + nsresult outErr = NS_OK; + if (outFormatVersion) outFormatVersion->mYarn_Fill = 0; + if (outFilePath) outFilePath->mYarn_Fill = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + if (mStore_File) + mStore_File->Path(mev, outFilePath); + else + NilStoreFileError(ev); + + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkStore::GetPortFile( + nsIMdbEnv* mev, // context + nsIMdbFile** acqFile) // acquire file used by port or store +{ + nsresult outErr = NS_OK; + if (acqFile) *acqFile = 0; + + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + if (mStore_File) { + if (acqFile) { + mStore_File->AddRef(); + if (ev->Good()) *acqFile = mStore_File; + } + } else + NilStoreFileError(ev); + + outErr = ev->AsErr(); + } + return outErr; +} +// } ----- end filepath methods ----- + +// { ----- begin export methods ----- +NS_IMETHODIMP +morkStore::BestExportFormat( // determine preferred export format + nsIMdbEnv* mev, // context + mdbYarn* outFormatVersion) // file format description +{ + nsresult outErr = NS_OK; + if (outFormatVersion) outFormatVersion->mYarn_Fill = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + ev->StubMethodOnlyError(); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkStore::CanExportToFormat( // can export content in given specific format? + nsIMdbEnv* mev, // context + const char* inFormatVersion, // file format description + mdb_bool* outCanExport) // whether ExportSource() might succeed +{ + MORK_USED_1(inFormatVersion); + mdb_bool canExport = morkBool_kFalse; + nsresult outErr = NS_OK; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + ev->StubMethodOnlyError(); + outErr = ev->AsErr(); + } + if (outCanExport) *outCanExport = canExport; + return outErr; +} + +NS_IMETHODIMP +morkStore::ExportToFormat( // export content in given specific format + nsIMdbEnv* mev, // context + // const char* inFilePath, // the file to receive exported content + nsIMdbFile* ioFile, // destination abstract file interface + const char* inFormatVersion, // file format description + nsIMdbThumb** acqThumb) // acquire thumb for incremental export +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then the export will be finished. +{ + nsresult outErr = NS_OK; + nsIMdbThumb* outThumb = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + if (ioFile && inFormatVersion && acqThumb) { + ev->StubMethodOnlyError(); + } else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if (acqThumb) *acqThumb = outThumb; + return outErr; +} + +// } ----- end export methods ----- + +// { ----- begin token methods ----- +NS_IMETHODIMP +morkStore::TokenToString( // return a string name for an integer token + nsIMdbEnv* mev, // context + mdb_token inToken, // token for inTokenName inside this port + mdbYarn* outTokenName) // the type of table to access +{ + nsresult outErr = NS_OK; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + TokenToString(ev, inToken, outTokenName); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkStore::StringToToken( // return an integer token for scope name + nsIMdbEnv* mev, // context + const char* inTokenName, // Latin1 string to tokenize if possible + mdb_token* outToken) // 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. +{ + nsresult outErr = NS_OK; + mdb_token token = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + token = StringToToken(ev, inTokenName); + outErr = ev->AsErr(); + } + if (outToken) *outToken = token; + return outErr; +} + +NS_IMETHODIMP +morkStore::QueryToken( // like StringToToken(), but without adding + nsIMdbEnv* mev, // context + const char* inTokenName, // Latin1 string to tokenize if possible + mdb_token* outToken) // 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. +{ + nsresult outErr = NS_OK; + mdb_token token = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + token = QueryToken(ev, inTokenName); + outErr = ev->AsErr(); + } + if (outToken) *outToken = token; + return outErr; +} + +// } ----- end token methods ----- + +// { ----- begin row methods ----- +NS_IMETHODIMP +morkStore::HasRow( // contains a row with the specified oid? + nsIMdbEnv* mev, // context + const mdbOid* inOid, // hypothetical row oid + mdb_bool* outHasRow) // whether GetRow() might succeed +{ + nsresult outErr = NS_OK; + mdb_bool hasRow = morkBool_kFalse; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkRow* row = GetRow(ev, inOid); + if (row) hasRow = morkBool_kTrue; + + outErr = ev->AsErr(); + } + if (outHasRow) *outHasRow = hasRow; + return outErr; +} + +NS_IMETHODIMP +morkStore::GetRow( // access one row with specific oid + nsIMdbEnv* mev, // context + const mdbOid* inOid, // hypothetical row oid + nsIMdbRow** acqRow) // acquire specific row (or null) +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkRow* row = GetRow(ev, inOid); + if (row && ev->Good()) outRow = row->AcquireRowHandle(ev, this); + + outErr = ev->AsErr(); + } + if (acqRow) *acqRow = outRow; + return outErr; +} + +NS_IMETHODIMP +morkStore::GetRowRefCount( // get number of tables that contain a row + nsIMdbEnv* mev, // context + const mdbOid* inOid, // hypothetical row oid + mdb_count* outRefCount) // number of tables containing inRowKey +{ + nsresult outErr = NS_OK; + mdb_count count = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkRow* row = GetRow(ev, inOid); + if (row && ev->Good()) count = row->mRow_GcUses; + + outErr = ev->AsErr(); + } + if (outRefCount) *outRefCount = count; + return outErr; +} + +NS_IMETHODIMP +morkStore::FindRow( + nsIMdbEnv* mev, // 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) // acquire matching row (or nil for no match) +// 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. +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + mdbOid rowOid; + rowOid.mOid_Scope = 0; + rowOid.mOid_Id = (mdb_id)-1; + + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkRow* row = FindRow(ev, inRowScope, inColumn, inTargetCellValue); + if (row && ev->Good()) { + rowOid = row->mRow_Oid; + if (acqRow) outRow = row->AcquireRowHandle(ev, this); + } + outErr = ev->AsErr(); + } + if (acqRow) *acqRow = outRow; + if (outRowOid) *outRowOid = rowOid; + + return outErr; +} + +// } ----- end row methods ----- + +// { ----- begin table methods ----- +NS_IMETHODIMP +morkStore::HasTable( // supports a table with the specified oid? + nsIMdbEnv* mev, // context + const mdbOid* inOid, // hypothetical table oid + mdb_bool* outHasTable) // whether GetTable() might succeed +{ + nsresult outErr = NS_OK; + mork_bool hasTable = morkBool_kFalse; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkTable* table = GetTable(ev, inOid); + if (table) hasTable = morkBool_kTrue; + + outErr = ev->AsErr(); + } + if (outHasTable) *outHasTable = hasTable; + return outErr; +} + +NS_IMETHODIMP +morkStore::GetTable( // access one table with specific oid + nsIMdbEnv* mev, // context + const mdbOid* inOid, // hypothetical table oid + nsIMdbTable** acqTable) // acquire specific table (or null) +{ + nsresult outErr = NS_OK; + nsIMdbTable* outTable = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkTable* table = GetTable(ev, inOid); + if (table && ev->Good()) outTable = table->AcquireTableHandle(ev); + outErr = ev->AsErr(); + } + if (acqTable) *acqTable = outTable; + return outErr; +} + +NS_IMETHODIMP +morkStore::HasTableKind( // supports a table of the specified type? + nsIMdbEnv* mev, // 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) // whether GetTableKind() might succeed +{ + nsresult outErr = NS_OK; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + *outSupportsTable = + HasTableKind(ev, inRowScope, inTableKind, outTableCount); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkStore::GetTableKind( // access one (random) table of specific type + nsIMdbEnv* mev, // 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) // acquire scoped collection of rows +{ + nsresult outErr = NS_OK; + nsIMdbTable* outTable = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkTable* table = GetTableKind(ev, inRowScope, inTableKind, outTableCount, + outMustBeUnique); + if (table && ev->Good()) outTable = table->AcquireTableHandle(ev); + outErr = ev->AsErr(); + } + if (acqTable) *acqTable = outTable; + return outErr; +} + +NS_IMETHODIMP +morkStore::GetPortTableCursor( // get cursor for all tables of specific type + nsIMdbEnv* mev, // context + mdb_scope inRowScope, // row scope for row ids + mdb_kind inTableKind, // the type of table to access + nsIMdbPortTableCursor** acqCursor) // all such tables in the port +{ + nsresult outErr = NS_OK; + nsIMdbPortTableCursor* outCursor = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkPortTableCursor* cursor = + GetPortTableCursor(ev, inRowScope, inTableKind); + if (cursor && ev->Good()) outCursor = cursor; + + outErr = ev->AsErr(); + } + if (acqCursor) *acqCursor = outCursor; + return outErr; +} +// } ----- end table methods ----- + +// { ----- begin commit methods ----- + +NS_IMETHODIMP +morkStore::ShouldCompress( // store wastes at least inPercentWaste? + nsIMdbEnv* mev, // context + mdb_percent inPercentWaste, // 0..100 percent file size waste threshold + mdb_percent* outActualWaste, // 0..100 percent of file actually wasted + mdb_bool* outShould) // true when about inPercentWaste% is wasted +{ + mdb_percent actualWaste = 0; + mdb_bool shouldCompress = morkBool_kFalse; + nsresult outErr = NS_OK; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + actualWaste = PercentOfStoreWasted(ev); + if (inPercentWaste > 100) inPercentWaste = 100; + shouldCompress = (actualWaste >= inPercentWaste); + outErr = ev->AsErr(); + } + if (outActualWaste) *outActualWaste = actualWaste; + if (outShould) *outShould = shouldCompress; + return outErr; +} + +// } ===== end nsIMdbPort methods ===== + +NS_IMETHODIMP +morkStore::NewTable( // make one new table of specific type + nsIMdbEnv* mev, // 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) // acquire scoped collection of rows +{ + nsresult outErr = NS_OK; + nsIMdbTable* outTable = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkTable* table = NewTable(ev, inRowScope, inTableKind, inMustBeUnique, + inOptionalMetaRowOid); + if (table && ev->Good()) outTable = table->AcquireTableHandle(ev); + outErr = ev->AsErr(); + } + if (acqTable) *acqTable = outTable; + return outErr; +} + +NS_IMETHODIMP +morkStore::NewTableWithOid( // make one new table of specific type + nsIMdbEnv* mev, // 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) // acquire scoped collection of rows +{ + nsresult outErr = NS_OK; + nsIMdbTable* outTable = 0; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkTable* table = OidToTable(ev, inOid, inOptionalMetaRowOid); + if (table && ev->Good()) { + table->mTable_Kind = inTableKind; + if (inMustBeUnique) table->SetTableUnique(); + outTable = table->AcquireTableHandle(ev); + } + outErr = ev->AsErr(); + } + if (acqTable) *acqTable = outTable; + return outErr; +} +// } ----- end table methods ----- + +// { ----- begin row scope methods ----- +NS_IMETHODIMP +morkStore::RowScopeHasAssignedIds( + nsIMdbEnv* mev, + mdb_scope inRowScope, // row scope for row ids + mdb_bool* outCallerAssigned, // nonzero if caller assigned specified + mdb_bool* outStoreAssigned) // nonzero if store db assigned specified +{ + NS_ASSERTION(false, " not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkStore::SetCallerAssignedIds( + nsIMdbEnv* mev, + mdb_scope inRowScope, // row scope for row ids + mdb_bool* outCallerAssigned, // nonzero if caller assigned specified + mdb_bool* outStoreAssigned) // nonzero if store db assigned specified +{ + NS_ASSERTION(false, " not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkStore::SetStoreAssignedIds( + nsIMdbEnv* mev, + mdb_scope inRowScope, // row scope for row ids + mdb_bool* outCallerAssigned, // nonzero if caller assigned specified + mdb_bool* outStoreAssigned) // nonzero if store db assigned specified +{ + NS_ASSERTION(false, " not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} +// } ----- end row scope methods ----- + +// { ----- begin row methods ----- +NS_IMETHODIMP +morkStore::NewRowWithOid(nsIMdbEnv* mev, // new row w/ caller assigned oid + const mdbOid* inOid, // caller assigned oid + nsIMdbRow** acqRow) // create new row +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkRow* row = NewRowWithOid(ev, inOid); + if (row && ev->Good()) outRow = row->AcquireRowHandle(ev, this); + + outErr = ev->AsErr(); + } + if (acqRow) *acqRow = outRow; + return outErr; +} + +NS_IMETHODIMP +morkStore::NewRow(nsIMdbEnv* mev, // new row with db assigned oid + mdb_scope inRowScope, // row scope for row ids + nsIMdbRow** acqRow) // 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. +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkRow* row = NewRow(ev, inRowScope); + if (row && ev->Good()) outRow = row->AcquireRowHandle(ev, this); + + outErr = ev->AsErr(); + } + if (acqRow) *acqRow = outRow; + return outErr; +} +// } ----- end row methods ----- + +// { ----- begin import/export methods ----- +NS_IMETHODIMP +morkStore::ImportContent( // import content from port + nsIMdbEnv* mev, // context + mdb_scope inRowScope, // scope for rows (or zero for all?) + nsIMdbPort* ioPort, // the port with content to add to store + nsIMdbThumb** acqThumb) // acquire thumb for incremental import +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then the import will be finished. +{ + NS_ASSERTION(false, " not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkStore::ImportFile( // import content from port + nsIMdbEnv* mev, // context + nsIMdbFile* ioFile, // the file with content to add to store + nsIMdbThumb** acqThumb) // acquire thumb for incremental import +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then the import will be finished. +{ + NS_ASSERTION(false, " not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} +// } ----- end import/export methods ----- + +// { ----- begin hinting methods ----- +NS_IMETHODIMP +morkStore::ShareAtomColumnsHint( // advise re shared col content atomizing + nsIMdbEnv* mev, // context + mdb_scope inScopeHint, // zero, or suggested shared namespace + const mdbColumnSet* inColumnSet) // cols desired tokenized together +{ + MORK_USED_2(inColumnSet, inScopeHint); + nsresult outErr = NS_OK; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + // ev->StubMethodOnlyError(); // okay to do nothing for a hint method + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkStore::AvoidAtomColumnsHint( // advise col w/ poor atomizing prospects + nsIMdbEnv* mev, // context + const mdbColumnSet* inColumnSet) // cols with poor atomizing prospects +{ + MORK_USED_1(inColumnSet); + nsresult outErr = NS_OK; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + // ev->StubMethodOnlyError(); // okay to do nothing for a hint method + outErr = ev->AsErr(); + } + return outErr; +} +// } ----- end hinting methods ----- + +// { ----- begin commit methods ----- +NS_IMETHODIMP +morkStore::LargeCommit( // save important changes if at all possible + nsIMdbEnv* mev, // context + nsIMdbThumb** acqThumb) // 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. +{ + nsresult outErr = NS_OK; + nsIMdbThumb* outThumb = 0; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkThumb* thumb = 0; + // morkFile* file = store->mStore_File; + if (DoPreferLargeOverCompressCommit(ev)) { + thumb = morkThumb::Make_LargeCommit(ev, mPort_Heap, this); + } else { + mork_bool doCollect = morkBool_kFalse; + thumb = morkThumb::Make_CompressCommit(ev, mPort_Heap, this, doCollect); + } + + if (thumb) { + outThumb = thumb; + thumb->AddRef(); + } + + outErr = ev->AsErr(); + } + if (acqThumb) *acqThumb = outThumb; + return outErr; +} + +NS_IMETHODIMP +morkStore::SessionCommit( // save all changes if large commits delayed + nsIMdbEnv* mev, // context + nsIMdbThumb** acqThumb) // 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. +{ + nsresult outErr = NS_OK; + nsIMdbThumb* outThumb = 0; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + morkThumb* thumb = 0; + if (DoPreferLargeOverCompressCommit(ev)) { + thumb = morkThumb::Make_LargeCommit(ev, mPort_Heap, this); + } else { + mork_bool doCollect = morkBool_kFalse; + thumb = morkThumb::Make_CompressCommit(ev, mPort_Heap, this, doCollect); + } + + if (thumb) { + outThumb = thumb; + thumb->AddRef(); + } + outErr = ev->AsErr(); + } + if (acqThumb) *acqThumb = outThumb; + return outErr; +} + +NS_IMETHODIMP +morkStore::CompressCommit( // commit and make db smaller if possible + nsIMdbEnv* mev, // context + nsIMdbThumb** acqThumb) // 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. +{ + nsresult outErr = NS_OK; + nsIMdbThumb* outThumb = 0; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if (ev) { + mork_bool doCollect = morkBool_kFalse; + morkThumb* thumb = + morkThumb::Make_CompressCommit(ev, mPort_Heap, this, doCollect); + if (thumb) { + outThumb = thumb; + thumb->AddRef(); + mStore_CanWriteIncremental = morkBool_kTrue; + } + + outErr = ev->AsErr(); + } + if (acqThumb) *acqThumb = outThumb; + return outErr; +} + +// } ----- end commit methods ----- + +// } ===== end nsIMdbStore methods ===== + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkStore.h b/comm/mailnews/db/mork/morkStore.h new file mode 100644 index 0000000000..c2a08cb7d2 --- /dev/null +++ b/comm/mailnews/db/mork/morkStore.h @@ -0,0 +1,770 @@ +/* -*- 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_ */ diff --git a/comm/mailnews/db/mork/morkStream.cpp b/comm/mailnews/db/mork/morkStream.cpp new file mode 100644 index 0000000000..23fd7b91ae --- /dev/null +++ b/comm/mailnews/db/mork/morkStream.cpp @@ -0,0 +1,790 @@ +/* -*- 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 _MORKFILE_ +# include "morkFile.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKSTREAM_ +# include "morkStream.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkStream::CloseMorkNode( + morkEnv* ev) // CloseStream() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseStream(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkStream::~morkStream() // assert CloseStream() executed earlier +{ + MORK_ASSERT(mStream_ContentFile == 0); + MORK_ASSERT(mStream_Buf == 0); +} + +/*public non-poly*/ +morkStream::morkStream(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbFile* ioContentFile, + mork_size inBufSize, mork_bool inFrozen) + : morkFile(ev, inUsage, ioHeap, ioHeap), + mStream_At(0), + mStream_ReadEnd(0), + mStream_WriteEnd(0) + + , + mStream_ContentFile(0) + + , + mStream_Buf(0), + mStream_BufSize(inBufSize), + mStream_BufPos(0), + mStream_Dirty(morkBool_kFalse), + mStream_HitEof(morkBool_kFalse) { + if (ev->Good()) { + if (inBufSize < morkStream_kMinBufSize) + mStream_BufSize = inBufSize = morkStream_kMinBufSize; + else if (inBufSize > morkStream_kMaxBufSize) + mStream_BufSize = inBufSize = morkStream_kMaxBufSize; + + if (ioContentFile && ioHeap) { + // if ( ioContentFile->FileFrozen() ) // forced to be readonly? + // inFrozen = morkBool_kTrue; // override the input value + + nsIMdbFile_SlotStrongFile(ioContentFile, ev, &mStream_ContentFile); + if (ev->Good()) { + mork_u1* buf = 0; + ioHeap->Alloc(ev->AsMdbEnv(), inBufSize, (void**)&buf); + if (buf) { + mStream_At = mStream_Buf = buf; + + if (!inFrozen) { + // physical buffer end never moves: + mStream_WriteEnd = buf + inBufSize; + } else + mStream_WriteEnd = 0; // no writing is allowed + + if (inFrozen) { + // logical buffer end starts at Buf with no content: + mStream_ReadEnd = buf; + this->SetFileFrozen(inFrozen); + } else + mStream_ReadEnd = 0; // no reading is allowed + + this->SetFileActive(morkBool_kTrue); + this->SetFileIoOpen(morkBool_kTrue); + } + if (ev->Good()) mNode_Derived = morkDerived_kStream; + } + } else + ev->NilPointerError(); + } +} + +/*public non-poly*/ void morkStream::CloseStream( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + nsIMdbFile_SlotStrongFile((nsIMdbFile*)0, ev, &mStream_ContentFile); + nsIMdbHeap* heap = mFile_SlotHeap; + mork_u1* buf = mStream_Buf; + mStream_Buf = 0; + + if (heap && buf) heap->Free(ev->AsMdbEnv(), buf); + + this->CloseFile(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +#define morkStream_kSpacesPerIndent 1 /* one space per indent */ +#define morkStream_kMaxIndentDepth 70 /* max indent of 70 space bytes */ +static const char morkStream_kSpaces[] // next line to ease length perception + = " " + " "; +// 123456789_123456789_123456789_123456789_123456789_123456789_123456789_ +// morkStream_kSpaces above must contain (at least) 70 spaces (ASCII 0x20) + +mork_size morkStream::PutIndent(morkEnv* ev, mork_count inDepth) +// PutIndent() puts a linebreak, and then +// "indents" by inDepth, and returns the line length after indentation. +{ + mork_size outLength = 0; + nsIMdbEnv* mev = ev->AsMdbEnv(); + if (ev->Good()) { + this->PutLineBreak(ev); + if (ev->Good()) { + outLength = inDepth; + mdb_size bytesWritten; + if (inDepth) this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten); + } + } + return outLength; +} + +mork_size morkStream::PutByteThenIndent(morkEnv* ev, int inByte, + mork_count inDepth) +// PutByteThenIndent() puts the byte, then a linebreak, and then +// "indents" by inDepth, and returns the line length after indentation. +{ + mork_size outLength = 0; + nsIMdbEnv* mev = ev->AsMdbEnv(); + + if (inDepth > morkStream_kMaxIndentDepth) + inDepth = morkStream_kMaxIndentDepth; + + this->Putc(ev, inByte); + if (ev->Good()) { + this->PutLineBreak(ev); + if (ev->Good()) { + outLength = inDepth; + mdb_size bytesWritten; + if (inDepth) this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten); + } + } + return outLength; +} + +mork_size morkStream::PutStringThenIndent(morkEnv* ev, const char* inString, + mork_count inDepth) +// PutStringThenIndent() puts the string, then a linebreak, and then +// "indents" by inDepth, and returns the line length after indentation. +{ + mork_size outLength = 0; + mdb_size bytesWritten; + nsIMdbEnv* mev = ev->AsMdbEnv(); + + if (inDepth > morkStream_kMaxIndentDepth) + inDepth = morkStream_kMaxIndentDepth; + + if (inString) { + mork_size length = strlen(inString); + if (length && ev->Good()) // any bytes to write? + this->Write(mev, inString, length, &bytesWritten); + } + + if (ev->Good()) { + this->PutLineBreak(ev); + if (ev->Good()) { + outLength = inDepth; + if (inDepth) this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten); + } + } + return outLength; +} + +mork_size morkStream::PutString(morkEnv* ev, const char* inString) { + nsIMdbEnv* mev = ev->AsMdbEnv(); + mork_size outSize = 0; + mdb_size bytesWritten; + if (inString) { + outSize = strlen(inString); + if (outSize && ev->Good()) // any bytes to write? + { + this->Write(mev, inString, outSize, &bytesWritten); + } + } + return outSize; +} + +mork_size morkStream::PutStringThenNewline(morkEnv* ev, const char* inString) +// PutStringThenNewline() returns total number of bytes written. +{ + nsIMdbEnv* mev = ev->AsMdbEnv(); + mork_size outSize = 0; + mdb_size bytesWritten; + if (inString) { + outSize = strlen(inString); + if (outSize && ev->Good()) // any bytes to write? + { + this->Write(mev, inString, outSize, &bytesWritten); + if (ev->Good()) outSize += this->PutLineBreak(ev); + } + } + return outSize; +} + +mork_size morkStream::PutByteThenNewline(morkEnv* ev, int inByte) +// PutByteThenNewline() returns total number of bytes written. +{ + mork_size outSize = 1; // one for the following byte + this->Putc(ev, inByte); + if (ev->Good()) outSize += this->PutLineBreak(ev); + return outSize; +} + +mork_size morkStream::PutLineBreak(morkEnv* ev) { +#if defined(MORK_MAC) + + this->Putc(ev, mork_kCR); + return 1; + +#else +# if defined(MORK_WIN) + + this->Putc(ev, mork_kCR); + this->Putc(ev, mork_kLF); + return 2; + +# else +# ifdef MORK_UNIX + + this->Putc(ev, mork_kLF); + return 1; + +# endif /* MORK_UNIX */ +# endif /* MORK_WIN */ +#endif /* MORK_MAC */ +} +// ````` ````` ````` ````` ````` ````` ````` ````` +// public: // virtual morkFile methods + +NS_IMETHODIMP +morkStream::Steal(nsIMdbEnv* mev, nsIMdbFile* ioThief) +// Steal: tell this file to close any associated i/o stream in the file +// system, because the file ioThief intends to reopen the file in order +// to provide the MDB implementation with more exotic file access than is +// offered by the nsIMdbFile alone. Presumably the thief knows enough +// from Path() in order to know which file to reopen. If Steal() is +// successful, this file should probably delegate all future calls to +// the nsIMdbFile interface down to the thief files, so that even after +// the file has been stolen, it can still be read, written, or forcibly +// closed (by a call to CloseMdbObject()). +{ + MORK_USED_1(ioThief); + morkEnv* ev = morkEnv::FromMdbEnv(mev); + ev->StubMethodOnlyError(); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkStream::BecomeTrunk(nsIMdbEnv* mev) +// If this file is a file version branch created by calling AcquireBud(), +// BecomeTrunk() causes this file's content to replace the original +// file's content, typically by assuming the original file's identity. +{ + morkEnv* ev = morkEnv::FromMdbEnv(mev); + ev->StubMethodOnlyError(); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkStream::AcquireBud(nsIMdbEnv* mev, nsIMdbHeap* ioHeap, nsIMdbFile** acqBud) +// AcquireBud() starts a new "branch" version of the file, empty of content, +// so that a new version of the file can be written. This new file +// can later be told to BecomeTrunk() the original file, so the branch +// created by budding the file will replace the original file. Some +// file subclasses might initially take the unsafe but expedient +// approach of simply truncating this file down to zero length, and +// then returning the same morkFile pointer as this, with an extra +// reference count increment. Note that the caller of AcquireBud() is +// expected to eventually call CutStrongRef() on the returned file +// in order to release the strong reference. High quality versions +// of morkFile subclasses will create entirely new files which later +// are renamed to become the old file, so that better transactional +// behavior is exhibited by the file, so crashes protect old files. +// Note that AcquireBud() is an illegal operation on readonly files. +{ + MORK_USED_1(ioHeap); + morkFile* outFile = 0; + nsIMdbFile* file = mStream_ContentFile; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (this->IsOpenAndActiveFile() && file) { + // figure out how this interacts with buffering and mStream_WriteEnd: + ev->StubMethodOnlyError(); + } else + this->NewFileDownError(ev); + + *acqBud = outFile; + return NS_ERROR_NOT_IMPLEMENTED; +} + +mork_pos morkStream::Length(morkEnv* ev) const // eof +{ + mork_pos outPos = 0; + + nsIMdbFile* file = mStream_ContentFile; + if (this->IsOpenAndActiveFile() && file) { + mork_pos contentEof = 0; + file->Eof(ev->AsMdbEnv(), &contentEof); + if (ev->Good()) { + if (mStream_WriteEnd) // this stream supports writing? + { + // the local buffer might have buffered content past content eof + if (ev->Good()) // no error happened during Length() above? + { + mork_u1* at = mStream_At; + mork_u1* buf = mStream_Buf; + if (at >= buf) // expected cursor order? + { + mork_pos localContent = mStream_BufPos + (at - buf); + if (localContent > contentEof) // buffered past eof? + contentEof = localContent; // return new logical eof + + outPos = contentEof; + } else + this->NewBadCursorOrderError(ev); + } + } else + outPos = contentEof; // frozen files get length from content file + } + } else + this->NewFileDownError(ev); + + return outPos; +} + +void morkStream::NewBadCursorSlotsError(morkEnv* ev) const { + ev->NewError("bad stream cursor slots"); +} + +void morkStream::NewNullStreamBufferError(morkEnv* ev) const { + ev->NewError("null stream buffer"); +} + +void morkStream::NewCantReadSinkError(morkEnv* ev) const { + ev->NewError("can't read stream sink"); +} + +void morkStream::NewCantWriteSourceError(morkEnv* ev) const { + ev->NewError("can't write stream source"); +} + +void morkStream::NewPosBeyondEofError(morkEnv* ev) const { + ev->NewError("stream pos beyond eof"); +} + +void morkStream::NewBadCursorOrderError(morkEnv* ev) const { + ev->NewError("bad stream cursor order"); +} + +NS_IMETHODIMP +morkStream::Tell(nsIMdbEnv* mdbev, mork_pos* aOutPos) const { + nsresult rv = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mdbev); + + NS_ENSURE_ARG_POINTER(aOutPos); + + nsIMdbFile* file = mStream_ContentFile; + if (this->IsOpenAndActiveFile() && file) { + mork_u1* buf = mStream_Buf; + mork_u1* at = mStream_At; + + mork_u1* readEnd = mStream_ReadEnd; // nonzero only if readonly + mork_u1* writeEnd = mStream_WriteEnd; // nonzero only if writeonly + + if (writeEnd) { + if (buf && at >= buf && at <= writeEnd) { + *aOutPos = mStream_BufPos + (at - buf); + } else + this->NewBadCursorOrderError(ev); + } else if (readEnd) { + if (buf && at >= buf && at <= readEnd) { + *aOutPos = mStream_BufPos + (at - buf); + } else + this->NewBadCursorOrderError(ev); + } + } else + this->NewFileDownError(ev); + + return rv; +} + +NS_IMETHODIMP +morkStream::Read(nsIMdbEnv* mdbev, void* outBuf, mork_size inSize, + mork_size* aOutSize) { + NS_ENSURE_ARG_POINTER(aOutSize); + // First we satisfy the request from buffered bytes, if any. Then + // if additional bytes are needed, we satisfy these by direct reads + // from the content file without any local buffering (but we still need + // to adjust the buffer position to reflect the current i/o point). + + morkEnv* ev = morkEnv::FromMdbEnv(mdbev); + nsresult rv = NS_OK; + + nsIMdbFile* file = mStream_ContentFile; + if (this->IsOpenAndActiveFile() && file) { + mork_u1* end = mStream_ReadEnd; // byte after last buffered byte + if (end) // file is open for read access? + { + if (inSize) // caller wants any output? + { + mork_u1* sink = (mork_u1*)outBuf; // where we plan to write bytes + if (sink) // caller passed good buffer address? + { + mork_u1* at = mStream_At; + mork_u1* buf = mStream_Buf; + if (at >= buf && at <= end) // expected cursor order? + { + mork_num remaining = (mork_num)(end - at); // bytes left in buffer + + mork_num quantum = inSize; // number of bytes to copy + if (quantum > remaining) // more than buffer content? + quantum = remaining; // restrict to buffered bytes + + if (quantum) // any bytes left in the buffer? + { + MORK_MEMCPY(sink, at, quantum); // from buffer bytes + + at += quantum; // advance past read bytes + mStream_At = at; + *aOutSize += quantum; // this much copied so far + + sink += quantum; // in case we need to copy more + inSize -= quantum; // filled this much of request + mStream_HitEof = morkBool_kFalse; + } + + if (inSize) // we still need to read more content? + { + // We need to read more bytes directly from the + // content file, without local buffering. We have + // exhausted the local buffer, so we need to show + // it is now empty, and adjust the current buf pos. + + mork_num posDelta = (mork_num)(at - buf); // old buf content + mStream_BufPos += posDelta; // past now empty buf + + mStream_At = mStream_ReadEnd = buf; // empty buffer + + // file->Seek(ev, mStream_BufPos); // set file pos + // if ( ev->Good() ) // no seek error? + // { + // } + + mork_num actual = 0; + nsIMdbEnv* menv = ev->AsMdbEnv(); + file->Get(menv, sink, inSize, mStream_BufPos, &actual); + if (ev->Good()) // no read error? + { + if (actual) { + *aOutSize += actual; + mStream_BufPos += actual; + mStream_HitEof = morkBool_kFalse; + } else if (!*aOutSize) + mStream_HitEof = morkBool_kTrue; + } + } + } else + this->NewBadCursorOrderError(ev); + } else + this->NewNullStreamBufferError(ev); + } + } else + this->NewCantReadSinkError(ev); + } else + this->NewFileDownError(ev); + + if (ev->Bad()) *aOutSize = 0; + + return rv; +} + +NS_IMETHODIMP +morkStream::Seek(nsIMdbEnv* mdbev, mork_pos inPos, mork_pos* aOutPos) { + NS_ENSURE_ARG_POINTER(aOutPos); + morkEnv* ev = morkEnv::FromMdbEnv(mdbev); + *aOutPos = 0; + nsresult rv = NS_OK; + nsIMdbFile* file = mStream_ContentFile; + if (this->IsOpenOrClosingNode() && this->FileActive() && file) { + mork_u1* at = mStream_At; // current position in buffer + mork_u1* buf = mStream_Buf; // beginning of buffer + mork_u1* readEnd = mStream_ReadEnd; // nonzero only if readonly + mork_u1* writeEnd = mStream_WriteEnd; // nonzero only if writeonly + + if (writeEnd) // file is mutable/writeonly? + { + if (mStream_Dirty) // need to commit buffer changes? + this->Flush(mdbev); + + if (ev->Good()) // no errors during flush or earlier? + { + if (at == buf) // expected post flush cursor value? + { + if (mStream_BufPos != inPos) // need to change pos? + { + mork_pos eof = 0; + nsIMdbEnv* menv = ev->AsMdbEnv(); + file->Eof(menv, &eof); + if (ev->Good()) // no errors getting length? + { + if (inPos <= eof) // acceptable new position? + { + mStream_BufPos = inPos; // new stream position + *aOutPos = inPos; + } else + this->NewPosBeyondEofError(ev); + } + } + } else + this->NewBadCursorOrderError(ev); + } + } else if (readEnd) // file is frozen/readonly? + { + if (at >= buf && at <= readEnd) // expected cursor order? + { + mork_pos eof = 0; + nsIMdbEnv* menv = ev->AsMdbEnv(); + file->Eof(menv, &eof); + if (ev->Good()) // no errors getting length? + { + if (inPos <= eof) // acceptable new position? + { + *aOutPos = inPos; + mStream_BufPos = inPos; // new stream position + mStream_At = mStream_ReadEnd = buf; // empty buffer + if (inPos == eof) // notice eof reached? + mStream_HitEof = morkBool_kTrue; + } else + this->NewPosBeyondEofError(ev); + } + } else + this->NewBadCursorOrderError(ev); + } + + } else + this->NewFileDownError(ev); + + return rv; +} + +NS_IMETHODIMP +morkStream::Write(nsIMdbEnv* menv, const void* inBuf, mork_size inSize, + mork_size* aOutSize) { + mork_num outActual = 0; + morkEnv* ev = morkEnv::FromMdbEnv(menv); + + nsIMdbFile* file = mStream_ContentFile; + if (this->IsOpenActiveAndMutableFile() && file) { + mork_u1* end = mStream_WriteEnd; // byte after last buffered byte + if (end) // file is open for write access? + { + if (inSize) // caller provided any input? + { + const mork_u1* source = (const mork_u1*)inBuf; // from where + if (source) // caller passed good buffer address? + { + mork_u1* at = mStream_At; + mork_u1* buf = mStream_Buf; + if (at >= buf && at <= end) // expected cursor order? + { + mork_num space = (mork_num)(end - at); // space left in buffer + + mork_num quantum = inSize; // number of bytes to write + if (quantum > space) // more than buffer size? + quantum = space; // restrict to avail space + + if (quantum) // any space left in the buffer? + { + mStream_Dirty = morkBool_kTrue; // to ensure later flush + MORK_MEMCPY(at, source, quantum); // into buffer + + mStream_At += quantum; // advance past written bytes + outActual += quantum; // this much written so far + + source += quantum; // in case we need to write more + inSize -= quantum; // filled this much of request + } + + if (inSize) // we still need to write more content? + { + // We need to write more bytes directly to the + // content file, without local buffering. We have + // exhausted the local buffer, so we need to flush + // it and empty it, and adjust the current buf pos. + // After flushing, if the rest of the write fits + // inside the buffer, we will put bytes into the + // buffer rather than write them to content file. + + if (mStream_Dirty) + this->Flush(menv); // will update mStream_BufPos + + at = mStream_At; + if (at < buf || at > end) // bad cursor? + this->NewBadCursorOrderError(ev); + + if (ev->Good()) // no errors? + { + space = (mork_num)(end - at); // space left in buffer + if (space > inSize) // write to buffer? + { + mStream_Dirty = morkBool_kTrue; // ensure flush + MORK_MEMCPY(at, source, inSize); // copy + + mStream_At += inSize; // past written bytes + outActual += inSize; // this much written + } else // directly to content file instead + { + // file->Seek(ev, mStream_BufPos); // set pos + // if ( ev->Good() ) // no seek error? + // { + // } + + mork_num actual = 0; + file->Put(menv, source, inSize, mStream_BufPos, &actual); + if (ev->Good()) // no write error? + { + outActual += actual; + mStream_BufPos += actual; + } + } + } + } + } else + this->NewBadCursorOrderError(ev); + } else + this->NewNullStreamBufferError(ev); + } + } else + this->NewCantWriteSourceError(ev); + } else + this->NewFileDownError(ev); + + if (ev->Bad()) outActual = 0; + + *aOutSize = outActual; + return ev->AsErr(); +} + +NS_IMETHODIMP +morkStream::Flush(nsIMdbEnv* ev) { + morkEnv* mev = morkEnv::FromMdbEnv(ev); + nsresult rv = NS_ERROR_FAILURE; + nsIMdbFile* file = mStream_ContentFile; + if (this->IsOpenOrClosingNode() && this->FileActive() && file) { + if (mStream_Dirty) this->spill_buf(mev); + + rv = file->Flush(ev); + } else + this->NewFileDownError(mev); + return rv; +} + +// ````` ````` ````` ````` ````` ````` ````` ````` +// protected: // protected non-poly morkStream methods (for char io) + +int morkStream::fill_getc(morkEnv* ev) { + int c = EOF; + + nsIMdbFile* file = mStream_ContentFile; + if (this->IsOpenAndActiveFile() && file) { + mork_u1* buf = mStream_Buf; + mork_u1* end = mStream_ReadEnd; // beyond buf after earlier read + if (end > buf) // any earlier read bytes buffered? + { + mStream_BufPos += (end - buf); // advance past old read + } + + if (ev->Good()) // no errors yet? + { + // file->Seek(ev, mStream_BufPos); // set file pos + // if ( ev->Good() ) // no seek error? + // { + // } + + nsIMdbEnv* menv = ev->AsMdbEnv(); + mork_num actual = 0; + file->Get(menv, buf, mStream_BufSize, mStream_BufPos, &actual); + if (ev->Good()) // no read errors? + { + if (actual > mStream_BufSize) // more than asked for?? + actual = mStream_BufSize; + + mStream_At = buf; + mStream_ReadEnd = buf + actual; + if (actual) // any bytes actually read? + { + c = *mStream_At++; // return first byte from buffer + mStream_HitEof = morkBool_kFalse; + } else + mStream_HitEof = morkBool_kTrue; + } + } + } else + this->NewFileDownError(ev); + + return c; +} + +void morkStream::spill_putc(morkEnv* ev, int c) { + this->spill_buf(ev); + if (ev->Good() && mStream_At < mStream_WriteEnd) this->Putc(ev, c); +} + +void morkStream::spill_buf(morkEnv* ev) // spill/flush from buffer to file +{ + nsIMdbFile* file = mStream_ContentFile; + if (this->IsOpenOrClosingNode() && this->FileActive() && file) { + mork_u1* buf = mStream_Buf; + if (mStream_Dirty) { + mork_u1* at = mStream_At; + if (at >= buf && at <= mStream_WriteEnd) // order? + { + mork_num count = (mork_num)(at - buf); // bytes buffered + if (count) // anything to write to the string? + { + if (count > mStream_BufSize) // no more than max? + { + count = mStream_BufSize; + mStream_WriteEnd = buf + mStream_BufSize; + this->NewBadCursorSlotsError(ev); + } + if (ev->Good()) { + // file->Seek(ev, mStream_BufPos); + // if ( ev->Good() ) + // { + // } + nsIMdbEnv* menv = ev->AsMdbEnv(); + mork_num actual = 0; + + file->Put(menv, buf, count, mStream_BufPos, &actual); + if (ev->Good()) { + mStream_BufPos += actual; // past bytes written + mStream_At = buf; // reset buffer cursor + mStream_Dirty = morkBool_kFalse; + } + } + } + } else + this->NewBadCursorOrderError(ev); + } else { +#ifdef MORK_DEBUG + ev->NewWarning("stream:spill:not:dirty"); +#endif /*MORK_DEBUG*/ + } + } else + this->NewFileDownError(ev); +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkStream.h b/comm/mailnews/db/mork/morkStream.h new file mode 100644 index 0000000000..be7a40c1dc --- /dev/null +++ b/comm/mailnews/db/mork/morkStream.h @@ -0,0 +1,258 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MORKSTREAM_ +#define _MORKSTREAM_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKFILE_ +# include "morkFile.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*============================================================================= + * morkStream: buffered file i/o + */ + +/*| morkStream exists to define an morkFile subclass that provides buffered +**| i/o for an underlying content file. Naturally this arrangement only makes +**| sense when the underlying content file is itself not efficiently buffered +**| (especially for character by character i/o). +**| +**|| morkStream is intended for either reading use or writing use, but not +**| both simultaneously or interleaved. Pick one when the stream is created +**| and don't change your mind. This restriction is intended to avoid obscure +**| and complex bugs that might arise from interleaved reads and writes -- so +**| just don't do it. A stream is either a sink or a source, but not both. +**| +**|| (When the underlying content file is intended to support both reading and +**| writing, a developer might use two instances of morkStream where one is for +**| reading and the other is for writing. In this case, a developer must take +**| care to keep the two streams in sync because each will maintain a separate +**| buffer representing a cache consistency problem for the other. A simple +**| approach is to invalidate the buffer of one when one uses the other, with +**| the assumption that closely mixed reading and writing is not expected, so +**| that little cost is associated with changing read/write streaming modes.) +**| +**|| Exactly one of mStream_ReadEnd or mStream_WriteEnd must be a null pointer, +**| and this will cause the right thing to occur when inlines use them, because +**| mStream_At < mStream_WriteEnd (for example) will always be false and the +**| else branch of the statement calls a function that raises an appropriate +**| error to complain about either reading a sink or writing a source. +**| +**|| morkStream is a direct clone of ab_Stream from Communicator 4.5's +**| address book code, which in turn was based on the stream class in the +**| public domain Mithril programming language. +|*/ + +#define morkStream_kPrintBufSize /*i*/ 512 /* buffer size used by printf() */ + +#define morkStream_kMinBufSize /*i*/ 512 /* buffer no fewer bytes */ +#define morkStream_kMaxBufSize /*i*/ (32 * 1024) /* buffer no more bytes */ + +#define morkDerived_kStream /*i*/ 0x7A74 /* ascii 'zt' */ + +class morkStream /*d*/ : public morkFile { /* from Mithril's AgStream class */ + + // ````` ````` ````` ````` ````` ````` ````` ````` + protected: // protected morkStream members + mork_u1* mStream_At; // pointer into mStream_Buf + mork_u1* mStream_ReadEnd; // null or one byte past last readable byte + mork_u1* mStream_WriteEnd; // null or mStream_Buf + mStream_BufSize + + nsIMdbFile* mStream_ContentFile; // where content is read and written + + mork_u1* mStream_Buf; // dynamically allocated memory to buffer io + mork_size mStream_BufSize; // requested buf size (fixed by min and max) + mork_pos mStream_BufPos; // logical position of byte at mStream_Buf + mork_bool mStream_Dirty; // does the buffer need to be flushed? + mork_bool mStream_HitEof; // has eof been reached? (only frozen streams) + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseStream() only if open + virtual ~morkStream(); // assert that CloseStream() executed earlier + + public: // morkStream construction & destruction + morkStream(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbFile* ioContentFile, mork_size inBufSize, + mork_bool inFrozen); + void CloseStream(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkStream(const morkStream& other); + morkStream& operator=(const morkStream& other); + + public: // dynamic type identification + mork_bool IsStream() const { + return IsNode() && mNode_Derived == morkDerived_kStream; + } + // } ===== end morkNode methods ===== + + public: // typing + void NonStreamTypeError(morkEnv* ev); + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // virtual morkFile methods + NS_IMETHOD Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) override; + // Steal: tell this file to close any associated i/o stream in the file + // system, because the file ioThief intends to reopen the file in order + // to provide the MDB implementation with more exotic file access than is + // offered by the nsIMdbFile alone. Presumably the thief knows enough + // from Path() in order to know which file to reopen. If Steal() is + // successful, this file should probably delegate all future calls to + // the nsIMdbFile interface down to the thief files, so that even after + // the file has been stolen, it can still be read, written, or forcibly + // closed (by a call to CloseMdbObject()). + + NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev) override; + // If this file is a file version branch created by calling AcquireBud(), + // BecomeTrunk() causes this file's content to replace the original + // file's content, typically by assuming the original file's identity. + + NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + nsIMdbFile** acqBud) override; + // AcquireBud() starts a new "branch" version of the file, empty of content, + // so that a new version of the file can be written. This new file + // can later be told to BecomeTrunk() the original file, so the branch + // created by budding the file will replace the original file. Some + // file subclasses might initially take the unsafe but expedient + // approach of simply truncating this file down to zero length, and + // then returning the same morkFile pointer as this, with an extra + // reference count increment. Note that the caller of AcquireBud() is + // expected to eventually call CutStrongRef() on the returned file + // in order to release the strong reference. High quality versions + // of morkFile subclasses will create entirely new files which later + // are renamed to become the old file, so that better transactional + // behavior is exhibited by the file, so crashes protect old files. + // Note that AcquireBud() is an illegal operation on readonly files. + + virtual mork_pos Length(morkEnv* ev) const override; // eof + NS_IMETHOD Tell(nsIMdbEnv* ev, mork_pos* aOutPos) const override; + NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mork_size inSize, + mork_size* aOutCount) override; + NS_IMETHOD Seek(nsIMdbEnv* ev, mork_pos inPos, mork_pos* aOutPos) override; + NS_IMETHOD Write(nsIMdbEnv* ev, const void* inBuf, mork_size inSize, + mork_size* aOutCount) override; + NS_IMETHOD Flush(nsIMdbEnv* ev) override; + + // ````` ````` ````` ````` ````` ````` ````` ````` + protected: // protected non-poly morkStream methods (for char io) + int fill_getc(morkEnv* ev); + void spill_putc(morkEnv* ev, int c); + void spill_buf(morkEnv* ev); // spill/flush from buffer to file + + // ````` ````` ````` ````` ````` ````` ````` ````` + public: // public non-poly morkStream methods + void NewBadCursorSlotsError(morkEnv* ev) const; + void NewBadCursorOrderError(morkEnv* ev) const; + void NewNullStreamBufferError(morkEnv* ev) const; + void NewCantReadSinkError(morkEnv* ev) const; + void NewCantWriteSourceError(morkEnv* ev) const; + void NewPosBeyondEofError(morkEnv* ev) const; + + nsIMdbFile* GetStreamContentFile() const { return mStream_ContentFile; } + mork_size GetStreamBufferSize() const { return mStream_BufSize; } + + mork_size PutIndent(morkEnv* ev, mork_count inDepth); + // PutIndent() puts a linebreak, and then + // "indents" by inDepth, and returns the line length after indentation. + + mork_size PutByteThenIndent(morkEnv* ev, int inByte, mork_count inDepth); + // PutByteThenIndent() puts the byte, then a linebreak, and then + // "indents" by inDepth, and returns the line length after indentation. + + mork_size PutStringThenIndent(morkEnv* ev, const char* inString, + mork_count inDepth); + // PutStringThenIndent() puts the string, then a linebreak, and then + // "indents" by inDepth, and returns the line length after indentation. + + mork_size PutString(morkEnv* ev, const char* inString); + // PutString() returns the length of the string written. + + mork_size PutStringThenNewline(morkEnv* ev, const char* inString); + // PutStringThenNewline() returns total number of bytes written. + + mork_size PutByteThenNewline(morkEnv* ev, int inByte); + // PutByteThenNewline() returns total number of bytes written. + + // ````` ````` stdio type methods ````` ````` + void Ungetc(int c) /*i*/ + { + if (mStream_At > mStream_Buf && c > 0) *--mStream_At = (mork_u1)c; + } + + // Note Getc() returns EOF consistently after any fill_getc() error occurs. + int Getc(morkEnv* ev) /*i*/ + { + return (mStream_At < mStream_ReadEnd) ? *mStream_At++ : fill_getc(ev); + } + + void Putc(morkEnv* ev, int c) /*i*/ + { + mStream_Dirty = morkBool_kTrue; + if (mStream_At < mStream_WriteEnd) + *mStream_At++ = (mork_u1)c; + else + spill_putc(ev, c); + } + + mork_size PutLineBreak(morkEnv* ev); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakStream(morkStream* me, morkEnv* ev, morkStream** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongStream(morkStream* me, morkEnv* ev, + morkStream** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKSTREAM_ */ diff --git a/comm/mailnews/db/mork/morkTable.cpp b/comm/mailnews/db/mork/morkTable.cpp new file mode 100644 index 0000000000..ee27ff1328 --- /dev/null +++ b/comm/mailnews/db/mork/morkTable.cpp @@ -0,0 +1,1415 @@ +/* -*- 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 diff --git a/comm/mailnews/db/mork/morkTable.h b/comm/mailnews/db/mork/morkTable.h new file mode 100644 index 0000000000..c0ca5ddd84 --- /dev/null +++ b/comm/mailnews/db/mork/morkTable.h @@ -0,0 +1,742 @@ +/* -*- 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 _MORKTABLE_ +#define _MORKTABLE_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKDEQUE_ +# include "morkDeque.h" +#endif + +#ifndef _MORKOBJECT_ +# include "morkObject.h" +#endif + +#ifndef _MORKARRAY_ +# include "morkArray.h" +#endif + +#ifndef _MORKROWMAP_ +# include "morkRowMap.h" +#endif + +#ifndef _MORKNODEMAP_ +# include "morkNodeMap.h" +#endif + +#ifndef _MORKPROBEMAP_ +# include "morkProbeMap.h" +#endif + +#ifndef _MORKBEAD_ +# include "morkBead.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class nsIMdbTable; +#define morkDerived_kTable /*i*/ 0x5462 /* ascii 'Tb' */ + +/*| kStartRowArraySize: starting physical size of array for mTable_RowArray. +**| We want this number very small, so that a table containing exactly one +**| row member will not pay too significantly in space overhead. But we want +**| a number bigger than one, so there is some space for growth. +|*/ +#define morkTable_kStartRowArraySize 3 /* modest starting size for array */ + +/*| kMakeRowMapThreshold: this is the number of rows in a table which causes +**| a hash table (mTable_RowMap) to be lazily created for faster member row +**| identification, during such operations as cuts and adds. This number must +**| be small enough that linear searches are not bad for member counts less +**| than this; but this number must also be large enough that creating a hash +**| table does not increase the per-row space overhead by a big percentage. +**| For speed, numbers on the order of ten to twenty are all fine; for space, +**| I believe a number as small as ten will have too much space overhead. +|*/ +#define morkTable_kMakeRowMapThreshold 17 /* when to build mTable_RowMap */ + +#define morkTable_kStartRowMapSlotCount 13 +#define morkTable_kMaxTableGcUses 0x0FF /* max for 8-bit unsigned int */ + +#define morkTable_kUniqueBit ((mork_u1)(1 << 0)) +#define morkTable_kVerboseBit ((mork_u1)(1 << 1)) +#define morkTable_kNotedBit ((mork_u1)(1 << 2)) /* space has change notes */ +#define morkTable_kRewriteBit ((mork_u1)(1 << 3)) /* must rewrite all rows */ +#define morkTable_kNewMetaBit ((mork_u1)(1 << 4)) /* new table meta row */ + +class morkTable : public morkObject, public morkLink, public nsIMdbTable { + // NOTE the morkLink base is for morkRowSpace::mRowSpace_TablesByPriority + + // public: // slots inherited from morkObject (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // mork_color mBead_Color; // ID for this bead + // morkHandle* mObject_Handle; // weak ref to handle for this object + + public: // bead color setter & getter replace obsolete member mTable_Id: + NS_DECL_ISUPPORTS_INHERITED + mork_tid TableId() const { return mBead_Color; } + void SetTableId(mork_tid inTid) { mBead_Color = inTid; } + + // we override these so we use xpcom ref-counting semantics. +#ifndef _MSC_VER + // The first declaration of AddStrongRef is to suppress + // -Werror,-Woverloaded-virtual. + virtual mork_refs AddStrongRef(nsIMdbEnv* ev) override; +#endif + virtual mork_refs AddStrongRef(morkEnv* ev) override; +#ifndef _MSC_VER + // The first declaration of CutStrongRef is to suppress + // -Werror,-Woverloaded-virtual. + virtual nsresult CutStrongRef(nsIMdbEnv* ev) override; +#endif + virtual mork_refs CutStrongRef(morkEnv* ev) override; + + public: + // { ===== begin nsIMdbCollection methods ===== + // { ----- begin attribute methods ----- + NS_IMETHOD GetSeed(nsIMdbEnv* ev, + mdb_seed* outSeed) override; // member change count + NS_IMETHOD GetCount(nsIMdbEnv* ev, + mdb_count* outCount) override; // member count + + NS_IMETHOD GetPort(nsIMdbEnv* ev, + nsIMdbPort** acqPort) override; // collection container + // } ----- end attribute methods ----- + + // { ----- begin cursor methods ----- + NS_IMETHOD GetCursor( // make a cursor starting iter at inMemberPos + nsIMdbEnv* ev, // context + mdb_pos inMemberPos, // zero-based ordinal pos of member in collection + nsIMdbCursor** acqCursor) override; // acquire new cursor instance + // } ----- end cursor methods ----- + + // { ----- begin ID methods ----- + NS_IMETHOD GetOid(nsIMdbEnv* ev, + mdbOid* outOid) override; // read object identity + NS_IMETHOD BecomeContent(nsIMdbEnv* ev, + const mdbOid* inOid) override; // exchange content + // } ----- end ID methods ----- + + // { ----- begin activity dropping methods ----- + NS_IMETHOD DropActivity( // tell collection usage no longer expected + nsIMdbEnv* ev) override; + // } ----- end activity dropping methods ----- + + // } ===== end nsIMdbCollection methods ===== + NS_IMETHOD SetTablePriority(nsIMdbEnv* ev, mdb_priority inPrio) override; + NS_IMETHOD GetTablePriority(nsIMdbEnv* ev, mdb_priority* outPrio) override; + + NS_IMETHOD GetTableBeVerbose(nsIMdbEnv* ev, mdb_bool* outBeVerbose) override; + NS_IMETHOD SetTableBeVerbose(nsIMdbEnv* ev, mdb_bool inBeVerbose) override; + + NS_IMETHOD GetTableIsUnique(nsIMdbEnv* ev, mdb_bool* outIsUnique) override; + + NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) override; + NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) override; + + NS_IMETHOD GetMetaRow( + nsIMdbEnv* ev, // context + const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying + mdbOid* outOid, // output meta row oid, can be nil to suppress output + nsIMdbRow** acqRow) + override; // 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 "m" (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. + // } ----- end meta attribute methods ----- + + // { ----- begin cursor methods ----- + NS_IMETHOD + GetTableRowCursor( // make a cursor, starting iteration at inRowPos + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbTableRowCursor** acqCursor) + override; // acquire new cursor instance + // } ----- end row position methods ----- + + // { ----- begin row position methods ----- + NS_IMETHOD PosToOid( // get row member for a table position + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + mdbOid* outOid) override; // row oid at the specified position + + NS_IMETHOD OidToPos( // test for the table position of a row member + nsIMdbEnv* ev, // context + const mdbOid* inOid, // row to find in table + mdb_pos* outPos) override; // zero-based ordinal position of row in table + + NS_IMETHOD PosToRow( // test for the table position of a row member + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbRow** acqRow) override; // acquire row at table position inRowPos + + NS_IMETHOD RowToPos( // test for the table position of a row member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow, // row to find in table + mdb_pos* outPos) override; // zero-based ordinal position of row in table + // } ----- end row position methods ----- + + // { ----- begin oid set methods ----- + NS_IMETHOD AddOid( // make sure the row with inOid is a table member + nsIMdbEnv* ev, // context + const mdbOid* inOid) override; // row to ensure membership in table + + NS_IMETHOD HasOid( // test for the table position of a row member + nsIMdbEnv* ev, // context + const mdbOid* inOid, // row to find in table + mdb_bool* outHasOid) override; // whether inOid is a member row + + NS_IMETHOD CutOid( // make sure the row with inOid is not a member + nsIMdbEnv* ev, // context + const mdbOid* inOid) override; // row to remove from table + // } ----- end oid set methods ----- + + // { ----- begin row set methods ----- + NS_IMETHOD NewRow( // create a new row instance in table + nsIMdbEnv* ev, // context + mdbOid* + ioOid, // please use minus one (unbound) rowId for db-assigned IDs + nsIMdbRow** acqRow) override; // create new row + + NS_IMETHOD AddRow( // make sure the row with inOid is a table member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow) override; // row to ensure membership in table + + NS_IMETHOD HasRow( // test for the table position of a row member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow, // row to find in table + mdb_bool* outHasRow) override; // whether row is a table member + + NS_IMETHOD CutRow( // make sure the row with inOid is not a member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow) override; // row to remove from table + + NS_IMETHOD CutAllRows( // remove all rows from the table + nsIMdbEnv* ev) override; // context + // } ----- end row set methods ----- + + // { ----- begin hinting methods ----- + NS_IMETHOD SearchColumnsHint( // advise re future expected search cols + nsIMdbEnv* ev, // context + const mdbColumnSet* inColumnSet) + override; // columns likely to be searched + + NS_IMETHOD SortColumnsHint( // advise re future expected sort columns + nsIMdbEnv* ev, // context + const mdbColumnSet* inColumnSet) + override; // columns for likely sort requests + + NS_IMETHOD StartBatchChangeHint( // advise before many adds and cuts + nsIMdbEnv* ev, // context + const void* inLabel) override; // 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. + + NS_IMETHOD EndBatchChangeHint( // advise before many adds and cuts + nsIMdbEnv* ev, // context + const void* inLabel) override; // 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. + // } ----- end hinting methods ----- + + // { ----- begin searching methods ----- + NS_IMETHOD FindRowMatches( // search variable number of sorted cols + nsIMdbEnv* ev, // context + const mdbYarn* + inPrefix, // content to find as prefix in row's column cell + nsIMdbTableRowCursor** acqCursor) override; // set of matching rows + + NS_IMETHOD GetSearchColumns( // query columns used by FindRowMatches() + nsIMdbEnv* ev, // context + mdb_count* outCount, // context + mdbColumnSet* outColSet) + override; // 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. + // } ----- end searching 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_IMETHOD + CanSortColumn( // query which column is currently used for sorting + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to query sorting potential + mdb_bool* outCanSort) override; // whether the column can be sorted + + NS_IMETHOD GetSorting( // view same table in particular sorting + nsIMdbEnv* ev, // context + mdb_column inColumn, // requested new column for sorting table + nsIMdbSorting** acqSorting) override; // acquire sorting for column + + NS_IMETHOD SetSearchSorting( // use this sorting in FindRowMatches() + nsIMdbEnv* ev, // context + mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn() + nsIMdbSorting* ioSorting) override; // 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). + // } ----- end sorting methods ----- + + // { ----- begin moving methods ----- + // moving a row does nothing unless a table is currently unsorted + + NS_IMETHOD MoveOid( // change position of row in unsorted table + nsIMdbEnv* ev, // 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 inRowId + mdb_pos* outActualPos) override; // actual new position of row in table + + NS_IMETHOD MoveRow( // change position of row in unsorted table + nsIMdbEnv* ev, // 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 inRowId + mdb_pos* outActualPos) override; // actual new position of row in table + // } ----- end moving methods ----- + + // { ----- begin index methods ----- + NS_IMETHOD AddIndex( // create a sorting index for column if possible + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column to sort by index + nsIMdbThumb** acqThumb) + override; // 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_IMETHOD CutIndex( // stop supporting a specific column index + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column with index to be removed + nsIMdbThumb** acqThumb) + override; // 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_IMETHOD HasIndex( // query for current presence of a column index + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column to investigate + mdb_bool* outHasIndex) + override; // whether column has index for this column + + NS_IMETHOD EnableIndexOnSort( // create an index for col on first sort + nsIMdbEnv* ev, // context + mdb_column inColumn) override; // the column to index if ever sorted + + NS_IMETHOD QueryIndexOnSort( // check whether index on sort is enabled + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column to investigate + mdb_bool* outIndexOnSort) + override; // whether column has index-on-sort enabled + + NS_IMETHOD DisableIndexOnSort( // prevent future index creation on sort + nsIMdbEnv* ev, // context + mdb_column inColumn) override; // the column to index if ever sorted + // } ----- end index methods ----- + + morkStore* mTable_Store; // non-refcnted ptr to port + + // mTable_RowSpace->SpaceScope() is row scope + morkRowSpace* mTable_RowSpace; // non-refcnted ptr to containing space + + morkRow* mTable_MetaRow; // table's actual meta row + mdbOid mTable_MetaRowOid; // oid for meta row + + morkRowMap* mTable_RowMap; // (strong ref) hash table of all members + morkArray mTable_RowArray; // array of morkRow pointers + + morkList mTable_ChangeList; // list of table changes + mork_u2 mTable_ChangesCount; // length of changes list + mork_u2 mTable_ChangesMax; // max list length before rewrite + + // mork_tid mTable_Id; + mork_kind mTable_Kind; + + mork_u1 mTable_Flags; // bit flags + mork_priority mTable_Priority; // 0..9, any other value equals 9 + mork_u1 mTable_GcUses; // persistent references from cells + mork_u1 mTable_Pad; // for u4 alignment + + public: // flags bit twiddling + void SetTableUnique() { mTable_Flags |= morkTable_kUniqueBit; } + void SetTableVerbose() { mTable_Flags |= morkTable_kVerboseBit; } + void SetTableNoted() { mTable_Flags |= morkTable_kNotedBit; } + void SetTableRewrite() { mTable_Flags |= morkTable_kRewriteBit; } + void SetTableNewMeta() { mTable_Flags |= morkTable_kNewMetaBit; } + + void ClearTableUnique() { mTable_Flags &= (mork_u1)~morkTable_kUniqueBit; } + void ClearTableVerbose() { mTable_Flags &= (mork_u1)~morkTable_kVerboseBit; } + void ClearTableNoted() { mTable_Flags &= (mork_u1)~morkTable_kNotedBit; } + void ClearTableRewrite() { mTable_Flags &= (mork_u1)~morkTable_kRewriteBit; } + void ClearTableNewMeta() { mTable_Flags &= (mork_u1)~morkTable_kNewMetaBit; } + + mork_bool IsTableUnique() const { + return (mTable_Flags & morkTable_kUniqueBit) != 0; + } + + mork_bool IsTableVerbose() const { + return (mTable_Flags & morkTable_kVerboseBit) != 0; + } + + mork_bool IsTableNoted() const { + return (mTable_Flags & morkTable_kNotedBit) != 0; + } + + mork_bool IsTableRewrite() const { + return (mTable_Flags & morkTable_kRewriteBit) != 0; + } + + mork_bool IsTableNewMeta() const { + return (mTable_Flags & morkTable_kNewMetaBit) != 0; + } + + public + : // table dirty handling more complex than morkNode::SetNodeDirty() etc. + void SetTableDirty() { this->SetNodeDirty(); } + void SetTableClean(morkEnv* ev); + + mork_bool IsTableClean() const { return this->IsNodeClean(); } + mork_bool IsTableDirty() const { return this->IsNodeDirty(); } + + public: // morkNode memory management operators + void* operator new(size_t inSize, nsIMdbHeap& ioHeap, + morkEnv* ev) noexcept(true) { + return morkNode::MakeNew(inSize, ioHeap, ev); + } + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseTable() if open + + public: // morkTable construction & destruction + morkTable( + morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap, + morkStore* ioStore, nsIMdbHeap* ioSlotHeap, morkRowSpace* ioRowSpace, + const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying + mork_tid inTableId, mork_kind inKind, mork_bool inMustBeUnique); + void CloseTable(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkTable(const morkTable& other); + morkTable& operator=(const morkTable& other); + virtual ~morkTable(); // assert that close executed earlier + + public: // dynamic type identification + mork_bool IsTable() const { + return IsNode() && mNode_Derived == morkDerived_kTable; + } + // } ===== end morkNode methods ===== + + public: // errors + static void NonTableTypeError(morkEnv* ev); + static void NonTableTypeWarning(morkEnv* ev); + static void NilRowSpaceError(morkEnv* ev); + + public: // warnings + static void TableGcUsesUnderflowWarning(morkEnv* ev); + + public: // noting table changes + mork_bool HasChangeOverflow() const { + return mTable_ChangesCount >= mTable_ChangesMax; + } + + void NoteTableSetAll(morkEnv* ev); + void NoteTableMoveRow(morkEnv* ev, morkRow* ioRow, mork_pos inPos); + + void note_row_change(morkEnv* ev, mork_change inChange, morkRow* ioRow); + void note_row_move(morkEnv* ev, morkRow* ioRow, mork_pos inNewPos); + + void NoteTableAddRow(morkEnv* ev, morkRow* ioRow) { + this->note_row_change(ev, morkChange_kAdd, ioRow); + } + + void NoteTableCutRow(morkEnv* ev, morkRow* ioRow) { + this->note_row_change(ev, morkChange_kCut, ioRow); + } + + protected: // internal row map methods + morkRow* find_member_row(morkEnv* ev, morkRow* ioRow); + void build_row_map(morkEnv* ev); + + public: // other table methods + mork_bool MaybeDirtySpaceStoreAndTable(); + + morkRow* GetMetaRow(morkEnv* ev, const mdbOid* inOptionalMetaRowOid); + + mork_u2 AddTableGcUse(morkEnv* ev); + mork_u2 CutTableGcUse(morkEnv* ev); + + // void DirtyAllTableContent(morkEnv* ev); + + mork_seed TableSeed() const { return mTable_RowArray.mArray_Seed; } + + morkRow* SafeRowAt(morkEnv* ev, mork_pos inPos) { + return (morkRow*)mTable_RowArray.SafeAt(ev, inPos); + } + + nsIMdbTable* AcquireTableHandle(morkEnv* ev); // mObject_Handle + + mork_count GetRowCount() const { return mTable_RowArray.mArray_Fill; } + + mork_bool IsTableUsed() const { + return (mTable_GcUses != 0 || this->GetRowCount() != 0); + } + + void GetTableOid(morkEnv* ev, mdbOid* outOid); + mork_pos ArrayHasOid(morkEnv* ev, const mdbOid* inOid); + mork_bool MapHasOid(morkEnv* ev, const mdbOid* inOid); + mork_bool AddRow(morkEnv* ev, morkRow* ioRow); // returns ev->Good() + mork_bool CutRow(morkEnv* ev, morkRow* ioRow); // returns ev->Good() + mork_bool CutAllRows(morkEnv* ev); // returns ev->Good() + + mork_pos 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. + + morkTableRowCursor* NewTableRowCursor(morkEnv* ev, mork_pos inRowPos); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakTable(morkTable* me, morkEnv* ev, morkTable** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongTable(morkTable* me, morkEnv* ev, morkTable** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// use negative values for kCut and kAdd, to keep non-neg move pos distinct: +#define morkTableChange_kCut ((mork_pos)-1) /* shows row was cut */ +#define morkTableChange_kAdd ((mork_pos)-2) /* shows row was added */ +#define morkTableChange_kNone ((mork_pos)-3) /* unknown change */ + +class morkTableChange : public morkNext { + public: // state is public because the entire Mork system is private + morkRow* mTableChange_Row; // the row in the change + + mork_pos mTableChange_Pos; // kAdd, kCut, or non-neg for row move + + public: + morkTableChange(morkEnv* ev, mork_change inChange, morkRow* ioRow); + // use this constructor for inChange == morkChange_kAdd or morkChange_kCut + + morkTableChange(morkEnv* ev, morkRow* ioRow, mork_pos inPos); + // use this constructor when the row is moved + + public: + void UnknownChangeError( + morkEnv* ev) const; // morkChange_kAdd or morkChange_kCut + void NegativeMovePosError( + morkEnv* ev) const; // move must be non-neg position + + public: + mork_bool IsAddRowTableChange() const { + return (mTableChange_Pos == morkTableChange_kAdd); + } + + mork_bool IsCutRowTableChange() const { + return (mTableChange_Pos == morkTableChange_kCut); + } + + mork_bool IsMoveRowTableChange() const { return (mTableChange_Pos >= 0); } + + public: + mork_pos GetMovePos() const { return mTableChange_Pos; } + // GetMovePos() assumes that IsMoveRowTableChange() is true. +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kTableMap /*i*/ 0x744D /* ascii 'tM' */ + +/*| morkTableMap: maps mork_token -> morkTable +|*/ +#ifdef MORK_BEAD_OVER_NODE_MAPS +class morkTableMap : public morkBeadMap { +#else /*MORK_BEAD_OVER_NODE_MAPS*/ +class morkTableMap : public morkNodeMap { // for mapping tokens to tables +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ + + public: + virtual ~morkTableMap(); + morkTableMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + + public: // other map methods +#ifdef MORK_BEAD_OVER_NODE_MAPS + mork_bool AddTable(morkEnv* ev, morkTable* ioTable) { + return this->AddBead(ev, ioTable); + } + // the AddTable() boolean return equals ev->Good(). + + mork_bool CutTable(morkEnv* ev, mork_tid inTid) { + return this->CutBead(ev, inTid); + } + // The CutTable() boolean return indicates whether removal happened. + + morkTable* GetTable(morkEnv* ev, mork_tid inTid) { + return (morkTable*)this->GetBead(ev, inTid); + } + // Note the returned table does NOT have an increase in refcount for this. + + mork_num CutAllTables(morkEnv* ev) { return this->CutAllBeads(ev); } + // CutAllTables() releases all the referenced table values. + +#else /*MORK_BEAD_OVER_NODE_MAPS*/ + mork_bool AddTable(morkEnv* ev, morkTable* ioTable) { + return this->AddNode(ev, ioTable->TableId(), ioTable); + } + // the AddTable() boolean return equals ev->Good(). + + mork_bool CutTable(morkEnv* ev, mork_tid inTid) { + return this->CutNode(ev, inTid); + } + // The CutTable() boolean return indicates whether removal happened. + + morkTable* GetTable(morkEnv* ev, mork_tid inTid) { + return (morkTable*)this->GetNode(ev, inTid); + } + // Note the returned table does NOT have an increase in refcount for this. + + mork_num CutAllTables(morkEnv* ev) { return this->CutAllNodes(ev); } + // CutAllTables() releases all the referenced table values. +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ +}; + +#ifdef MORK_BEAD_OVER_NODE_MAPS +class morkTableMapIter : public morkBeadMapIter { +#else /*MORK_BEAD_OVER_NODE_MAPS*/ +class morkTableMapIter : public morkMapIter { // typesafe wrapper class +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ + + public: +#ifdef MORK_BEAD_OVER_NODE_MAPS + morkTableMapIter(morkEnv* ev, morkTableMap* ioMap) + : morkBeadMapIter(ev, ioMap) {} + + morkTableMapIter() : morkBeadMapIter() {} + void InitTableMapIter(morkEnv* ev, morkTableMap* ioMap) { + this->InitBeadMapIter(ev, ioMap); + } + + morkTable* FirstTable(morkEnv* ev) { return (morkTable*)this->FirstBead(ev); } + + morkTable* NextTable(morkEnv* ev) { return (morkTable*)this->NextBead(ev); } + + morkTable* HereTable(morkEnv* ev) { return (morkTable*)this->HereBead(ev); } + +#else /*MORK_BEAD_OVER_NODE_MAPS*/ + morkTableMapIter(morkEnv* ev, morkTableMap* ioMap) : morkMapIter(ev, ioMap) {} + + morkTableMapIter() : morkMapIter() {} + void InitTableMapIter(morkEnv* ev, morkTableMap* ioMap) { + this->InitMapIter(ev, ioMap); + } + + mork_change* FirstTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable) { + return this->First(ev, outTid, outTable); + } + + mork_change* NextTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable) { + return this->Next(ev, outTid, outTable); + } + + mork_change* HereTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable) { + return this->Here(ev, outTid, outTable); + } + + // cutting while iterating hash map might dirty the parent table: + mork_change* CutHereTable(morkEnv* ev, mork_tid* outTid, + morkTable** outTable) { + return this->CutHere(ev, outTid, outTable); + } +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKTABLE_ */ diff --git a/comm/mailnews/db/mork/morkTableRowCursor.cpp b/comm/mailnews/db/mork/morkTableRowCursor.cpp new file mode 100644 index 0000000000..6644d2c2b3 --- /dev/null +++ b/comm/mailnews/db/mork/morkTableRowCursor.cpp @@ -0,0 +1,410 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKCURSOR_ +# include "morkCursor.h" +#endif + +#ifndef _MORKTABLEROWCURSOR_ +# include "morkTableRowCursor.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +#ifndef _MORKTABLE_ +# include "morkTable.h" +#endif + +#ifndef _MORKROW_ +# include "morkRow.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkTableRowCursor::CloseMorkNode( + morkEnv* ev) // CloseTableRowCursor() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseTableRowCursor(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkTableRowCursor::~morkTableRowCursor() // CloseTableRowCursor() executed + // earlier +{ + CloseMorkNode(mMorkEnv); + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkTableRowCursor::morkTableRowCursor(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkTable* ioTable, + mork_pos inRowPos) + : morkCursor(ev, inUsage, ioHeap), mTableRowCursor_Table(0) { + if (ev->Good()) { + if (ioTable) { + mCursor_Pos = inRowPos; + mCursor_Seed = ioTable->TableSeed(); + morkTable::SlotWeakTable(ioTable, ev, &mTableRowCursor_Table); + if (ev->Good()) mNode_Derived = morkDerived_kTableRowCursor; + } else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkTableRowCursor, morkCursor, + nsIMdbTableRowCursor) +/*public non-poly*/ void morkTableRowCursor::CloseTableRowCursor(morkEnv* ev) { + if (this->IsNode()) { + mCursor_Pos = -1; + mCursor_Seed = 0; + morkTable::SlotWeakTable((morkTable*)0, ev, &mTableRowCursor_Table); + this->CloseCursor(ev); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` +// { ----- begin attribute methods ----- +/*virtual*/ nsresult morkTableRowCursor::GetCount(nsIMdbEnv* mev, + mdb_count* outCount) { + nsresult outErr = NS_OK; + mdb_count count = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + count = GetMemberCount(ev); + outErr = ev->AsErr(); + } + if (outCount) *outCount = count; + return outErr; +} + +/*virtual*/ nsresult morkTableRowCursor::GetSeed(nsIMdbEnv* mev, + mdb_seed* outSeed) { + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/*virtual*/ nsresult morkTableRowCursor::SetPos(nsIMdbEnv* mev, mdb_pos inPos) { + mCursor_Pos = inPos; + return NS_OK; +} + +/*virtual*/ nsresult morkTableRowCursor::GetPos(nsIMdbEnv* mev, + mdb_pos* outPos) { + *outPos = mCursor_Pos; + return NS_OK; +} + +/*virtual*/ nsresult morkTableRowCursor::SetDoFailOnSeedOutOfSync( + nsIMdbEnv* mev, mdb_bool inFail) { + mCursor_DoFailOnSeedOutOfSync = inFail; + return NS_OK; +} + +/*virtual*/ nsresult morkTableRowCursor::GetDoFailOnSeedOutOfSync( + nsIMdbEnv* mev, mdb_bool* outFail) { + NS_ENSURE_ARG_POINTER(outFail); + *outFail = mCursor_DoFailOnSeedOutOfSync; + return NS_OK; +} +// } ----- end attribute methods ----- + +// { ===== begin nsIMdbTableRowCursor methods ===== + +// { ----- begin attribute methods ----- + +NS_IMETHODIMP +morkTableRowCursor::GetTable(nsIMdbEnv* mev, nsIMdbTable** acqTable) { + nsresult outErr = NS_OK; + nsIMdbTable* outTable = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (mTableRowCursor_Table) + outTable = mTableRowCursor_Table->AcquireTableHandle(ev); + + outErr = ev->AsErr(); + } + if (acqTable) *acqTable = outTable; + return outErr; +} +// } ----- end attribute methods ----- + +// { ----- begin oid iteration methods ----- +NS_IMETHODIMP +morkTableRowCursor::NextRowOid( // get row id of next row in the table + nsIMdbEnv* mev, // context + mdbOid* outOid, // out row oid + mdb_pos* outRowPos) { + nsresult outErr = NS_OK; + mork_pos pos = -1; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (outOid) { + pos = NextRowOid(ev, outOid); + } else + ev->NilPointerError(); + outErr = ev->AsErr(); + } + if (outRowPos) *outRowPos = pos; + return outErr; +} + +NS_IMETHODIMP +morkTableRowCursor::PrevRowOid( // get row id of previous row in the table + nsIMdbEnv* mev, // context + mdbOid* outOid, // out row oid + mdb_pos* outRowPos) { + nsresult outErr = NS_OK; + mork_pos pos = -1; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + if (outOid) { + pos = PrevRowOid(ev, outOid); + } else + ev->NilPointerError(); + outErr = ev->AsErr(); + } + if (outRowPos) *outRowPos = pos; + return outErr; +} +// } ----- end oid iteration methods ----- + +// { ----- begin row iteration methods ----- +NS_IMETHODIMP +morkTableRowCursor::NextRow( // get row cells from table for cells already in + // row + nsIMdbEnv* mev, // context + nsIMdbRow** acqRow, // acquire next row in table + mdb_pos* outRowPos) { + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + mdbOid oid; // place to put oid we intend to ignore + morkRow* row = NextRow(ev, &oid, outRowPos); + if (row) { + morkStore* store = row->GetRowSpaceStore(ev); + if (store) outRow = row->AcquireRowHandle(ev, store); + } + outErr = ev->AsErr(); + } + if (acqRow) *acqRow = outRow; + return outErr; +} + +NS_IMETHODIMP +morkTableRowCursor::PrevRow( // get row cells from table for cells already in + // row + nsIMdbEnv* mev, // context + nsIMdbRow** acqRow, // acquire previous row in table + mdb_pos* outRowPos) { + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + mdbOid oid; // place to put oid we intend to ignore + morkRow* row = PrevRow(ev, &oid, outRowPos); + if (row) { + morkStore* store = row->GetRowSpaceStore(ev); + if (store) outRow = row->AcquireRowHandle(ev, store); + } + outErr = ev->AsErr(); + } + if (acqRow) *acqRow = outRow; + return outErr; +} + +// } ----- end row iteration methods ----- + +// { ----- begin duplicate row removal methods ----- +NS_IMETHODIMP +morkTableRowCursor::CanHaveDupRowMembers( + nsIMdbEnv* mev, // cursor might hold dups? + mdb_bool* outCanHaveDups) { + nsresult outErr = NS_OK; + mdb_bool canHaveDups = mdbBool_kFalse; + + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + canHaveDups = CanHaveDupRowMembers(ev); + outErr = ev->AsErr(); + } + if (outCanHaveDups) *outCanHaveDups = canHaveDups; + return outErr; +} + +NS_IMETHODIMP +morkTableRowCursor::MakeUniqueCursor( // clone cursor, removing duplicate rows + nsIMdbEnv* mev, // context + nsIMdbTableRowCursor** acqCursor) // acquire clone with no dups +// Note that MakeUniqueCursor() is never necessary for a cursor which was +// created by table method nsIMdbTable::GetTableRowCursor(), because a table +// never contains the same row as a member more than once. However, a cursor +// created by table method nsIMdbTable::FindRowMatches() might contain the +// same row more than once, because the same row can generate a hit by more +// than one column with a matching string prefix. Note this method can +// return the very same cursor instance with just an incremented refcount, +// when the original cursor could not contain any duplicate rows (calling +// CanHaveDupRowMembers() shows this case on a false return). Otherwise +// this method returns a different cursor instance. Callers should not use +// this MakeUniqueCursor() method lightly, because it tends to defeat the +// purpose of lazy programming techniques, since it can force creation of +// an explicit row collection in a new cursor's representation, in order to +// inspect the row membership and remove any duplicates; this can have big +// impact if a collection holds tens of thousands of rows or more, when +// the original cursor with dups simply referenced rows indirectly by row +// position ranges, without using an explicit row set representation. +// Callers are encouraged to use nsIMdbCursor::GetCount() to determine +// whether the row collection is very large (tens of thousands), and to +// delay calling MakeUniqueCursor() when possible, until a user interface +// element actually demands the creation of an explicit set representation. +{ + nsresult outErr = NS_OK; + nsIMdbTableRowCursor* outCursor = 0; + + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + AddRef(); + outCursor = this; + + outErr = ev->AsErr(); + } + if (acqCursor) *acqCursor = outCursor; + return outErr; +} +// } ----- end duplicate row removal methods ----- + +// } ===== end nsIMdbTableRowCursor methods ===== + +/*static*/ void morkTableRowCursor::NonTableRowCursorTypeError(morkEnv* ev) { + ev->NewError("non morkTableRowCursor"); +} + +mdb_pos morkTableRowCursor::NextRowOid(morkEnv* ev, mdbOid* outOid) { + mdb_pos outPos = -1; + (void)this->NextRow(ev, outOid, &outPos); + return outPos; +} + +mdb_pos morkTableRowCursor::PrevRowOid(morkEnv* ev, mdbOid* outOid) { + mdb_pos outPos = -1; + (void)this->PrevRow(ev, outOid, &outPos); + return outPos; +} + +mork_bool morkTableRowCursor::CanHaveDupRowMembers(morkEnv* ev) { + return morkBool_kFalse; // false default is correct +} + +mork_count morkTableRowCursor::GetMemberCount(morkEnv* ev) { + morkTable* table = mTableRowCursor_Table; + if (table) + return table->mTable_RowArray.mArray_Fill; + else + return 0; +} + +morkRow* morkTableRowCursor::PrevRow(morkEnv* ev, mdbOid* outOid, + mdb_pos* outPos) { + morkRow* outRow = 0; + mork_pos pos = -1; + + morkTable* table = mTableRowCursor_Table; + if (table) { + if (table->IsOpenNode()) { + morkArray* array = &table->mTable_RowArray; + pos = mCursor_Pos - 1; + + if (pos >= 0 && pos < (mork_pos)(array->mArray_Fill)) { + mCursor_Pos = pos; // update for next time + morkRow* row = (morkRow*)array->At(pos); + if (row) { + if (row->IsRow()) { + outRow = row; + *outOid = row->mRow_Oid; + } else + row->NonRowTypeError(ev); + } else + ev->NilPointerError(); + } else { + outOid->mOid_Scope = 0; + outOid->mOid_Id = morkId_kMinusOne; + } + } else + table->NonOpenNodeError(ev); + } else + ev->NilPointerError(); + + *outPos = pos; + return outRow; +} + +morkRow* morkTableRowCursor::NextRow(morkEnv* ev, mdbOid* outOid, + mdb_pos* outPos) { + morkRow* outRow = 0; + mork_pos pos = -1; + + morkTable* table = mTableRowCursor_Table; + if (table) { + if (table->IsOpenNode()) { + morkArray* array = &table->mTable_RowArray; + pos = mCursor_Pos; + if (pos < 0) + pos = 0; + else + ++pos; + + if (pos < (mork_pos)(array->mArray_Fill)) { + mCursor_Pos = pos; // update for next time + morkRow* row = (morkRow*)array->At(pos); + if (row) { + if (row->IsRow()) { + outRow = row; + *outOid = row->mRow_Oid; + } else + row->NonRowTypeError(ev); + } else + ev->NilPointerError(); + } else { + outOid->mOid_Scope = 0; + outOid->mOid_Id = morkId_kMinusOne; + } + } else + table->NonOpenNodeError(ev); + } else + ev->NilPointerError(); + + *outPos = pos; + return outRow; +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkTableRowCursor.h b/comm/mailnews/db/mork/morkTableRowCursor.h new file mode 100644 index 0000000000..9801eb174b --- /dev/null +++ b/comm/mailnews/db/mork/morkTableRowCursor.h @@ -0,0 +1,150 @@ +/* -*- 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 _MORKTABLEROWCURSOR_ +#define _MORKTABLEROWCURSOR_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKCURSOR_ +# include "morkCursor.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class orkinTableRowCursor; +#define morkDerived_kTableRowCursor /*i*/ 0x7243 /* ascii 'rC' */ + +class morkTableRowCursor : public morkCursor, + public nsIMdbTableRowCursor { // row iterator + + // public: // slots inherited from morkObject (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 + + // morkFactory* mObject_Factory; // weak ref to suite factory + + // mork_seed mCursor_Seed; + // mork_pos mCursor_Pos; + // mork_bool mCursor_DoFailOnSeedOutOfSync; + // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment + + public: // state is public because the entire Mork system is private + morkTable* mTableRowCursor_Table; // weak ref to table + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseTableRowCursor() + + protected: + virtual ~morkTableRowCursor(); // assert that close executed earlier + + public: // morkTableRowCursor construction & destruction + morkTableRowCursor(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkTable* ioTable, mork_pos inRowPos); + void CloseTableRowCursor(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkTableRowCursor(const morkTableRowCursor& other); + morkTableRowCursor& operator=(const morkTableRowCursor& other); + + public: + NS_DECL_ISUPPORTS_INHERITED + + // { ----- begin attribute methods ----- + NS_IMETHOD GetCount(nsIMdbEnv* ev, mdb_count* outCount) override; // readonly + NS_IMETHOD GetSeed(nsIMdbEnv* ev, mdb_seed* outSeed) override; // readonly + + NS_IMETHOD SetPos(nsIMdbEnv* ev, mdb_pos inPos) override; // mutable + NS_IMETHOD GetPos(nsIMdbEnv* ev, mdb_pos* outPos) override; + + NS_IMETHOD SetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool inFail) override; + NS_IMETHOD GetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, + mdb_bool* outFail) override; + + // } ----- end attribute methods ----- + NS_IMETHOD GetTable(nsIMdbEnv* ev, nsIMdbTable** acqTable) override; + // } ----- end attribute methods ----- + + // { ----- begin duplicate row removal methods ----- + NS_IMETHOD CanHaveDupRowMembers(nsIMdbEnv* ev, // cursor might hold dups? + mdb_bool* outCanHaveDups) override; + + NS_IMETHOD MakeUniqueCursor( // clone cursor, removing duplicate rows + nsIMdbEnv* ev, // context + nsIMdbTableRowCursor** acqCursor) override; // acquire clone with no dups + // } ----- end duplicate row removal methods ----- + + // { ----- begin oid iteration methods ----- + NS_IMETHOD NextRowOid( // get row id of next row in the table + nsIMdbEnv* ev, // context + mdbOid* outOid, // out row oid + mdb_pos* outRowPos) override; // zero-based position of the row in table + NS_IMETHOD PrevRowOid( // get row id of previous row in the table + nsIMdbEnv* ev, // context + mdbOid* outOid, // out row oid + mdb_pos* outRowPos) override; // zero-based position of the row in table + // } ----- end oid iteration methods ----- + + // { ----- begin row iteration methods ----- + NS_IMETHOD NextRow( // get row cells from table for cells already in row + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow, // acquire next row in table + mdb_pos* outRowPos) override; // zero-based position of the row in table + NS_IMETHOD PrevRow( // get row cells from table for cells already in row + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow, // acquire previous row in table + mdb_pos* outRowPos) override; // zero-based position of the row in table + + // } ----- end row iteration methods ----- + + public: // dynamic type identification + mork_bool IsTableRowCursor() const { + return IsNode() && mNode_Derived == morkDerived_kTableRowCursor; + } + // } ===== end morkNode methods ===== + + public: // typing + static void NonTableRowCursorTypeError(morkEnv* ev); + + public: // oid only iteration + mdb_pos NextRowOid(morkEnv* ev, mdbOid* outOid); + mdb_pos PrevRowOid(morkEnv* ev, mdbOid* outOid); + + public: // other table row cursor methods + virtual mork_bool CanHaveDupRowMembers(morkEnv* ev); + virtual mork_count GetMemberCount(morkEnv* ev); + + virtual morkRow* NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos); + virtual morkRow* PrevRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakTableRowCursor(morkTableRowCursor* me, morkEnv* ev, + morkTableRowCursor** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongTableRowCursor(morkTableRowCursor* me, morkEnv* ev, + morkTableRowCursor** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKTABLEROWCURSOR_ */ diff --git a/comm/mailnews/db/mork/morkThumb.cpp b/comm/mailnews/db/mork/morkThumb.cpp new file mode 100644 index 0000000000..3076ca6f3e --- /dev/null +++ b/comm/mailnews/db/mork/morkThumb.cpp @@ -0,0 +1,455 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKTHUMB_ +# include "morkThumb.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +// #ifndef _MORKFILE_ +// #include "morkFile.h" +// #endif + +#ifndef _MORKWRITER_ +# include "morkWriter.h" +#endif + +#ifndef _MORKPARSER_ +# include "morkParser.h" +#endif + +#ifndef _MORKBUILDER_ +# include "morkBuilder.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkThumb::CloseMorkNode( + morkEnv* ev) // CloseThumb() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseThumb(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkThumb::~morkThumb() // assert CloseThumb() executed earlier +{ + CloseMorkNode(mMorkEnv); + MORK_ASSERT(mThumb_Magic == 0); + MORK_ASSERT(mThumb_Store == 0); + MORK_ASSERT(mThumb_File == 0); +} + +/*public non-poly*/ +morkThumb::morkThumb(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap, mork_magic inMagic) + : morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*)0), + mThumb_Magic(0), + mThumb_Total(0), + mThumb_Current(0) + + , + mThumb_Done(morkBool_kFalse), + mThumb_Broken(morkBool_kFalse), + mThumb_Seed(0) + + , + mThumb_Store(0), + mThumb_File(0), + mThumb_Writer(0), + mThumb_Builder(0), + mThumb_SourcePort(0) + + , + mThumb_DoCollect(morkBool_kFalse) { + if (ev->Good()) { + if (ioSlotHeap) { + mThumb_Magic = inMagic; + mNode_Derived = morkDerived_kThumb; + } else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkThumb, morkObject, nsIMdbThumb) + +/*public non-poly*/ void morkThumb::CloseThumb( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + mThumb_Magic = 0; + if (mThumb_Builder && mThumb_Store) mThumb_Store->ForgetBuilder(ev); + morkBuilder::SlotStrongBuilder((morkBuilder*)0, ev, &mThumb_Builder); + + morkWriter::SlotStrongWriter((morkWriter*)0, ev, &mThumb_Writer); + nsIMdbFile_SlotStrongFile((nsIMdbFile*)0, ev, &mThumb_File); + morkStore::SlotStrongStore((morkStore*)0, ev, &mThumb_Store); + morkStore::SlotStrongPort((morkPort*)0, ev, &mThumb_SourcePort); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +// { ===== begin nsIMdbThumb methods ===== +NS_IMETHODIMP +morkThumb::GetProgress(nsIMdbEnv* mev, mdb_count* outTotal, + mdb_count* outCurrent, mdb_bool* outDone, + mdb_bool* outBroken) { + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + GetProgress(ev, outTotal, outCurrent, outDone, outBroken); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkThumb::DoMore(nsIMdbEnv* mev, mdb_count* outTotal, mdb_count* outCurrent, + mdb_bool* outDone, mdb_bool* outBroken) { + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + DoMore(ev, outTotal, outCurrent, outDone, outBroken); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkThumb::CancelAndBreakThumb(nsIMdbEnv* mev) { + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + mThumb_Done = morkBool_kTrue; + mThumb_Broken = morkBool_kTrue; + CloseMorkNode(ev); // should I close this here? + outErr = ev->AsErr(); + } + return outErr; +} +// } ===== end nsIMdbThumb methods ===== + +/*static*/ void morkThumb::NonThumbTypeError(morkEnv* ev) { + ev->NewError("non morkThumb"); +} + +/*static*/ void morkThumb::UnsupportedThumbMagicError(morkEnv* ev) { + ev->NewError("unsupported mThumb_Magic"); +} + +/*static*/ void morkThumb::NilThumbStoreError(morkEnv* ev) { + ev->NewError("nil mThumb_Store"); +} + +/*static*/ void morkThumb::NilThumbFileError(morkEnv* ev) { + ev->NewError("nil mThumb_File"); +} + +/*static*/ void morkThumb::NilThumbWriterError(morkEnv* ev) { + ev->NewError("nil mThumb_Writer"); +} + +/*static*/ void morkThumb::NilThumbBuilderError(morkEnv* ev) { + ev->NewError("nil mThumb_Builder"); +} + +/*static*/ void morkThumb::NilThumbSourcePortError(morkEnv* ev) { + ev->NewError("nil mThumb_SourcePort"); +} + +/*static*/ morkThumb* morkThumb::Make_OpenFileStore(morkEnv* ev, + nsIMdbHeap* ioHeap, + morkStore* ioStore) { + morkThumb* outThumb = 0; + if (ioHeap && ioStore) { + nsIMdbFile* file = ioStore->mStore_File; + if (file) { + mork_pos fileEof = 0; + file->Eof(ev->AsMdbEnv(), &fileEof); + if (ev->Good()) { + outThumb = + new (*ioHeap, ev) morkThumb(ev, morkUsage::kHeap, ioHeap, ioHeap, + morkThumb_kMagic_OpenFileStore); + + if (outThumb) { + morkBuilder* builder = ioStore->LazyGetBuilder(ev); + if (builder) { + outThumb->mThumb_Total = (mork_count)fileEof; + morkStore::SlotStrongStore(ioStore, ev, &outThumb->mThumb_Store); + morkBuilder::SlotStrongBuilder(builder, ev, + &outThumb->mThumb_Builder); + } + } + } + } else + ioStore->NilStoreFileError(ev); + } else + ev->NilPointerError(); + + return outThumb; +} + +/*static*/ morkThumb* morkThumb::Make_LargeCommit(morkEnv* ev, + nsIMdbHeap* ioHeap, + morkStore* ioStore) { + morkThumb* outThumb = 0; + if (ioHeap && ioStore) { + nsIMdbFile* file = ioStore->mStore_File; + if (file) { + outThumb = new (*ioHeap, ev) morkThumb( + ev, morkUsage::kHeap, ioHeap, ioHeap, morkThumb_kMagic_LargeCommit); + + if (outThumb) { + morkWriter* writer = new (*ioHeap, ev) + morkWriter(ev, morkUsage::kHeap, ioHeap, ioStore, file, ioHeap); + if (writer) { + writer->mWriter_CommitGroupIdentity = + ++ioStore->mStore_CommitGroupIdentity; + writer->mWriter_NeedDirtyAll = morkBool_kFalse; + outThumb->mThumb_DoCollect = morkBool_kFalse; + morkStore::SlotStrongStore(ioStore, ev, &outThumb->mThumb_Store); + + nsIMdbFile_SlotStrongFile(file, ev, &outThumb->mThumb_File); + + outThumb->mThumb_Writer = writer; // pass writer ownership to thumb + } + } + } else + ioStore->NilStoreFileError(ev); + } else + ev->NilPointerError(); + + return outThumb; +} + +/*static*/ morkThumb* morkThumb::Make_CompressCommit(morkEnv* ev, + nsIMdbHeap* ioHeap, + morkStore* ioStore, + mork_bool inDoCollect) { + morkThumb* outThumb = 0; + if (ioHeap && ioStore) { + nsIMdbFile* file = ioStore->mStore_File; + if (file) { + outThumb = + new (*ioHeap, ev) morkThumb(ev, morkUsage::kHeap, ioHeap, ioHeap, + morkThumb_kMagic_CompressCommit); + + if (outThumb) { + morkWriter* writer = new (*ioHeap, ev) + morkWriter(ev, morkUsage::kHeap, ioHeap, ioStore, file, ioHeap); + if (writer) { + writer->mWriter_NeedDirtyAll = morkBool_kTrue; + outThumb->mThumb_DoCollect = inDoCollect; + morkStore::SlotStrongStore(ioStore, ev, &outThumb->mThumb_Store); + nsIMdbFile_SlotStrongFile(file, ev, &outThumb->mThumb_File); + outThumb->mThumb_Writer = writer; // pass writer ownership to thumb + + // cope with fact that parsed transaction groups are going away: + ioStore->mStore_FirstCommitGroupPos = 0; + ioStore->mStore_SecondCommitGroupPos = 0; + } + } + } else + ioStore->NilStoreFileError(ev); + } else + ev->NilPointerError(); + + return outThumb; +} + +// { ===== begin non-poly methods imitating nsIMdbThumb ===== +void morkThumb::GetProgress(morkEnv* ev, mdb_count* outTotal, + mdb_count* outCurrent, mdb_bool* outDone, + mdb_bool* outBroken) { + MORK_USED_1(ev); + if (outTotal) *outTotal = mThumb_Total; + if (outCurrent) *outCurrent = mThumb_Current; + if (outDone) *outDone = mThumb_Done; + if (outBroken) *outBroken = mThumb_Broken; +} + +void morkThumb::DoMore(morkEnv* ev, mdb_count* outTotal, mdb_count* outCurrent, + mdb_bool* outDone, mdb_bool* outBroken) { + if (!mThumb_Done && !mThumb_Broken) { + switch (mThumb_Magic) { + case morkThumb_kMagic_OpenFilePort: // 1 /* factory method */ + this->DoMore_OpenFilePort(ev); + break; + + case morkThumb_kMagic_OpenFileStore: // 2 /* factory method */ + this->DoMore_OpenFileStore(ev); + break; + + case morkThumb_kMagic_ExportToFormat: // 3 /* port method */ + this->DoMore_ExportToFormat(ev); + break; + + case morkThumb_kMagic_ImportContent: // 4 /* store method */ + this->DoMore_ImportContent(ev); + break; + + case morkThumb_kMagic_LargeCommit: // 5 /* store method */ + this->DoMore_LargeCommit(ev); + break; + + case morkThumb_kMagic_SessionCommit: // 6 /* store method */ + this->DoMore_SessionCommit(ev); + break; + + case morkThumb_kMagic_CompressCommit: // 7 /* store method */ + this->DoMore_CompressCommit(ev); + break; + + case morkThumb_kMagic_SearchManyColumns: // 8 /* table method */ + this->DoMore_SearchManyColumns(ev); + break; + + case morkThumb_kMagic_NewSortColumn: // 9 /* table metho) */ + this->DoMore_NewSortColumn(ev); + break; + + case morkThumb_kMagic_NewSortColumnWithCompare: // 10 /* table method */ + this->DoMore_NewSortColumnWithCompare(ev); + break; + + case morkThumb_kMagic_CloneSortColumn: // 11 /* table method */ + this->DoMore_CloneSortColumn(ev); + break; + + case morkThumb_kMagic_AddIndex: // 12 /* table method */ + this->DoMore_AddIndex(ev); + break; + + case morkThumb_kMagic_CutIndex: // 13 /* table method */ + this->DoMore_CutIndex(ev); + break; + + default: + this->UnsupportedThumbMagicError(ev); + break; + } + } + if (outTotal) *outTotal = mThumb_Total; + if (outCurrent) *outCurrent = mThumb_Current; + if (outDone) *outDone = mThumb_Done; + if (outBroken) *outBroken = mThumb_Broken; +} + +void morkThumb::CancelAndBreakThumb(morkEnv* ev) { + MORK_USED_1(ev); + mThumb_Broken = morkBool_kTrue; +} + +// } ===== end non-poly methods imitating nsIMdbThumb ===== + +morkStore* morkThumb::ThumbToOpenStore(morkEnv* ev) +// for orkinFactory::ThumbToOpenStore() after OpenFileStore() +{ + MORK_USED_1(ev); + return mThumb_Store; +} + +void morkThumb::DoMore_OpenFilePort(morkEnv* ev) { + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_OpenFileStore(morkEnv* ev) { + morkBuilder* builder = mThumb_Builder; + if (builder) { + mork_pos pos = 0; + builder->ParseMore(ev, &pos, &mThumb_Done, &mThumb_Broken); + // mThumb_Total = builder->mBuilder_TotalCount; + // mThumb_Current = builder->mBuilder_DoneCount; + mThumb_Current = (mork_count)pos; + } else { + this->NilThumbBuilderError(ev); + mThumb_Broken = morkBool_kTrue; + mThumb_Done = morkBool_kTrue; + } +} + +void morkThumb::DoMore_ExportToFormat(morkEnv* ev) { + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_ImportContent(morkEnv* ev) { + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_LargeCommit(morkEnv* ev) { this->DoMore_Commit(ev); } + +void morkThumb::DoMore_SessionCommit(morkEnv* ev) { this->DoMore_Commit(ev); } + +void morkThumb::DoMore_Commit(morkEnv* ev) { + morkWriter* writer = mThumb_Writer; + if (writer) { + writer->WriteMore(ev); + mThumb_Total = writer->mWriter_TotalCount; + mThumb_Current = writer->mWriter_DoneCount; + mThumb_Done = (ev->Bad() || writer->IsWritingDone()); + mThumb_Broken = ev->Bad(); + } else { + this->NilThumbWriterError(ev); + mThumb_Broken = morkBool_kTrue; + mThumb_Done = morkBool_kTrue; + } +} + +void morkThumb::DoMore_CompressCommit(morkEnv* ev) { this->DoMore_Commit(ev); } + +void morkThumb::DoMore_SearchManyColumns(morkEnv* ev) { + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_NewSortColumn(morkEnv* ev) { + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_NewSortColumnWithCompare(morkEnv* ev) { + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_CloneSortColumn(morkEnv* ev) { + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_AddIndex(morkEnv* ev) { + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_CutIndex(morkEnv* ev) { + this->UnsupportedThumbMagicError(ev); +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkThumb.h b/comm/mailnews/db/mork/morkThumb.h new file mode 100644 index 0000000000..0e4f9f4592 --- /dev/null +++ b/comm/mailnews/db/mork/morkThumb.h @@ -0,0 +1,176 @@ +/* -*- 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 _MORKTHUMB_ +#define _MORKTHUMB_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKOBJECT_ +# include "morkObject.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkThumb_kMagic_OpenFilePort 1 /* factory method */ +#define morkThumb_kMagic_OpenFileStore 2 /* factory method */ +#define morkThumb_kMagic_ExportToFormat 3 /* port method */ +#define morkThumb_kMagic_ImportContent 4 /* store method */ +#define morkThumb_kMagic_LargeCommit 5 /* store method */ +#define morkThumb_kMagic_SessionCommit 6 /* store method */ +#define morkThumb_kMagic_CompressCommit 7 /* store method */ +#define morkThumb_kMagic_SearchManyColumns 8 /* table method */ +#define morkThumb_kMagic_NewSortColumn 9 /* table metho) */ +#define morkThumb_kMagic_NewSortColumnWithCompare 10 /* table method */ +#define morkThumb_kMagic_CloneSortColumn 11 /* table method */ +#define morkThumb_kMagic_AddIndex 12 /* table method */ +#define morkThumb_kMagic_CutIndex 13 /* table method */ + +#define morkDerived_kThumb /*i*/ 0x5468 /* ascii 'Th' */ + +/*| morkThumb: +|*/ +class morkThumb : public morkObject, public nsIMdbThumb { + // public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // mork_color mBead_Color; // ID for this bead + // morkHandle* mObject_Handle; // weak ref to handle for this object + + public: // state is public because the entire Mork system is private + NS_DECL_ISUPPORTS_INHERITED + + // { ===== begin nsIMdbThumb methods ===== + NS_IMETHOD GetProgress(nsIMdbEnv* ev, mdb_count* outTotal, + mdb_count* outCurrent, mdb_bool* outDone, + mdb_bool* outBroken) override; + + NS_IMETHOD DoMore(nsIMdbEnv* ev, mdb_count* outTotal, mdb_count* outCurrent, + mdb_bool* outDone, mdb_bool* outBroken) override; + + NS_IMETHOD CancelAndBreakThumb(nsIMdbEnv* ev) override; + // } ===== end nsIMdbThumb methods ===== + + // might as well include all the return values here: + + mork_magic mThumb_Magic; // magic sig different in each thumb type + mork_count mThumb_Total; + mork_count mThumb_Current; + + mork_bool mThumb_Done; + mork_bool mThumb_Broken; + mork_u2 mThumb_Seed; // optional seed for u4 alignment padding + + morkStore* mThumb_Store; // weak ref to created store + nsIMdbFile* mThumb_File; // strong ref to file (store, import, export) + morkWriter* mThumb_Writer; // strong ref to writer (for commit) + morkBuilder* mThumb_Builder; // strong ref to builder (for store open) + morkPort* mThumb_SourcePort; // strong ref to port for import + + mork_bool mThumb_DoCollect; // influence whether a collect happens + mork_bool mThumb_Pad[3]; // padding for u4 alignment + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode( + morkEnv* ev) override; // CloseThumb() only if open + + public: // morkThumb construction & destruction + morkThumb(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap, mork_magic inMagic); + void CloseThumb(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkThumb(const morkThumb& other); + morkThumb& operator=(const morkThumb& other); + virtual ~morkThumb(); // assert that CloseThumb() executed earlier + + public: // dynamic type identification + mork_bool IsThumb() const { + return IsNode() && mNode_Derived == morkDerived_kThumb; + } + // } ===== end morkNode methods ===== + + public: // typing + static void NonThumbTypeError(morkEnv* ev); + static void UnsupportedThumbMagicError(morkEnv* ev); + + static void NilThumbStoreError(morkEnv* ev); + static void NilThumbFileError(morkEnv* ev); + static void NilThumbWriterError(morkEnv* ev); + static void NilThumbBuilderError(morkEnv* ev); + static void NilThumbSourcePortError(morkEnv* ev); + + public: // 'do more' methods + void DoMore_OpenFilePort(morkEnv* ev); + void DoMore_OpenFileStore(morkEnv* ev); + void DoMore_ExportToFormat(morkEnv* ev); + void DoMore_ImportContent(morkEnv* ev); + void DoMore_LargeCommit(morkEnv* ev); + void DoMore_SessionCommit(morkEnv* ev); + void DoMore_CompressCommit(morkEnv* ev); + void DoMore_Commit(morkEnv* ev); + void DoMore_SearchManyColumns(morkEnv* ev); + void DoMore_NewSortColumn(morkEnv* ev); + void DoMore_NewSortColumnWithCompare(morkEnv* ev); + void DoMore_CloneSortColumn(morkEnv* ev); + void DoMore_AddIndex(morkEnv* ev); + void DoMore_CutIndex(morkEnv* ev); + + public: // other thumb methods + morkStore* ThumbToOpenStore(morkEnv* ev); + // for orkinFactory::ThumbToOpenStore() after OpenFileStore() + + public: // assorted thumb constructors + static morkThumb* Make_OpenFileStore(morkEnv* ev, nsIMdbHeap* ioHeap, + morkStore* ioStore); + + static morkThumb* Make_CompressCommit(morkEnv* ev, nsIMdbHeap* ioHeap, + morkStore* ioStore, + mork_bool inDoCollect); + + static morkThumb* Make_LargeCommit(morkEnv* ev, nsIMdbHeap* ioHeap, + morkStore* ioStore); + + // { ===== begin non-poly methods imitating nsIMdbThumb ===== + void GetProgress(morkEnv* ev, mdb_count* outTotal, mdb_count* outCurrent, + mdb_bool* outDone, mdb_bool* outBroken); + + void DoMore(morkEnv* ev, mdb_count* outTotal, mdb_count* outCurrent, + mdb_bool* outDone, mdb_bool* outBroken); + + void CancelAndBreakThumb(morkEnv* ev); + // } ===== end non-poly methods imitating nsIMdbThumb ===== + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakThumb(morkThumb* me, morkEnv* ev, morkThumb** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongThumb(morkThumb* me, morkEnv* ev, morkThumb** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKTHUMB_ */ diff --git a/comm/mailnews/db/mork/morkUniqRowCursor.h b/comm/mailnews/db/mork/morkUniqRowCursor.h new file mode 100644 index 0000000000..4099f19996 --- /dev/null +++ b/comm/mailnews/db/mork/morkUniqRowCursor.h @@ -0,0 +1,89 @@ +/* -*- 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 _MORKUNIQROWCURSOR_ +#define _MORKUNIQROWCURSOR_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKCURSOR_ +# include "morkCursor.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class orkinTableRowCursor; +// #define morkDerived_kUniqRowCursor /*i*/ 0x7352 /* ascii 'sR' */ + +class morkUniqRowCursor : public morkTableRowCursor { // row iterator + + // public: // slots inherited from morkObject (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 + + // morkFactory* mObject_Factory; // weak ref to suite factory + + // mork_seed mCursor_Seed; + // mork_pos mCursor_Pos; + // mork_bool mCursor_DoFailOnSeedOutOfSync; + // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment + + // morkTable* mTableRowCursor_Table; // weak ref to table + + // { ===== begin morkNode interface ===== + public: + virtual void CloseMorkNode(morkEnv* ev) override; // CloseUniqRowCursor() + virtual ~morkUniqRowCursor(); // assert that close executed earlier + + public: // morkUniqRowCursor construction & destruction + morkUniqRowCursor(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkTable* ioTable, mork_pos inRowPos); + void CloseUniqRowCursor(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkUniqRowCursor(const morkUniqRowCursor& other); + morkUniqRowCursor& operator=(const morkUniqRowCursor& other); + // } ===== end morkNode methods ===== + + public: // typing + static void NonUniqRowCursorTypeError(morkEnv* ev); + + public: // other search row cursor methods + virtual mork_bool CanHaveDupRowMembers(morkEnv* ev); + virtual mork_count GetMemberCount(morkEnv* ev); + + virtual orkinTableRowCursor* AcquireUniqueRowCursorHandle(morkEnv* ev); + + // virtual mdb_pos NextRowOid(morkEnv* ev, mdbOid* outOid); + virtual morkRow* NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakUniqRowCursor(morkUniqRowCursor* me, morkEnv* ev, + morkUniqRowCursor** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongUniqRowCursor(morkUniqRowCursor* me, morkEnv* ev, + morkUniqRowCursor** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKUNIQROWCURSOR_ */ diff --git a/comm/mailnews/db/mork/morkWriter.cpp b/comm/mailnews/db/mork/morkWriter.cpp new file mode 100644 index 0000000000..dc1bb1a1ed --- /dev/null +++ b/comm/mailnews/db/mork/morkWriter.cpp @@ -0,0 +1,1936 @@ +/* -*- 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 _MORKBLOB_ +# include "morkBlob.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKARRAY_ +# include "morkWriter.h" +#endif + +// #ifndef _MORKFILE_ +// #include "morkFile.h" +// #endif + +#ifndef _MORKSTREAM_ +# include "morkStream.h" +#endif + +#ifndef _MORKSTORE_ +# include "morkStore.h" +#endif + +#ifndef _MORKATOMSPACE_ +# include "morkAtomSpace.h" +#endif + +#ifndef _MORKROWSPACE_ +# include "morkRowSpace.h" +#endif + +#ifndef _MORKROWMAP_ +# include "morkRowMap.h" +#endif + +#ifndef _MORKATOMMAP_ +# include "morkAtomMap.h" +#endif + +#ifndef _MORKROW_ +# include "morkRow.h" +#endif + +#ifndef _MORKTABLE_ +# include "morkTable.h" +#endif + +#ifndef _MORKCELL_ +# include "morkCell.h" +#endif + +#ifndef _MORKATOM_ +# include "morkAtom.h" +#endif + +#ifndef _MORKCH_ +# include "morkCh.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkWriter::CloseMorkNode( + morkEnv* ev) // CloseTable() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseWriter(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkWriter::~morkWriter() // assert CloseTable() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); + MORK_ASSERT(mWriter_Store == 0); +} + +/*public non-poly*/ +morkWriter::morkWriter(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkStore* ioStore, + nsIMdbFile* ioFile, nsIMdbHeap* ioSlotHeap) + : morkNode(ev, inUsage, ioHeap), + mWriter_Store(0), + mWriter_File(0), + mWriter_Bud(0), + mWriter_Stream(0), + mWriter_SlotHeap(0) + + , + mWriter_CommitGroupIdentity(0) // see mStore_CommitGroupIdentity + , + mWriter_GroupBufFill(0) + + , + mWriter_TotalCount(morkWriter_kCountNumberOfPhases), + mWriter_DoneCount(0) + + , + mWriter_LineSize(0), + mWriter_MaxIndent(morkWriter_kMaxIndent), + mWriter_MaxLine(morkWriter_kMaxLine) + + , + mWriter_TableForm(0), + mWriter_TableAtomScope('v'), + mWriter_TableRowScope(0), + mWriter_TableKind(0) + + , + mWriter_RowForm(0), + mWriter_RowAtomScope(0), + mWriter_RowScope(0) + + , + mWriter_DictForm(0), + mWriter_DictAtomScope('v') + + , + mWriter_NeedDirtyAll(morkBool_kFalse), + mWriter_Incremental(morkBool_kTrue) // opposite of mWriter_NeedDirtyAll + , + mWriter_DidStartDict(morkBool_kFalse), + mWriter_DidEndDict(morkBool_kTrue) + + , + mWriter_SuppressDirtyRowNewline(morkBool_kFalse), + mWriter_DidStartGroup(morkBool_kFalse), + mWriter_DidEndGroup(morkBool_kTrue), + mWriter_Phase(morkWriter_kPhaseNothingDone) + + , + mWriter_BeVerbose(ev->mEnv_BeVerbose) + + , + mWriter_TableRowArrayPos(0) + + // empty constructors for map iterators: + , + mWriter_StoreAtomSpacesIter(), + mWriter_AtomSpaceAtomAidsIter() + + , + mWriter_StoreRowSpacesIter(), + mWriter_RowSpaceTablesIter(), + mWriter_RowSpaceRowsIter() { + mWriter_GroupBuf[0] = 0; + + mWriter_SafeNameBuf[0] = 0; + mWriter_SafeNameBuf[morkWriter_kMaxColumnNameSize * 2] = 0; + mWriter_ColNameBuf[0] = 0; + mWriter_ColNameBuf[morkWriter_kMaxColumnNameSize] = 0; + + mdbYarn* y = &mWriter_ColYarn; + y->mYarn_Buf = mWriter_ColNameBuf; // where to put col bytes + y->mYarn_Fill = 0; // set later by writer + y->mYarn_Size = morkWriter_kMaxColumnNameSize; // our buf size + y->mYarn_More = 0; // set later by writer + y->mYarn_Form = 0; // set later by writer + y->mYarn_Grow = 0; // do not allow buffer growth + + y = &mWriter_SafeYarn; + y->mYarn_Buf = mWriter_SafeNameBuf; // where to put col bytes + y->mYarn_Fill = 0; // set later by writer + y->mYarn_Size = morkWriter_kMaxColumnNameSize * 2; // our buf size + y->mYarn_More = 0; // set later by writer + y->mYarn_Form = 0; // set later by writer + y->mYarn_Grow = 0; // do not allow buffer growth + + if (ev->Good()) { + if (ioSlotHeap && ioFile && ioStore) { + morkStore::SlotWeakStore(ioStore, ev, &mWriter_Store); + nsIMdbFile_SlotStrongFile(ioFile, ev, &mWriter_File); + nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mWriter_SlotHeap); + if (ev->Good()) { + mNode_Derived = morkDerived_kWriter; + } + } else + ev->NilPointerError(); + } +} + +void morkWriter::MakeWriterStream(morkEnv* ev) // give writer a suitable stream +{ + mWriter_Incremental = !mWriter_NeedDirtyAll; // opposites + + if (!mWriter_Stream && ev->Good()) { + if (mWriter_File) { + morkStream* stream = 0; + mork_bool frozen = morkBool_kFalse; // need to modify + nsIMdbHeap* heap = mWriter_SlotHeap; + + if (mWriter_Incremental) { + stream = + new (*heap, ev) morkStream(ev, morkUsage::kHeap, heap, mWriter_File, + morkWriter_kStreamBufSize, frozen); + } else // compress commit + { + nsIMdbFile* bud = 0; + mWriter_File->AcquireBud(ev->AsMdbEnv(), heap, &bud); + if (bud) { + if (ev->Good()) { + mWriter_Bud = bud; + stream = + new (*heap, ev) morkStream(ev, morkUsage::kHeap, heap, bud, + morkWriter_kStreamBufSize, frozen); + } else + bud->Release(); + } + } + + if (stream) { + if (ev->Good()) + mWriter_Stream = stream; + else + stream->CutStrongRef(ev->AsMdbEnv()); + } + } else + this->NilWriterFileError(ev); + } +} + +/*public non-poly*/ void morkWriter::CloseWriter( + morkEnv* ev) // called by CloseMorkNode(); +{ + if (this->IsNode()) { + morkStore::SlotWeakStore((morkStore*)0, ev, &mWriter_Store); + nsIMdbFile_SlotStrongFile((nsIMdbFile*)0, ev, &mWriter_File); + nsIMdbFile_SlotStrongFile((nsIMdbFile*)0, ev, &mWriter_Bud); + morkStream::SlotStrongStream((morkStream*)0, ev, &mWriter_Stream); + nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*)0, ev, &mWriter_SlotHeap); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void morkWriter::NonWriterTypeError(morkEnv* ev) { + ev->NewError("non morkWriter"); +} + +/*static*/ void morkWriter::NilWriterStoreError(morkEnv* ev) { + ev->NewError("nil mWriter_Store"); +} + +/*static*/ void morkWriter::NilWriterBudError(morkEnv* ev) { + ev->NewError("nil mWriter_Bud"); +} + +/*static*/ void morkWriter::NilWriterFileError(morkEnv* ev) { + ev->NewError("nil mWriter_File"); +} + +/*static*/ void morkWriter::NilWriterStreamError(morkEnv* ev) { + ev->NewError("nil mWriter_Stream"); +} + +/*static*/ void morkWriter::UnsupportedPhaseError(morkEnv* ev) { + ev->NewError("unsupported mWriter_Phase"); +} + +mork_bool morkWriter::WriteMore( + morkEnv* ev) // call until IsWritingDone() is true +{ + if (this->IsOpenNode()) { + if (this->IsWriter()) { + if (!mWriter_Stream) this->MakeWriterStream(ev); + + if (mWriter_Stream) { + if (ev->Bad()) { + ev->NewWarning("writing stops on error"); + mWriter_Phase = morkWriter_kPhaseWritingDone; + } + switch (mWriter_Phase) { + case morkWriter_kPhaseNothingDone: + OnNothingDone(ev); + break; + + case morkWriter_kPhaseDirtyAllDone: + OnDirtyAllDone(ev); + break; + + case morkWriter_kPhasePutHeaderDone: + OnPutHeaderDone(ev); + break; + + case morkWriter_kPhaseRenumberAllDone: + OnRenumberAllDone(ev); + break; + + case morkWriter_kPhaseStoreAtomSpaces: + OnStoreAtomSpaces(ev); + break; + + case morkWriter_kPhaseAtomSpaceAtomAids: + OnAtomSpaceAtomAids(ev); + break; + + case morkWriter_kPhaseStoreRowSpacesTables: + OnStoreRowSpacesTables(ev); + break; + + case morkWriter_kPhaseRowSpaceTables: + OnRowSpaceTables(ev); + break; + + case morkWriter_kPhaseTableRowArray: + OnTableRowArray(ev); + break; + + case morkWriter_kPhaseStoreRowSpacesRows: + OnStoreRowSpacesRows(ev); + break; + + case morkWriter_kPhaseRowSpaceRows: + OnRowSpaceRows(ev); + break; + + case morkWriter_kPhaseContentDone: + OnContentDone(ev); + break; + + case morkWriter_kPhaseWritingDone: + OnWritingDone(ev); + break; + + default: + this->UnsupportedPhaseError(ev); + } + } else + this->NilWriterStreamError(ev); + } else + this->NonWriterTypeError(ev); + } else + this->NonOpenNodeError(ev); + + return ev->Good(); +} + +static const char morkWriter_kHexDigits[] = "0123456789ABCDEF"; + +mork_size morkWriter::WriteYarn(morkEnv* ev, const mdbYarn* inYarn) +// return number of atom bytes written on the current line (which +// implies that escaped line breaks will make the size value smaller +// than the entire yarn's size, since only part goes on a last line). +{ + mork_size outSize = 0; + mork_size lineSize = mWriter_LineSize; + morkStream* stream = mWriter_Stream; + + const mork_u1* b = (const mork_u1*)inYarn->mYarn_Buf; + if (b) { + int c; + mork_fill fill = inYarn->mYarn_Fill; + + const mork_u1* end = b + fill; + while (b < end && ev->Good()) { + if (lineSize + outSize >= mWriter_MaxLine) // continue line? + { + stream->PutByteThenNewline(ev, '\\'); + mWriter_LineSize = lineSize = outSize = 0; + } + + c = *b++; // next byte to print + if (morkCh_IsValue(c)) { + stream->Putc(ev, c); + ++outSize; // c + } else if (c == ')' || c == '$' || c == '\\') { + stream->Putc(ev, '\\'); + stream->Putc(ev, c); + outSize += 2; // '\' c + } else { + outSize += 3; // '$' hex hex + stream->Putc(ev, '$'); + stream->Putc(ev, morkWriter_kHexDigits[(c >> 4) & 0x0F]); + stream->Putc(ev, morkWriter_kHexDigits[c & 0x0F]); + } + } + } + mWriter_LineSize += outSize; + + return outSize; +} + +mork_size morkWriter::WriteAtom(morkEnv* ev, const morkAtom* inAtom) +// return number of atom bytes written on the current line (which +// implies that escaped line breaks will make the size value smaller +// than the entire atom's size, since only part goes on a last line). +{ + mork_size outSize = 0; + mdbYarn yarn; // to ref content inside atom + + if (morkAtom::AliasYarn(inAtom, &yarn)) { + if (mWriter_DidStartDict && yarn.mYarn_Form != mWriter_DictForm) + this->ChangeDictForm(ev, yarn.mYarn_Form); + + outSize = this->WriteYarn(ev, &yarn); + // mWriter_LineSize += stream->Write(ev, inYarn->mYarn_Buf, outSize); + } else + inAtom->BadAtomKindError(ev); + + return outSize; +} + +void morkWriter::WriteAtomSpaceAsDict(morkEnv* ev, morkAtomSpace* ioSpace) { + morkStream* stream = mWriter_Stream; + nsIMdbEnv* mdbev = ev->AsMdbEnv(); + mork_scope scope = ioSpace->SpaceScope(); + if (scope < 0x80) { + if (mWriter_LineSize) stream->PutLineBreak(ev); + stream->PutString(ev, "< <(a="); + stream->Putc(ev, (int)scope); + ++mWriter_LineSize; + stream->PutString(ev, ")> // (f=iso-8859-1)"); + mWriter_LineSize = stream->PutIndent(ev, morkWriter_kDictAliasDepth); + } else + ioSpace->NonAsciiSpaceScopeName(ev); + + if (ev->Good()) { + mdbYarn yarn; // to ref content inside atom + char buf[64]; // buffer for staging the dict alias hex ID + char* idBuf = buf + 1; // where the id always starts + buf[0] = '('; // we always start with open paren + morkBookAtom* atom = 0; + morkAtomAidMapIter* ai = &mWriter_AtomSpaceAtomAidsIter; + ai->InitAtomAidMapIter(ev, &ioSpace->mAtomSpace_AtomAids); + mork_change* c = 0; + + for (c = ai->FirstAtom(ev, &atom); c && ev->Good(); + c = ai->NextAtom(ev, &atom)) { + if (atom) { + if (atom->IsAtomDirty()) { + atom->SetAtomClean(); // neutralize change + + morkAtom::AliasYarn(atom, &yarn); + mork_size size = ev->TokenAsHex(idBuf, atom->mBookAtom_Id); + + if (yarn.mYarn_Form != mWriter_DictForm) + this->ChangeDictForm(ev, yarn.mYarn_Form); + + mork_size pending = + yarn.mYarn_Fill + size + morkWriter_kYarnEscapeSlop + 4; + this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth); + mork_size bytesWritten; + stream->Write(mdbev, buf, size + 1, &bytesWritten); // + '(' + mWriter_LineSize += bytesWritten; + + pending -= (size + 1); + this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasValueDepth); + stream->Putc(ev, '='); // start alias + ++mWriter_LineSize; + + this->WriteYarn(ev, &yarn); + stream->Putc(ev, ')'); // end alias + ++mWriter_LineSize; + + ++mWriter_DoneCount; + } + } else + ev->NilPointerError(); + } + ai->CloseMapIter(ev); + } + + if (ev->Good()) { + ioSpace->SetAtomSpaceClean(); + // this->IndentAsNeeded(ev, 0); + // stream->PutByteThenNewline(ev, '>'); // end dict + + stream->Putc(ev, '>'); // end dict + ++mWriter_LineSize; + } +} + +/* +(I'm putting the text of this message in file morkWriter.cpp.) + +I'm making a change which should cause rows and tables to go away +when a Mork db is compress committed, when the rows and tables +are no longer needed. Because this is subtle, I'm describing it +here in case misbehavior is ever observed. Otherwise you'll have +almost no hope of fixing a related bug. + +This is done entirely in morkWriter.cpp: morkWriter::DirtyAll(), +which currently marks all rows and tables dirty so they will be +written in a later phase of the commit. My change is to merely +selectively not mark certain rows and tables dirty, when they seem +to be superfluous. + +A row is no longer needed when the mRow_GcUses slot hits zero, and +this is used by the following inline morkRow method: + + mork_bool IsRowUsed() const { return mRow_GcUses != 0; } + +Naturally disaster ensues if mRow_GcUses is ever smaller than right. + +Similarly, we should drop tables when mTable_GcUses hits zero, but +only when a table contains no row members. We consider tables to +self reference (and prevent collection) when they contain content. +Again, disaster ensues if mTable_GcUses is ever smaller than right. + + mork_count GetRowCount() const + { return mTable_RowArray.mArray_Fill; } + + mork_bool IsTableUsed() const + { return (mTable_GcUses != 0 || this->GetRowCount() != 0); } + +Now let's question why the design involves filtering what gets set +to dirty. Why not apply a filter in the later phase when we write +content? Because I'm afraid of missing some subtle interaction in +updating table and row relationships. It seems safer to write a row +or table when it starts out dirty, before morkWriter::DirtyAll() is +called. So this design calls for writing out rows and tables when +they are still clearly used, and additionally, <i>when we have just +been actively writing to them right before this commit</i>. + +Presumably if they are truly useless, they will no longer be dirtied +in later sessions and will get collected during the next compress +commit. So we wait to collect them until they become all dead, and +not just mostly dead. (At which time you can feel free to go through +their pockets looking for loose change.) +*/ + +mork_bool morkWriter::DirtyAll(morkEnv* ev) +// DirtyAll() visits every store sub-object 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). Note the method can +// stop early when any error happens, since this will abort any commit. +{ + morkStore* store = mWriter_Store; + if (store) { + store->SetStoreDirty(); + mork_change* c = 0; + + if (ev->Good()) { + morkAtomSpaceMapIter* asi = &mWriter_StoreAtomSpacesIter; + asi->InitAtomSpaceMapIter(ev, &store->mStore_AtomSpaces); + + mork_scope* key = 0; // ignore keys in map + morkAtomSpace* space = 0; // old val node in the map + + for (c = asi->FirstAtomSpace(ev, key, &space); c && ev->Good(); + c = asi->NextAtomSpace(ev, key, &space)) { + if (space) { + if (space->IsAtomSpace()) { + space->SetAtomSpaceDirty(); + morkBookAtom* atom = 0; + morkAtomAidMapIter* ai = &mWriter_AtomSpaceAtomAidsIter; + ai->InitAtomAidMapIter(ev, &space->mAtomSpace_AtomAids); + + for (c = ai->FirstAtom(ev, &atom); c && ev->Good(); + c = ai->NextAtom(ev, &atom)) { + if (atom) { + atom->SetAtomDirty(); + ++mWriter_TotalCount; + } else + ev->NilPointerError(); + } + + ai->CloseMapIter(ev); + } else + space->NonAtomSpaceTypeError(ev); + } else + ev->NilPointerError(); + } + } + + if (ev->Good()) { + morkRowSpaceMapIter* rsi = &mWriter_StoreRowSpacesIter; + rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces); + + mork_scope* key = 0; // ignore keys in map + morkRowSpace* space = 0; // old val node in the map + + for (c = rsi->FirstRowSpace(ev, key, &space); c && ev->Good(); + c = rsi->NextRowSpace(ev, key, &space)) { + if (space) { + if (space->IsRowSpace()) { + space->SetRowSpaceDirty(); + if (ev->Good()) { +#ifdef MORK_ENABLE_PROBE_MAPS + morkRowProbeMapIter* ri = &mWriter_RowSpaceRowsIter; +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkRowMapIter* ri = &mWriter_RowSpaceRowsIter; +#endif /*MORK_ENABLE_PROBE_MAPS*/ + ri->InitRowMapIter(ev, &space->mRowSpace_Rows); + + morkRow* row = 0; // old key row in the map + + for (c = ri->FirstRow(ev, &row); c && ev->Good(); + c = ri->NextRow(ev, &row)) { + if (row && row->IsRow()) // need to dirty row? + { + if (row->IsRowUsed() || row->IsRowDirty()) { + row->DirtyAllRowContent(ev); + ++mWriter_TotalCount; + } + } else + row->NonRowTypeWarning(ev); + } + ri->CloseMapIter(ev); + } + + if (ev->Good()) { + morkTableMapIter* ti = &mWriter_RowSpaceTablesIter; + ti->InitTableMapIter(ev, &space->mRowSpace_Tables); + +#ifdef MORK_BEAD_OVER_NODE_MAPS + morkTable* table = ti->FirstTable(ev); + + for (; table && ev->Good(); table = ti->NextTable(ev)) +#else /*MORK_BEAD_OVER_NODE_MAPS*/ + mork_tid* tableKey = 0; // ignore keys in table map + morkTable* table = 0; // old key row in the map + + for (c = ti->FirstTable(ev, tableKey, &table); c && ev->Good(); + c = ti->NextTable(ev, tableKey, &table)) +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ + { + if (table && table->IsTable()) // need to dirty table? + { + if (table->IsTableUsed() || table->IsTableDirty()) { + // table->DirtyAllTableContent(ev); + // only necessary to mark table itself dirty: + table->SetTableDirty(); + table->SetTableRewrite(); + ++mWriter_TotalCount; + } + } else + table->NonTableTypeWarning(ev); + } + ti->CloseMapIter(ev); + } + } else + space->NonRowSpaceTypeError(ev); + } else + ev->NilPointerError(); + } + } + } else + this->NilWriterStoreError(ev); + + return ev->Good(); +} + +mork_bool morkWriter::OnNothingDone(morkEnv* ev) { + mWriter_Incremental = !mWriter_NeedDirtyAll; // opposites + + if (!mWriter_Store->IsStoreDirty() && !mWriter_NeedDirtyAll) { + mWriter_Phase = morkWriter_kPhaseWritingDone; + return morkBool_kTrue; + } + + // morkStream* stream = mWriter_Stream; + if (mWriter_NeedDirtyAll) this->DirtyAll(ev); + + if (ev->Good()) + mWriter_Phase = morkWriter_kPhaseDirtyAllDone; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool morkWriter::StartGroup(morkEnv* ev) { + nsIMdbEnv* mdbev = ev->AsMdbEnv(); + morkStream* stream = mWriter_Stream; + mWriter_DidStartGroup = morkBool_kTrue; + mWriter_DidEndGroup = morkBool_kFalse; + + char buf[4 + morkWriter_kGroupBufSize + 2]; // "@$${" + groupid + "{@" + char* p = buf; + *p++ = '@'; + *p++ = '$'; + *p++ = '$'; + *p++ = '{'; + + mork_token groupID = mWriter_CommitGroupIdentity; + mork_fill idFill = ev->TokenAsHex(p, groupID); + mWriter_GroupBufFill = 0; + // ev->TokenAsHex(mWriter_GroupBuf, groupID); + if (idFill < morkWriter_kGroupBufSize) { + // TokenAsHex appends a '\0', but it's not included in idFill count. + MORK_MEMCPY(mWriter_GroupBuf, p, idFill + 1); + mWriter_GroupBufFill = idFill; + } else { + *mWriter_GroupBuf = '\0'; + } + + p += idFill; + *p++ = '{'; + *p++ = '@'; + + stream->PutLineBreak(ev); + + morkStore* store = mWriter_Store; + if (store) // might need to capture commit group position? + { + mork_pos groupPos; + stream->Tell(mdbev, &groupPos); + if (!store->mStore_FirstCommitGroupPos) + store->mStore_FirstCommitGroupPos = groupPos; + else if (!store->mStore_SecondCommitGroupPos) + store->mStore_SecondCommitGroupPos = groupPos; + } + + mork_size bytesWritten; + stream->Write(mdbev, buf, 4 + idFill + 2, + &bytesWritten); // '@$${' + idFill + '{@' + stream->PutLineBreak(ev); + mWriter_LineSize = 0; + + return ev->Good(); +} + +mork_bool morkWriter::CommitGroup(morkEnv* ev) { + if (mWriter_DidStartGroup) { + nsIMdbEnv* mdbev = ev->AsMdbEnv(); + mork_size bytesWritten; + morkStream* stream = mWriter_Stream; + + if (mWriter_LineSize) stream->PutLineBreak(ev); + + stream->Putc(ev, '@'); + stream->Putc(ev, '$'); + stream->Putc(ev, '$'); + stream->Putc(ev, '}'); + + mork_fill bufFill = mWriter_GroupBufFill; + if (bufFill) stream->Write(mdbev, mWriter_GroupBuf, bufFill, &bytesWritten); + + stream->Putc(ev, '}'); + stream->Putc(ev, '@'); + stream->PutLineBreak(ev); + + mWriter_LineSize = 0; + } + + mWriter_DidStartGroup = morkBool_kFalse; + mWriter_DidEndGroup = morkBool_kTrue; + + return ev->Good(); +} + +mork_bool morkWriter::AbortGroup(morkEnv* ev) { + if (mWriter_DidStartGroup) { + morkStream* stream = mWriter_Stream; + stream->PutLineBreak(ev); + stream->PutStringThenNewline(ev, "@$$}~~}@"); + mWriter_LineSize = 0; + } + + mWriter_DidStartGroup = morkBool_kFalse; + mWriter_DidEndGroup = morkBool_kTrue; + + return ev->Good(); +} + +mork_bool morkWriter::OnDirtyAllDone(morkEnv* ev) { + if (ev->Good()) { + nsIMdbEnv* mdbev = ev->AsMdbEnv(); + morkStream* stream = mWriter_Stream; + mork_pos resultPos; + if (mWriter_NeedDirtyAll) // compress commit + { + stream->Seek(mdbev, 0, &resultPos); // beginning of stream + stream->PutStringThenNewline(ev, morkWriter_kFileHeader); + mWriter_LineSize = 0; + } else // else mWriter_Incremental + { + mork_pos eos = stream->Length(ev); // length is end of stream + if (ev->Good()) { + stream->Seek(mdbev, eos, &resultPos); // goto end of stream + if (eos < 128) // maybe need file header? + { + stream->PutStringThenNewline(ev, morkWriter_kFileHeader); + mWriter_LineSize = 0; + } + this->StartGroup(ev); // begin incremental transaction + } + } + } + + if (ev->Good()) + mWriter_Phase = morkWriter_kPhasePutHeaderDone; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool morkWriter::OnPutHeaderDone(morkEnv* ev) { + morkStream* stream = mWriter_Stream; + if (mWriter_LineSize) stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnPutHeaderDone()"); + mWriter_LineSize = 0; + + if (mWriter_NeedDirtyAll) // compress commit + { + morkStore* store = mWriter_Store; + if (store) + store->RenumberAllCollectableContent(ev); + else + this->NilWriterStoreError(ev); + } + + if (ev->Good()) + mWriter_Phase = morkWriter_kPhaseRenumberAllDone; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool morkWriter::OnRenumberAllDone(morkEnv* ev) { + morkStream* stream = mWriter_Stream; + if (mWriter_LineSize) stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnRenumberAllDone()"); + mWriter_LineSize = 0; + + if (mWriter_NeedDirtyAll) // compress commit + { + } + + if (ev->Good()) + mWriter_Phase = morkWriter_kPhaseStoreAtomSpaces; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool morkWriter::OnStoreAtomSpaces(morkEnv* ev) { + morkStream* stream = mWriter_Stream; + if (mWriter_LineSize) stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnStoreAtomSpaces()"); + mWriter_LineSize = 0; + + if (mWriter_NeedDirtyAll) // compress commit + { + } + + if (ev->Good()) { + morkStore* store = mWriter_Store; + if (store) { + morkAtomSpace* space = store->LazyGetGroundColumnSpace(ev); + if (space && space->IsAtomSpaceDirty()) { + // stream->PutStringThenNewline(ev, "// ground column space dict:"); + + if (mWriter_LineSize) { + stream->PutLineBreak(ev); + mWriter_LineSize = 0; + } + this->WriteAtomSpaceAsDict(ev, space); + space->SetAtomSpaceClean(); + } + } else + this->NilWriterStoreError(ev); + } + + if (ev->Good()) + mWriter_Phase = morkWriter_kPhaseStoreRowSpacesTables; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool morkWriter::OnAtomSpaceAtomAids(morkEnv* ev) { + morkStream* stream = mWriter_Stream; + if (mWriter_LineSize) stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnAtomSpaceAtomAids()"); + mWriter_LineSize = 0; + + if (mWriter_NeedDirtyAll) // compress commit + { + } + + if (ev->Good()) + mWriter_Phase = morkWriter_kPhaseStoreRowSpacesTables; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +void morkWriter::WriteAllStoreTables(morkEnv* ev) { + morkStore* store = mWriter_Store; + if (store && ev->Good()) { + morkRowSpaceMapIter* rsi = &mWriter_StoreRowSpacesIter; + rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces); + + mork_scope* key = 0; // ignore keys in map + morkRowSpace* space = 0; // old val node in the map + mork_change* c = 0; + + for (c = rsi->FirstRowSpace(ev, key, &space); c && ev->Good(); + c = rsi->NextRowSpace(ev, key, &space)) { + if (space) { + if (space->IsRowSpace()) { + space->SetRowSpaceClean(); + if (ev->Good()) { + morkTableMapIter* ti = &mWriter_RowSpaceTablesIter; + ti->InitTableMapIter(ev, &space->mRowSpace_Tables); + +#ifdef MORK_BEAD_OVER_NODE_MAPS + morkTable* table = ti->FirstTable(ev); + + for (; table && ev->Good(); table = ti->NextTable(ev)) +#else /*MORK_BEAD_OVER_NODE_MAPS*/ + mork_tid* key2 = 0; // ignore keys in table map + morkTable* table = 0; // old key row in the map + + for (c = ti->FirstTable(ev, key2, &table); c && ev->Good(); + c = ti->NextTable(ev, key2, &table)) +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ + { + if (table && table->IsTable()) { + if (table->IsTableDirty()) { + mWriter_BeVerbose = + (ev->mEnv_BeVerbose || table->IsTableVerbose()); + + if (this->PutTableDict(ev, table)) this->PutTable(ev, table); + + table->SetTableClean(ev); + mWriter_BeVerbose = ev->mEnv_BeVerbose; + } + } else + table->NonTableTypeWarning(ev); + } + ti->CloseMapIter(ev); + } + if (ev->Good()) { + mWriter_TableRowScope = 0; // ensure no table context now + +#ifdef MORK_ENABLE_PROBE_MAPS + morkRowProbeMapIter* ri = &mWriter_RowSpaceRowsIter; +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkRowMapIter* ri = &mWriter_RowSpaceRowsIter; +#endif /*MORK_ENABLE_PROBE_MAPS*/ + ri->InitRowMapIter(ev, &space->mRowSpace_Rows); + + morkRow* row = 0; // old row in the map + + for (c = ri->FirstRow(ev, &row); c && ev->Good(); + c = ri->NextRow(ev, &row)) { + if (row && row->IsRow()) { + // later we should also check that table use count is nonzero: + if (row->IsRowDirty()) // && row->IsRowUsed() ?? + { + mWriter_BeVerbose = ev->mEnv_BeVerbose; + if (this->PutRowDict(ev, row)) { + if (ev->Good() && mWriter_DidStartDict) { + this->EndDict(ev); + if (mWriter_LineSize < 32 && ev->Good()) + mWriter_SuppressDirtyRowNewline = morkBool_kTrue; + } + + if (ev->Good()) this->PutRow(ev, row); + } + mWriter_BeVerbose = ev->mEnv_BeVerbose; + } + } else + row->NonRowTypeWarning(ev); + } + ri->CloseMapIter(ev); + } + } else + space->NonRowSpaceTypeError(ev); + } else + ev->NilPointerError(); + } + } +} + +mork_bool morkWriter::OnStoreRowSpacesTables(morkEnv* ev) { + morkStream* stream = mWriter_Stream; + if (mWriter_LineSize) stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnStoreRowSpacesTables()"); + mWriter_LineSize = 0; + + if (mWriter_NeedDirtyAll) // compress commit + { + } + + // later we'll break this up, but today we'll write all in one shot: + this->WriteAllStoreTables(ev); + + if (ev->Good()) + mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool morkWriter::OnRowSpaceTables(morkEnv* ev) { + morkStream* stream = mWriter_Stream; + if (mWriter_LineSize) stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnRowSpaceTables()"); + mWriter_LineSize = 0; + + if (mWriter_NeedDirtyAll) // compress commit + { + } + + if (ev->Good()) + mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool morkWriter::OnTableRowArray(morkEnv* ev) { + morkStream* stream = mWriter_Stream; + if (mWriter_LineSize) stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnTableRowArray()"); + mWriter_LineSize = 0; + + if (mWriter_NeedDirtyAll) // compress commit + { + } + + if (ev->Good()) + mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool morkWriter::OnStoreRowSpacesRows(morkEnv* ev) { + morkStream* stream = mWriter_Stream; + if (mWriter_LineSize) stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnStoreRowSpacesRows()"); + mWriter_LineSize = 0; + + if (mWriter_NeedDirtyAll) // compress commit + { + } + + if (ev->Good()) + mWriter_Phase = morkWriter_kPhaseContentDone; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool morkWriter::OnRowSpaceRows(morkEnv* ev) { + morkStream* stream = mWriter_Stream; + if (mWriter_LineSize) stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnRowSpaceRows()"); + mWriter_LineSize = 0; + + if (mWriter_NeedDirtyAll) // compress commit + { + } + + if (ev->Good()) + mWriter_Phase = morkWriter_kPhaseContentDone; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool morkWriter::OnContentDone(morkEnv* ev) { + morkStream* stream = mWriter_Stream; + if (mWriter_LineSize) stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnContentDone()"); + mWriter_LineSize = 0; + + if (mWriter_Incremental) { + if (ev->Good()) + this->CommitGroup(ev); + else + this->AbortGroup(ev); + } else if (mWriter_Store && ev->Good()) { + // after rewriting everything, there are no transaction groups: + mWriter_Store->mStore_FirstCommitGroupPos = 0; + mWriter_Store->mStore_SecondCommitGroupPos = 0; + } + + stream->Flush(ev->AsMdbEnv()); + nsIMdbFile* bud = mWriter_Bud; + if (bud) { + bud->Flush(ev->AsMdbEnv()); + bud->BecomeTrunk(ev->AsMdbEnv()); + nsIMdbFile_SlotStrongFile((nsIMdbFile*)0, ev, &mWriter_Bud); + } else if (!mWriter_Incremental) // should have a bud? + this->NilWriterBudError(ev); + + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop always + mWriter_DoneCount = mWriter_TotalCount; + + return ev->Good(); +} + +mork_bool morkWriter::OnWritingDone(morkEnv* ev) { + mWriter_DoneCount = mWriter_TotalCount; + ev->NewWarning("writing is done"); + return ev->Good(); +} + +mork_bool morkWriter::PutTableChange(morkEnv* ev, + const morkTableChange* inChange) { + nsIMdbEnv* mdbev = ev->AsMdbEnv(); + if (inChange->IsAddRowTableChange()) { + this->PutRow(ev, inChange->mTableChange_Row); // row alone means add + } else if (inChange->IsCutRowTableChange()) { + mWriter_Stream->Putc(ev, '-'); // prefix '-' indicates cut row + ++mWriter_LineSize; + this->PutRow(ev, inChange->mTableChange_Row); + } else if (inChange->IsMoveRowTableChange()) { + this->PutRow(ev, inChange->mTableChange_Row); + char buf[64]; + char* p = buf; + *p++ = '!'; // for moves, position is indicated by prefix '!' + mork_size posSize = ev->TokenAsHex(p, inChange->mTableChange_Pos); + p += posSize; + *p++ = ' '; + mork_size bytesWritten; + mWriter_Stream->Write(mdbev, buf, posSize + 2, &bytesWritten); + mWriter_LineSize += bytesWritten; + } else + inChange->UnknownChangeError(ev); + + return ev->Good(); +} + +mork_bool morkWriter::PutTable(morkEnv* ev, morkTable* ioTable) { + if (ev->Good()) this->StartTable(ev, ioTable); + + if (ev->Good()) { + if (ioTable->IsTableRewrite() || mWriter_NeedDirtyAll) { + morkArray* array = &ioTable->mTable_RowArray; // vector of rows + mork_fill fill = array->mArray_Fill; // count of rows + morkRow** rows = (morkRow**)array->mArray_Slots; + if (rows && fill) { + morkRow** end = rows + fill; + while (rows < end && ev->Good()) { + morkRow* r = *rows++; // next row to consider + this->PutRow(ev, r); + } + } + } else // incremental write only table changes + { + morkList* list = &ioTable->mTable_ChangeList; + morkNext* next = list->GetListHead(); + while (next && ev->Good()) { + this->PutTableChange(ev, (morkTableChange*)next); + next = next->GetNextLink(); + } + } + } + + if (ev->Good()) this->EndTable(ev); + + ioTable->SetTableClean(ev); // note this also cleans change list + mWriter_TableRowScope = 0; + + ++mWriter_DoneCount; + return ev->Good(); +} + +mork_bool morkWriter::PutTableDict(morkEnv* ev, morkTable* ioTable) { + morkRowSpace* space = ioTable->mTable_RowSpace; + mWriter_TableRowScope = space->SpaceScope(); + mWriter_TableForm = 0; // (f=iso-8859-1) + mWriter_TableAtomScope = 'v'; // (a=v) + mWriter_TableKind = ioTable->mTable_Kind; + + mWriter_RowForm = mWriter_TableForm; + mWriter_RowAtomScope = mWriter_TableAtomScope; + mWriter_RowScope = mWriter_TableRowScope; + + mWriter_DictForm = mWriter_TableForm; + mWriter_DictAtomScope = mWriter_TableAtomScope; + + // if ( ev->Good() ) + // this->StartDict(ev); // delay as long as possible + + if (ev->Good()) { + morkRow* r = ioTable->mTable_MetaRow; + if (r) { + if (r->IsRow()) + this->PutRowDict(ev, r); + else + r->NonRowTypeError(ev); + } + morkArray* array = &ioTable->mTable_RowArray; // vector of rows + mork_fill fill = array->mArray_Fill; // count of rows + morkRow** rows = (morkRow**)array->mArray_Slots; + if (rows && fill) { + morkRow** end = rows + fill; + while (rows < end && ev->Good()) { + r = *rows++; // next row to consider + if (r && r->IsRow()) + this->PutRowDict(ev, r); + else + r->NonRowTypeError(ev); + } + } + // we may have a change for a row which is no longer in the + // table, but contains a cell with something not in the dictionary. + // So, loop through the rows in the change log, writing out any + // dirty dictionary elements. + morkList* list = &ioTable->mTable_ChangeList; + morkNext* next = list->GetListHead(); + while (next && ev->Good()) { + r = ((morkTableChange*)next)->mTableChange_Row; + if (r && r->IsRow()) this->PutRowDict(ev, r); + next = next->GetNextLink(); + } + } + if (ev->Good()) this->EndDict(ev); + + return ev->Good(); +} + +void morkWriter::WriteTokenToTokenMetaCell(morkEnv* ev, mork_token inCol, + mork_token inValue) { + morkStream* stream = mWriter_Stream; + mork_bool isKindCol = (morkStore_kKindColumn == inCol); + mork_u1 valSep = (mork_u1)((isKindCol) ? '^' : '='); + + char buf[128]; // buffer for staging the two hex IDs + char* p = buf; + + mork_size bytesWritten; + if (inCol < 0x80) { + stream->Putc(ev, '('); + stream->Putc(ev, (char)inCol); + stream->Putc(ev, valSep); + } else { + *p++ = '('; // we always start with open paren + + *p++ = '^'; // indicates col is hex ID + mork_size colSize = ev->TokenAsHex(p, inCol); + p += colSize; + *p++ = (char)valSep; + stream->Write(ev->AsMdbEnv(), buf, colSize + 3, &bytesWritten); + + mWriter_LineSize += bytesWritten; + } + + if (isKindCol) { + p = buf; + mork_size valSize = ev->TokenAsHex(p, inValue); + p += valSize; + *p++ = ':'; + *p++ = 'c'; + *p++ = ')'; + stream->Write(ev->AsMdbEnv(), buf, valSize + 3, &bytesWritten); + mWriter_LineSize += bytesWritten; + } else { + this->IndentAsNeeded(ev, morkWriter_kTableMetaCellValueDepth); + mdbYarn* yarn = &mWriter_ColYarn; + // mork_u1* yarnBuf = (mork_u1*) yarn->mYarn_Buf; + mWriter_Store->TokenToString(ev, inValue, yarn); + this->WriteYarn(ev, yarn); + stream->Putc(ev, ')'); + ++mWriter_LineSize; + } + + // mork_fill fill = yarn->mYarn_Fill; + // yarnBuf[ fill ] = ')'; // append terminator + // mWriter_LineSize += stream->Write(ev, yarnBuf, fill + 1); // +1 for ')' +} + +void morkWriter::WriteStringToTokenDictCell(morkEnv* ev, const char* inCol, + mork_token inValue) +// Note inCol should begin with '(' and end with '=', with col in between. +{ + morkStream* stream = mWriter_Stream; + mWriter_LineSize += stream->PutString(ev, inCol); + + this->IndentAsNeeded(ev, morkWriter_kDictMetaCellValueDepth); + mdbYarn* yarn = &mWriter_ColYarn; + // mork_u1* yarnBuf = (mork_u1*) yarn->mYarn_Buf; + mWriter_Store->TokenToString(ev, inValue, yarn); + this->WriteYarn(ev, yarn); + stream->Putc(ev, ')'); + ++mWriter_LineSize; + + // mork_fill fill = yarn->mYarn_Fill; + // yarnBuf[ fill ] = ')'; // append terminator + // mWriter_LineSize += stream->Write(ev, yarnBuf, fill + 1); // +1 for ')' +} + +void morkWriter::ChangeDictAtomScope(morkEnv* ev, mork_scope inScope) { + if (inScope != mWriter_DictAtomScope) { + ev->NewWarning("unexpected atom scope change"); + + morkStream* stream = mWriter_Stream; + if (mWriter_LineSize) stream->PutLineBreak(ev); + mWriter_LineSize = 0; + + char buf[128]; // buffer for staging the two hex IDs + char* p = buf; + *p++ = '<'; // we always start with open paren + *p++ = '('; // we always start with open paren + *p++ = (char)morkStore_kAtomScopeColumn; + + mork_size scopeSize = 1; // default to one byte + if (inScope >= 0x80) { + *p++ = '^'; // indicates col is hex ID + scopeSize = ev->TokenAsHex(p, inScope); + p += scopeSize; + } else { + *p++ = '='; // indicates col is imm byte + *p++ = (char)(mork_u1)inScope; + } + + *p++ = ')'; + *p++ = '>'; + *p = 0; + + mork_size pending = scopeSize + 6; + this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth); + mork_size bytesWritten; + + stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten); + mWriter_LineSize += bytesWritten; + + mWriter_DictAtomScope = inScope; + } +} + +void morkWriter::ChangeRowForm(morkEnv* ev, mork_cscode inNewForm) { + if (inNewForm != mWriter_RowForm) { + morkStream* stream = mWriter_Stream; + if (mWriter_LineSize) stream->PutLineBreak(ev); + mWriter_LineSize = 0; + + char buf[128]; // buffer for staging the two hex IDs + char* p = buf; + *p++ = '['; // we always start with open bracket + *p++ = '('; // we always start with open paren + *p++ = (char)morkStore_kFormColumn; + + mork_size formSize = 1; // default to one byte + if (!morkCh_IsValue(inNewForm)) { + *p++ = '^'; // indicates col is hex ID + formSize = ev->TokenAsHex(p, inNewForm); + p += formSize; + } else { + *p++ = '='; // indicates col is imm byte + *p++ = (char)(mork_u1)inNewForm; + } + + *p++ = ')'; + *p++ = ']'; + *p = 0; + + mork_size pending = formSize + 6; + this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth); + mork_size bytesWritten; + stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten); + mWriter_LineSize += bytesWritten; + + mWriter_RowForm = inNewForm; + } +} + +void morkWriter::ChangeDictForm(morkEnv* ev, mork_cscode inNewForm) { + if (inNewForm != mWriter_DictForm) { + morkStream* stream = mWriter_Stream; + if (mWriter_LineSize) stream->PutLineBreak(ev); + mWriter_LineSize = 0; + + char buf[128]; // buffer for staging the two hex IDs + char* p = buf; + *p++ = '<'; // we always start with open angle + *p++ = '('; // we always start with open paren + *p++ = (char)morkStore_kFormColumn; + + mork_size formSize = 1; // default to one byte + if (!morkCh_IsValue(inNewForm)) { + *p++ = '^'; // indicates col is hex ID + formSize = ev->TokenAsHex(p, inNewForm); + p += formSize; + } else { + *p++ = '='; // indicates col is imm byte + *p++ = (char)(mork_u1)inNewForm; + } + + *p++ = ')'; + *p++ = '>'; + *p = 0; + + mork_size pending = formSize + 6; + this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth); + + mork_size bytesWritten; + stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten); + mWriter_LineSize += bytesWritten; + + mWriter_DictForm = inNewForm; + } +} + +void morkWriter::StartDict(morkEnv* ev) { + morkStream* stream = mWriter_Stream; + if (mWriter_DidStartDict) { + stream->Putc(ev, '>'); // end dict + ++mWriter_LineSize; + } + mWriter_DidStartDict = morkBool_kTrue; + mWriter_DidEndDict = morkBool_kFalse; + + if (mWriter_LineSize) stream->PutLineBreak(ev); + mWriter_LineSize = 0; + + if (mWriter_TableRowScope) // blank line before table's dict? + stream->PutLineBreak(ev); + + if (mWriter_DictForm || mWriter_DictAtomScope != 'v') { + stream->Putc(ev, '<'); + stream->Putc(ev, ' '); + stream->Putc(ev, '<'); + mWriter_LineSize = 3; + if (mWriter_DictForm) + this->WriteStringToTokenDictCell(ev, "(f=", mWriter_DictForm); + if (mWriter_DictAtomScope != 'v') + this->WriteStringToTokenDictCell(ev, "(a=", mWriter_DictAtomScope); + + stream->Putc(ev, '>'); + ++mWriter_LineSize; + + mWriter_LineSize = stream->PutIndent(ev, morkWriter_kDictAliasDepth); + } else { + stream->Putc(ev, '<'); + // stream->Putc(ev, ' '); + ++mWriter_LineSize; + } +} + +void morkWriter::EndDict(morkEnv* ev) { + morkStream* stream = mWriter_Stream; + if (mWriter_DidStartDict) { + stream->Putc(ev, '>'); // end dict + ++mWriter_LineSize; + } + mWriter_DidStartDict = morkBool_kFalse; + mWriter_DidEndDict = morkBool_kTrue; +} + +void morkWriter::StartTable(morkEnv* ev, morkTable* ioTable) { + mdbOid toid; // to receive table oid + ioTable->GetTableOid(ev, &toid); + + if (ev->Good()) { + morkStream* stream = mWriter_Stream; + if (mWriter_LineSize) stream->PutLineBreak(ev); + mWriter_LineSize = 0; + // stream->PutLineBreak(ev); + + char buf[64 + 16]; // buffer for staging hex + char* p = buf; + *p++ = '{'; // punct 1 + mork_size punctSize = + (mWriter_BeVerbose) ? 10 : 3; // counting "{ {/*r=*/ " + + if (ioTable->IsTableRewrite() && mWriter_Incremental) { + *p++ = '-'; + ++punctSize; // counting '-' // punct ++ + ++mWriter_LineSize; + } + mork_size oidSize = ev->OidAsHex(p, toid); + p += oidSize; + *p++ = ' '; // punct 2 + *p++ = '{'; // punct 3 + if (mWriter_BeVerbose) { + *p++ = '/'; // punct=4 + *p++ = '*'; // punct=5 + *p++ = 'r'; // punct=6 + *p++ = '='; // punct=7 + + mork_token tableUses = (mork_token)ioTable->mTable_GcUses; + mork_size usesSize = ev->TokenAsHex(p, tableUses); + punctSize += usesSize; + p += usesSize; + + *p++ = '*'; // punct=8 + *p++ = '/'; // punct=9 + *p++ = ' '; // punct=10 + } + mork_size bytesWritten; + + stream->Write(ev->AsMdbEnv(), buf, oidSize + punctSize, &bytesWritten); + mWriter_LineSize += bytesWritten; + + mork_kind tk = mWriter_TableKind; + if (tk) { + this->IndentAsNeeded(ev, morkWriter_kTableMetaCellDepth); + this->WriteTokenToTokenMetaCell(ev, morkStore_kKindColumn, tk); + } + + stream->Putc(ev, '('); // start 's' col cell + stream->Putc(ev, 's'); // column + stream->Putc(ev, '='); // column + mWriter_LineSize += 3; + + int prio = (int)ioTable->mTable_Priority; + if (prio > 9) // need to force down to max decimal digit? + prio = 9; + prio += '0'; // add base digit zero + stream->Putc(ev, prio); // priority: (s=0 + ++mWriter_LineSize; + + if (ioTable->IsTableUnique()) { + stream->Putc(ev, 'u'); // (s=0u + ++mWriter_LineSize; + } + if (ioTable->IsTableVerbose()) { + stream->Putc(ev, 'v'); // (s=0uv + ++mWriter_LineSize; + } + + // stream->Putc(ev, ':'); // (s=0uv: + // stream->Putc(ev, 'c'); // (s=0uv:c + stream->Putc(ev, ')'); // end 's' col cell (s=0uv:c) + mWriter_LineSize += 1; // maybe 3 if we add ':' and 'c' + + morkRow* r = ioTable->mTable_MetaRow; + if (r) { + if (r->IsRow()) { + mWriter_SuppressDirtyRowNewline = morkBool_kTrue; + this->PutRow(ev, r); + } else + r->NonRowTypeError(ev); + } + + stream->Putc(ev, '}'); // end meta + ++mWriter_LineSize; + + if (mWriter_LineSize < mWriter_MaxIndent) { + stream->Putc(ev, ' '); // nice white space + ++mWriter_LineSize; + } + } +} + +void morkWriter::EndTable(morkEnv* ev) { + morkStream* stream = mWriter_Stream; + stream->Putc(ev, '}'); // end table + ++mWriter_LineSize; + + mWriter_TableAtomScope = 'v'; // (a=v) +} + +mork_bool morkWriter::PutRowDict(morkEnv* ev, morkRow* ioRow) { + mWriter_RowForm = mWriter_TableForm; + + morkCell* cells = ioRow->mRow_Cells; + if (cells) { + morkStream* stream = mWriter_Stream; + mdbYarn yarn; // to ref content inside atom + char buf[64]; // buffer for staging the dict alias hex ID + char* idBuf = buf + 1; // where the id always starts + buf[0] = '('; // we always start with open paren + + morkCell* end = cells + ioRow->mRow_Length; + --cells; // prepare for preincrement: + while (++cells < end && ev->Good()) { + morkAtom* atom = cells->GetAtom(); + if (atom && atom->IsAtomDirty()) { + if (atom->IsBook()) // is it possible to write atom ID? + { + if (!this->DidStartDict()) { + this->StartDict(ev); + if (ev->Bad()) break; + } + atom->SetAtomClean(); // neutralize change + + this->IndentAsNeeded(ev, morkWriter_kDictAliasDepth); + morkBookAtom* ba = (morkBookAtom*)atom; + mork_size size = ev->TokenAsHex(idBuf, ba->mBookAtom_Id); + mork_size bytesWritten; + stream->Write(ev->AsMdbEnv(), buf, size + 1, &bytesWritten); // '(' + mWriter_LineSize += bytesWritten; + + if (morkAtom::AliasYarn(atom, &yarn)) { + mork_scope atomScope = atom->GetBookAtomSpaceScope(ev); + if (atomScope && atomScope != mWriter_DictAtomScope) + this->ChangeDictAtomScope(ev, atomScope); + + if (mWriter_DidStartDict && yarn.mYarn_Form != mWriter_DictForm) + this->ChangeDictForm(ev, yarn.mYarn_Form); + + mork_size pending = + yarn.mYarn_Fill + morkWriter_kYarnEscapeSlop + 1; + this->IndentOverMaxLine(ev, pending, + morkWriter_kDictAliasValueDepth); + + stream->Putc(ev, '='); // start value + ++mWriter_LineSize; + + this->WriteYarn(ev, &yarn); + + stream->Putc(ev, ')'); // end value + ++mWriter_LineSize; + } else + atom->BadAtomKindError(ev); + + ++mWriter_DoneCount; + } + } + } + } + return ev->Good(); +} + +mork_bool morkWriter::IsYarnAllValue(const mdbYarn* inYarn) { + mork_fill fill = inYarn->mYarn_Fill; + const mork_u1* buf = (const mork_u1*)inYarn->mYarn_Buf; + const mork_u1* end = buf + fill; + --buf; // prepare for preincrement + while (++buf < end) { + mork_ch c = *buf; + if (!morkCh_IsValue(c)) return morkBool_kFalse; + } + return morkBool_kTrue; +} + +mork_bool morkWriter::PutVerboseCell(morkEnv* ev, morkCell* ioCell, + mork_bool inWithVal) { + morkStream* stream = mWriter_Stream; + morkStore* store = mWriter_Store; + + mdbYarn* colYarn = &mWriter_ColYarn; + + morkAtom* atom = (inWithVal) ? ioCell->GetAtom() : (morkAtom*)0; + + mork_column col = ioCell->GetColumn(); + store->TokenToString(ev, col, colYarn); + + mdbYarn yarn; // to ref content inside atom + morkAtom::AliasYarn(atom, &yarn); // works even when atom==nil + + if (yarn.mYarn_Form != mWriter_RowForm) + this->ChangeRowForm(ev, yarn.mYarn_Form); + + mork_size pending = + yarn.mYarn_Fill + colYarn->mYarn_Fill + morkWriter_kYarnEscapeSlop + 3; + this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth); + + stream->Putc(ev, '('); // start cell + ++mWriter_LineSize; + + this->WriteYarn(ev, colYarn); // column + + pending = yarn.mYarn_Fill + morkWriter_kYarnEscapeSlop; + this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellValueDepth); + stream->Putc(ev, '='); + ++mWriter_LineSize; + + this->WriteYarn(ev, &yarn); // value + + stream->Putc(ev, ')'); // end cell + ++mWriter_LineSize; + + return ev->Good(); +} + +mork_bool morkWriter::PutVerboseRowCells(morkEnv* ev, morkRow* ioRow) { + morkCell* cells = ioRow->mRow_Cells; + if (cells) { + morkCell* end = cells + ioRow->mRow_Length; + --cells; // prepare for preincrement: + while (++cells < end && ev->Good()) { + // note we prefer to avoid writing cells here with no value: + if (cells->GetAtom()) // does cell have any value? + this->PutVerboseCell(ev, cells, /*inWithVal*/ morkBool_kTrue); + } + } + return ev->Good(); +} + +mork_bool morkWriter::PutCell(morkEnv* ev, morkCell* ioCell, + mork_bool inWithVal) { + morkStream* stream = mWriter_Stream; + char buf[128]; // buffer for staging hex ids + char* idBuf = buf + 2; // where the id always starts + buf[0] = '('; // we always start with open paren + buf[1] = '^'; // column is always a hex ID + + mork_size colSize = 0; // the size of col hex ID + mork_size bytesWritten; + + morkAtom* atom = (inWithVal) ? ioCell->GetAtom() : (morkAtom*)0; + + mork_column col = ioCell->GetColumn(); + char* p = idBuf; + colSize = ev->TokenAsHex(p, col); + p += colSize; + + mdbYarn yarn; // to ref content inside atom + morkAtom::AliasYarn(atom, &yarn); // works even when atom==nil + + if (yarn.mYarn_Form != mWriter_RowForm) + this->ChangeRowForm(ev, yarn.mYarn_Form); + + if (atom && atom->IsBook()) // is it possible to write atom ID? + { + this->IndentAsNeeded(ev, morkWriter_kRowCellDepth); + *p++ = '^'; + morkBookAtom* ba = (morkBookAtom*)atom; + + mork_size valSize = ev->TokenAsHex(p, ba->mBookAtom_Id); + mork_fill yarnFill = yarn.mYarn_Fill; + mork_bool putImmYarn = (yarnFill <= valSize); + if (putImmYarn) putImmYarn = this->IsYarnAllValue(&yarn); + + if (putImmYarn) // value no bigger than id? + { + p[-1] = '='; // go back and clobber '^' with '=' instead + if (yarnFill) { + MORK_MEMCPY(p, yarn.mYarn_Buf, yarnFill); + p += yarnFill; + } + *p++ = ')'; + mork_size distance = (mork_size)(p - buf); + stream->Write(ev->AsMdbEnv(), buf, distance, &bytesWritten); + mWriter_LineSize += bytesWritten; + } else { + p += valSize; + *p = ')'; + stream->Write(ev->AsMdbEnv(), buf, colSize + valSize + 4, &bytesWritten); + mWriter_LineSize += bytesWritten; + } + + if (atom->IsAtomDirty()) { + atom->SetAtomClean(); + ++mWriter_DoneCount; + } + } else // must write an anonymous atom + { + mork_size pending = + yarn.mYarn_Fill + colSize + morkWriter_kYarnEscapeSlop + 2; + this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth); + + mork_size bytesWritten; + stream->Write(ev->AsMdbEnv(), buf, colSize + 2, &bytesWritten); + mWriter_LineSize += bytesWritten; + + pending -= (colSize + 2); + this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth); + stream->Putc(ev, '='); + ++mWriter_LineSize; + + this->WriteYarn(ev, &yarn); + stream->Putc(ev, ')'); // end cell + ++mWriter_LineSize; + } + return ev->Good(); +} + +mork_bool morkWriter::PutRowCells(morkEnv* ev, morkRow* ioRow) { + morkCell* cells = ioRow->mRow_Cells; + if (cells) { + morkCell* end = cells + ioRow->mRow_Length; + --cells; // prepare for preincrement: + while (++cells < end && ev->Good()) { + // note we prefer to avoid writing cells here with no value: + if (cells->GetAtom()) // does cell have any value? + this->PutCell(ev, cells, /*inWithVal*/ morkBool_kTrue); + } + } + return ev->Good(); +} + +mork_bool morkWriter::PutRow(morkEnv* ev, morkRow* ioRow) { + if (ioRow && ioRow->IsRow()) { + mWriter_RowForm = mWriter_TableForm; + + mork_size bytesWritten; + morkStream* stream = mWriter_Stream; + char buf[128 + 16]; // buffer for staging hex + char* p = buf; + mdbOid* roid = &ioRow->mRow_Oid; + mork_size ridSize = 0; + + mork_scope tableScope = mWriter_TableRowScope; + + if (ioRow->IsRowDirty()) { + if (mWriter_SuppressDirtyRowNewline || !mWriter_LineSize) + mWriter_SuppressDirtyRowNewline = morkBool_kFalse; + else { + if (tableScope) // in a table? + mWriter_LineSize = stream->PutIndent(ev, morkWriter_kRowDepth); + else + mWriter_LineSize = stream->PutIndent(ev, 0); // no indent + } + + // mork_rid rid = roid->mOid_Id; + *p++ = '['; // start row punct=1 + mork_size punctSize = + (mWriter_BeVerbose) ? 9 : 1; // counting "[ /*r=*/ " + + mork_bool rowRewrite = ioRow->IsRowRewrite(); + + if (rowRewrite && mWriter_Incremental) { + *p++ = '-'; + ++punctSize; // counting '-' + ++mWriter_LineSize; + } + + if (tableScope && roid->mOid_Scope == tableScope) + ridSize = ev->TokenAsHex(p, roid->mOid_Id); + else + ridSize = ev->OidAsHex(p, *roid); + + p += ridSize; + + if (mWriter_BeVerbose) { + *p++ = ' '; // punct=2 + *p++ = '/'; // punct=3 + *p++ = '*'; // punct=4 + *p++ = 'r'; // punct=5 + *p++ = '='; // punct=6 + + mork_size usesSize = ev->TokenAsHex(p, (mork_token)ioRow->mRow_GcUses); + punctSize += usesSize; + p += usesSize; + + *p++ = '*'; // punct=7 + *p++ = '/'; // punct=8 + *p++ = ' '; // punct=9 + } + stream->Write(ev->AsMdbEnv(), buf, ridSize + punctSize, &bytesWritten); + mWriter_LineSize += bytesWritten; + + // special case situation where row puts exactly one column: + if (!rowRewrite && mWriter_Incremental && ioRow->HasRowDelta()) { + mork_column col = ioRow->GetDeltaColumn(); + morkCell dummy(col, morkChange_kNil, (morkAtom*)0); + morkCell* cell = 0; + + mork_bool withVal = (ioRow->GetDeltaChange() != morkChange_kCut); + + if (withVal) { + mork_pos cellPos = 0; // dummy pos + cell = ioRow->GetCell(ev, col, &cellPos); + } + if (!cell) cell = &dummy; + + if (mWriter_BeVerbose) + this->PutVerboseCell(ev, cell, withVal); + else + this->PutCell(ev, cell, withVal); + } else // put entire row? + { + if (mWriter_BeVerbose) + this->PutVerboseRowCells(ev, ioRow); // write all, verbosely + else + this->PutRowCells(ev, ioRow); // write all, hex notation + } + + stream->Putc(ev, ']'); // end row + ++mWriter_LineSize; + } else { + this->IndentAsNeeded(ev, morkWriter_kRowDepth); + + if (tableScope && roid->mOid_Scope == tableScope) + ridSize = ev->TokenAsHex(p, roid->mOid_Id); + else + ridSize = ev->OidAsHex(p, *roid); + + stream->Write(ev->AsMdbEnv(), buf, ridSize, &bytesWritten); + mWriter_LineSize += bytesWritten; + stream->Putc(ev, ' '); + ++mWriter_LineSize; + } + + ++mWriter_DoneCount; + + ioRow->SetRowClean(); // try to do this at the very last + } else + ioRow->NonRowTypeWarning(ev); + + return ev->Good(); +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkWriter.h b/comm/mailnews/db/mork/morkWriter.h new file mode 100644 index 0000000000..7e716bec6a --- /dev/null +++ b/comm/mailnews/db/mork/morkWriter.h @@ -0,0 +1,340 @@ +/* -*- 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 _MORKWRITER_ +#define _MORKWRITER_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +# include "morkMap.h" +#endif + +#ifndef _MORKROWMAP_ +# include "morkRowMap.h" +#endif + +#ifndef _MORKTABLE_ +# include "morkTable.h" +#endif + +#ifndef _MORKATOMMAP_ +# include "morkAtomMap.h" +#endif + +#ifndef _MORKATOMSPACE_ +# include "morkAtomSpace.h" +#endif + +#ifndef _MORKROWSPACE_ +# include "morkRowSpace.h" +#endif + +#ifndef _MORKSTREAM_ +# include "morkStream.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/* buffer size for stream */ +#define morkWriter_kStreamBufSize /*i*/ (16 * 1024) + +#define morkDerived_kWriter /*i*/ 0x5772 /* ascii 'Wr' */ + +#define morkWriter_kPhaseNothingDone 0 /* nothing has yet been done */ +#define morkWriter_kPhaseDirtyAllDone 1 /* DirtyAll() is done */ +#define morkWriter_kPhasePutHeaderDone 2 /* PutHeader() is done */ + +#define morkWriter_kPhaseRenumberAllDone 3 /* RenumberAll() is done */ + +#define morkWriter_kPhaseStoreAtomSpaces 4 /*mWriter_StoreAtomSpacesIter*/ +#define morkWriter_kPhaseAtomSpaceAtomAids 5 /*mWriter_AtomSpaceAtomAidsIter*/ + +#define morkWriter_kPhaseStoreRowSpacesTables 6 /*mWriter_StoreRowSpacesIter*/ +#define morkWriter_kPhaseRowSpaceTables 7 /*mWriter_RowSpaceTablesIter*/ +#define morkWriter_kPhaseTableRowArray 8 /*mWriter_TableRowArrayPos */ + +#define morkWriter_kPhaseStoreRowSpacesRows 9 /*mWriter_StoreRowSpacesIter*/ +#define morkWriter_kPhaseRowSpaceRows 10 /*mWriter_RowSpaceRowsIter*/ + +#define morkWriter_kPhaseContentDone 11 /* all content written */ +#define morkWriter_kPhaseWritingDone 12 /* everything has been done */ + +#define morkWriter_kCountNumberOfPhases 13 /* part of mWrite_TotalCount */ + +#define morkWriter_kMaxColumnNameSize 128 /* longest writable col name */ + +#define morkWriter_kMaxIndent 66 /* default value for mWriter_MaxIndent */ +#define morkWriter_kMaxLine 78 /* default value for mWriter_MaxLine */ + +#define morkWriter_kYarnEscapeSlop 4 /* guess average yarn escape overhead */ + +#define morkWriter_kTableMetaCellDepth 4 /* */ +#define morkWriter_kTableMetaCellValueDepth 6 /* */ + +#define morkWriter_kDictMetaCellDepth 4 /* */ +#define morkWriter_kDictMetaCellValueDepth 6 /* */ + +#define morkWriter_kDictAliasDepth 2 /* */ +#define morkWriter_kDictAliasValueDepth 4 /* */ + +#define morkWriter_kRowDepth 2 /* */ +#define morkWriter_kRowCellDepth 4 /* */ +#define morkWriter_kRowCellValueDepth 6 /* */ + +#define morkWriter_kGroupBufSize 64 /* */ + +// v=1.1 retired on 23-Mar-99 (for metainfo one char column names) +// v=1.2 retired on 20-Apr-99 (for ":c" suffix on table kind hex refs) +// v=1.3 retired on 20-Apr-99 (for 1CE:m instead of ill-formed 1CE:^6D) +#define morkWriter_kFileHeader "// <!-- <mdb:mork:z v=\"1.4\"/> -->" + +class morkWriter : public morkNode { // row iterator + + // public: // slots inherited from morkObject (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 + + public: // state is public because the entire Mork system is private + morkStore* mWriter_Store; // weak ref to committing store + nsIMdbFile* mWriter_File; // strong ref to store's file + nsIMdbFile* mWriter_Bud; // strong ref to bud of mWriter_File + morkStream* mWriter_Stream; // strong ref to stream on bud file + nsIMdbHeap* mWriter_SlotHeap; // strong ref to slot heap + + // GroupIdentity should be based on mStore_CommitGroupIdentity: + mork_gid mWriter_CommitGroupIdentity; // transaction ID number + + // GroupBuf holds a hex version of mWriter_CommitGroupIdentity: + char mWriter_GroupBuf[morkWriter_kGroupBufSize]; + mork_fill mWriter_GroupBufFill; // actual bytes in GroupBuf + + mork_count mWriter_TotalCount; // count of all things to be written + mork_count mWriter_DoneCount; // count of things already written + + mork_size mWriter_LineSize; // length of current line being written + mork_size mWriter_MaxIndent; // line size forcing a line break + mork_size mWriter_MaxLine; // line size forcing a value continuation + + mork_cscode mWriter_TableForm; // current charset metainfo + mork_scope mWriter_TableAtomScope; // current atom scope + mork_scope mWriter_TableRowScope; // current row scope + mork_kind mWriter_TableKind; // current table kind + + mork_cscode mWriter_RowForm; // current charset metainfo + mork_scope mWriter_RowAtomScope; // current atom scope + mork_scope mWriter_RowScope; // current row scope + + mork_cscode mWriter_DictForm; // current charset metainfo + mork_scope mWriter_DictAtomScope; // current atom scope + + mork_bool mWriter_NeedDirtyAll; // need to call DirtyAll() + mork_bool mWriter_Incremental; // opposite of mWriter_NeedDirtyAll + mork_bool mWriter_DidStartDict; // true when a dict has been started + mork_bool mWriter_DidEndDict; // true when a dict has been ended + + mork_bool mWriter_SuppressDirtyRowNewline; // for table meta rows + mork_bool mWriter_DidStartGroup; // true when a group has been started + mork_bool mWriter_DidEndGroup; // true when a group has been ended + mork_u1 mWriter_Phase; // status of writing process + + mork_bool mWriter_BeVerbose; // driven by env and table verbose settings: + // mWriter_BeVerbose equals ( ev->mEnv_BeVerbose || table->IsTableVerbose() ) + + mork_u1 mWriter_Pad[3]; // for u4 alignment + + mork_pos mWriter_TableRowArrayPos; // index into mTable_RowArray + + char mWriter_SafeNameBuf[(morkWriter_kMaxColumnNameSize * 2) + 4]; + // Note: extra four bytes in ColNameBuf means we can always append to yarn + + char mWriter_ColNameBuf[morkWriter_kMaxColumnNameSize + 4]; + // Note: extra four bytes in ColNameBuf means we can always append to yarn + + mdbYarn mWriter_ColYarn; // a yarn to describe space in ColNameBuf: + // mYarn_Buf == mWriter_ColNameBuf, mYarn_Size == + // morkWriter_kMaxColumnNameSize + + mdbYarn mWriter_SafeYarn; // a yarn to describe space in ColNameBuf: + // mYarn_Buf == mWriter_SafeNameBuf, mYarn_Size == (kMaxColumnNameSize * 2) + + morkAtomSpaceMapIter mWriter_StoreAtomSpacesIter; // for mStore_AtomSpaces + morkAtomAidMapIter mWriter_AtomSpaceAtomAidsIter; // for AtomSpace_AtomAids + + morkRowSpaceMapIter mWriter_StoreRowSpacesIter; // for mStore_RowSpaces + morkTableMapIter mWriter_RowSpaceTablesIter; // for mRowSpace_Tables + +#ifdef MORK_ENABLE_PROBE_MAPS + morkRowProbeMapIter mWriter_RowSpaceRowsIter; // for mRowSpace_Rows +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkRowMapIter mWriter_RowSpaceRowsIter; // for mRowSpace_Rows +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseWriter() + virtual ~morkWriter(); // assert that close executed earlier + + public: // morkWriter construction & destruction + morkWriter(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkStore* ioStore, nsIMdbFile* ioFile, nsIMdbHeap* ioSlotHeap); + void CloseWriter(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkWriter(const morkWriter& other); + morkWriter& operator=(const morkWriter& other); + + public: // dynamic type identification + mork_bool IsWriter() const { + return IsNode() && mNode_Derived == morkDerived_kWriter; + } + // } ===== end morkNode methods ===== + + public: // typing & errors + static void NonWriterTypeError(morkEnv* ev); + static void NilWriterStoreError(morkEnv* ev); + static void NilWriterBudError(morkEnv* ev); + static void NilWriterStreamError(morkEnv* ev); + static void NilWriterFileError(morkEnv* ev); + static void UnsupportedPhaseError(morkEnv* ev); + + public: // utitlities + void ChangeRowForm(morkEnv* ev, mork_cscode inNewForm); + void ChangeDictForm(morkEnv* ev, mork_cscode inNewForm); + void ChangeDictAtomScope(morkEnv* ev, mork_scope inScope); + + public: // inlines + mork_bool DidStartDict() const { return mWriter_DidStartDict; } + mork_bool DidEndDict() const { return mWriter_DidEndDict; } + + void IndentAsNeeded(morkEnv* ev, mork_size inDepth) { + if (mWriter_LineSize > mWriter_MaxIndent) + mWriter_LineSize = mWriter_Stream->PutIndent(ev, inDepth); + } + + void IndentOverMaxLine(morkEnv* ev, mork_size inPendingSize, + mork_size inDepth) { + if (mWriter_LineSize + inPendingSize > mWriter_MaxLine) + mWriter_LineSize = mWriter_Stream->PutIndent(ev, inDepth); + } + + public: // delayed construction + void MakeWriterStream(morkEnv* ev); // give writer a suitable stream + + public: // iterative/asynchronous writing + mork_bool WriteMore(morkEnv* ev); // call until IsWritingDone() is true + + mork_bool IsWritingDone() const // don't call WriteMore() any longer? + { + return mWriter_Phase == morkWriter_kPhaseWritingDone; + } + + public: // marking all content dirty + mork_bool DirtyAll(morkEnv* ev); + // DirtyAll() visits every store sub-object 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). Note the method can + // stop early when any error happens, since this will abort any commit. + + public: // group commit transactions + mork_bool StartGroup(morkEnv* ev); + mork_bool CommitGroup(morkEnv* ev); + mork_bool AbortGroup(morkEnv* ev); + + public: // phase methods + mork_bool OnNothingDone(morkEnv* ev); + mork_bool OnDirtyAllDone(morkEnv* ev); + mork_bool OnPutHeaderDone(morkEnv* ev); + + mork_bool OnRenumberAllDone(morkEnv* ev); + + mork_bool OnStoreAtomSpaces(morkEnv* ev); + mork_bool OnAtomSpaceAtomAids(morkEnv* ev); + + mork_bool OnStoreRowSpacesTables(morkEnv* ev); + mork_bool OnRowSpaceTables(morkEnv* ev); + mork_bool OnTableRowArray(morkEnv* ev); + + mork_bool OnStoreRowSpacesRows(morkEnv* ev); + mork_bool OnRowSpaceRows(morkEnv* ev); + + mork_bool OnContentDone(morkEnv* ev); + mork_bool OnWritingDone(morkEnv* ev); + + public: // writing dict items first pass + mork_bool PutTableDict(morkEnv* ev, morkTable* ioTable); + mork_bool PutRowDict(morkEnv* ev, morkRow* ioRow); + + public: // writing node content second pass + mork_bool PutTable(morkEnv* ev, morkTable* ioTable); + mork_bool PutRow(morkEnv* ev, morkRow* ioRow); + mork_bool PutRowCells(morkEnv* ev, morkRow* ioRow); + mork_bool PutVerboseRowCells(morkEnv* ev, morkRow* ioRow); + + mork_bool PutCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal); + mork_bool PutVerboseCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal); + + mork_bool PutTableChange(morkEnv* ev, const morkTableChange* inChange); + + public: // other writer methods + mork_bool IsYarnAllValue(const mdbYarn* inYarn); + + mork_size WriteYarn(morkEnv* ev, const mdbYarn* inYarn); + // return number of atom bytes written on the current line (which + // implies that escaped line breaks will make the size value smaller + // than the entire yarn's size, since only part goes on a last line). + + mork_size WriteAtom(morkEnv* ev, const morkAtom* inAtom); + // return number of atom bytes written on the current line (which + // implies that escaped line breaks will make the size value smaller + // than the entire atom's size, since only part goes on a last line). + + void WriteAllStoreTables(morkEnv* ev); + void WriteAtomSpaceAsDict(morkEnv* ev, morkAtomSpace* ioSpace); + + void WriteTokenToTokenMetaCell(morkEnv* ev, mork_token inCol, + mork_token inValue); + void WriteStringToTokenDictCell(morkEnv* ev, const char* inCol, + mork_token inValue); + // Note inCol should begin with '(' and end with '=', with col in between. + + void StartDict(morkEnv* ev); + void EndDict(morkEnv* ev); + + void StartTable(morkEnv* ev, morkTable* ioTable); + void EndTable(morkEnv* ev); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakWriter(morkWriter* me, morkEnv* ev, morkWriter** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongWriter(morkWriter* me, morkEnv* ev, + morkWriter** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKTABLEROWCURSOR_ */ diff --git a/comm/mailnews/db/mork/morkYarn.cpp b/comm/mailnews/db/mork/morkYarn.cpp new file mode 100644 index 0000000000..013b36c754 --- /dev/null +++ b/comm/mailnews/db/mork/morkYarn.cpp @@ -0,0 +1,70 @@ +/* -*- 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 _MORKENV_ +# include "morkEnv.h" +#endif + +#ifndef _MORKYARN_ +# include "morkYarn.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void morkYarn::CloseMorkNode( + morkEnv* ev) /*i*/ // CloseYarn() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseYarn(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkYarn::~morkYarn() /*i*/ // assert CloseYarn() executed earlier +{ + MORK_ASSERT(mYarn_Body.mYarn_Buf == 0); +} + +/*public non-poly*/ +morkYarn::morkYarn(morkEnv* ev, /*i*/ + const morkUsage& inUsage, nsIMdbHeap* ioHeap) + : morkNode(ev, inUsage, ioHeap) { + if (ev->Good()) mNode_Derived = morkDerived_kYarn; +} + +/*public non-poly*/ void morkYarn::CloseYarn( + morkEnv* ev) /*i*/ // called by CloseMorkNode(); +{ + if (this->IsNode()) + this->MarkShut(); + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void morkYarn::NonYarnTypeError(morkEnv* ev) { + ev->NewError("non morkYarn"); +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/morkYarn.h b/comm/mailnews/db/mork/morkYarn.h new file mode 100644 index 0000000000..e4cca8a843 --- /dev/null +++ b/comm/mailnews/db/mork/morkYarn.h @@ -0,0 +1,75 @@ +/* -*- 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 _MORKYARN_ +#define _MORKYARN_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kYarn /*i*/ 0x7952 /* ascii 'yR' */ + +/*| morkYarn: a reference counted nsIMdbYarn C struct. This is for use in those +**| few cases where single instances of reference counted buffers are needed +**| in Mork, and we expect few enough instances that overhead is not a factor +**| in decided whether to use such a thing. +|*/ +class morkYarn : public morkNode { // refcounted yarn + + // public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + public: // state is public because the entire Mork system is private + mdbYarn mYarn_Body; + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseYarn() only if open + virtual ~morkYarn(); // assert that CloseYarn() executed earlier + + public: // morkYarn construction & destruction + morkYarn(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap); + void CloseYarn(morkEnv* ev); // called by CloseMorkNode(); + + private: // copying is not allowed + morkYarn(const morkYarn& other); + morkYarn& operator=(const morkYarn& other); + + public: // dynamic type identification + mork_bool IsYarn() const { + return IsNode() && mNode_Derived == morkDerived_kYarn; + } + // } ===== end morkNode methods ===== + + public: // typing + static void NonYarnTypeError(morkEnv* ev); + + public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakYarn(morkYarn* me, morkEnv* ev, morkYarn** ioSlot) { + morkNode::SlotWeakNode((morkNode*)me, ev, (morkNode**)ioSlot); + } + + static void SlotStrongYarn(morkYarn* me, morkEnv* ev, morkYarn** ioSlot) { + morkNode::SlotStrongNode((morkNode*)me, ev, (morkNode**)ioSlot); + } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKYARN_ */ diff --git a/comm/mailnews/db/mork/morkZone.cpp b/comm/mailnews/db/mork/morkZone.cpp new file mode 100644 index 0000000000..6ee3032f48 --- /dev/null +++ b/comm/mailnews/db/mork/morkZone.cpp @@ -0,0 +1,487 @@ +/* -*- 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 _MORKZONE_ +# include "morkZone.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// { ===== begin morkNode interface ===== +// public: // morkNode virtual methods +void morkZone::CloseMorkNode(morkEnv* ev) // CloseZone() only if open +{ + if (this->IsOpenNode()) { + this->MarkClosing(); + this->CloseZone(ev); + this->MarkShut(); + } +} + +morkZone::~morkZone() // assert that CloseZone() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +// public: // morkMap construction & destruction +morkZone::morkZone(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioZoneHeap) + : morkNode(ev, inUsage, ioNodeHeap), + mZone_Heap(0), + mZone_HeapVolume(0), + mZone_BlockVolume(0), + mZone_RunVolume(0), + mZone_ChipVolume(0) + + , + mZone_FreeOldRunVolume(0) + + , + mZone_HunkCount(0), + mZone_FreeOldRunCount(0) + + , + mZone_HunkList(0), + mZone_FreeOldRunList(0) + + , + mZone_At(0), + mZone_AtSize(0) + +// morkRun* mZone_FreeRuns[ morkZone_kBuckets + 1 ]; +{ + morkRun** runs = mZone_FreeRuns; + morkRun** end = runs + (morkZone_kBuckets + 1); // one past last slot + --runs; // prepare for preincrement + while (++runs < end) // another slot in array? + *runs = 0; // clear all the slots + + if (ev->Good()) { + if (ioZoneHeap) { + nsIMdbHeap_SlotStrongHeap(ioZoneHeap, ev, &mZone_Heap); + if (ev->Good()) mNode_Derived = morkDerived_kZone; + } else + ev->NilPointerError(); + } +} + +void morkZone::CloseZone(morkEnv* ev) // called by CloseMorkNode() +{ + if (this->IsNode()) { + nsIMdbHeap* heap = mZone_Heap; + if (heap) { + morkHunk* hunk = 0; + nsIMdbEnv* mev = ev->AsMdbEnv(); + + morkHunk* next = mZone_HunkList; + while ((hunk = next) != 0) { +#ifdef morkHunk_USE_TAG_SLOT + if (!hunk->HunkGoodTag()) hunk->BadHunkTagWarning(ev); +#endif /* morkHunk_USE_TAG_SLOT */ + + next = hunk->HunkNext(); + heap->Free(mev, hunk); + } + } + nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*)0, ev, &mZone_Heap); + this->MarkShut(); + } else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== + +/*static*/ void morkZone::NonZoneTypeError(morkEnv* ev) { + ev->NewError("non morkZone"); +} + +/*static*/ void morkZone::NilZoneHeapError(morkEnv* ev) { + ev->NewError("nil mZone_Heap"); +} + +/*static*/ void morkHunk::BadHunkTagWarning(morkEnv* ev) { + ev->NewWarning("bad mHunk_Tag"); +} + +/*static*/ void morkRun::BadRunTagError(morkEnv* ev) { + ev->NewError("bad mRun_Tag"); +} + +/*static*/ void morkRun::RunSizeAlignError(morkEnv* ev) { + ev->NewError("bad RunSize() alignment"); +} + +// { ===== begin morkZone methods ===== + +mork_size morkZone::zone_grow_at(morkEnv* ev, mork_size inNeededSize) { + mZone_At = 0; // remove any ref to current hunk + mZone_AtSize = 0; // zero available bytes in current hunk + + mork_size runSize = 0; // actual size of a particular run + + // try to find a run in old run list with at least inNeededSize bytes: + morkRun* run = mZone_FreeOldRunList; // cursor in list scan + morkRun* prev = 0; // the node before run in the list scan + + while (run) // another run in list to check? + { + morkOldRun* oldRun = (morkOldRun*)run; + mork_size oldSize = oldRun->OldSize(); + if (oldSize >= inNeededSize) // found one big enough? + { + runSize = oldSize; + break; // end while loop early + } + prev = run; // remember last position in singly linked list + run = run->RunNext(); // advance cursor to next node in list + } + if (runSize && run) // found a usable old run? + { + morkRun* next = run->RunNext(); + if (prev) // another node in free list precedes run? + prev->RunSetNext(next); // unlink run + else + mZone_FreeOldRunList = next; // unlink run from head of list + + morkOldRun* oldRun = (morkOldRun*)run; + oldRun->OldSetSize(runSize); + mZone_At = (mork_u1*)run; + mZone_AtSize = runSize; + +#ifdef morkZone_CONFIG_DEBUG +# ifdef morkZone_CONFIG_ALIGN_8 + mork_ip lowThree = ((mork_ip)mZone_At) & 7; + if (lowThree) // not 8 byte aligned? +# else /*morkZone_CONFIG_ALIGN_8*/ + mork_ip lowTwo = ((mork_ip)mZone_At) & 3; + if (lowTwo) // not 4 byte aligned? +# endif /*morkZone_CONFIG_ALIGN_8*/ + ev->NewWarning("mZone_At not aligned"); +#endif /*morkZone_CONFIG_DEBUG*/ + } else // need to allocate a brand new run + { + inNeededSize += 7; // allow for possible alignment padding + mork_size newSize = (inNeededSize > morkZone_kNewHunkSize) + ? inNeededSize + : morkZone_kNewHunkSize; + + morkHunk* hunk = this->zone_new_hunk(ev, newSize); + if (hunk) { + morkRun* hunkRun = hunk->HunkRun(); + mork_u1* at = (mork_u1*)hunkRun->RunAsBlock(); + mork_ip lowBits = ((mork_ip)at) & 7; + if (lowBits) // not 8 byte aligned? + { + mork_ip skip = (8 - lowBits); // skip the complement to align + at += skip; + newSize -= skip; + } + mZone_At = at; + mZone_AtSize = newSize; + } + } + + return mZone_AtSize; +} + +morkHunk* morkZone::zone_new_hunk(morkEnv* ev, mdb_size inSize) // alloc +{ + mdb_size hunkSize = inSize + sizeof(morkHunk); + void* outBlock = 0; // we are going straight to the heap: + mZone_Heap->Alloc(ev->AsMdbEnv(), hunkSize, &outBlock); + if (outBlock) { +#ifdef morkZone_CONFIG_VOL_STATS + mZone_HeapVolume += hunkSize; // track all heap allocations +#endif /* morkZone_CONFIG_VOL_STATS */ + + morkHunk* hunk = (morkHunk*)outBlock; +#ifdef morkHunk_USE_TAG_SLOT + hunk->HunkInitTag(); +#endif /* morkHunk_USE_TAG_SLOT */ + + hunk->HunkSetNext(mZone_HunkList); + mZone_HunkList = hunk; + ++mZone_HunkCount; + + morkRun* run = hunk->HunkRun(); + run->RunSetSize(inSize); +#ifdef morkRun_USE_TAG_SLOT + run->RunInitTag(); +#endif /* morkRun_USE_TAG_SLOT */ + + return hunk; + } + if (ev->Good()) // got this far without any error reported yet? + ev->OutOfMemoryError(); + return (morkHunk*)0; +} + +void* morkZone::zone_new_chip(morkEnv* ev, mdb_size inSize) // alloc +{ +#ifdef morkZone_CONFIG_VOL_STATS + mZone_BlockVolume += inSize; // sum sizes of both chips and runs +#endif /* morkZone_CONFIG_VOL_STATS */ + + mork_u1* at = mZone_At; + mork_size atSize = mZone_AtSize; // available bytes in current hunk + if (atSize >= inSize) // current hunk can satisfy request? + { + mZone_At = at + inSize; + mZone_AtSize = atSize - inSize; + return at; + } else if (atSize > morkZone_kMaxHunkWaste) // over max waste allowed? + { + morkHunk* hunk = this->zone_new_hunk(ev, inSize); + if (hunk) return hunk->HunkRun(); + + return (void*)0; // show allocation has failed + } else // get ourselves a new hunk for suballocation: + { + atSize = this->zone_grow_at(ev, inSize); // get a new hunk + } + + if (atSize >= inSize) // current hunk can satisfy request? + { + at = mZone_At; + mZone_At = at + inSize; + mZone_AtSize = atSize - inSize; + return at; + } + + if (ev->Good()) // got this far without any error reported yet? + ev->OutOfMemoryError(); + + return (void*)0; // show allocation has failed +} + +void* morkZone::ZoneNewChip(morkEnv* ev, mdb_size inSize) // alloc +{ +#ifdef morkZone_CONFIG_ARENA + +# ifdef morkZone_CONFIG_DEBUG + if (!this->IsZone()) + this->NonZoneTypeError(ev); + else if (!mZone_Heap) + this->NilZoneHeapError(ev); +# endif /*morkZone_CONFIG_DEBUG*/ + +# ifdef morkZone_CONFIG_ALIGN_8 + inSize += 7; + inSize &= ~((mork_ip)7); // force to multiple of 8 bytes +# else /*morkZone_CONFIG_ALIGN_8*/ + inSize += 3; + inSize &= ~((mork_ip)3); // force to multiple of 4 bytes +# endif /*morkZone_CONFIG_ALIGN_8*/ + +# ifdef morkZone_CONFIG_VOL_STATS + mZone_ChipVolume += inSize; // sum sizes of chips only +# endif /* morkZone_CONFIG_VOL_STATS */ + + return this->zone_new_chip(ev, inSize); + +#else /*morkZone_CONFIG_ARENA*/ + void* outBlock = 0; + mZone_Heap->Alloc(ev->AsMdbEnv(), inSize, &outBlock); + return outBlock; +#endif /*morkZone_CONFIG_ARENA*/ +} + +// public: // ...but runs do indeed know how big they are +void* morkZone::ZoneNewRun(morkEnv* ev, mdb_size inSize) // alloc +{ +#ifdef morkZone_CONFIG_ARENA + +# ifdef morkZone_CONFIG_DEBUG + if (!this->IsZone()) + this->NonZoneTypeError(ev); + else if (!mZone_Heap) + this->NilZoneHeapError(ev); +# endif /*morkZone_CONFIG_DEBUG*/ + + inSize += morkZone_kRoundAdd; + inSize &= morkZone_kRoundMask; + if (inSize <= morkZone_kMaxCachedRun) { + morkRun** bucket = mZone_FreeRuns + (inSize >> morkZone_kRoundBits); + morkRun* hit = *bucket; + if (hit) // cache hit? + { + *bucket = hit->RunNext(); + hit->RunSetSize(inSize); + return hit->RunAsBlock(); + } + } + mdb_size blockSize = inSize + sizeof(morkRun); // plus run overhead +# ifdef morkZone_CONFIG_VOL_STATS + mZone_RunVolume += blockSize; // sum sizes of runs only +# endif /* morkZone_CONFIG_VOL_STATS */ + morkRun* run = (morkRun*)this->zone_new_chip(ev, blockSize); + if (run) { + run->RunSetSize(inSize); +# ifdef morkRun_USE_TAG_SLOT + run->RunInitTag(); +# endif /* morkRun_USE_TAG_SLOT */ + return run->RunAsBlock(); + } + + if (ev->Good()) // got this far without any error reported yet? + ev->OutOfMemoryError(); + + return (void*)0; // indicate failed allocation + +#else /*morkZone_CONFIG_ARENA*/ + void* outBlock = 0; + mZone_Heap->Alloc(ev->AsMdbEnv(), inSize, &outBlock); + return outBlock; +#endif /*morkZone_CONFIG_ARENA*/ +} + +void morkZone::ZoneZapRun(morkEnv* ev, void* ioRunBlock) // free +{ +#ifdef morkZone_CONFIG_ARENA + + morkRun* run = morkRun::BlockAsRun(ioRunBlock); + mdb_size runSize = run->RunSize(); +# ifdef morkZone_CONFIG_VOL_STATS + mZone_BlockVolume -= runSize; // tracking sizes of both chips and runs +# endif /* morkZone_CONFIG_VOL_STATS */ + +# ifdef morkZone_CONFIG_DEBUG + if (!this->IsZone()) + this->NonZoneTypeError(ev); + else if (!mZone_Heap) + this->NilZoneHeapError(ev); + else if (!ioRunBlock) + ev->NilPointerError(); + else if (runSize & morkZone_kRoundAdd) + run->RunSizeAlignError(ev); +# ifdef morkRun_USE_TAG_SLOT + else if (!run->RunGoodTag()) + run->BadRunTagError(ev); +# endif /* morkRun_USE_TAG_SLOT */ +# endif /*morkZone_CONFIG_DEBUG*/ + + if (runSize <= morkZone_kMaxCachedRun) // goes into free run list? + { + morkRun** bucket = mZone_FreeRuns + (runSize >> morkZone_kRoundBits); + run->RunSetNext(*bucket); // push onto free run list + *bucket = run; + } else // free old run list + { + run->RunSetNext(mZone_FreeOldRunList); // push onto free old run list + mZone_FreeOldRunList = run; + ++mZone_FreeOldRunCount; +# ifdef morkZone_CONFIG_VOL_STATS + mZone_FreeOldRunVolume += runSize; +# endif /* morkZone_CONFIG_VOL_STATS */ + + morkOldRun* oldRun = (morkOldRun*)run; // to access extra size slot + oldRun->OldSetSize(runSize); // so we know how big this is later + } + +#else /*morkZone_CONFIG_ARENA*/ + mZone_Heap->Free(ev->AsMdbEnv(), ioRunBlock); +#endif /*morkZone_CONFIG_ARENA*/ +} + +void* morkZone::ZoneGrowRun(morkEnv* ev, void* ioRunBlock, mdb_size inSize) { +#ifdef morkZone_CONFIG_ARENA + + morkRun* run = morkRun::BlockAsRun(ioRunBlock); + mdb_size runSize = run->RunSize(); + +# ifdef morkZone_CONFIG_DEBUG + if (!this->IsZone()) + this->NonZoneTypeError(ev); + else if (!mZone_Heap) + this->NilZoneHeapError(ev); +# endif /*morkZone_CONFIG_DEBUG*/ + +# ifdef morkZone_CONFIG_ALIGN_8 + inSize += 7; + inSize &= ~((mork_ip)7); // force to multiple of 8 bytes +# else /*morkZone_CONFIG_ALIGN_8*/ + inSize += 3; + inSize &= ~((mork_ip)3); // force to multiple of 4 bytes +# endif /*morkZone_CONFIG_ALIGN_8*/ + + if (inSize > runSize) { + void* newBuf = this->ZoneNewRun(ev, inSize); + if (newBuf) { + MORK_MEMCPY(newBuf, ioRunBlock, runSize); + this->ZoneZapRun(ev, ioRunBlock); + + return newBuf; + } + } else + return ioRunBlock; // old size is big enough + + if (ev->Good()) // got this far without any error reported yet? + ev->OutOfMemoryError(); + + return (void*)0; // indicate failed allocation + +#else /*morkZone_CONFIG_ARENA*/ + void* outBlock = 0; + mZone_Heap->Free(ev->AsMdbEnv(), ioRunBlock); + return outBlock; +#endif /*morkZone_CONFIG_ARENA*/ +} + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// { ===== begin nsIMdbHeap methods ===== +/*virtual*/ nsresult morkZone::Alloc( + nsIMdbEnv* mev, // allocate a piece of memory + mdb_size inSize, // requested size of new memory block + void** outBlock) // memory block of inSize bytes, or nil +{ + nsresult outErr = NS_OK; + void* block = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + block = this->ZoneNewRun(ev, inSize); + outErr = ev->AsErr(); + } else + outErr = morkEnv_kOutOfMemoryError; + + if (outBlock) *outBlock = block; + + return outErr; +} + +/*virtual*/ nsresult morkZone::Free( + nsIMdbEnv* mev, // free block allocated earlier by Alloc() + void* inBlock) { + nsresult outErr = NS_OK; + if (inBlock) { + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if (ev) { + this->ZoneZapRun(ev, inBlock); + outErr = ev->AsErr(); + } else + // XXX 1 is not a valid nsresult + outErr = static_cast<nsresult>(1); + } + + return outErr; +} + +// } ===== end nsIMdbHeap methods ===== diff --git a/comm/mailnews/db/mork/morkZone.h b/comm/mailnews/db/mork/morkZone.h new file mode 100644 index 0000000000..5399bb9827 --- /dev/null +++ b/comm/mailnews/db/mork/morkZone.h @@ -0,0 +1,313 @@ +/* -*- 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 _MORKZONE_ +#define _MORKZONE_ 1 + +#ifndef _MORK_ +# include "mork.h" +#endif + +#ifndef _MORKNODE_ +# include "morkNode.h" +#endif + +#ifndef _MORKDEQUE_ +# include "morkDeque.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*| CONFIG_DEBUG: do paranoid debug checks if defined. +|*/ +#ifdef MORK_DEBUG +# define morkZone_CONFIG_DEBUG 1 /* debug paranoid if defined */ +#endif /*MORK_DEBUG*/ + +/*| CONFIG_STATS: keep volume and usage statistics. +|*/ +#define morkZone_CONFIG_VOL_STATS 1 /* count space used by zone instance */ + +/*| CONFIG_ARENA: if this is defined, then the morkZone class will alloc big +**| blocks from the zone's heap, and suballocate from these. If undefined, +**| then morkZone will just pass all calls through to the zone's heap. +|*/ +#ifdef MORK_ENABLE_ZONE_ARENAS +# define morkZone_CONFIG_ARENA 1 /* be arena, if defined; otherwise no-op */ +#endif /*MORK_ENABLE_ZONE_ARENAS*/ + +/*| CONFIG_ALIGN_8: if this is defined, then the morkZone class will give +**| blocks 8 byte alignment instead of only 4 byte alignment. +|*/ +#ifdef MORK_CONFIG_ALIGN_8 +# define morkZone_CONFIG_ALIGN_8 1 /* ifdef: align to 8 bytes, otherwise 4 */ +#endif /*MORK_CONFIG_ALIGN_8*/ + +/*| CONFIG_PTR_SIZE_4: if this is defined, then the morkZone class will +**| assume sizeof(void*) == 4, so a tag slot for padding is needed. +|*/ +#ifdef MORK_CONFIG_PTR_SIZE_4 +# define morkZone_CONFIG_PTR_SIZE_4 1 /* ifdef: sizeof(void*) == 4 */ +#endif /*MORK_CONFIG_PTR_SIZE_4*/ + +/*| morkZone_USE_TAG_SLOT: if this is defined, then define slot mRun_Tag +**| in order to achieve eight byte alignment after the mRun_Next slot. +|*/ +#if defined(morkZone_CONFIG_ALIGN_8) && defined(morkZone_CONFIG_PTR_SIZE_4) +# define morkRun_USE_TAG_SLOT 1 /* need mRun_Tag slot inside morkRun */ +# define morkHunk_USE_TAG_SLOT 1 /* need mHunk_Tag slot inside morkHunk */ +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkRun_kTag ((mork_u4)0x6D52754E) /* ascii 'mRuN' */ + +/*| morkRun: structure used by morkZone for sized blocks +|*/ +class morkRun { + protected: // member variable slots +#ifdef morkRun_USE_TAG_SLOT + mork_u4 mRun_Tag; // force 8 byte alignment after mRun_Next +#endif /* morkRun_USE_TAG_SLOT */ + + morkRun* mRun_Next; + + public: // pointer interpretation of mRun_Next (when inside a list): + morkRun* RunNext() const { return mRun_Next; } + void RunSetNext(morkRun* ioNext) { mRun_Next = ioNext; } + + public: // size interpretation of mRun_Next (when not inside a list): + mork_size RunSize() const { return (mork_size)((mork_ip)mRun_Next); } + void RunSetSize(mork_size inSize) { mRun_Next = (morkRun*)((mork_ip)inSize); } + + public: // maintenance and testing of optional tag magic signature slot: +#ifdef morkRun_USE_TAG_SLOT + void RunInitTag() { mRun_Tag = morkRun_kTag; } + mork_bool RunGoodTag() { return (mRun_Tag == morkRun_kTag); } +#endif /* morkRun_USE_TAG_SLOT */ + + public: // conversion back and forth to inline block following run instance: + void* RunAsBlock() { return (((mork_u1*)this) + sizeof(morkRun)); } + + static morkRun* BlockAsRun(void* ioBlock) { + return (morkRun*)(((mork_u1*)ioBlock) - sizeof(morkRun)); + } + + public: // typing & errors + static void BadRunTagError(morkEnv* ev); + static void RunSizeAlignError(morkEnv* ev); +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*| morkOldRun: more space to record size when run is put into old free list +|*/ +class morkOldRun : public morkRun { + protected: // need another size field when mRun_Next is used for linkage: + mdb_size mOldRun_Size; + + public: // size getter/setter + mork_size OldSize() const { return mOldRun_Size; } + void OldSetSize(mork_size inSize) { mOldRun_Size = inSize; } +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkHunk_kTag ((mork_u4)0x68556E4B) /* ascii 'hUnK' */ + +/*| morkHunk: structure used by morkZone for heap allocations. +|*/ +class morkHunk { + protected: // member variable slots +#ifdef morkHunk_USE_TAG_SLOT + mork_u4 mHunk_Tag; // force 8 byte alignment after mHunk_Next +#endif /* morkHunk_USE_TAG_SLOT */ + + morkHunk* mHunk_Next; + + morkRun mHunk_Run; + + public: // setters + void HunkSetNext(morkHunk* ioNext) { mHunk_Next = ioNext; } + + public: // getters + morkHunk* HunkNext() const { return mHunk_Next; } + + morkRun* HunkRun() { return &mHunk_Run; } + + public: // maintenance and testing of optional tag magic signature slot: +#ifdef morkHunk_USE_TAG_SLOT + void HunkInitTag() { mHunk_Tag = morkHunk_kTag; } + mork_bool HunkGoodTag() { return (mHunk_Tag == morkHunk_kTag); } +#endif /* morkHunk_USE_TAG_SLOT */ + + public: // typing & errors + static void BadHunkTagWarning(morkEnv* ev); +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*| kNewHunkSize: the default size for a hunk, assuming we must allocate +**| a new one whenever the free hunk list does not already have. Note this +**| number should not be changed without also considering suitable changes +**| in the related kMaxHunkWaste and kMinHunkSize constants. +|*/ +#define morkZone_kNewHunkSize ((mork_size)(64 * 1024)) /* 64K per hunk */ + +/*| kMaxFreeVolume: some number of bytes of free space in the free hunk list +**| over which we no longer want to add more free hunks to the list, for fear +**| of accumulating too much unused, fragmented free space. This should be a +**| small multiple of kNewHunkSize, say about two to four times as great, to +**| allow for no more free hunk space than fits in a handful of new hunks. +**| This strategy will let us usefully accumulate "some" free space in the +**| free hunk list, but without accumulating "too much" free space that way. +|*/ +#define morkZone_kMaxFreeVolume (morkZone_kNewHunkSize * 3) + +/*| kMaxHunkWaste: if a current request is larger than this, and we cannot +**| satisfy the request with the current hunk, then we just allocate the +**| block from the heap without changing the current hunk. Basically this +**| number represents the largest amount of memory we are willing to waste, +**| since a block request barely less than this can cause the current hunk +**| to be retired (with any unused space wasted) as well get a new hunk. +|*/ +#define morkZone_kMaxHunkWaste ((mork_size)4096) /* 1/16 kNewHunkSize */ + +/*| kRound*: the algorithm for rounding up allocation sizes for caching +**| in free lists works like the following. We add kRoundAdd to any size +**| requested, and then bitwise AND with kRoundMask, and this will give us +**| the smallest multiple of kRoundSize that is at least as large as the +**| requested size. Then if we rightshift this number by kRoundBits, we +**| will have the index into the mZone_FreeRuns array which will hold any +**| cache runs of that size. So 4 bits of shift gives us a granularity +**| of 16 bytes, so that free lists will hold successive runs that are +**| 16 bytes greater than the next smaller run size. If we have 256 free +**| lists of nonzero sized runs altogether, then the largest run that can +**| be cached is 4096, or 4K (since 4096 == 16 * 256). A larger run that +**| gets freed will go in to the free hunk list (or back to the heap). +|*/ +#define morkZone_kRoundBits 4 /* bits to round-up size for free lists */ +#define morkZone_kRoundSize (1 << morkZone_kRoundBits) +#define morkZone_kRoundAdd ((1 << morkZone_kRoundBits) - 1) +#define morkZone_kRoundMask (~((mork_ip)morkZone_kRoundAdd)) + +#define morkZone_kBuckets 256 /* number of distinct free lists */ + +/*| kMaxCachedRun: the largest run that will be stored inside a free +**| list of old zapped runs. A run larger than this cannot be put in +**| a free list, and must be allocated from the heap at need, and put +**| into the free hunk list when discarded. +|*/ +#define morkZone_kMaxCachedRun (morkZone_kBuckets * morkZone_kRoundSize) + +#define morkDerived_kZone /*i*/ 0x5A6E /* ascii 'Zn' */ + +/*| morkZone: a pooled memory allocator like an NSPR arena. The term 'zone' +**| is roughly synonymous with 'heap'. I avoid calling this class a "heap" +**| to avoid any confusion with nsIMdbHeap, and I avoid calling this class +**| an arean to avoid confusion with NSPR usage. +|*/ +class morkZone : public morkNode, public nsIMdbHeap { + // public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // 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_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + public: // state is public because the entire Mork system is private + nsIMdbHeap* mZone_Heap; // strong ref to heap allocating all space + + mork_size mZone_HeapVolume; // total bytes allocated from heap + mork_size mZone_BlockVolume; // total bytes in all zone blocks + mork_size mZone_RunVolume; // total bytes in all zone runs + mork_size mZone_ChipVolume; // total bytes in all zone chips + + mork_size mZone_FreeOldRunVolume; // total bytes in all used hunks + + mork_count mZone_HunkCount; // total number of used hunks + mork_count mZone_FreeOldRunCount; // total free old runs + + morkHunk* mZone_HunkList; // linked list of all used hunks + morkRun* mZone_FreeOldRunList; // linked list of free old runs + + // note mZone_At is a byte pointer for single byte address arithmetic: + mork_u1* mZone_At; // current position in most recent hunk + mork_size mZone_AtSize; // number of bytes remaining in this hunk + + // kBuckets+1 so indexes zero through kBuckets are all okay to use: + + morkRun* mZone_FreeRuns[morkZone_kBuckets + 1]; + // Each piece of memory stored in list mZone_FreeRuns[ i ] has an + // allocation size equal to sizeof(morkRun) + (i * kRoundSize), so + // that callers can be given a piece of memory with (i * kRoundSize) + // bytes of writeable space while reserving the first sizeof(morkRun) + // bytes to keep track of size information for later re-use. Note + // that mZone_FreeRuns[ 0 ] is unused because no run will be zero + // bytes in size (and morkZone plans to complain about zero sizes). + + protected: // zone utilities + mork_size zone_grow_at(morkEnv* ev, mork_size inNeededSize); + + void* zone_new_chip(morkEnv* ev, mdb_size inSize); // alloc + morkHunk* zone_new_hunk(morkEnv* ev, mdb_size inRunSize); // alloc + + // { ===== begin nsIMdbHeap methods ===== + public: + NS_IMETHOD Alloc( + nsIMdbEnv* ev, // allocate a piece of memory + mdb_size inSize, // requested size of new memory block + void** outBlock) override; // memory block of inSize bytes, or nil + + NS_IMETHOD Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc() + void* inBlock) override; + + virtual size_t GetUsedSize() override { return mZone_Heap->GetUsedSize(); } + // } ===== end nsIMdbHeap methods ===== + + // { ===== begin morkNode interface ===== + public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseZone() only if open + virtual ~morkZone(); // assert that CloseMap() executed earlier + + public: // morkMap construction & destruction + morkZone(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap, + nsIMdbHeap* ioZoneHeap); + + void CloseZone(morkEnv* ev); // called by CloseMorkNode() + + public: // dynamic type identification + mork_bool IsZone() const { + return IsNode() && mNode_Derived == morkDerived_kZone; + } + // } ===== end morkNode methods ===== + + // { ===== begin morkZone methods ===== + public: // chips do not know how big they are... + void* ZoneNewChip(morkEnv* ev, mdb_size inSize); // alloc + + public: // ...but runs do indeed know how big they are + void* ZoneNewRun(morkEnv* ev, mdb_size inSize); // alloc + void ZoneZapRun(morkEnv* ev, void* ioRunBody); // free + void* ZoneGrowRun(morkEnv* ev, void* ioRunBody, mdb_size inSize); // realloc + + // } ===== end morkZone methods ===== + + public: // typing & errors + static void NonZoneTypeError(morkEnv* ev); + static void NilZoneHeapError(morkEnv* ev); + static void BadZoneTagError(morkEnv* ev); +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKZONE_ */ diff --git a/comm/mailnews/db/mork/moz.build b/comm/mailnews/db/mork/moz.build new file mode 100644 index 0000000000..4d97c3e562 --- /dev/null +++ b/comm/mailnews/db/mork/moz.build @@ -0,0 +1,68 @@ +# vim: set filetype=python: +# 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/. + +EXPORTS += [ + "mdb.h", + "nsIMdbFactoryFactory.h", +] + +SOURCES += [ + "morkArray.cpp", + "morkAtom.cpp", + "morkAtomMap.cpp", + "morkAtomSpace.cpp", + "morkBead.cpp", + "morkBlob.cpp", + "morkBuilder.cpp", + "morkCell.cpp", + "morkCellObject.cpp", + "morkCh.cpp", + "morkConfig.cpp", + "morkCursor.cpp", + "morkDeque.cpp", + "morkEnv.cpp", + "morkFactory.cpp", + "morkFile.cpp", + "morkHandle.cpp", + "morkIntMap.cpp", + "morkMap.cpp", + "morkNode.cpp", + "morkNodeMap.cpp", + "morkObject.cpp", + "morkParser.cpp", + "morkPool.cpp", + "morkPortTableCursor.cpp", + "morkProbeMap.cpp", + "morkRow.cpp", + "morkRowCellCursor.cpp", + "morkRowMap.cpp", + "morkRowObject.cpp", + "morkRowSpace.cpp", + "morkSink.cpp", + "morkSpace.cpp", + "morkStore.cpp", + "morkStream.cpp", + "morkTable.cpp", + "morkTableRowCursor.cpp", + "morkThumb.cpp", + "morkWriter.cpp", + "morkYarn.cpp", + "morkZone.cpp", + "nsMorkFactory.cpp", + "orkinHeap.cpp", +] + +if CONFIG["OS_ARCH"] == "WINNT": + SOURCES += ["morkSearchRowCursor.cpp"] + +Library("mork") +FINAL_LIBRARY = "mail" +# clang-cl complains about this. +if CONFIG["CC_TYPE"] == "clang-cl": + CXXFLAGS += ["-Wno-overloaded-virtual"] + +XPCOM_MANIFESTS += [ + "components.conf", +] diff --git a/comm/mailnews/db/mork/nsIMdbFactoryFactory.h b/comm/mailnews/db/mork/nsIMdbFactoryFactory.h new file mode 100644 index 0000000000..06a362078d --- /dev/null +++ b/comm/mailnews/db/mork/nsIMdbFactoryFactory.h @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 nsIMdbFactoryFactory_h__ +#define nsIMdbFactoryFactory_h__ + +#include "nsISupports.h" +#include "nsIFactory.h" +#include "nsIComponentManager.h" + +class nsIMdbFactory; + +// 2794D0B7-E740-47a4-91C0-3E4FCB95B806 +#define NS_IMDBFACTORYFACTORY_IID \ + { \ + 0x2794d0b7, 0xe740, 0x47a4, { \ + 0x91, 0xc0, 0x3e, 0x4f, 0xcb, 0x95, 0xb8, 0x6 \ + } \ + } + +// because Mork doesn't support XPCOM, we have to wrap the mdb factory interface +// with an interface that gives you an mdb factory. +class nsIMdbFactoryService : public nsISupports { + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBFACTORYFACTORY_IID) + NS_IMETHOD GetMdbFactory(nsIMdbFactory** aFactory) = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbFactoryService, NS_IMDBFACTORYFACTORY_IID) + +#endif diff --git a/comm/mailnews/db/mork/nsMorkFactory.cpp b/comm/mailnews/db/mork/nsMorkFactory.cpp new file mode 100644 index 0000000000..f1354699df --- /dev/null +++ b/comm/mailnews/db/mork/nsMorkFactory.cpp @@ -0,0 +1,14 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include "nsMorkFactory.h" + +NS_IMPL_ISUPPORTS(nsMorkFactoryService, nsIMdbFactoryService) + +NS_IMETHODIMP nsMorkFactoryService::GetMdbFactory(nsIMdbFactory** aFactory) { + if (!mMdbFactory) mMdbFactory = MakeMdbFactory(); + NS_IF_ADDREF(*aFactory = mMdbFactory); + return *aFactory ? NS_OK : NS_ERROR_OUT_OF_MEMORY; +} diff --git a/comm/mailnews/db/mork/nsMorkFactory.h b/comm/mailnews/db/mork/nsMorkFactory.h new file mode 100644 index 0000000000..582ffe3a83 --- /dev/null +++ b/comm/mailnews/db/mork/nsMorkFactory.h @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 nsMorkFactory_h__ +#define nsMorkFactory_h__ + +#include "mozilla/ModuleUtils.h" +#include "nsCOMPtr.h" +#include "nsIMdbFactoryFactory.h" +#include "mdb.h" + +class nsMorkFactoryService final : public nsIMdbFactoryService { + public: + nsMorkFactoryService(){}; + // nsISupports methods + NS_DECL_ISUPPORTS + + NS_IMETHOD GetMdbFactory(nsIMdbFactory** aFactory) override; + + protected: + ~nsMorkFactoryService() {} + nsCOMPtr<nsIMdbFactory> mMdbFactory; +}; + +#endif diff --git a/comm/mailnews/db/mork/orkinHeap.cpp b/comm/mailnews/db/mork/orkinHeap.cpp new file mode 100644 index 0000000000..0bd2545a7b --- /dev/null +++ b/comm/mailnews/db/mork/orkinHeap.cpp @@ -0,0 +1,72 @@ +/* -*- 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 _ORKINHEAP_ +# include "orkinHeap.h" +#endif + +#ifndef _MORKENV_ +# include "morkEnv.h" +#endif + +#include "nsIMemoryReporter.h" + +#include <stdlib.h> + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +orkinHeap::orkinHeap() // does nothing + : mUsedSize(0) {} + +/*virtual*/ +orkinHeap::~orkinHeap() // does nothing +{} + +MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MorkSizeOfOnAlloc) +MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MorkSizeOfOnFree) + +// { ===== begin nsIMdbHeap methods ===== +/*virtual*/ nsresult orkinHeap::Alloc( + nsIMdbEnv* mev, // allocate a piece of memory + mdb_size inSize, // requested size of new memory block + void** outBlock) // memory block of inSize bytes, or nil +{ + MORK_USED_1(mev); + nsresult outErr = NS_OK; + void* block = malloc(inSize); + if (!block) + outErr = morkEnv_kOutOfMemoryError; + else + mUsedSize += MorkSizeOfOnAlloc(block); + + MORK_ASSERT(outBlock); + if (outBlock) *outBlock = block; + return outErr; +} + +/*virtual*/ nsresult orkinHeap::Free( + nsIMdbEnv* mev, // free block allocated earlier by Alloc() + void* inBlock) { + MORK_USED_1(mev); + MORK_ASSERT(inBlock); + if (inBlock) { + mUsedSize -= MorkSizeOfOnFree(inBlock); + free(inBlock); + } + return NS_OK; +} + +size_t orkinHeap::GetUsedSize() { return mUsedSize; } +// } ===== end nsIMdbHeap methods ===== + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/comm/mailnews/db/mork/orkinHeap.h b/comm/mailnews/db/mork/orkinHeap.h new file mode 100644 index 0000000000..f431d6fe82 --- /dev/null +++ b/comm/mailnews/db/mork/orkinHeap.h @@ -0,0 +1,50 @@ +/* -*- 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 _ORKINHEAP_ +#define _ORKINHEAP_ 1 + +#ifndef _MDB_ +# include "mdb.h" +#endif + +#ifndef _MORK_ +# include "mork.h" +#endif + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define orkinHeap_kTag 0x68456150 /* ascii 'hEaP' */ + +/*| orkinHeap: +|*/ +class orkinHeap : public nsIMdbHeap { // + protected: + size_t mUsedSize; + + public: + orkinHeap(); // does nothing + virtual ~orkinHeap(); // does nothing + + private: // copying is not allowed + orkinHeap(const orkinHeap& other); + orkinHeap& operator=(const orkinHeap& other); + + public: + // { ===== begin nsIMdbHeap methods ===== + NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory + mdb_size inSize, // requested size of new memory block + void** outBlock); // memory block of inSize bytes, or nil + + NS_IMETHOD Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc() + void* inBlock); + + virtual size_t GetUsedSize(); + // } ===== end nsIMdbHeap methods ===== +}; + +// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _ORKINHEAP_ */ |