summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/db/mork
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/db/mork')
-rw-r--r--comm/mailnews/db/mork/components.conf12
-rw-r--r--comm/mailnews/db/mork/mdb.h2550
-rw-r--r--comm/mailnews/db/mork/mork.h255
-rw-r--r--comm/mailnews/db/mork/morkArray.cpp250
-rw-r--r--comm/mailnews/db/mork/morkArray.h97
-rw-r--r--comm/mailnews/db/mork/morkAtom.cpp432
-rw-r--r--comm/mailnews/db/mork/morkAtom.h362
-rw-r--r--comm/mailnews/db/mork/morkAtomMap.cpp378
-rw-r--r--comm/mailnews/db/mork/morkAtomMap.h394
-rw-r--r--comm/mailnews/db/mork/morkAtomSpace.cpp233
-rw-r--r--comm/mailnews/db/mork/morkAtomSpace.h227
-rw-r--r--comm/mailnews/db/mork/morkBead.cpp361
-rw-r--r--comm/mailnews/db/mork/morkBead.h244
-rw-r--r--comm/mailnews/db/mork/morkBlob.cpp96
-rw-r--r--comm/mailnews/db/mork/morkBlob.h140
-rw-r--r--comm/mailnews/db/mork/morkBuilder.cpp892
-rw-r--r--comm/mailnews/db/mork/morkBuilder.h303
-rw-r--r--comm/mailnews/db/mork/morkCell.cpp99
-rw-r--r--comm/mailnews/db/mork/morkCell.h91
-rw-r--r--comm/mailnews/db/mork/morkCellObject.cpp453
-rw-r--r--comm/mailnews/db/mork/morkCellObject.h180
-rw-r--r--comm/mailnews/db/mork/morkCh.cpp344
-rw-r--r--comm/mailnews/db/mork/morkCh.h125
-rw-r--r--comm/mailnews/db/mork/morkConfig.cpp173
-rw-r--r--comm/mailnews/db/mork/morkConfig.h170
-rw-r--r--comm/mailnews/db/mork/morkCursor.cpp173
-rw-r--r--comm/mailnews/db/mork/morkCursor.h134
-rw-r--r--comm/mailnews/db/mork/morkDeque.cpp246
-rw-r--r--comm/mailnews/db/mork/morkDeque.h244
-rw-r--r--comm/mailnews/db/mork/morkEnv.cpp519
-rw-r--r--comm/mailnews/db/mork/morkEnv.h221
-rw-r--r--comm/mailnews/db/mork/morkFactory.cpp521
-rw-r--r--comm/mailnews/db/mork/morkFactory.h214
-rw-r--r--comm/mailnews/db/mork/morkFile.cpp738
-rw-r--r--comm/mailnews/db/mork/morkFile.h360
-rw-r--r--comm/mailnews/db/mork/morkHandle.cpp357
-rw-r--r--comm/mailnews/db/mork/morkHandle.h183
-rw-r--r--comm/mailnews/db/mork/morkIntMap.cpp212
-rw-r--r--comm/mailnews/db/mork/morkIntMap.h144
-rw-r--r--comm/mailnews/db/mork/morkMap.cpp852
-rw-r--r--comm/mailnews/db/mork/morkMap.h379
-rw-r--r--comm/mailnews/db/mork/morkNode.cpp550
-rw-r--r--comm/mailnews/db/mork/morkNode.h290
-rw-r--r--comm/mailnews/db/mork/morkNodeMap.cpp139
-rw-r--r--comm/mailnews/db/mork/morkNodeMap.h101
-rw-r--r--comm/mailnews/db/mork/morkObject.cpp176
-rw-r--r--comm/mailnews/db/mork/morkObject.h146
-rw-r--r--comm/mailnews/db/mork/morkParser.cpp1331
-rw-r--r--comm/mailnews/db/mork/morkParser.h547
-rw-r--r--comm/mailnews/db/mork/morkPool.cpp483
-rw-r--r--comm/mailnews/db/mork/morkPool.h162
-rw-r--r--comm/mailnews/db/mork/morkPortTableCursor.cpp381
-rw-r--r--comm/mailnews/db/mork/morkPortTableCursor.h142
-rw-r--r--comm/mailnews/db/mork/morkProbeMap.cpp1107
-rw-r--r--comm/mailnews/db/mork/morkProbeMap.h423
-rw-r--r--comm/mailnews/db/mork/morkQuickSort.cpp182
-rw-r--r--comm/mailnews/db/mork/morkQuickSort.h24
-rw-r--r--comm/mailnews/db/mork/morkRow.cpp769
-rw-r--r--comm/mailnews/db/mork/morkRow.h208
-rw-r--r--comm/mailnews/db/mork/morkRowCellCursor.cpp220
-rw-r--r--comm/mailnews/db/mork/morkRowCellCursor.h118
-rw-r--r--comm/mailnews/db/mork/morkRowMap.cpp250
-rw-r--r--comm/mailnews/db/mork/morkRowMap.h228
-rw-r--r--comm/mailnews/db/mork/morkRowObject.cpp530
-rw-r--r--comm/mailnews/db/mork/morkRowObject.h204
-rw-r--r--comm/mailnews/db/mork/morkRowSpace.cpp540
-rw-r--r--comm/mailnews/db/mork/morkRowSpace.h243
-rw-r--r--comm/mailnews/db/mork/morkSearchRowCursor.cpp153
-rw-r--r--comm/mailnews/db/mork/morkSearchRowCursor.h100
-rw-r--r--comm/mailnews/db/mork/morkSink.cpp247
-rw-r--r--comm/mailnews/db/mork/morkSink.h155
-rw-r--r--comm/mailnews/db/mork/morkSpace.cpp136
-rw-r--r--comm/mailnews/db/mork/morkSpace.h108
-rw-r--r--comm/mailnews/db/mork/morkStore.cpp1981
-rw-r--r--comm/mailnews/db/mork/morkStore.h770
-rw-r--r--comm/mailnews/db/mork/morkStream.cpp790
-rw-r--r--comm/mailnews/db/mork/morkStream.h258
-rw-r--r--comm/mailnews/db/mork/morkTable.cpp1415
-rw-r--r--comm/mailnews/db/mork/morkTable.h742
-rw-r--r--comm/mailnews/db/mork/morkTableRowCursor.cpp410
-rw-r--r--comm/mailnews/db/mork/morkTableRowCursor.h150
-rw-r--r--comm/mailnews/db/mork/morkThumb.cpp455
-rw-r--r--comm/mailnews/db/mork/morkThumb.h176
-rw-r--r--comm/mailnews/db/mork/morkUniqRowCursor.h89
-rw-r--r--comm/mailnews/db/mork/morkWriter.cpp1936
-rw-r--r--comm/mailnews/db/mork/morkWriter.h340
-rw-r--r--comm/mailnews/db/mork/morkYarn.cpp70
-rw-r--r--comm/mailnews/db/mork/morkYarn.h75
-rw-r--r--comm/mailnews/db/mork/morkZone.cpp487
-rw-r--r--comm/mailnews/db/mork/morkZone.h313
-rw-r--r--comm/mailnews/db/mork/moz.build68
-rw-r--r--comm/mailnews/db/mork/nsIMdbFactoryFactory.h33
-rw-r--r--comm/mailnews/db/mork/nsMorkFactory.cpp14
-rw-r--r--comm/mailnews/db/mork/nsMorkFactory.h27
-rw-r--r--comm/mailnews/db/mork/orkinHeap.cpp72
-rw-r--r--comm/mailnews/db/mork/orkinHeap.h50
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_ */