/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ /* file: abmodel.h ** Many portions derive from public domain IronDoc code and interfaces ** (so of course those portions remain public domain). ** ** This is the private address book interface defining abstract class interfaces ** and other auxiliary classes that comprise the model used by address books ** under the covers. The intention of this private interface is to allow ** third parties to provide alternative implementations of address books by ** conforming to a database independent interface. Additionally, this interface ** permits Netscape to use multiple address book dbs with only one interface. ** ** The public address book interface is specified in abtable.h, which defines ** the interface that clients must know and use in order to access address ** book content. The public interface in abtable.h relies on the private ** interface of abmodel.h to actually create objects or access them by calling ** "factory" methods, and this allows dynamic choice of polymorphic subclasses. ** ** Changes: ** <2> 05Jan1998 first implementation ** <1> 23Dec1997 first interface draft ** <0> 20Nov1997 first draft */ #ifndef _ABMODEL_ #define _ABMODEL_ 1 #ifndef _ABTABLE_ #include "abtable.h" #endif #ifndef _ABDEQUE_ #include "abdeque.h" #endif /* ----- ----- ----- classes defined in this interface ----- ----- ----- */ #ifndef AB_Table_typedef typedef struct AB_Table AB_Table; #define AB_Table_typedef 1 #endif #ifndef AB_Row_typedef typedef struct AB_Row AB_Row; #define AB_Row_typedef 1 #endif #ifndef XP_MAC /* ===== ===== ===== ===== misc temp kludge defines ===== ===== ===== ===== */ // artifacts of supporting an experimental file format with the old format: //#define AB_kGromitDbFileName "da5id.0a0" //#define AB_CONFIG_USE_GROMIT_FILE_FORMAT 1 /* ===== ===== ===== ===== forwards ===== ===== ===== ===== */ #endif /* XP_MAC */ class ab_Change; class ab_ColumnSet; class ab_Debugger; class ab_Defaults; class ab_Env; class ab_File; class ab_FilePrinter; class ab_FileTracer; class ab_Model; class ab_NameSet; class ab_Object; class ab_ObjectLink; class ab_ObjectSet; class ab_Part; class ab_Printer; class ab_Row; class ab_RowContent; class ab_RowSet; class ab_Search; class ab_Session; class ab_StaleRowView; class ab_Store; class ab_Stream; class ab_String; class ab_Table; class ab_Thumb; class ab_Tracer; class ab_View; /*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/ /* ===== ===== ===== ===== ab_Usage ===== ===== ===== ===== */ // usage: #define ab_Usage_kHeap /*i*/ 0x68656170 /* ascii 'heap' (no endian) */ #define ab_Usage_kStack /*i*/ 0x7374636B /* ascii 'stck' (no endian) */ #define ab_Usage_kMember /*i*/ 0x6D656D62 /* ascii 'memb' (no endian) */ #define ab_Usage_kGlobal /*i*/ 0x676C6F62 /* ascii 'glob' (no endian) */ #define ab_Usage_kGhost /*i*/ 0x67687374 /* ascii 'ghst' (no endian) */ class ab_Usage { public: ab_u4 mUsage_Code; /* kHeap, kStack, kMember, or kGhost */ public: ab_Usage(ab_u4 code); ab_Usage(); // does nothing except maybe call EnsureReadyStaticUsage() void InitUsage(ab_u4 code) { mUsage_Code = code; } ~ab_Usage() { } ab_u4 Code() const { return mUsage_Code; } static void EnsureReadyStaticUsage(); public: static const ab_Usage& kHeap; static const ab_Usage& kStack; static const ab_Usage& kMember; static const ab_Usage& kGlobal; static const ab_Usage& kGhost; static const ab_Usage& GetHeap(); // kHeap safe at static init time static const ab_Usage& GetStack(); // kHeap safe at static init time static const ab_Usage& GetMember(); // kHeap safe at static init time static const ab_Usage& GetGlobal(); // kHeap safe at static init time static const ab_Usage& GetGhost(); // kHeap safe at static init time private: // only ab_Usage gets to use the empty constructor }; /* ===== ===== ===== ===== actions ===== ===== ===== ===== */ typedef ab_bool (* ab_Object_mAction) (ab_Object* self, ab_Env* ev, void* closure); /* ===== ===== ===== ===== ab_Object ===== ===== ===== ===== */ // access: #define ab_Object_kOpen /*i*/ 0x6F70656E /* ascii 'open' (no endian) */ #define ab_Object_kClosing /*i*/ 0x636C6F73 /* ascii 'clos' (no endian) */ #define ab_Object_kShut /*i*/ 0x73687574 /* ascii 'shut' (no endian) */ #define ab_Object_kDead /*i*/ 0x64656164 /* ascii 'dead' (no endian) */ #define ab_Object_kXmlBufSize /*i*/ (256+64) /* size for ObjectAsString() */ class ab_Object /*d*/ { protected: ab_ref_count mObject_RefCount; /* reference count */ ab_u4 mObject_Access; /* kOpen, kClosing, kShut, or kDead */ ab_u4 mObject_Usage; /* kHeap, kStack, kMember, or kGhost */ // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Object(); // mObject_Access gets ab_Object_kDead // ````` ````` ````` ````` ````` ````` ````` ````` private: // private non-poly ab_Object memory management methods // It's hard to make the first ab_Env when one is needed for new() ... friend class ab_Env; // ... so we let ab_Env allocate without an instance // void* operator new(size_t inSize, const ab_Usage& inUsage); // ````` ````` ````` ````` ````` ````` ````` ````` public: // public non-poly ab_Object memory management methods void* operator new(size_t inSize, ab_Env& ev); // the env instance passed can actually be a reference to a null // pointer, which needs to happen for the first env created. (This // approach was used to help remove the other operator::new(), because // having both operators was giving ambiguity problems on one platform.) // ````` ````` ````` ````` ````` ````` ````` ````` // operator delete should be private but XP_WIN has problems compiling this, so // we only make it private on the Mac, and this catches illegal object deletes. #if defined(XP_MAC) private: // private non-poly ab_Object memory management methods #endif void* operator new(size_t inSize); // DO NOT USE THIS OPERATOR. This operator is intended to quiet compilers // which autogenerate calls to operator new() inside object constructors // (which was done in old compilers which checked for this==null). Note // that this operator is private on the Mac, so you *will* break the build // if you explicitly use this operator. // *only* ab_Object::ReleaseObject() is allowed to delete objects void operator delete(void* ioAddress) { ::operator delete(ioAddress); } // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_Object methods ab_Object(const ab_Usage& inUsage); // mObject_Access(ab_Object_kOpen), mObject_Usage(inUsage.Code()), // mObject_RefCount(1) void CastAwayConstCloseObject(ab_Env* ev) const; void MarkShut() { mObject_Access = ab_Object_kShut; } void MarkClosing() { mObject_Access = ab_Object_kClosing; } void MarkDead() { mObject_Access = ab_Object_kDead; } void TraceObject(ab_Env* ev) const; /* uses ObjectAsString() */ void BreakObject(ab_Env* ev) const; /* uses ObjectAsString() */ ab_ref_count ObjectRefCount() const { return mObject_RefCount; } ab_ref_count AcquireObject(ab_Env* ev); ab_ref_count ReleaseObject(ab_Env* ev); const char* GetObjectAccessAsString() const; // e.g. "open", "shut", etc. const char* GetObjectUsageAsString() const; // e.g. "heap", "stack", etc. ab_u4 ObjectUsage() const { return mObject_Usage; } ab_bool IsHeapObject() const { return mObject_Usage == ab_Usage_kHeap; } ab_bool IsStackObject() const { return mObject_Usage == ab_Usage_kStack; } ab_bool IsGhostObject() const { return mObject_Usage == ab_Usage_kGhost; } ab_bool IsGlobalObject() const { return mObject_Usage == ab_Usage_kGlobal; } ab_bool IsMemberObject() const { return mObject_Usage == ab_Usage_kMember; } ab_bool IsOpenObject() const { return mObject_Access == ab_Object_kOpen; } ab_bool IsShutObject() const { return mObject_Access == ab_Object_kShut; } ab_bool IsDeadObject() const { return mObject_Access == ab_Object_kDead; } ab_bool IsClosingObject() const { return mObject_Access == ab_Object_kClosing; } ab_bool IsOpenOrClosingObject() const { return IsOpenObject() || IsClosingObject(); } ab_bool IsObject() const { return ( IsOpenObject() || IsShutObject() || IsClosingObject() ); } void ReportObjectNotOpen(ab_Env* ev); // ````` ````` ````` ````` ````` ````` ````` ````` public: // panic error handling void ObjectNotReleasedPanic(const char* inMessage) const; void ObjectPanic(const char* inWhere) const; static void PushPanicEnv(ab_Env* ev, ab_Env* inPanicEnv); static ab_Env* PopPanicEnv(ab_Env* ev); static ab_Env* TopPanicEnv(); static int TopPanicEnvDepth(); // ````` ````` ````` ````` ````` ````` ````` ````` protected: // copying is sometimes not allowed ab_Object(const ab_Object& other, const ab_Usage& inUsage); ab_Object& operator=(const ab_Object& other); private: // copying without usage is not allowed ab_Object(const ab_Object& other); }; enum ab_Object_eError { ab_Object_kFaultNotOpen = /*i*/ AB_Fault_kObject, /* !IsOpenObject() */ ab_Object_kFaultNotOpenOrClosing, /*i*/ /* nn !IsOpenOrClosingObject() */ ab_Object_kFaultNotObject, /*i*/ /* nn !IsObject() */ ab_Object_kFaultNotHeapObject, /*i*/ /* nn !IsHeapObject() */ ab_Object_kFaultUnderflowRefCount, ab_Object_kFaultNullObjectParameter }; /* ===== ===== ===== ===== ab_Env ===== ===== ===== ===== */ #define ab_Env_kHeapSafeTag /*i*/ 0x73416645 /* ascii 'sAfE' (no endian) */ #define ab_Env_kDefaultMaxRefDelta /*i*/ (32 * 1024) #define ab_Env_kFaultMaxStackTop /*i*/ 16 #define ab_Env_kFaultStackSlots /*i*/ (16 + 1) class ab_Env : public ab_Object { // ````` ````` ````` ````` ````` ````` ````` ````` public: // public member variables AB_Env mEnv_Self; // tracing/printing flags (four desired for four byte alignment) */ ab_bool mEnv_BeShallow; /* write shallow depth information */ ab_bool mEnv_BeComplete; /* write more inclusive information */ ab_bool mEnv_BeConcise; /* write smaller information */ ab_bool mEnv_BeVerbose; /* write more detailed information */ // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected member variables ab_Debugger* mEnv_Debugger; /* optional debugging interface */ ab_Tracer* mEnv_Tracer; /* optional tracing interface */ const void* mEnv_StackRef; /* pointer to some local var if nonzero */ ab_u4 mEnv_MaxRefDelta; /* allowed distance from StackRef */ ab_num mEnv_StackTop; /* top of the fault stack */ AB_Fault mEnv_FaultStack[ ab_Env_kFaultStackSlots ]; // standard convenience objects ab_u1 mEnv_EmptyString[ 1 ]; /* must be zero: empty string */ ab_u1 mEnv_Pad[ 3 ]; /* u4 alignment pad until used later */ // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Env(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Env methods virtual ab_error_count CutAllFaults(); virtual ab_error_count GetAllFaults(AB_Fault* outVector, ab_error_count inSize, ab_error_count* outLength) const; virtual ab_error_uid NewFault(ab_error_uid code, ab_u4 space); virtual void BreakString(const char* inMessage); virtual void TraceString(const char* inMessage); // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected non-poly ab_Env methods ab_bool is_stack_depth_okay() const; void error_notify(const AB_Fault* inFault); void push_error(ab_error_uid inCode, ab_u4 inSpace); // ````` ````` ````` ````` ````` ````` ````` ````` public: // public non-poly ab_Env methods // ````` construction ````` ab_Env(const ab_Usage& inUsage); ab_Env(const ab_Usage& inUsage, const ab_Env& other); ab_Env(const ab_Usage& inUsage, ab_Debugger* ioDebugger, ab_Tracer* ioTracer); // ````` standard logging env ````` static ab_Env* GetSimpleEnv(); // This env just has a debugger instance installed. It is used // when instantiating the standard session log file objects. This // env is installed as the top panic env when first instantiated. // (Please, never close this env instance.) static ab_Env* GetLogFileEnv(); // standard session log env (uses ab_StdioFile::GetLogStdioFile()). // This uses standard session debugger, tracer, printer, etc. // (Please, never close this env instance.) // ````` destruction ````` void CloseEnv(ab_Env* ev); // CloseObject(); // ````` type conversion ````` AB_Env* AsSelf() { return &mEnv_Self; } // C version of this static ab_Env* AsThis(AB_Env* cenv) { return ((ab_Env*) (((ab_u1*) cenv) - ((unsigned) &((ab_Env*) 0)->mEnv_Self))); } // ````` memory management ````` void* HeapAlloc(ab_num inSize); // XP_ALLOC with auto error report void HeapFree(void* ioAddress); // XP_FREE char* CopyString(const char* inStringToCopy); // XP_STRLEN, XP_ALLOC, XP_STRCPY etc. void FreeString(char* ioStringToFree); // XP_FREE // ````` tagged memory management ````` void* HeapSafeTagAlloc(ab_num inSize); // append a kHeapSafeTag on the front void HeapSafeTagFree(void* ioAddress); // check for kHeapSafeTag on the front static ab_bool GoodHeapSafeTag(void* ioAddress); // ````` fault creation covers ````` void NewAbookFault(ab_error_uid faultCode); // ````` debugging covers ````` void Break(const char* format, ...); // ````` tracing covers ````` #ifdef AB_CONFIG_TRACE void Trace(const char* format, ...); #endif /*AB_CONFIG_TRACE*/ void TraceBeginMethod(const char* cls, const char* method) const; void TraceEndMethod() const; // ````` fault access ````` ab_error_count ErrorCount() const { return mEnv_Self.sEnv_FaultCount; } ab_error_uid GetError() const { return mEnv_FaultStack[ mEnv_StackTop ].sFault_Code; } ab_error_count GetAllErrors(ab_error_uid* outVector, ab_error_count inSize, ab_error_count* outLength); // ````` error status ````` ab_bool Good() const { return (mEnv_Self.sEnv_FaultCount == 0); } ab_bool Bad() const { return (mEnv_Self.sEnv_FaultCount != 0); } // ````` trace status ````` ab_bool DoTrace() const { return mEnv_Self.sEnv_DoTrace; } ab_bool DoErrBreak() const { return mEnv_Self.sEnv_DoErrBreak; } ab_bool DoErrorBreak() const { return mEnv_Self.sEnv_DoErrBreak; } ab_bool BeParanoid() const { return mEnv_Self.sEnv_BeParanoid; } // ````` member access ````` ab_Debugger* Debugger() const { return mEnv_Debugger; } void ChangeDebugger(ab_Debugger* ioDebugger); ab_Tracer* Tracer() const { return mEnv_Tracer; } void ChangeTracer(ab_Tracer* ioTracer); const void* StackRef() const { return mEnv_StackRef; } ab_u4 MaxRefDelta() const { return mEnv_MaxRefDelta; } void SetStackControl(const void* inRef, ab_u4 inDelta) { mEnv_StackRef = inRef; mEnv_MaxRefDelta = inDelta; } // ````` ````` ````` ````` ````` ````` ````` ````` private: // copying is not allowed ab_Env(const ab_Env& other); ab_Env& operator=(const ab_Env& other); }; // default ab_IntMap methods using integer pseudo random number generator: ab_u4 ab_Env_HashKey(ab_Env* ev, ab_map_int inKey); ab_bool ab_Env_EqualKey(ab_Env* ev, ab_map_int inTest, ab_map_int inMember); char* ab_Env_AssocString(ab_Env* ev, ab_map_int inKey, ab_map_int inValue, char* outBuf, ab_num inHashBucket, ab_num inActualBucket); #define ab_Env_kSelfOffset ((unsigned) &((ab_Env*) 0)->mEnv_Self) // offset of mEnv_Self slot inside ab_Env #define AB_Env_AsThis(cenv) ((ab_Env*) (((ab_u1*) cenv) - ab_Env_kSelfOffset)) // Cast AB_Env* to ab_Env*, but using offset of mEnv_Self. // This macro is the inverse of ab_Env::AsSelf(). #if defined(AB_CONFIG_TRACE_CALL_TREE) && AB_CONFIG_TRACE_CALL_TREE #define ab_Env_BeginMethod(ev,c,m) /*i*/ \ { if ( (ev)->DoTrace() ) (ev)->TraceBeginMethod((c), (m)); } { #define ab_Env_EndMethod(ev) /*i*/ \ } { if ( (ev)->DoTrace() ) (ev)->TraceEndMethod(); } #else /* end AB_CONFIG_TRACE_CALL_TREE*/ #define ab_Env_BeginMethod(ev,c,m) #define ab_Env_EndMethod(ev) #endif /* end AB_CONFIG_TRACE_CALL_TREE*/ /* ===== ===== ===== ===== ab_Row ===== ===== ===== ===== */ enum ab_Row_eHint { ab_Row_kMinCellHint = 3, /* min for starting cells */ ab_Row_kDefaultCellHint = 16, /* recommended default */ ab_Row_kMaxCellHint = (4 * 1024), /* max for starting cells */ ab_Row_kMaxCellSize = (32 * 1024) /* max long term size for cells */ }; class ab_Row /*d*/ : public ab_Object { protected: ab_Table* mRow_Table; ab_num mRow_CellSeed; /* count times cell structure changes */ ab_cell_count mRow_Capacity; /* number of allocated cells */ ab_cell_count mRow_Count; /* number of used cells */ AB_Cell* mRow_Cells; /* vector of cells */ // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseRow() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Row(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_Row methods const char* get_col_as_ldif_type(ab_Env* ev, ab_column_uid inColUid) const; void write_cell_as_ldif_attrib(ab_Env* ev, const AB_Cell* inCell, ab_Stream* ioStream) const; void write_person_class(ab_Env* ev, ab_Stream* ioStream) const; void write_list_class(ab_Env* ev, ab_Stream* ioStream) const; void write_list_members(ab_Env* ev, ab_row_uid inRowUid, ab_Stream* ioStream) const; void write_leading_dn_to_ldif_stream(ab_Env* ev, ab_Stream* ioStream) const; void write_member_dn_to_ldif_stream(ab_Env* ev, ab_Stream* ioStream) const; void add_cells_for_ldif_dn_attrib(ab_Env* ev); ab_RowContent* get_row_content(ab_Env* ev) const; // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_Row methods ab_Row(ab_Env* ev, const ab_Usage& inUsage, ab_Table* ioTable, ab_cell_count inHint); void CloseRow(ab_Env* ev); // CloseObject() const AB_Row* AsConstSelf() const { return (const AB_Row*) this; } AB_Row* AsSelf() { return (AB_Row*) this; } static ab_Row* AsThis(AB_Row* self) { return (ab_Row*) self; } static const ab_Row* AsConstThis(const AB_Row* self) { return (const ab_Row*) self; } ab_bool GetDistinguishedName(ab_Env* ev, char* outBuf256) const; // Write the distinguished name attribute to outBuf256, which must be // at least 256 bytes in size. The DN is currently composed of // FullName (aka FullName,CommonName, cn, or DisplayName) using the // AB_Column_kFullName column, and the email address using the // AB_Column_kEmail column. If either of these cells is not in the // row, then an empty string is used instead. The DN is formated as // follows "cn=,mail=". True when ev->Good() is true. // for example "cn=John Smith,mail=johns@netscape.com" ab_bool WriteToLdifStream(ab_Env* ev, ab_row_uid inRowUid, ab_Stream* ioStream) const; char* AsVCardString(ab_Env* ev) const; // returned string allocated by ab_Env::CopyString() which advertises // itself as using XP_ALLOC for space allocation. A null pointer is // typically returned in case of error. (It would not be that safe // to return an empty static string which could not be deallocated.) ab_row_uid FindRowUid(ab_Env* ev) const; // is this row a duplicate of a row already inside the row's table? // If so, then return the row uid for the row already in the table. // (according to some (unspecified here) method of identifying rows // uniquely -- probably fullname plus email). ab_cell_count CountCells() const { return mRow_Count; } ab_Table* GetRowTable() const { return mRow_Table; } ab_bool ChangeTable(ab_Env* ev, ab_Table* ioTable); ab_Row* DuplicateRow(ab_Env* ev) const; ab_bool CopyRow(ab_Env* ev, const ab_Row* other); ab_bool ClearCells(ab_Env* ev); ab_bool WriteCell(ab_Env* ev, const char* inCell, ab_column_uid inCol); ab_bool PutCell(ab_Env* ev, const AB_Cell* inCell); ab_bool PutHexLongCol(ab_Env* ev, long inLong, ab_column_uid inCol, ab_bool inDoAdd); ab_bool PutShortCol(ab_Env* ev, short inShort, ab_column_uid inCol, ab_bool inDoAdd); ab_bool PutBoolCol(ab_Env* ev, short inBool, ab_column_uid inCol, ab_bool inDoAdd); ab_cell_count GetCells(ab_Env* ev, AB_Cell* outVector, ab_cell_count inSize, ab_cell_count* outLength) const; AB_Cell* GetCellAt(ab_Env* ev, ab_cell_pos inPos) const; AB_Cell* GetColumnCell(ab_Env* ev, ab_column_uid inCol) const; const char* GetCellContent(ab_Env* ev, ab_column_uid inCol) const; // returns null when no such cell exists const char* GetColumnValue(ab_Env* ev, ab_column_uid inCol) const; // returns empty static string when no such cell exists ab_bool GetCellNames(ab_Env* ev, const char** inGivenName, const char** inFamilyName, const char** inFullName) const; // For each pointer that is non-null, perform the equivalent // of calling GetCellContent() for appropriate columns (which are // AB_Column_kGivenName AB_Column_kFamilyName AB_Column_kFullName) // and return the cell content pointer in the location pointed at // by each argument. The boolean return true if ev->Good() // and if at least one of the names is found and returned. ab_bool AddColumns(ab_Env* ev, const AB_Column* inVector); ab_bool AddCells(ab_Env* ev, const AB_Cell* inVector); AB_Cell* AddCell(ab_Env* ev, ab_column_uid inCol, ab_num inSize); ab_bool CutCell(ab_Env* ev, ab_column_uid inCol); ab_bool IsListRow(ab_Env* ev) const { const char* value = GetCellContent(ev, AB_Column_kIsPerson); return ( value && *value != 't' ); } ab_bool IsPersonRow(ab_Env* ev) const { const char* value = GetCellContent(ev, AB_Column_kIsPerson); return ( !value || *value == 't' ); } // ````` list and person predicates requiring EXPLICIT value ````` ab_bool HasExplicitListOrPersonValue(ab_Env* ev) const { const char* value = GetCellContent(ev, AB_Column_kIsPerson); return ( value && *value && ev->Good() ); } ab_bool IsExplicitListRow(ab_Env* ev) const { const char* value = GetCellContent(ev, AB_Column_kIsPerson); return ( value && *value == 'f' ); } ab_bool IsExplicitPersonRow(ab_Env* ev) const { const char* value = GetCellContent(ev, AB_Column_kIsPerson); return ( value || *value == 't' ); } // ````` ````` ````` ````` ````` ````` ````` ````` protected: // non-poly ab_Row protected helper methods ab_bool grow_cells(ab_Env* ev, ab_num newCapacity); ab_bool zap_cells(ab_Env* ev); ab_bool sync_cell_columns(ab_Env* ev, ab_Table* ioTable); AB_Cell* find_cell(ab_Env* ev, ab_column_uid inColUid) const; AB_Cell* new_cell(ab_Env* ev); private: // copying is not allowed ab_Row(const ab_Row& other); ab_Row& operator=(const ab_Row& other); }; /*============================================================================= * ab_Change: a kind of change */ typedef ab_u4 ab_change_uid; /* unique id for a change */ typedef ab_u4 ab_change_mask; /* bit mask for change codes */ class ab_Change /*d*/ : public ab_Object { public: ab_change_uid mChange_Uid; /* id for this session change */ ab_change_mask mChange_Mask; /* mask for change codes */ ab_model_uid mChange_ModelUid; /* id of target row's container */ ab_store_uid mChange_StoreUid; /* target row's model */ ab_row_uid mChange_Row; /* id of target row */ ab_row_pos mChange_RowPos; /* pos of changed target row */ ab_row_count mChange_RowCount; /* number of rows involved */ ab_row_uid mChange_RowParent; /* id of row's parent */ ab_column_uid mChange_Column; /* column that changed */ ab_column_pos mChange_ColPos; /* pos of the changed column */ ab_row_count mChange_ColCount; /* number of columns involved */ // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseChange() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Change(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly methods ab_Change(const ab_Usage& inUsage, const ab_Model* inModel, ab_change_mask inMask); void CloseChange(ab_Env* ev); // CloseObject() // ````` ````` ````` ````` ````` ````` ````` ````` public: // copying *is* allowed ab_Change(const ab_Change& other, const ab_Usage& inUsage); ab_Change& operator=(const ab_Change& other); protected: void SetChange(const ab_Change& other); // common copy code private: // ... but not copying without a usage specified ab_Change(const ab_Change& other); }; /*| Init: initialize this change instance to send a change notification for **| the table ioTable. True is returned if a notification is needed, which **| is true when ioTable has any members in it's View list. Otherwise **| the change instance is not altered at all. **| **|| When a change notification is needed, this change instance is zeroed **| so all slots contain zero, and then sChange_Table and sChange_Container **| are set to values appropriate ioTable and it's container. A new temp uid **| for the session change uid is coined by ab_Session, and this value is **| returned in sChange_Uid. |*/ /*ab_Change::Init(ab_Env* ev, ab_Table* ioTable);*/ typedef enum { /* bit flags for sChange_Mask */ ab_Change_kNewRow = (1L << 0), /*i*/ /* Target was just created */ /* AB_Row_NewTableRowAt(), Db, Table, Row, RowCount=1 */ ab_Change_kNewColumn = (1L << 1), /*i*/ /* column added to db */ /* AB_Table_NewColumnId(), Db, Col */ ab_Change_kSort = (1L << 2), /*i*/ /* table sort col changed */ /* AB_Table_SortByUid()/AB_Table_SortByName(), Db, Table, Column */ ab_Change_kLayout = (1L << 3), /*i*/ /* table column layout modified */ /* AB_Table_ChangeColumnLayout(), Db, Table, ColCount=n */ /* AB_Table_AddColumnAt(), Db, Table, Column, ColCount=1 */ /* AB_Table_CutColumn(), Db, Table, Column, ColCount=1 */ /* AB_Table_PutColumnAt(), Db, Table, Column, ColCount=1 */ ab_Change_kAddColumn = (1L << 4), /*i*/ /* layout column added */ /* AB_Table_AddColumnAt(), Db, Table, Column, ColPos, ColCount=1 */ ab_Change_kCutColumn = (1L << 5), /*i*/ /* layout column removed */ /* AB_Table_CutColumn(), Db, Table, Column, ColPos, ColCount=1 */ ab_Change_kPutColumn = (1L << 6), /*i*/ /* layout column modified */ /* AB_Table_PutColumnAt(), Db, Table, Column, ColPos, ColCount=1 */ ab_Change_kAddRow = (1L << 7), /*i*/ /* row added to table */ /* AB_Table_AddRow(), Db, Table, Row, RowPos, RowCount=1 */ ab_Change_kCutRow = (1L << 8), /*i*/ /* row cut from table */ /* AB_Table_CutRow(), Db, Table, Row, RowPos, RowCount=1 */ ab_Change_kPutRow = (1L << 9), /*i*/ /* row was modified */ /* AB_Row_UpdateTableRow(), Db, Table, Row, RowCount=1, ColCount=N */ /* AB_Row_ResetTableRow(), Db, Table, Row, RowCount=1, ColCount=N */ ab_Change_kMoveRow = (1L << 10), /*i*/ /* unsorted row pos moved */ /* AB_Table_ChangeRowPos(), Db, Table, Row, RowCount=1 */ ab_Change_kAddRowSet = (1L << 11), /*i*/ /* set of rows added */ /* AB_Table_AddAllRows(), Db, Table, RowCount=N */ ab_Change_kCutRowSet = (1L << 12), /*i*/ /* set of rows removed */ /* AB_Table_CutRowRange(), Db, Table, RowPos, RowCount=N */ ab_Change_kKillRow = (1L << 13), /*i*/ /* row destroyed */ /* AB_Table_DestroyRow(), Db, Table, Row, RowCount=1 */ ab_Change_kNewToken = (1L << 14), /*i*/ /* another column tokenized */ /* AB_Table_NewColumnId(), Store, Col, ColCount=1 */ ab_Change_kScramble = (1L << 15), /*i*/ /* big content change */ ab_Change_kClosing = (1L << 16) /*i*/ /* model is closing */ } ab_Change_eBits; #define ab_Change_kAllRowChanges \ ( ab_Change_kNewRow | ab_Change_kSort | ab_Change_kAddRow | \ ab_Change_kCutRow | ab_Change_kPutRow | ab_Change_kMoveRow | \ ab_Change_kAddRowSet | ab_Change_kCutRowSet | ab_Change_kKillRow | \ ab_Change_kScramble ) #define ab_Change_kAllColumnChanges \ ( ab_Change_kNewColumn | ab_Change_kLayout | ab_Change_kAddColumn | \ ab_Change_kCutColumn | ab_Change_kPutColumn | ab_Change_kNewToken | \ ab_Change_kScramble ) /* ===== ===== ===== ===== ab_Debugger ===== ===== ===== ===== */ class ab_Debugger /*d*/ : public ab_Object { /* from IronDoc's FeDebugger */ // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Debugger methods virtual void BreakString(ab_Env* ev, const char* inMessage); // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseFileTracer() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Debugger(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_Debugger methods ab_Debugger(const ab_Usage& inUsage); void Break(ab_Env* ev, const char* inFormat, ...); void CloseDebugger(ab_Env* ev); static ab_Debugger* GetLogFileDebugger(ab_Env* ev); // standard session debugger (uses ab_StdioFile::GetLogStdioFile()). // This debugger instance is used by ab_Env::GetLogFileEnv(). // (Please, never close this debugger instance.) private: // copying is not allowed ab_Debugger(const ab_Debugger& other); ab_Debugger& operator=(const ab_Debugger& other); }; /* ===== ===== ===== ===== line characters ===== ===== ===== ===== */ #define ab_kCR '\015' #define ab_kLF '\012' #define ab_kVTAB '\013' #define ab_kFF '\014' #define ab_kTAB '\011' #define ab_kCRLF "\015\012" /* A CR LF equivalent string */ #ifdef XP_MAC # define ab_kNewline "\015" # define ab_kNewlineSize 1 #else # if defined(XP_WIN) || defined(XP_OS2) # define ab_kNewline "\015\012" # define ab_kNewlineSize 2 # else # ifdef XP_UNIX # define ab_kNewline "\012" # define ab_kNewlineSize 1 # endif /* XP_UNIX */ # endif /* XP_WIN */ #endif /* XP_MAC */ /* ===== ===== ===== ===== ab_Tracer ===== ===== ===== ===== */ class ab_Tracer /*d*/ : public ab_Object { /* from IronDoc's FeTracer class */ // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected ab_FileTracer members ab_u4 mTracer_MethodDepth; // count begins without matching ends // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Tracer methods virtual void TraceString(ab_Env* ev, const char* inMessage) = 0; virtual void BeginMethod(ab_Env* ev, const char* c, const char* m) = 0; virtual void EndMethod(ab_Env* ev) = 0; virtual void Flush(ab_Env* ev) = 0; virtual ab_Printer* GetPrinter(ab_Env* ev) = 0; // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods //virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; //virtual void CloseObject(ab_Env* ev); // CloseFileTracer() //virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Tracer(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_Tracer methods void Trace(ab_Env* ev, const char* inFormat, ...); ab_Tracer(const ab_Usage& inUsage); void CloseTracer(ab_Env* ev) { AB_USED_PARAMS_1(ev); } ab_u4 GetTracerMethodDepth() const { return mTracer_MethodDepth; } private: // copying is not allowed ab_Tracer(const ab_Tracer& other); ab_Tracer& operator=(const ab_Tracer& other); }; enum ab_Tracer_eError { ab_Tracer_kFaultNullFile = /*i*/ AB_Fault_kTracer }; /* ===== ===== ===== ===== ab_FileTracer ===== ===== ===== ===== */ class ab_FileTracer /*d*/ : public ab_Tracer { /* from the IronDoc class */ // ````` ````` ````` ````` ````` ````` ````` ````` protected: ab_File* mFileTracer_File; // presumably uses same file ab_Printer* mFileTracer_Printer; // presumably uses same file as tracer // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Tracer methods virtual void TraceString(ab_Env* ev, const char* inMessage); virtual void BeginMethod(ab_Env* ev, const char* c, const char* m); virtual void EndMethod(ab_Env* ev); virtual void Flush(ab_Env* ev); virtual ab_Printer* GetPrinter(ab_Env* ev); // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseFileTracer() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_FileTracer(); // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected non-poly ab_FileTracer methods void trace_indent(ab_Env* ev); void trace_string(ab_Env* ev, const char* inMessage, ab_bool inXmlWrap); // ````` ````` ````` ````` ````` ````` ````` ````` public: // public non-poly ab_FileTracer methods ab_FileTracer(ab_Env* ev, const ab_Usage& inUsage, ab_File* ioFile, ab_Printer* ioPrinter); void CloseFileTracer(ab_Env* ev); static ab_FileTracer* GetLogFileTracer(ab_Env* ev); // standard session file tracer (uses ab_StdioFile::GetLogStdioFile()). // This tracer instance is used by ab_Env::GetLogFileEnv(). // (Please, never close this tracer instance.) ab_File* GetFileTracerFile() const { return mFileTracer_File; } ab_Printer* GetFileTracerPrinter() const { return mFileTracer_Printer; } private: // copying is not allowed ab_FileTracer(const ab_FileTracer& other); ab_FileTracer& operator=(const ab_FileTracer& other); }; /* ===== ===== ===== ===== ab_Printer ===== ===== ===== ===== */ class ab_Printer /*d*/ : public ab_Object { /* from IronDoc's FeSink class */ // ````` ````` ````` ````` ````` ````` ````` ````` protected: // ab_FilePrinter members ab_num mPrinter_Depth; // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Printer methods virtual void PutString(ab_Env* ev, const char* inMessage) = 0; virtual void Newline(ab_Env* ev, ab_num inNewlines) = 0; virtual void NewlineIndent(ab_Env* ev, ab_num inNewlines) = 0; virtual void Flush(ab_Env* ev) = 0; virtual void Hex(ab_Env* ev, const void* b, ab_num size, ab_pos p) = 0; virtual ab_num PushDepth(ab_Env* ev) = 0; virtual ab_num PopDepth(ab_Env* ev) = 0; // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods //virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; //virtual void CloseObject(ab_Env* ev); // ClosePrinter() //virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Printer(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_Printer methods void Print(ab_Env* ev, const char* inFormat, ...); ab_Printer(const ab_Usage& inUsage); void ClosePrinter(ab_Env* ev) { AB_USED_PARAMS_1(ev); } ab_num GetPrinterDepth() const { return mPrinter_Depth; } void DefaultPrinterHex(ab_Env* ev, const void* inBuf, ab_num inSize, ab_pos inPos); // DefaultPrinterHex() is a good default implementation for Hex() private: // copying is not allowed ab_Printer(const ab_Printer& other); ab_Printer& operator=(const ab_Printer& other); }; enum ab_Printer_eError { ab_Printer_kFaultNullFile = /*i*/ AB_Fault_kPrinter, ab_Printer_kFaultFileNotOpen, ab_Printer_kFaultHexSizeTooBig, ab_Printer_kFaultConfused }; /* ===== ===== ===== ===== ab_FilePrinter ===== ===== ===== ===== */ class ab_FilePrinter /*d*/ : public ab_Printer { /* from the IronDoc class */ // ````` ````` ````` ````` ````` ````` ````` ````` protected: // ab_FilePrinter members ab_File* mFilePrinter_File; // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Printer methods virtual void PutString(ab_Env* ev, const char* inMessage); virtual void Newline(ab_Env* ev, ab_num inNewlines); virtual void NewlineIndent(ab_Env* ev, ab_num inNewlines); virtual void Flush(ab_Env* ev) ; virtual void Hex(ab_Env* ev, const void* b, ab_num inSize, ab_pos inPos); virtual ab_num PushDepth(ab_Env* ev); virtual ab_num PopDepth(ab_Env* ev); // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseFilePrinter() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_FilePrinter(); // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected non-poly ab_FilePrinter methods void file_printer_not_open(ab_Env* ev, const char* inMethod) const; void file_printer_bad_file(ab_Env* ev, const char* inMethod) const; void file_printer_indent(ab_Env* ev) const; // ````` ````` ````` ````` ````` ````` ````` ````` public: // public non-poly ab_FilePrinter methods ab_FilePrinter(ab_Env* ev, const ab_Usage& inUsage, ab_File* ioFile); void CloseFilePrinter(ab_Env* ev); static ab_FilePrinter* GetLogFilePrinter(ab_Env* ev); // standard session printer (uses ab_StdioFile::GetLogStdioFile()). // This printer instance is used by ab_Env::GetLogFileEnv(). // This printer instance is used by ab_FileTracer::GetLogFileTracer(). // (Please, never close this printer instance.) private: // copying is not allowed ab_FilePrinter(const ab_FilePrinter& other); ab_FilePrinter& operator=(const ab_FilePrinter& other); }; /*============================================================================= * ab_ObjectLink: type specific link subclass for lists (deques) of ab_Object */ class ab_ObjectLink /*d*/ : public AB_Link { public: ab_Object* mObjectLink_Object; /* pointer to object this link knows */ ab_ref_count mObjectLink_RefCount; /* ref count for this object in a list */ // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_ObjectLink methods ab_ObjectLink(ab_Object* ioObject) : mObjectLink_Object(ioObject), mObjectLink_RefCount(1) { } ~ab_ObjectLink() { } // do nothing private: // copying is not allowed ab_ObjectLink(const ab_ObjectLink& other); ab_ObjectLink& operator=(const ab_ObjectLink& other); }; /* ===== ===== ===== ===== ab_ObjectSet ===== ===== ===== ===== */ class ab_ObjectSet /*d*/ : public ab_Object { protected: AB_Deque mObjectSet_Objects; // list of ab_ObjectLink ab_num mObjectSet_Seed; // times list is modified ab_Object* mObjectSet_Member; // current obj in iteration ab_ObjectLink* mObjectSet_NextLink; // next link in iteration // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseObjectSet() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_ObjectSet(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_ObjectSet methods ab_ObjectSet(const ab_Usage& inUsage); void CloseObjectSet(ab_Env* ev); // CloseObject() void TraceSet(ab_Env* ev); ab_num GetSeed() const { return mObjectSet_Seed; } ab_ref_count AddObject(ab_Env* ev, ab_Object* ioObject); ab_ref_count HasObject(ab_Env* ev, const ab_Object* inObject) const; ab_ref_count SubObject(ab_Env* ev, ab_Object* ioObject); ab_ref_count CutObject(ab_Env* ev, ab_Object* ioObject); ab_num CutAllObjects(ab_Env* ev); ab_num CountObjects(ab_Env* ev) const; ab_Object* CutAnyObject(ab_Env* ev); ab_bool DoObjects(ab_Env* ev, ab_Object_mAction a, void* closure) const; ab_bool IsEmpty() const { return AB_Deque_IsEmpty(&mObjectSet_Objects); } ab_bool HasMembers() const { return !AB_Deque_IsEmpty(&mObjectSet_Objects); } // ````` iteration methods ````` ab_Object* FirstMember(ab_Env* ev); ab_Object* NextMember(ab_Env* ev); ab_Object* CurrentMember() const { return mObjectSet_Member; } // ````` ````` ````` ````` ````` ````` ````` ````` public: // protected by convention ab_num count_links() const; // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected list manipulation ab_ObjectLink* get_link(const ab_Object* ioObject) const; void cut_link(ab_Env* ev, ab_ObjectLink* link); friend class ab_Model; friend class ab_View; private: // copying is not allowed ab_ObjectSet(const ab_ObjectSet& other); ab_ObjectSet& operator=(const ab_ObjectSet& other); }; enum ab_ObjectSet_eError { ab_ObjectSet_kFaultIterOutOfSync = /*i*/ AB_Fault_kObjectSet, ab_ObjectSet_kFaultOutOfMemory }; /* ===== ===== ===== ===== ab_Part ===== ===== ===== ===== */ class ab_Part /*d*/ : public ab_Object { protected: ab_Store* mPart_Store; // this part's database (can be self) ab_row_uid mPart_RowUid; // temp or persistent uid for this part ab_num mPart_NameSetSeed; // copy of store's name set seed ab_num mPart_ColumnSetSeed; // copy of store's column set seed ab_num mPart_RowSetSeed; // copy of store's row set seed ab_num mPart_RowContentSeed; // copy of store's row content seed // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // ClosePart() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Part(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_Part methods ab_Part(ab_Env* ev, const ab_Usage& inUsage, ab_row_uid inRowUid, ab_Store* ioStore); void ClosePart(ab_Env* ev); // CloseObject() ab_Store* GetPartStore() const { return mPart_Store; } ab_row_uid GetPartRowUid() const { return mPart_RowUid; } ab_Store* GetOpenStoreFromOpenPart(ab_Env* ev) const; ab_bool PartNameSetSeedMatchesStore() const; // inline defined later ab_bool PartColumnSetSeedMatchesStore() const; // inline defined later ab_bool PartRowSetSeedMatchesStore() const; // inline defined later ab_bool PartRowContentSeedMatchesStore() const; // inline defined later ab_num GetPartNameSetSeed() const { return mPart_NameSetSeed; } ab_num GetPartColumnSetSeed() const { return mPart_ColumnSetSeed; } ab_num GetPartRowSetSeed() const { return mPart_RowSetSeed; } ab_num GetPartRowContentSeed() const { return mPart_RowContentSeed; } private: // copying is not allowed ab_Part(const ab_Part& other); ab_Part& operator=(const ab_Part& other); }; enum ab_Part_eError { ab_Part_kFaultNullStore = /*i*/ AB_Fault_kPart, ab_Part_kFaultNotOpen, ab_Part_kFaultStoreNotOpen }; /* ===== ===== ===== ===== ab_Model ===== ===== ===== ===== */ class ab_Model /*d*/ : public ab_Part { protected: ab_Store* mModel_Store; // this model's database (can be self) ab_row_uid mModel_RowUid; // temp or persistent uid for this model ab_ObjectSet mModel_Views; // list of ab_ObjectLink // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseModel() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Model(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Model methods (typically no need to override) virtual ab_ref_count AddView(ab_Env* ev, ab_View* ioView); virtual ab_ref_count HasView(ab_Env* ev, const ab_View* inView) const; virtual ab_ref_count SubView(ab_Env* ev, ab_View* ioView); virtual ab_ref_count CutView(ab_Env* ev, ab_View* ioView); virtual ab_num CutAllViews(ab_Env* ev); virtual ab_num CountViews(ab_Env* ev) const; // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_Model methods ab_Model(ab_Env* ev, const ab_Usage& inUsage, ab_row_uid inRowUid, ab_Store* ioStore); void CloseModel(ab_Env* ev); // CloseObject() void BeginModelFlux(ab_Env* ev); void EndModelFlux(ab_Env* ev); void ClosingModel(ab_Env* ev, const ab_Change* inChange); void ChangedModel(ab_Env* ev, const ab_Change* inChange); private: // copying is not allowed ab_Model(const ab_Model& other); ab_Model& operator=(const ab_Model& other); }; enum ab_Model_eError { ab_Model_kFaultNullStore = /*i*/ AB_Fault_kModel }; /*============================================================================= * ab_View: thing which is sees shown model changes */ class ab_View /*d*/ : public ab_Object { protected: ab_ObjectSet mView_Models; /* list of AB_ObjectLink */ ab_change_mask mView_Interests; /* changes this View cares about */ // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_View responses to ab_Model messages virtual void SeeBeginModelFlux(ab_Env* ev, ab_Model* m) = 0; virtual void SeeEndModelFlux(ab_Env* ev, ab_Model* m) = 0; virtual void SeeChangedModel(ab_Env* ev, ab_Model* m, const ab_Change* c) = 0; virtual void SeeClosingModel(ab_Env* ev, ab_Model* m, const ab_Change* c) = 0; // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseView() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_View(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_View methods ab_View(const ab_Usage& inUsage, ab_change_mask interests); void CloseView(ab_Env* ev); // CloseObject(); ab_ref_count AddModel(ab_Env* ev, ab_Model* ioModel) { return mView_Models.AddObject(ev, ioModel); } ab_ref_count HasModel(ab_Env* ev, const ab_Model* inModel) const { return mView_Models.HasObject(ev, inModel); } ab_ref_count SubModel(ab_Env* ev, ab_Model* ioModel) { return mView_Models.SubObject(ev, ioModel); } ab_ref_count CutModel(ab_Env* ev, ab_Model* ioModel) { return mView_Models.CutObject(ev, ioModel); } ab_num CutAllModels(ab_Env* ev) { return mView_Models.CutAllObjects(ev); } ab_num CountModels(ab_Env* ev) const { return mView_Models.CountObjects(ev); } private: // copying is not allowed ab_View(const ab_View& other); ab_View& operator=(const ab_View& other); }; /*============================================================================= * ab_StaleRowView: a View that notices stale rows resulting from changes */ class ab_StaleRowView /*d*/ : public ab_View { public: ab_Object* mStaleRowView_CloseTarget; /* close/release when row stales */ ab_row_uid mStaleRowView_RowTarget; /* row that might become stale */ // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_View responses to ab_Model messages virtual void SeeBeginModelFlux(ab_Env* ev, ab_Model* ioModel); virtual void SeeEndModelFlux(ab_Env* ev, ab_Model* ioModel); virtual void SeeChangedModel(ab_Env* ev, ab_Model* ioModel, const ab_Change* inChange); virtual void SeeClosingModel(ab_Env* ev, ab_Model* ioModel, const ab_Change* inChange); // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseStaleRowView() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_StaleRowView(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_StaleRowView methods ab_StaleRowView(ab_Env* ev, const ab_Usage& inUsage, ab_Object* ioCloseTarget, ab_row_uid inRowTarget); void CloseStaleRowView(ab_Env* ev); // CloseObject(); void PerformStaleRowHandling(ab_Env* ev); private: // copying is not allowed ab_StaleRowView(const ab_View& other); ab_StaleRowView& operator=(const ab_View& other); }; /*============================================================================= * ab_TableStoreView: a View that notices stale rows resulting from changes */ class ab_TableStoreView /*d*/ : public ab_View { public: ab_Store* mTableStoreView_Store; // store watched by this view ab_Table* mTableStoreView_Table; // table notified by this view // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_View responses to ab_Model messages virtual void SeeBeginModelFlux(ab_Env* ev, ab_Model* ioModel); virtual void SeeEndModelFlux(ab_Env* ev, ab_Model* ioModel); virtual void SeeChangedModel(ab_Env* ev, ab_Model* ioModel, const ab_Change* inChange); virtual void SeeClosingModel(ab_Env* ev, ab_Model* ioModel, const ab_Change* inChange); // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseTableStoreView() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_TableStoreView(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_TableStoreView methods ab_TableStoreView(ab_Env* ev, const ab_Usage& inUsage, ab_Store* ioStore, ab_Table* ioTable); void CloseTableStoreView(ab_Env* ev); // CloseObject(); private: // copying is not allowed ab_TableStoreView(const ab_View& other); ab_TableStoreView& operator=(const ab_View& other); }; /*============================================================================= * ab_File: abstract file interface */ typedef enum ab_File_eFormat { /* duplicate of AB_File_eFormat type */ ab_File_kUnknownFormat = 0x3F3F3F3F, /* '????' */ ab_File_kLdifFormat = 0x6C646966, /* 'ldif' */ ab_File_kHtmlFormat = 0x68746D6C, /* 'html' */ ab_File_kXmlFormat = 0x61786D6C, /* 'axml' */ ab_File_kVCardFormat = 0x76637264, /* 'vcrd' */ ab_File_kBinaryFormat = 0x626E7279, /* 'bnry' */ ab_File_kNativeFormat = 0x6E617476 /* 'natv' current native format */ } ab_File_eFormat; class ab_File /*d*/ : public ab_Object { /* ````` simple file API ````` */ // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected ab_File members (similar to IronDoc) ab_u1 mFile_Frozen; // 'F' => file allows only read access ab_u1 mFile_DoTrace; // 'T' trace if ev->DoTrace() ab_u1 mFile_IoOpen; // 'O' => io stream is open (& needs a close) ab_u1 mFile_Active; // 'A' => file is active and usable const char* mFile_Name; // always non-nil (uses static "" when necessary) ab_u4 mFile_FormatHint; // hint regarding the file's content format // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_File methods virtual ab_pos Length(ab_Env* ev) const = 0; // eof virtual ab_pos Tell(ab_Env* ev) const = 0; virtual ab_num Read(ab_Env* ev, void* outBuf, ab_num inSize) = 0; virtual ab_pos Seek(ab_Env* ev, ab_pos inPos) = 0; virtual ab_num Write(ab_Env* ev, const void* inBuf, ab_num inSize) = 0; virtual void Flush(ab_Env* ev) = 0; // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; // virtual void CloseObject(ab_Env* ev); // CloseFile() // virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_File(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_File methods ab_File(const ab_Usage& inUsage); void CloseFile(ab_Env* ev); ab_u4 GetFileFormatHint() const { return mFile_FormatHint; } void ChangeFileFormatHint(ab_u4 inHint) { mFile_FormatHint = inHint; } ab_File_eFormat GuessFileFormat(ab_Env* ev); const char* GetFileName() const { return mFile_Name; } void ChangeFileName(ab_Env* ev, const char* inFileName); // inFileName can be nil (which has the same effect as passing "") ab_bool FileFrozen() const { return mFile_Frozen == 'F'; } ab_bool FileDoTrace() const { return mFile_DoTrace == 'T'; } ab_bool FileIoOpen() const { return mFile_IoOpen == 'O'; } ab_bool FileActive() const { return mFile_Active == 'A'; } void SetFileFrozen(ab_bool b) { mFile_Frozen = (b)? 'F' : 0; } void SetFileDoTrace(ab_bool b) { mFile_DoTrace = (b)? 'T' : 0; } void SetFileIoOpen(ab_bool b) { mFile_IoOpen = (b)? 'O' : 0; } void SetFileActive(ab_bool b) { mFile_Active = (b)? 'A' : 0; } ab_bool IsOpenActiveAndMutableFile() const { return ( IsOpenObject() && FileActive() && !FileFrozen() ); } // call IsOpenActiveAndMutableFile() before writing a file ab_bool IsOpenAndActiveFile() const { return ( this->IsOpenObject() && this->FileActive() ); } // call IsOpenAndActiveFile() before using a file void NewFileDownFault(ab_Env* ev) const; // call NewFileDownFault() when either IsOpenAndActiveFile() // is false, or when IsOpenActiveAndMutableFile() is false. void NewFileErrnoFault(ab_Env* ev) const; // call NewFileErrnoFault() to convert std C errno into AB fault void WriteNewlines(ab_Env* ev, ab_num inNewlines); private: // copying is not allowed ab_File(const ab_File& other); ab_File& operator=(const ab_File& other); }; enum ab_File_eError { ab_File_kFaultNotOpen = /*i*/ AB_Fault_kFile, /* !IsOpenObject() */ ab_File_kFaultNotActive, /*i*/ /* nn !FileActive() */ ab_File_kFaultAlreadyActive, /*i*/ /* nn FileActive() */ ab_File_kFaultNotIoOpen, /*i*/ /* nn !FileIoOpen() */ ab_File_kFaultNoFileName, /*i*/ /* nn */ ab_File_kFaultFrozen, /*i*/ /* nn FileFrozen() */ ab_File_kFaultDownUnknown, /*i*/ /* unknown reason for being down */ ab_File_kFaultMissingIo, /*i*/ /* file io object is missing */ ab_File_kFaultNullOpaqueParameter, /*i*/ /* */ ab_File_kFaultZeroErrno, /*i*/ /* errno surprisingly equals zero*/ ab_File_kFaultPosBeyondEof, /*i*/ /* position beyond end of file */ ab_File_kFaultNullBuffer, /*i*/ /* io buffer is null */ ab_File_kFaultCantReadSink, /*i*/ /* file is output sink/writeonly */ ab_File_kFaultCantWriteSource, /*i*/ /* file is input source/readonly */ ab_File_kFaultBadCursorOrder, /*i*/ /* buffer cursors badly ordered */ ab_File_kFaultApiDummyNoImpl, /*i*/ /* no file implementation */ ab_File_kFaultWrongTag /*i*/ /* not expected file subclass */ }; // NOTE ON METHOD TRACING IN FILES: it is very important that files installed // inside a tracer used by the environment NOT have tracing enabled, because // if they do, then tracing will infinitely recurse and overflow the stack (or // perhaps cause a panic halt in stack checking code if you are lucky). // method begin/end trace macros for files, also copied from IronDoc: #if defined(AB_CONFIG_TRACE_CALL_TREE) && AB_CONFIG_TRACE_CALL_TREE #define ab_File_BeginMethod(f,ev,c,m) /*i*/ \ { if ( (f)->FileDoTrace() && (ev)->DoTrace() ) \ (ev)->TraceBeginMethod((c), (m)); } { #define ab_File_EndMethod(f,ev) /*i*/ \ } { if ( (f)->FileDoTrace() && (ev)->DoTrace() ) (ev)->TraceEndMethod(); } #else /* end AB_CONFIG_TRACE_CALL_TREE*/ #define ab_File_BeginMethod(ev,c,m) #define ab_File_EndMethod(ev) #endif /* end AB_CONFIG_TRACE_CALL_TREE*/ /*============================================================================= * ab_StdioFile: concrete file using standard C file io */ class ab_StdioFile /*d*/ : public ab_File { /* `` copied from IronDoc `` */ // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected ab_StdioFile members void* mStdioFile_File; // actually type FILE*, but using opaque void* type // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_File methods virtual ab_pos Length(ab_Env* ev) const; // eof virtual ab_pos Tell(ab_Env* ev) const; virtual ab_num Read(ab_Env* ev, void* outBuf, ab_num inSize); virtual ab_pos Seek(ab_Env* ev, ab_pos inPos); virtual ab_num Write(ab_Env* ev, const void* inBuf, ab_num inSize); virtual void Flush(ab_Env* ev); // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseFile() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_StdioFile(); // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected non-poly ab_StdioFile methods void new_stdio_file_fault(ab_Env* ev) const; // ````` ````` ````` ````` ````` ````` ````` ````` public: // public non-poly ab_StdioFile methods ab_StdioFile(const ab_Usage& inUsage); ab_StdioFile(ab_Env* ev, const ab_Usage& inUsage, const char* inName, const char* inMode); // calls OpenStdio() after construction ab_StdioFile(ab_Env* ev, const ab_Usage& inUsage, void* ioFile, const char* inName, ab_bool inFrozen); // calls UseStdio() after construction static ab_StdioFile* GetLogStdioFile(ab_Env* ev); // return the standard log file used by ab_StdioFile for each session. void CloseStdioFile(ab_Env* ev); void OpenStdio(ab_Env* ev, const char* inName, const char* inMode); // Open a new FILE with name inName, using mode flags from inMode. void UseStdio(ab_Env* ev, void* ioFile, const char* inName, ab_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(ab_Env* 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. private: // copying is not allowed ab_StdioFile(const ab_StdioFile& other); ab_StdioFile& operator=(const ab_StdioFile& other); }; /*============================================================================= * ab_Stream: buffered file i/o */ /*| ab_Stream exists to define an ab_File 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). **| **|| ab_Stream 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. **| **|| 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. |*/ #define ab_Stream_kPrintBufSize /*i*/ 512 /* buffer size used by printf() */ #define ab_Stream_kMinBufSize /*i*/ 512 /* buffer no fewer bytes */ #define ab_Stream_kMaxBufSize /*i*/ (32 * 1024) /* buffer no more bytes */ class ab_Stream /*d*/ : public ab_File { /* from Mithril's AgStream class */ // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected ab_Stream members ab_u1* mStream_At; // pointer into mStream_Buf ab_u1* mStream_ReadEnd; // null or one byte past last readable byte ab_u1* mStream_WriteEnd; // null or mStream_Buf + mStream_BufSize ab_File* mStream_ContentFile; // where content is read and written ab_u1* mStream_Buf; // dynamically allocated memory to buffer io ab_num mStream_BufSize; // requested buf size (fixed by min and max) ab_pos mStream_BufPos; // logical position of byte at mStream_Buf ab_bool mStream_Dirty; // does the buffer need to be flushed? ab_bool mStream_HitEof; // has eof been reached? (only frozen streams) // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_File methods virtual ab_pos Length(ab_Env* ev) const; // eof virtual ab_pos Tell(ab_Env* ev) const; virtual ab_num Read(ab_Env* ev, void* outBuf, ab_num inSize); virtual ab_pos Seek(ab_Env* ev, ab_pos inPos); virtual ab_num Write(ab_Env* ev, const void* inBuf, ab_num inSize); virtual void Flush(ab_Env* ev); // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseStream() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Stream(); // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected non-poly ab_Stream methods (for char io) int fill_getc(ab_Env* ev); void spill_putc(ab_Env* ev, int c); void spill_buf(ab_Env* ev); // spill/flush from buffer to file // ````` ````` ````` ````` ````` ````` ````` ````` public: // public non-poly ab_Stream methods ab_Stream(ab_Env* ev, const ab_Usage& inUsage, ab_File* ioContentFile, ab_num inBufSize, ab_bool inFrozen); void CloseStream(ab_Env* ev); ab_File* GetStreamContentFile() const { return mStream_ContentFile; } ab_num GetStreamBufferSize() const { return mStream_BufSize; } void PutString(ab_Env* ev, const char* inString); void PutStringThenNewline(ab_Env* ev, const char* inString); // ````` ````` stdio type methods ````` ````` void Printf(ab_Env* ev, const char* inFormat, ...); ab_pos Ftell(ab_Env* ev) /*i*/ { return this->Tell(ev); } void Fsetpos(ab_Env* ev, ab_pos inPos) /*i*/ { this->Seek(ev, inPos); } void Rewind(ab_Env* ev) /*i*/ { this->Fsetpos(ev, 0); } void Fflush(ab_Env* ev) /*i*/ { this->Flush(ev); } ab_bool Feof() /*i*/ { return mStream_HitEof; } // Feof() is never true for writable stream output sinks void Puts(ab_Env* ev, const char* inString) /*i*/ { this->PutString(ev, inString); } void Ungetc(int c) /*i*/ { if ( mStream_At > mStream_Buf && c > 0 ) *--mStream_At = c; } int Getc(ab_Env* ev) /*i*/ { return ( mStream_At < mStream_ReadEnd )? *mStream_At++ : fill_getc(ev); } void Putc(ab_Env* ev, int c) /*i*/ { mStream_Dirty = AB_kTrue; if ( mStream_At < mStream_WriteEnd ) *mStream_At++ = c; else spill_putc(ev, c); } // ````` ````` high level i/o operations ````` ````` void GetDoubleNewlineDelimitedRecord(ab_Env* ev, ab_String* outString); // GetDoubleNewlineDelimitedRecord() reads a "record" from the stream // that is intended to correspond to an ldif record in a file being // imported into an address book. Such records are delimited by two // (or more) empty lines, where all lines separated by only a single // empty line are considered part of the record. The record is written // to outString which grows as necessary to hold the record. (This // string is first emptied with ab_String::TruncateStringLength(), so // the string should not be a nonempty static readonly string. The // same string can easily be used in successive calls to this method // to avoid any more memory management than it needed.) // // Let LF, CR, and BL denote bytes 0xA, 0xD, and 0x20 respectively. // This method collapses sequences of LF and CR to a single LF because // this is what ldif parsing code expects to separate lines in each // ldif record. // // UNLIKE the original spec, BLANKS ARE NOT REMOVED, because leading // blanks are significant to continued line parsing in ldif format. // // Counting the number of empty lines // encountered is made more complex by the need to make the process // cross platform, where a line-end on various platforms might be a // single LF or CR, or a combined CRLF. Additionally, ldif files were // once erroneously written with line-ends composed of CRCRLF, so this // should count as a single line as well. Otherwise, this method will // consider two empty lines encountered (thus ending the record) when // enough LF's and CR's are seen to account for two lines on some // platform or another. Once either LF or CR is seen, all instances // of LF, CR, and BL will be consumed from the input stream, but only a // single LF will be written to the output record string. While this // white space is being parsed and discarded, the loop decides whether // more than one line is being seen, which ends the record when true. // // This method is intended to be a more efficient way to parse groups // of lines from an input file than by parsing individual lines which // then get concatenated together. The performance difference might // matter for large ldif files, especially when more than one pass must // be performed on the same input file. // // When this method returns, the current file position should point to // either eof (end of file) or the first byte of the next record. // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected non-poly high-level support utilities ab_bool consuming_white_space_ends_record(ab_Env* ev, int c); // Consume the rest of the line ending after a leading 0xA or 0xD // which is passed in as the value of input argument c. Analyze the // consumed white space as per the description of public method // GetDoubleNewlineDelimitedRecord(), and return true if more than // one line-ending is detected which signals end of record. private: // copying is not allowed ab_Stream(const ab_Stream& other); ab_Stream& operator=(const ab_Stream& other); }; /*============================================================================= * ab_IntMap: collection of assocs from key uid to value uid */ #define ab_IntMap_kMailingListImportHint /*i*/ 512 // hint that less than 512 mailing lists will be typically imported at once /* ab_IntMap is a "closed" hash table that resolves hash collisions by finding ** an available slot. Performance is ensured by making sure occupancy is no ** more than 80 percent of capacity before growing the hash table, which of ** course also rehashes all the old associations. (This hash table follows ** the pattern of closed hash tables in public domain IronDoc.) ** ** The "key" and "value" of associations are each of type ab_map_int, which is ** defined to be a signed integer type whose size is the greater of either ** sizeof(ab_pos) or sizeof(void*), whichever is more. The intention is that ** the map must be able to contain either pointers or the largest integers that ** we want to store. The address book implementation might at times want to ** store integers in maps, but at other times addresses of objects. ** ** Zero is assumed never a valid key, so a zero key means a hash bucket slot ** is not being used. No allowance is made for removing associations in the ** table except by removing all associations at one time. This usage fits the ** need to build a map of uids or of positions while importing, which gets ** thrown away en masse when import is finished. ** ** When a map is constructed, a boolean parameter indicates whether assocs ** should have values in addition to keys, so that every assoc is a pair ** mapping a key to a value. This is normal usage. When this flag is false ** at creation time, no values will be saved so the collection is really just ** a set of keys, and this is useful for integer sets that don't need values. ** When values are not supported, every value equals ab_IntMap_kPretendValue ** when the API return the value for an assoc. ** ** Each key maps to a "target" bucket at index ab_IntMap::random_bucket(), and ** a collision is resolved by searching for the next empty slot (holding a ** zero key) with wrap-around at the table's end. The pseudo random number ** generator is used in bucket calculation to avoid expected clumping of keys ** from alignment patterns in either addresses or uid encodings. */ #define ab_IntMap_kPretendValue 0xFFFFFFFF /* used when values don't exist */ ab_i4 AbI4_Random(ab_i4 self); // pseudo RNG, also good for hashing // define AbMapInt_Hash(i) ( (ab_u4) AbI4_Random((ab_i4) self) ) // #define ab_IntMap_KeyBucket(self,key) \ // ( ( (ab_u4) AbI4_Random((ab_i4) key) ) % (self)->mIntMap_Capacity ) #define ab_IntMap_kMinSizeHint 3 #define ab_IntMap_kMaxSizeHint (12 * 1024) /* required invariant: (a equal b) implies (hash a) == (hash b) */ typedef ab_u4 (* ab_IntMap_mHashKey /*d*/) (ab_Env* ev, ab_map_int inKey); typedef ab_bool (* ab_IntMap_mEqualKey /*d*/) (ab_Env* ev, ab_map_int inTestKey, ab_map_int inMapMemberKey); typedef char* (* ab_IntMap_mAssocString /*d*/) (ab_Env* ev, ab_map_int inKey, ab_map_int inValue, char* outXmlBuf, ab_num inHashBucket, ab_num inActualBucket); class ab_KeyMethods { public: ab_IntMap_mHashKey mKeyMethods_Hash; ab_IntMap_mEqualKey mKeyMethods_Equal; ab_IntMap_mAssocString mKeyMethods_AssocString; ab_KeyMethods(ab_IntMap_mHashKey h, ab_IntMap_mEqualKey e, ab_IntMap_mAssocString a) { mKeyMethods_Hash = h; mKeyMethods_Equal = e; mKeyMethods_AssocString = a; } }; class ab_IntMap /*d*/ : public ab_Object { // this table derives from public domain IronDoc and Mithril hash tables // ````` ````` ````` ````` ````` ````` ````` ````` protected: ab_num mIntMap_ChangeCount; // count table changes ab_num mIntMap_Threshold; // max assocs before growth; 80% of cap ab_num mIntMap_Capacity; // number of buckets ab_count mIntMap_AssocCount; // number of member associations ab_map_int* mIntMap_KeyInts; // keys for each association ab_map_int* mIntMap_ValueInts; // values for each association // hash and equal method defaults: ab_Env_HashKey() and ab_Env_EqualKey() ab_IntMap_mHashKey mIntMap_Hash; // default ab_Env_HashKey() ab_IntMap_mEqualKey mIntMap_Equal; // default ab_Env_EqualKey() ab_IntMap_mAssocString mIntMap_AssocString; // default ab_Env_AssocString() ab_bool mIntMap_HasValuesToo; // use mIntMap_ValueInts to store values friend class ab_IntMapIter; // let ab_IntMapIter access member variables // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseIntMap() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_IntMap(); // ````` ````` ````` ````` ````` ````` ````` ````` private: // private non-poly ab_IntMap helper methods void grow_int_map(ab_Env* ev); // increase capacity by some percentage ab_bool init_with_size(ab_Env* ev, const ab_KeyMethods* inKeyMethods, ab_num inSizeHint); // ````` ````` bucket location ````` ````` ab_u4 hash_bucket(ab_Env* ev, ab_map_int inKey) const { return ( (*mIntMap_Hash)(ev, inKey) % mIntMap_Capacity ); } ab_map_int* find_bucket(ab_Env* ev, ab_map_int inKey) const; // ````` ````` bucket location ````` ````` ab_bool equal_key(ab_Env* ev, ab_map_int inTest, ab_map_int inMem) const { return (*mIntMap_Equal)(ev, inTest, inMem); } // ````` ````` adding with less error detection ````` ````` void fast_add(ab_Env* ev, ab_map_int inKey, ab_map_int inValue); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_IntMap methods ab_IntMap(ab_Env* ev, const ab_Usage& inUsage, const ab_KeyMethods* inKeyMethods, ab_num inSizeHint); // inKeyMethods can be null to get the default suite for integers. // defaults to supporting values in addition to keys. inSizeHint // is capped between kMinSizeHin and kMaxSizeHint, and this becomes // the new threshold, with capacity equal to 25% more. ab_IntMap(ab_Env* ev, const ab_Usage& inUsage, const ab_KeyMethods* inKeyMethods, ab_num inSizeHint, ab_bool inValuesToo); // inKeyMethods can be null to get the default suite for integers. // map contains no values when inValuesToo is false, which // technically creates a simple set and not a map. inSizeHint // is capped between kMinSizeHin and kMaxSizeHint, and this becomes // the new threshold, with capacity equal to 25% more. ab_IntMap(ab_Env* ev, const ab_Usage& inUsage, ab_num inSizeHint, const ab_IntMap& ioOtherMap); // The new map contains values provided ioOtherMap does. inSizeHint is // capped between kMinSizeHin and kMaxSizeHint, but is then increased // to ioOtherMap.GetThreshold() is this is greater, and then used for // the new threshold, with capacity equal to 25% more. The content in // ioOtherMap is copied into this one. (This constructor is used when // a map is grown with grow_int_map(), which makes a larger local map // before replacing the old slots when this is successful.) void GetKeyMethods(ab_KeyMethods* outMethods) { outMethods->mKeyMethods_Hash = mIntMap_Hash; outMethods->mKeyMethods_Equal = mIntMap_Equal; outMethods->mKeyMethods_AssocString = mIntMap_AssocString; } void CloseIntMap(ab_Env* ev); // ````` ````` accessors ````` ````` ab_num GetChangeCount() const { return mIntMap_ChangeCount; } ab_num GetThreshold() const { return mIntMap_Threshold; } ab_num GetCapacity() const { return mIntMap_Capacity; } ab_count GetAssocCount() const { return mIntMap_AssocCount; } ab_bool HasValuesToo() const { return mIntMap_HasValuesToo; } // ````` ````` entire table methods ````` ````` ab_bool AddTable(ab_Env* ev, const ab_IntMap& ioOtherMap); // add contents of ioOtherMap, and then return ev->Good(). // ````` ````` uid assoc methods ````` ````` void AddAssoc(ab_Env* ev, ab_map_int inKey, ab_map_int inValue); // add one new association (if an association already exists with // the same key, then the value is simply updated to the new value). ab_map_int GetValueForKey(ab_Env* ev, ab_map_int inKey); // search for one association keyed by inKey, and return the value. // Since values cannot be zero, zero is returned to mean "not found". void CutAllAssocs(ab_Env* ev); // remove all associations // ````` ````` uid key set only methods (with no values used) ````` ````` void AddKey(ab_Env* ev, ab_map_int inKey) { this->AddAssoc(ev, inKey, ab_IntMap_kPretendValue); } ab_bool ContainsKey(ab_Env* ev, ab_map_int inKey) { return (this->GetValueForKey(ev, inKey) != 0); } }; enum ab_Map_eError { ab_Map_kFaultZeroKey = /*i*/ AB_Fault_kMap, // zero key passed ab_Map_kFaultOverThreashold, // map not big enough ab_Map_kFaultNoBucketFound, // search for bucket failed ab_Map_kFaultIterPosInvalid, // iter position invalid ab_Map_kFaultIterEmptyBucket, // current iter bucket empty ab_Map_kFaultMissingVector // bad hash table structure }; class ab_IntMapIter /*d*/ { // this iter derives from public domain IronDoc and Mithril hash tables // ````` ````` ````` ````` ````` ````` ````` ````` protected: const ab_IntMap* mIntMapIter_Map; // copy of mIntMap_ChangeCount ab_pos mIntMapIter_BucketPos; // current bucket in iteration ab_num mIntMapIter_ChangeCount; // copy of mIntMap_ChangeCount // ab_ObjectSet_kFaultIterOutOfSync when change count does not match // ````` ````` ````` ````` ````` ````` ````` ````` private: // private non-poly ab_IntMapIter helper methods ab_map_int next_assoc(ab_pos inPos, ab_map_int* outValue); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_IntMapIter methods ab_IntMapIter(ab_Env* ev, const ab_IntMap* ioIntMap); // the ioIntMap object is *not* reference counted, because this iter is // expected to be only short-lived inside a single method. Callers must // use First() to start an iteration. // the following methods return keys, which is zero when iter is finished ab_map_int First(ab_Env* ev, ab_map_int* outValue); ab_map_int Here(ab_Env* ev, ab_map_int* outValue); ab_map_int Next(ab_Env* ev, ab_map_int* outValue); // query whether ab_ObjectSet_kFaultIterOutOfSync error will not occur: ab_bool IsInSyncWithMap() const { return mIntMapIter_ChangeCount == mIntMapIter_Map->GetChangeCount(); } // ````` ````` ````` ````` ````` ````` ````` ````` // these operators should be private but Irix has problems compiling this, so // we only make it private on the Mac, and this catches illegal object deletes. #if defined(XP_MAC) // ````` ````` ````` ````` ````` ````` ````` ````` private: // private heap methods: heap instantiation is *NOT ALLOWED*: void* operator new(size_t inSize); /* priv on Mac (actually always) */ void operator delete(void* ioAddress); /* priv on Mac (actually always) */ #endif /*XP_MAC*/ }; /*============================================================================= * ab_Thumb: represents entry counts, positions, and progress in import/export */ #define ab_Thumb_kAverageRowSize 512 /* assume half K for ldif record */ #define ab_Thumb_kMinSteamBufSize (4 * 1024) /* no point reading less */ #define ab_Thumb_kNormalSteamBufSize (16 * 1024) /* > disk block size */ #define ab_Thumb_kMaxSteamBufSize (48 * 1024) /* < 64K */ #define ab_Thumb_kMaxRealPass 2 /* no more than two passes through input */ class ab_Thumb /*d*/ : public ab_Object { // ````` ````` ````` ````` ````` ````` ````` ````` protected: // member variables with controlled access ab_IntMap* mThumb_ListPosMap; // map of list uid to list file pos ab_policy mThumb_ImportConflictPolicy; // start at 0 char* mThumb_ConflictReportFileName; // initially null ab_StdioFile* mThumb_ReportFile; void determine_import_conflict_policy(ab_Env* ev); // ````` ````` ````` ````` ````` ````` ````` ````` public: // member variables with free access ab_pos mThumb_FilePos; // where to position file to continue import ab_row_pos mThumb_RowPos; // cumulative total number of rows ported ab_row_count mThumb_RowCountLimit; // max rows to import before stopping ab_num mThumb_ByteCountLimit; // max file bytes read before stopping /* ````` used to capture progress limit information ````` */ ab_pos mThumb_ImportFileLength; // total file bytes to be imported ab_row_count mThumb_ExportTotalRowCount; // total rows to be exported /* ````` slots reserved for import and export code ````` */ ab_count mThumb_CurrentPass; // pass through import file, in [0..3] ab_u4 mThumb_FileFormat; // ab_File_eFormat (import format used) ab_num mThumb_ListCountInFirstPass; // number lists seen 1st pass ab_num mThumb_ListCountInSecondPass; // number lists done 2nd pass ab_num mThumb_PortingCallCount; // number of times thumb reused ab_bool mThumb_HavePreparedSecondPass; // reset file to beginning? // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseThumb() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Thumb(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_Thumb methods ab_Thumb(ab_Env* ev, const ab_Usage& inUsage); // When IsProgressFinished() returns true, this tells clients // they should stop trying to import and export asynchronously, because // there is no more content to process and the task is entirely finished. ab_bool IsProgressFinished() const { return ( mThumb_CurrentPass > ab_Thumb_kMaxRealPass ); } void SetCompletelyFinished() { mThumb_CurrentPass = ab_Thumb_kMaxRealPass + 1; } ab_count GetMappedListUidCount() const { return (mThumb_ListPosMap)? mThumb_ListPosMap->GetAssocCount(): 0; } ab_IntMap* GetListPosMap(ab_Env* ev); // Get the hash table that maps list uids to file positions. // This method creates this object lazily on the first request, and // then keeps the map until the thumb is closed. If one wishes to // know whether there is any content without forcing this table to // exist, one can call GetMappedListUidCount() to get the number of // lists that have been mapped, which can avoid creating the map. ab_policy GetImportConflictPolicy(ab_Env* ev); const char* GetConflictReportFileName(ab_Env* ev); ab_StdioFile* GetReportFile(ab_Env* ev); ab_num ListsCountedInFirstPass() const { return mThumb_ListCountInFirstPass; } ab_num ListsPortedInSecondPass() const { return mThumb_ListCountInSecondPass; } ab_bool StillOnFirstPassThroughFile() const { return ( mThumb_CurrentPass < 2 ); } ab_bool OnSecondPassThroughFile() const { return ( mThumb_CurrentPass >= 2 ); } ab_bool FinishedSecondPassThroughFile() const { return ( mThumb_CurrentPass > 2 ); } ab_num PickHeuristicStreamBufferSize() const; // Typically returns ab_Thumb_kNormalSteamBufSize except when values // of either (or both) mThumb_RowCountLimit and mThumb_ByteCountLimit // imply substantially more or less buffer space might make more sense. // Bounded by kMin and kMax values defined near kNormalSteamBufSize. // For sizing purposes, average row size is assumed kAverageRowSize. void CloseThumb(ab_Env* ev); }; /* ===== ===== ===== ===== ab_Factory ===== ===== ===== ===== */ class ab_Factory { public: static ab_Store* // for database purposes MakeStore(ab_Env* ev, const char* inFileName, ab_num inFootprint); static ab_File* // for old format binary readonly import purposes MakeOldFile(ab_Env* ev, const char* inFileName); static ab_File* // for current format binary readonly import purposes MakeNewFile(ab_Env* ev, const char* inFileName); }; /*============================================================================= * ab_Store: abstract database interface */ #define ab_Store_kMinFootprint /*i*/ (48 * 1024) #define ab_Store_kMinFootprintGrowth /*i*/ (32 * 1024) #define ab_Store_kGoodFootprintSpace /*i*/ (512 * 1024) #define ab_Store_kGoodFootprintGrowth /*i*/ (1024 * 1024) #define ab_Store_kBigStartingFootprint /*i*/ (8 * 1024 * 1024) #define ab_Store_kBigFootprintGrowth /*i*/ (4 * 1024 * 1024) class ab_Store /*d*/ : public ab_Model { /* ````` database ````` */ protected: char* mStore_FileName; // allocated copy of constructor parameter ab_num mStore_TargetFootprint; // current suggested memory footprint ab_u4 mStore_ContentAccess; // ab_Object_kOpen or ab_Object_kShut ab_num mStore_NameSetSeed; // changed when name set changes ab_num mStore_ColumnSetSeed; // changed when column set changes ab_num mStore_RowSetSeed; // changed when row set changes ab_num mStore_RowContentSeed; // changed when row content changes // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Store methods virtual ab_Table* GetTopStoreTable(ab_Env* ev) = 0; virtual ab_Table* AcquireTopStoreTable(ab_Env* ev) = 0; virtual void OpenStoreContent(ab_Env* ev) = 0; virtual void SaveStoreContent(ab_Env* ev) = 0; virtual void CloseStoreContent(ab_Env* ev) = 0; virtual void DumpStoreContent(ab_Env* ev, ab_Printer* p, ab_num max) = 0; virtual ab_num StoreContentSize(ab_Env* ev) const = 0; virtual ab_num MoreFootprint(ab_Env* ev, ab_num inMoreSpace) = 0; virtual ab_num LessFootprint(ab_Env* ev, ab_num inLessSpace) = 0; virtual ab_num OptimalFootprint(ab_Env* ev) = 0; virtual void StartBatchMode(ab_Env* ev, ab_num inEventThreshold) = 0; virtual void EndBatchMode(ab_Env* ev) = 0; // specify import file format: virtual void ImportOldBinary(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb) = 0; virtual void ImportCurrentNative(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb) = 0; // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseStore() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Store(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // public non-poly ab_Store methods // ab_Store is abstract, so you cannot call this constructor directly: ab_Store(ab_Env* ev, const ab_Usage& inUsage, const char* inFileName, ab_num inTargetFootprint); // but you can call this method to see what subclass to instantiate: // static ab_Store* MakeStoreSubclassAfterSeeingFileContent(ab_Env* ev, // const char* inFileName, ab_num inTargetFootprint); // See new replacement: ab_Factory::MakeStore() for the above. static void DestroyStoreWithFileName(ab_Env* ev, const char* inFileName); // DestroyStoreWithFileName() deletes the file from the file system // containing the store that would be opened if a store instance was // created using inFileName for the file name. This method is provided // to keep file destruction equally as abstract as the implicit file // creation occuring when a new store is created. Otherwise it would // be difficult to discard databases as easily as they are created. But // this method is dangerous in the sense that destruction of data is // definitely intended, so don't call this method unless the total // destruction of data in the store is what you have in mind. This // method does not attempt to verify the file contains a database before // destroying the file with name inFileName in whatever directory is // used to host the database files. Make sure you name the right file. void CloseStore(ab_Env* ev); // CloseObject() ab_bool ChangeStoreFileName(ab_Env* ev, const char* inFileName); const char* GetStoreFileName(ab_Env* ev) const; ab_num GetTargetFootprint(ab_Env* ev) const; ab_bool IsOpenStoreContent() const { return mStore_ContentAccess == ab_Object_kOpen; } ab_bool IsShutStoreContent() const { return mStore_ContentAccess == ab_Object_kShut; } ab_bool IsReadyStore() const { return this->IsOpenObject() && mStore_ContentAccess == ab_Object_kOpen; } void BumpStoreNameSetSeed() { ++mStore_NameSetSeed; } ab_num GetStoreNameSetSeed() const { return mStore_NameSetSeed; } void BumpStoreColumnSetSeed() { ++mStore_ColumnSetSeed; } ab_num GetStoreColumnSetSeed() const { return mStore_ColumnSetSeed; } void BumpStoreRowSetSeed() { ++mStore_RowSetSeed; } ab_num GetStoreRowSetSeed() const { return mStore_RowSetSeed; } void BumpStoreRowContentSeed() { ++mStore_RowContentSeed; } ab_num GetStoreRowContentSeed() const { return mStore_RowContentSeed; } // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_Store port utilities using the store's top level table void ImportWithPromptForFileName(ab_Env* ev, MWContext* ioContext); void ExportWithPromptForFileName(ab_Env* ev, MWContext* ioContext); void ExportEntireStoreToNamedFile(ab_Env* ev, const char* inFileName); void ExportStoreToNamedFile(ab_Env* ev, const char* inFileName, ab_Thumb* ioThumb); // prompt for file name, and then guess the format: void ImportEntireFileByName(ab_Env* ev, const char* inFileName); // ImportEntireFileByName() just calls ImportFileByName() with a // thumb instance intended to convey "all the file content". // guess file format: void ImportFileByName(ab_Env* ev, const char* inFileName, ab_Thumb* ioThumb); // ImportFileByName() opens a file in the file system named inFileName. // This is similar to ImportContent(), but ImportContent() does not // assume the "file" is a file in the file system (instead, the ab_File // subclass might represent a string or some other stream). // guess file format: void ImportContent(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb); void ExportContent(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb); void ImportVCard(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb); void ImportLdif(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb); void ImportHtml(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb); void ImportXml(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb); void ImportTabDelimited(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb); // specify export file format: void ExportLdif(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb); void ExportXml(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb); void ExportTabDelimited(ab_Env* ev, ab_File* ioFile, ab_Thumb* ioThumb); // performance recommendations ab_num GoodImportFootprint(ab_Env* ev, ab_File* ioFile) const { return ioFile->Length(ev) / 2; } // half the file size ab_num BetterImportFootprint(ab_Env* ev, ab_File* ioFile) const { return ioFile->Length(ev); } // the entire file size // ````` ````` random import for large AB testing ````` ````` #ifdef AB_CONFIG_DEBUG static ab_num MakeImportRandomSeed(ab_Env* ev); // MakeImportRandomSeed makes a new, fresh random seed for import. static ab_num* UseSessionImportRandomSeed(ab_Env* ev); // UseSessionImportRandomSeed always returns a pointer to same seed // which gets initialized just once per session with a single call to // MakeImportRandomSeed() the first time. void ImportRandomEntries(ab_Env* ev, ab_num inCount, ab_num* ioRandomSeed); // The random seed passed in ioRandomSeed is used to drive the content // of random entries added to the store, and a new seed representing the // continuation of this process is returned in ioRandomSeed. This lets // multiple calls to ImportRandomEntries share the same random number // sequence to ensure duplicate random entries are not possible. a good // strategy is to call MakeImportRandomSeed() once and save the result // in a global which is passed in all ImportRandomEntries() calls. (We // could do this internally, but we want to pass in the seed so the // process is deterministically repeatable if necessary.) // // A main reason to make many calls to ImportRandomEntries() with small // values for inCount (instead of one huge value for inCount) is to make // the system stay responsive, because ImportRandomEntries() might lock // up the current thread until all the inCount entries are added. // // For small memory footprints, and for dbs containing E entries, when // C new entries are added, elapsed time can be M minutes. M can be as // bad as M = (C/1000)*5 + (E/1000)*5, or as good as M = (C/10000), but // the good time probably requires footprint exceeding added entries. // (The "bad" equation translates to "five minutes for each new thousand // added plus five minutes for each thousand in the db", and the "good" // equation says "one minute per ten thousand added.") At the time of // this writing, expected time is about M = (C/1000)*2 + (E/1000)*2, // or two minutes per thousand added plus two minutes per db thousand. // // inCount is capped at a maximum of ten thousand to save your sanity. #endif /*AB_CONFIG_DEBUG*/ private: // copying is not allowed ab_Store(const ab_Store& other); ab_Store& operator=(const ab_Store& other); }; enum { ab_Store_kFaultMissingDatabase = /*i*/ AB_Fault_kStore, /* 900 */ ab_Store_kFaultMissingFilename, ab_Store_kFaultEmptyFilename, ab_Store_kFaultNotOpen, ab_Store_kFaultContentNotOpen, ab_Store_kFaultContentAlreadyOpen, ab_Store_kFaultNullCachedStore, ab_Store_kFaultNullCachedDatabase, ab_Store_kFaultExistingDatabase, ab_Store_kFaultBadDatabaseCreate, ab_Store_kFaultBadDatabaseOpen, ab_Store_kFaultBadDatabaseClose, ab_Store_kFaultCommitException, ab_Store_kFaultDbMetaClassThrow, ab_Store_kFaultOldDbFileFormat, ab_Store_kFaultUnknownImportFormat, ab_Store_kFaultNullStoreForImport, ab_Store_kFaultNotOpenStoreForImport, ab_Store_kFaultNullEnvForImport, ab_Store_kFaultNotOpenEnvForImport, ab_Store_kFaultUnknownStoreFormat, ab_Store_kFaultStaleStoreFormat, ab_Store_kFaultFutureStoreFormat, ab_Store_kFaultImportFormatOnly, ab_Store_kFaultNullClosureForImport, ab_Store_kFaultNotOldBinaryFormat, ab_Store_kFaultNotCurrentNativeFormat }; /* ===== ===== ===== ===== ab_Part (2) ===== ===== ===== ===== */ inline ab_bool ab_Part::PartNameSetSeedMatchesStore() const /*i*/ { return mPart_NameSetSeed == mPart_Store->GetStoreNameSetSeed(); } inline ab_bool ab_Part::PartColumnSetSeedMatchesStore() const /*i*/ { return mPart_ColumnSetSeed == mPart_Store->GetStoreColumnSetSeed(); } inline ab_bool ab_Part::PartRowSetSeedMatchesStore() const /*i*/ { return mPart_RowSetSeed == mPart_Store->GetStoreRowSetSeed(); } inline ab_bool ab_Part::PartRowContentSeedMatchesStore() const /*i*/ { return mPart_RowContentSeed == mPart_Store->GetStoreRowContentSeed(); } /*============================================================================= * ab_Defaults: */ class ab_Defaults /*d*/ : public ab_Part { /* ````` row and cell generator ````` */ // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Defaults methods virtual ab_Row* MakeDefaultRow(ab_Env* ev, ab_Table* ioTable); virtual ab_Row* MakeDefaultCellRow(ab_Env* ev, ab_Table* ioTable, const AB_Cell* inCells); virtual ab_Row* MakeDefaultColumnRow(ab_Env* ev, ab_Table* ioTable, const AB_Column* inColumns); virtual ab_cell_count GetDefaultPersonCells(ab_Env* ev, AB_Cell* outCells, ab_cell_count inSize, ab_cell_count* outLength); virtual ab_cell_count GetDefaultListCells(ab_Env* ev, AB_Cell* outCells, ab_cell_count inSize, ab_cell_count* outLength); virtual ab_column_count GetDefaultColumns(ab_Env* ev, AB_Column* outColumns, ab_column_count inSize, ab_column_count* outLength); // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseDefaults() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Defaults(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_Defaults methods ab_Defaults(ab_Env* ev, const ab_Usage& inUsage, ab_Store* ioStore); void CloseDefaults(ab_Env* ev); // CloseObject() private: // copying is not allowed ab_Defaults(const ab_Defaults& other); ab_Defaults& operator=(const ab_Defaults& other); }; /*============================================================================= * ab_NameSet: */ class ab_NameSet /*d*/ : public ab_Part { /* ````` token collection ````` */ // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseNameSet() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_NameSet(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_NameSet methods // default methods understand only standard column names virtual const char* GetName(ab_Env* ev, ab_column_uid token) const; virtual ab_column_uid GetToken(ab_Env* ev, const char* name) const; virtual ab_column_uid NewToken(ab_Env* ev, const char* name); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_NameSet methods ab_NameSet(ab_Env* ev, const ab_Usage& inUsage, ab_Store* ioStore); void CloseNameSet(ab_Env* ev); // CloseObject() private: // copying is not allowed ab_NameSet(const ab_NameSet& other); ab_NameSet& operator=(const ab_NameSet& other); }; /*============================================================================= * ab_Table: */ class ab_Table /*d*/ : public ab_Model { /* ````` table ````` */ protected: AB_Table_eType mTable_Type; ab_TableStoreView* mTable_View; // store to table notification transmuter ab_Defaults* mTable_Defaults; ab_NameSet* mTable_NameSet; ab_ColumnSet* mTable_ColumnSet; ab_RowSet* mTable_RowSet; ab_RowContent* mTable_RowContent; ab_bool mTable_HasOwnColumnSet; // ````` ````` ````` ````` ````` ````` ````` ````` protected: // virtual ab_Table responses to ab_TableStoreView messages friend class ab_TableStoreView; // ab_TableStoreView calls these methods // ````` ab_View style interface mimics multiple inheritance ````` virtual void TableSeesBeginStoreFlux(ab_Env* ev); virtual void TableSeesEndStoreFlux(ab_Env* ev); virtual void TableSeesChangedStore(ab_Env* ev, const ab_Change* inChange); virtual void TableSeesClosingStore(ab_Env* ev, const ab_Change* inChange); // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseTable() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Table(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_Table methods ab_Table(ab_Env* ev, const ab_Usage& inUsage, ab_row_uid inRowUid, ab_Store* ioStore, AB_Table_eType inType); ab_Table(ab_Env* ev, const ab_Table& other, ab_RowSet* ioRowSet, ab_row_uid inRowUid, AB_Table_eType inType); ab_bool TableHasAllSlots(ab_Env* ev) const; ab_row_uid FindRowWithDistName(ab_Env* ev, const char* inDistName); // cover for ab_RowContent method with the same name static ab_Table* MakeDefaultNameColumnTable(ab_Env* ev, ab_row_uid inRowUid, ab_Store* ioStore, AB_Table_eType inType); const AB_Table* AsConstSelf() const { return (const AB_Table*) this; } AB_Table* AsSelf() { return (AB_Table*) this; } static ab_Table* AsThis(AB_Table* self) { return (ab_Table*) self; } static const ab_Table* AsConstThis(const AB_Table* self) { return (const ab_Table*) self; } void CloseTable(ab_Env* ev); // CloseObject() /*AB_Table* MakeSimilarTable(ab_Env* ev, ab_row_uid inRowUid, AB_Table_eType inType);*/ const char* GetTableTypeAsString() const; // e.g. "list", etc. const char* GetColumnName(ab_Env* ev, ab_column_uid inColUid) const; AB_Table_eType TableType() const { return mTable_Type; } ab_bool TableHasOwnColumnSet() const { return mTable_HasOwnColumnSet; } ab_TableStoreView* TableView() const { return mTable_View; } ab_Defaults* TableDefaults() const { return mTable_Defaults; } ab_NameSet* TableNameSet() const { return mTable_NameSet; } ab_ColumnSet* TableColumnSet() const { return mTable_ColumnSet; } ab_RowSet* TableRowSet() const { return mTable_RowSet; } ab_RowContent* TableRowContent() const { return mTable_RowContent; } ab_bool SetTableDefaults(ab_Env* ev, ab_Defaults* ioDefaults); ab_bool SetTableNameSet(ab_Env* ev, ab_NameSet* ioNameSet); ab_bool SetTableColumnSet(ab_Env* ev, ab_ColumnSet* ioColumnSet); ab_bool SetTableRowSet(ab_Env* ev, ab_RowSet* ioRowSet); ab_bool SetTableRowContent(ab_Env* ev, ab_RowContent* ioRowContent); // ````` ````` ````` ````` ````` ````` ````` ````` private: // general copying is not allowed ab_Table(const ab_Table& other); ab_Table& operator=(const ab_Table& other); }; /*============================================================================= * ab_ColumnSet: */ class ab_ColumnSet /*d*/ : public ab_Part { /* ````` column collection ````` */ // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_ColumnSet methods // all default implementations just raise error ab_Env_kFaultMethodStubOnly virtual ab_column_pos AddColumn(ab_Env* ev, const AB_Column* col, ab_column_pos pos, ab_bool changeIndex); virtual ab_bool PutColumn(ab_Env* ev, const AB_Column* col, ab_column_pos pos, ab_bool changeIndex); virtual ab_bool GetColumn(ab_Env* ev, AB_Column* col, ab_column_pos pos); virtual ab_column_pos CutColumn(ab_Env* ev, ab_column_uid inColUid, ab_bool inDoChangeIndex); virtual ab_column_pos FindColumn(ab_Env* ev, const AB_Column* col); virtual ab_column_count GetAllColumns(ab_Env* ev, AB_Column* outVector, ab_column_count inSize, ab_column_count* outLength) const; virtual ab_column_count PutAllColumns(ab_Env* ev, const AB_Column* inVector, ab_bool changeIndex); virtual ab_column_count CountColumns(ab_Env* ev) const; // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseColumnSet() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_ColumnSet(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_ColumnSet methods ab_ColumnSet(ab_Env* ev, const ab_Usage& inUsage, ab_Store* ioStore); void CloseColumnSet(ab_Env* ev); // CloseObject() private: // copying is not allowed ab_ColumnSet(const ab_ColumnSet& other); ab_ColumnSet& operator=(const ab_ColumnSet& other); }; /*============================================================================= * ab_RowContent: */ class ab_RowContent /*d*/ : public ab_Part { /* ````` row attributes ````` */ // ````` ````` ````` ````` ````` ````` ````` ````` protected: ab_Table* mRowContent_Table; // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_RowContent methods virtual ab_row_uid FindRowWithDistName(ab_Env* ev, const char* inDistName) = 0; virtual ab_row_uid FindRow(ab_Env* ev, const ab_Row* inRow) = 0; /* find */ virtual ab_row_uid NewRow(ab_Env* ev, const ab_Row* inRow, ab_row_pos inRowPos) = 0; virtual ab_bool ZapRow(ab_Env* ev, ab_row_uid inRowUid) = 0; /* destroy */ virtual ab_row_uid CopyRow(ab_Env* ev, ab_RowContent* ioFromOther, ab_row_uid inRowUid) = 0; // make a new row which is a copy of the row inRowUid inside ioFromOther. virtual ab_bool GetAllCells(ab_Env* ev, ab_Row* outRow, ab_row_uid inRowUid, ab_cell_size inMaxCellSize) = 0; virtual ab_bool GetCells(ab_Env* ev, ab_Row* outRow, ab_row_uid inRowUid) = 0; virtual ab_bool PutAllCells(ab_Env* ev, const ab_Row* inRow, ab_row_uid inRowUid) = 0; virtual ab_bool PutCells(ab_Env* ev, const ab_Row* inRow, ab_row_uid inRowUid) = 0; virtual ab_cell_count CountRowCells(ab_Env* ev, ab_row_uid inRowUid) const = 0; virtual ab_row_count CountRowChildren(ab_Env* ev, ab_row_uid inRowUid) const = 0; virtual ab_RowSet* AcquireRowChildren(ab_Env* ev, ab_row_uid inRowUid) = 0; //virtual ab_RowSet* AcquireRowListChildren(ab_Env* ev, ab_row_uid inRowUid) = 0; virtual ab_row_count CountRowParents(ab_Env* ev, ab_row_uid inRowUid) const = 0; virtual ab_RowSet* AcquireRowParents(ab_Env* ev, ab_row_uid inRowUid) = 0; // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods // virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; // virtual void CloseObject(ab_Env* ev); // CloseRowContent() // virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_RowContent(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_RowContentmethods ab_RowContent(ab_Env* ev, const ab_Usage& inUsage, ab_Store* ioStore, ab_Table* ioTable); void CloseRowContent(ab_Env* ev); ab_bool SetRowContentTable(ab_Env* ev, ab_Table* ioTable); ab_Table* GetRowContentTable() const { return mRowContent_Table; } ab_row_pos FindRowInTableRowSet(ab_Env* ev, ab_row_uid inRowUid); // calls FindRow() on mRowContent_Table->mTable_RowSet private: // copying is not allowed ab_RowContent(const ab_RowContent& other); ab_RowContent& operator=(const ab_RowContent& other); }; enum { ab_RowContent_kFaultBadMemberCut = /*i*/ AB_Fault_kRowContent, /* 925 */ ab_RowContent_kFaultNoTable, ab_RowContent_kFaultNullTable }; /*============================================================================= * ab_RowSet: */ class ab_RowSet /*d*/ : public ab_Part { /* ````` row collection ````` */ // ````` ````` ````` ````` ````` ````` ````` ````` protected: ab_Table* mRowSet_Table; ab_column_uid mRowSet_SortColUid; ab_db_uid mRowSet_SortDbUid; ab_bool mRowSet_SortForward; // sort in ascending order // ````` ````` ````` ````` ````` ````` ````` ````` public: // protected by convention void ChangeRowSetSortColumnAndDbUid(ab_column_uid col, ab_db_uid dbUid) { mRowSet_SortColUid = col; mRowSet_SortDbUid = dbUid; } // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_RowSet methods // called by ab_Table::TableSeesChangedStore(): virtual void RowSetSeesChangedStore(ab_Env* ev, const ab_Change* inChange); // this default version notifies a scramble through mRowSet_Table // AddAllRows() has a default implementation based on Pick() and Add() virtual ab_row_count AddAllRows(ab_Env* ev, const ab_RowSet* inRowSet); // ChangeRowSetSortForward() has default impl setting mRowSet_SortForward: virtual void ChangeRowSetSortForward(ab_Env* ev, ab_bool inForward); // FindRowByPrefix() has default impl raising not-implemented error: virtual ab_row_uid FindRowByPrefix(ab_Env* ev, const char* inPrefix, const ab_column_uid inCol); // all the other row set methods are abstract and must be overridden virtual ab_row_pos AddRow(ab_Env* ev, ab_row_uid inRow, ab_row_pos inPos) = 0; virtual ab_row_uid GetRow(ab_Env* ev, ab_row_pos inPos) = 0; virtual ab_row_pos FindRow(ab_Env* ev, ab_row_uid inRowUid) = 0; virtual ab_row_count CountRows(ab_Env* ev) = 0; virtual ab_row_count PickRows(ab_Env* ev, ab_row_uid* outRows, ab_row_count inSize, ab_row_pos inPos) = 0; virtual ab_row_pos CutRow(ab_Env* ev, ab_row_uid inRowUid) = 0; virtual ab_row_count CutRowRange(ab_Env* ev, ab_row_pos inPos, ab_row_count inCount) = 0; virtual ab_RowSet* AcquireListSubset(ab_Env* ev) = 0; virtual ab_RowSet* AcquireSearchSubset(ab_Env* ev, const char* inValue, const ab_column_uid* inColumnVector) = 0; virtual ab_bool CanSortRows(ab_Env* ev, ab_column_uid inCol) const = 0; virtual ab_bool SortRows(ab_Env* ev, ab_column_uid inSortCol) = 0; virtual ab_column_uid GetRowSortOrder(ab_Env* ev) = 0; virtual ab_RowSet* NewSortClone(ab_Env* ev, ab_column_uid inSortCol) = 0; // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods // virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; // virtual void CloseObject(ab_Env* ev); // CloseRowSet() // virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_RowSet(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_RowSet methods ab_RowSet(ab_Env* ev, const ab_Usage& inUsage, ab_row_uid inRowUid, ab_Store* ioStore, ab_Table* ioTable); void CloseRowSet(ab_Env* ev); // void UseSessionSortColumnDefault(ab_Env* ev); ab_bool SetRowSetTable(ab_Env* ev, ab_Table* ioTable); ab_Table* GetRowSetTable() const { return mRowSet_Table; } ab_column_uid GetRowSetSortColumn() const { return mRowSet_SortColUid; } ab_db_uid GetRowSetSortDbUid() const { return mRowSet_SortDbUid; } ab_bool GetRowSetSortForward() const { return mRowSet_SortForward; } void NotifyRowSetScramble(ab_Env* ev) const; private: // copying is not allowed ab_RowSet(const ab_RowSet& other); ab_RowSet& operator=(const ab_RowSet& other); }; enum { ab_RowSet_kFaultBadMemberCut = /*i*/ AB_Fault_kRowSet, /* 675 */ ab_RowSet_kFaultBadMemberAdd, ab_RowSet_kFaultCannotSearchRows, ab_RowSet_kFaultNotSortableColumn, ab_RowSet_kFaultUnsorted, ab_RowSet_kFaultNoSearch, ab_RowSet_kFaultNullSearch, ab_RowSet_kFaultNullSearchString, ab_RowSet_kFaultNoParent, ab_RowSet_kFaultNullParent, ab_RowSet_kFaultNoTable, ab_RowSet_kFaultNullTable, ab_RowSet_kFaultNotOpen, ab_RowSet_kFaultCannotEditRows, ab_RowSet_kFaultCannotFindListRows, ab_RowSet_kFaultNoArrayPosFound, ab_RowSet_kFaultForwardBackwardOrder, ab_RowSet_kFaultCannotSortRows }; /*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/ /* ===== ===== ===== ===== ab_Session ===== ===== ===== ===== */ /* There should typically be only one ab_Session instance in use, which is ** the one returned from ab_Session::GetGlobalSession(). */ #define ab_Session_kTag /*i*/ 0x7345734E /* ascii 'sEsN' (no endian) */ class ab_Session /*d*/ { protected: ab_u4 mSession_Tag; /* equals ab_Session_kTag */ AB_Table* mSession_GlobalTable; /* table for AB_GetGlobalTable() */ ab_uid mSession_TempRowSeed; ab_uid mSession_TempChangeSeed; ab_column_uid mSession_DefaultSortColumn; // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_Session methods ab_Session(); // does nothing // initialization is separated from construction so platforms // with poor static construction support will not be a problem. void InitSession(); // actually constructs the session ab_bool GoodTag() const { return mSession_Tag == ab_Session_kTag; } AB_Table* GlobalTable() const { return mSession_GlobalTable; } ab_change_uid NewTempChangeUid() { return ++mSession_TempChangeSeed; } ab_row_uid NewTempRowUid() { return AB_UidSeed_AsTempRowUid(++mSession_TempRowSeed); } static ab_Session* GetGlobalSession(); // obviously default sort cols must be standard (and not store specific) ab_column_uid DefaultSortColumn() const { return mSession_DefaultSortColumn ; } void SetDefaultSortColumn(ab_Env* ev, ab_column_uid newSortCol); }; /*============================================================================= * AB_Cell: */ /* `````` `````` protos `````` `````` */ AB_BEGIN_C_PROTOS /* `````` `````` protos `````` `````` */ /*| Init: initialize and construct the cell so sCell_Size is at **| least inMinSize bytes in size, and contains the content pointed **| to by inStartingContent when inStartingContent is non-null. **| If inStartingContent is null, then zero the initial content. **| The inColumnUid arg cannot be zero or an error will occur; in **| fact, inColumnUid must return true from AB_Uid_IsColumn(). **| **|| If strlen(inStartingContent)+1 is greater than inMinSize, then **| make a cell that size instead (the plus one is for a null byte). If **| both inMinSize and the inStartingContent pointer are zero, allocate **| at least a byte to avoid weird edge cases. **| True is returned if and only if the environment shows no errors. |*/ extern ab_bool /* abcell.c */ AB_Cell_Init(AB_Cell* self, ab_Env* ev, ab_column_uid inColumnUid, ab_cell_size inMinSize, const char* inStartingContent); /*| Finalize: deallocate sCell_Content and zero all slots. **| True is returned if and only if the environment shows no errors. |*/ extern ab_bool /* abcell.c */ AB_Cell_Finalize(AB_Cell* self, ab_Env* ev); /*| Grow: increase the cell size sCell_Size to at least inMinSize **| and reallocate the sCell_Content space if necessary. But if **| sCell_Size is already that big, then do nothing. **| True is returned if and only if the environment shows no errors. |*/ extern ab_bool /* abcell.c */ AB_Cell_Grow(AB_Cell* self, ab_Env* ev, ab_cell_size inMinSize); /*| Become: duplicate all slots in other exactly, except the sCell_Content **| pointer must remain different, of course. This means sCell_Content **| almost certainly is reallocated when sCell_Capacity is different. **| Afterwards self has the same structure and content as other. **| **|| This self cell need not start in an initialized state -- it is okay if **| the cell is finalized, with all slots equal to zero. In particular, if **| the content slot is null this causes no problem and no attempt is made **| to deallocate the null pointer. **| (This method is intended to support AB_Row_BecomeRowClone().) **| True is returned if and only if the environment shows no errors. |*/ #if 0 extern ab_bool /* abcell.c */ AB_Cell_Become(AB_Cell* self, ab_Env* ev, const AB_Cell* other); #endif /*| Copy: make this cell contain the same content as other when capacity **| is greater than other's content. Otherwise make self hold as much of **| other's content as will fit without making self any larger. (This **| method is intended to support AB_Tuple_CopyRowContent().) **| True is returned if and only if the environment shows no errors. |*/ extern ab_bool /* abcell.c */ AB_Cell_Copy(AB_Cell* self, ab_Env* ev, const AB_Cell* other); extern void /* abrow.c */ AB_Cell_Print(const AB_Cell* self, ab_Env* ev, ab_Printer* ioPrinter, const char* inName); #define AB_Cell_kXmlBufSize /*i*/ (256+64) /* size for AB_Cell_AsString() */ #if AB_CONFIG_TRACE_orDEBUG_orPRINT extern char* /* abcell.c */ AB_Cell_AsString(const AB_Cell* self, ab_Env* ev, char* outBuf); #endif /*AB_CONFIG_TRACE_orDEBUG_orPRINT*/ #ifdef AB_CONFIG_TRACE extern void /* abcell.c */ AB_Cell_Trace(const AB_Cell* self, ab_Env* ev); #endif /*AB_CONFIG_TRACE*/ #ifdef AB_CONFIG_DEBUG extern void /* abcell.c */ AB_Cell_Break(const AB_Cell* self, ab_Env* ev); #endif /*AB_CONFIG_DEBUG*/ extern void /* abcell.c */ AB_Cell_TraceAndBreak(const AB_Cell* self, ab_Env* ev); extern short /* abcell.c */ AB_Cell_AsShort(const AB_Cell* self, ab_Env* ev); extern ab_bool /* abcell.c */ AB_Cell_WriteHexLong(AB_Cell* self, ab_Env* ev, long inLong); extern ab_bool /* abcell.c */ AB_Cell_WriteShort(AB_Cell* self, ab_Env* ev, short inShort); extern ab_bool /* abcell.c */ AB_Cell_WriteBool(AB_Cell* self, ab_Env* ev, ab_bool inBool); #define AB_Cell_AsBool(self) /*i*/ (*(self)->sCell_Content == 't') /* `````` `````` protos `````` `````` */ AB_END_C_PROTOS /* `````` `````` protos `````` `````` */ /*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/ /* ===== ===== ===== ===== ab_String ===== ===== ===== ===== */ #define ab_String_kMaxCapacity (48 * 1024) class ab_String /*d*/ : public ab_Object { // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected member variables char* mString_Content; // always non-null, even when closed ab_num mString_Capacity; // number of bytes in string space ab_num mString_Length; // non-null byte count before null terminator ab_bool mString_IsHeapBased; // must free when closed or content changes ab_bool mString_IsReadOnly; // cannot modify string // IsHeapBased and IsReadOnly are typically just opposites, but we have // both in case the evolving implementation needs to distinguish these // conditions independently. // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseString() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_String(); // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected non-poly ab_String methods ab_bool cut_string_content(ab_Env* ev); ab_bool grow_string_content(ab_Env* ev, ab_num inNewCapacity); #ifdef AB_CONFIG_DEBUG void fit_string_content(ab_Env* ev); #endif /*AB_CONFIG_DEBUG*/ // ````` ````` ````` ````` ````` ````` ````` ````` public: // public non-poly ab_String methods ab_String(ab_Env* ev, const ab_Usage& inUsage, const char* inCopiedString); // Make a heap based copy of inCopiedString, unless inCopiedString is // a nil pointer. If inCopiedString is a nil pointer, then // mString_Content gets an empty static string, mString_IsReadOnly // gets true, and mString_IsHeapBased gets false. To construct a new // string that uses a static string, the best approach is to pass a nil // pointer for inCopiedString, and then call TakeStaticStringContent(). ab_bool TakeStaticStringContent(ab_Env* ev, const char* inStaticString); // Use this readonly, non-heap string instead for mString_Content. // Of course this involves casting away const. Note the content // string should never be written other than using supplied methods, // and writing a readonly string will involve first replacing the // readonly copy with a heap allocated mutable copy before changes. // The return value equals ev->Good(). void CloseString(ab_Env* ev); // CloseObject() // Nearly equivalent to calling TakeStaticStringContent(ev, ""); ab_bool TruncateStringLength(ab_Env* ev, ab_num inNewLength); // Make the string have length inNewLength if this is less than // the current length. The typical expected new length is zero. // The capacity of the string is *not* changed, and this is the // principle difference between TruncateStringLength() and cutting // content with CutStringContent() which might resize the string, // and especially does so for empty strings. TruncateStringLength() // is intended to be useful for rewriting strings from scratch without // performing any memory allocation that can be avoided. This lets a // string buffer reach a high water mark and stay stable for a while. // The string should not be a readonly static string if nonempty. // The return value equals ev->Good(). // ````` ````` sensible API features currently unused ````` ````` #ifdef AB_CONFIG_MAXIMIZE_FEATURES ab_bool SetStringContent(ab_Env* ev, const char* inString); // Make the new string's content equal to inString, with length // strlen(inString). If this fits inside the old capacity and the // string is mutable, then mString_Content is modified; otherwise // mString_Content is freed and a new heap-based string is allocated. // The return value equals ev->Good(). ab_bool PutStringContent(ab_Env* ev, const char* inString, ab_pos inPos); // Overwrite the string starting at offset inPos. An error occurs if // inPos is not a value from zero up to and including mString_Length. // When inPos equals the length, this operation appends to the string. // If the string is readonly or if the string must be longer to hold // the new length of the string (plus null terminator), then the // string is grown by replacing mString_Content with bigger space. // The return value equals ev->Good(). ab_bool CutStringContent(ab_Env* ev, ab_pos inPos, ab_num inCount); // Remove inCount bytes of content from the string starting at offset // inPos, where inPos must be a value from zero up to the length of // the string. inCount can be any value, since any excess simply // ensures that all content through the end of the string will be cut. // So CutStringContent(ev, 0, 0xFFFFFFFF) is roughly equivalent to // SetStringContent(ev, ""); // The return value equals ev->Good(). #endif /*AB_CONFIG_MAXIMIZE_FEATURES*/ ab_bool PutBlockContent(ab_Env* ev, const char* inBytes, ab_num inSize, ab_pos inPos); // PutBlockContent() is the same as PutStringContent() except that // inSize is used as the string length instead of finding any ending // null byte. This is very useful when it is inconvenient to put a // null byte into an input string (which might be readonly) in order // to end the string. While this interface allows the input string to // contain null bytes, this is *still a very bad idea* because ab_String // cannot correctly handle embedded null bytes. You've been warned. // // Overwrite the string starting at offset inPos. An error occurs if // inPos is not a value from zero up to and including mString_Length. // When inPos equals the length, this operation appends to the string. // If the string is readonly or if the string must be longer to hold // the new length of the string (plus null terminator), then the // string is grown by replacing mString_Content with bigger space. // The return value equals ev->Good(). ab_bool AddStringContent(ab_Env* ev, const char* inString, ab_pos inPos); // Insert new content starting at offset inPos. An error occurs if // inPos is not a value from zero up to and including mString_Length. // When inPos equals the length, this operation appends to the string. // If the string is readonly or if the string must be longer to hold // the new length of the string (plus null terminator), then the // string is grown by replacing mString_Content with bigger space. // The return value equals ev->Good(). ab_bool AppendStringContent(ab_Env* ev, const char* inString) { return this->AddStringContent(ev, inString, mString_Length); } // ````` ````` accessors ````` ````` const char* GetStringContent() const { return mString_Content; } ab_num GetStringCapacity() const { return mString_Capacity; } ab_num GetStringLength() const { return mString_Length; } ab_bool IsStringHeapBased() const { return mString_IsHeapBased; } ab_bool IsStringReadOnly() const { return mString_IsReadOnly; } ab_bool IsStringMutable() const { return !mString_IsReadOnly; } private: // copying is not allowed (mimicing a native type is not the intent) ab_String(const ab_String& other); ab_String& operator=(const ab_String& other); }; enum ab_String_eError { ab_String_kFaultLengthExceedsSize = /*i*/ AB_Fault_kString, ab_String_kFaultLengthOutOfSync, ab_String_kFaultOverMaxCapacity, ab_String_kFaultPosAfterLength, ab_String_kFaultBadCursorFields, // garbled slots in string related object ab_String_kFaultBadCursorOrder // bad pointer order in string cursor(s) }; /* ===== ===== ===== ===== ab_StringFile ===== ===== ===== ===== */ /*| ab_StringFile is a ab_File subclass based on ab_String. Note that **| ab_String is not suitable for containing null bytes, so this restricts **| the content that can be written to this file (don't write null bytes). |*/ class ab_StringFile /*d*/ : public ab_File { // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected ab_StringFile members ab_String* mStringFile_String; // the content of the "file" ab_pos mStringFile_Pos; // current file position // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_File methods virtual ab_pos Length(ab_Env* ev) const; virtual ab_pos Tell(ab_Env* ev) const; virtual ab_num Read(ab_Env* ev, void* outBuf, ab_num inSize); virtual ab_pos Seek(ab_Env* ev, ab_pos inPos); virtual ab_num Write(ab_Env* ev, const void* inBuf, ab_num inSize); virtual void Flush(ab_Env* ev); // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseStringFile() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_StringFile(); // ````` ````` ````` ````` ````` ````` ````` ````` public: // non-poly ab_StringFile methods ab_StringFile(ab_Env* ev, const ab_Usage& inUsage, ab_String* ioString); ab_StringFile(ab_Env* ev, const ab_Usage& inUsage, const char* inBytes); // create a readonly string based on inBytes. void CloseStringFile(ab_Env* ev); ab_String* GetStringFileString() const { return mStringFile_String; } // You need to acquire this string if you hold a long term reference. // In particular, this file releases the string when the file closes. // ````` ````` ````` ````` ````` ````` ````` ````` private: // copying is not allowed ab_StringFile(const ab_StringFile& other); ab_StringFile& operator=(const ab_StringFile& other); }; /* ===== ===== ===== ===== ab_StringSink ===== ===== ===== ===== */ /*| ab_StringSink is intended to be a very cheap buffered i/o sink **| which writes to a ab_String a single byte at a time. (Roughly the same **| functionality is also provided by subclassing ab_File using a **| ab_String instance to hold the "file" content, and then using an instance **| of ab_Stream to buffer the i/o written to the string.) **| **|| The ab_String passed to the constructor is *not* reference counted. (A **| more heavy-weight implementation would refcount this string.) Instances **| of ab_StringSink are intended to be suitable for creation on the stack. **| The implementation of ab_String assumes that null bytes are not written, **| so we don't bother to buffer any attempts to write null bytes. |*/ #define ab_StringSink_kBufSize 256 /* small enough to go on stack */ class ab_StringSink /*d*/ { protected: // member variables (put At first for possible speed improvement) ab_u1* mStringSink_At; // pointer into mStringSink_Buf ab_u1* mStringSink_End; // one byte past last content byte ab_String* mStringSink_String; // where all the bytes are written ab_u1 mStringSink_Buf[ ab_StringSink_kBufSize + 2 ]; // need at least plus one to hold terminating null byte // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected non-poly ab_StringSink methods (for char io) void sink_spill_putc(ab_Env* ev, int c); /* abstring.cpp */ // ````` ````` ````` ````` ````` ````` ````` ````` public: // public non-poly ab_StringSink methods ab_StringSink(ab_Env* ev, ab_String* ioString); /* abstring.cpp */ void FlushStringSink(ab_Env* ev); /* abstring.cpp */ void Putc(ab_Env* ev, int c) /*i*/ { if ( c && mStringSink_At < mStringSink_End ) *mStringSink_At++ = c; else this->sink_spill_putc(ev, c); } }; /* ===== ===== ===== ===== ab_Search ===== ===== ===== ===== */ #define ab_Search_kImmedColCount 8 #define ab_Search_kMaxCapacity 256 /* mainly for sanity checking */ class ab_Search /*d*/ : public ab_Part { // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected member variables ab_Row* mSearch_Row; // optional row for finding exact matches ab_u4 mSearch_Seed; // might be used for synchronization ab_column_count mSearch_Capacity; // size capacity of string & col arrays ab_column_count mSearch_ColumnCount; // number of used search columns ab_column_uid* mSearch_Columns; // might point to mSearch_ImmedCols ab_column_uid mSearch_ImmedCols[ ab_Search_kImmedColCount ]; ab_String** mSearch_Strings; // might point to mSearch_ImmedStrings ab_String* mSearch_ImmedStrings[ ab_Search_kImmedColCount ]; // ````` ````` ````` ````` ````` ````` ````` ````` public: // virtual ab_Object methods virtual char* ObjectAsString(ab_Env* ev, char* outXmlBuf) const; virtual void CloseObject(ab_Env* ev); // CloseSearch() virtual void PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const; virtual ~ab_Search(); // ````` ````` ````` ````` ````` ````` ````` ````` protected: // protected non-poly ab_Search methods void CutSearchStorageSpace(ab_Env* ev); ab_bool GrowSearchCapacity(ab_Env* ev, ab_column_count inNewCapacity); ab_bool InitCapacity(ab_Env* ev, ab_column_count inCapacity); // ````` ````` ````` ````` ````` ````` ````` ````` public: // public non-poly ab_Search methods ab_Search(ab_Env* ev, const ab_Usage& inUsage, ab_Store* ioStore, const char* inStringValue, const ab_column_uid* inColUidVector, ab_column_count inCapacity); // inColUidVector is null terminated, so use the number of non-null // uid's as instead of inCapacity if this number is larger (so a // zero value can be passed for inCapacity when desired). Use // the same ab_String made from inStringValue for all strings. ab_Search(ab_Env* ev, const ab_Usage& inUsage, ab_Store* ioStore, ab_column_count inCapacity); // expect to have inColHint columns added with AddSearchString() void CloseSearch(ab_Env* ev); // CloseObject() ab_bool CutAllSearchStrings(ab_Env* ev); // return value equals ev->Good() ab_bool AddSearchString(ab_Env* ev, ab_String* ioString, ab_column_uid inColUid); // return value equals ev->Good() // ````` ````` accessors ````` ````` void BumpSearchSeed() { ++mSearch_Seed; } ab_num GetSearchSeed() const { return mSearch_Seed; } ab_column_count GetSearchColumnCapacity() const { return mSearch_Capacity; } ab_column_count GetSearchColumnCount() const { return mSearch_ColumnCount; } ab_bool ChangeSearchRow(ab_Env* ev, ab_Row* ioRow); // note that passing in a null pointer for ioRow is allowed. ab_Row* GetSearchRow(ab_Env* ev) const; ab_String* GetStringAt(ab_Env* ev, ab_pos inPos, ab_column_uid* outColUid) const; // inPos is a zero-based index from zero to GetSearchColumnCount()-1. private: // copying is not allowed ab_Search(const ab_Search& other); ab_Search& operator=(const ab_Search& other); }; enum ab_Search_eError { ab_Search_kFaultNotOpen = /*i*/ AB_Fault_kSearch, ab_Search_kFaultOverMaxCapacity, ab_Search_kFaultMissingArray, ab_Search_kFaultUnsortedColumn }; #endif /* _ABMODEL_ */