Back to index

lightning-sunbird  0.9+nobinonly
morkWriter.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #ifndef _MDB_
00039 #include "mdb.h"
00040 #endif
00041 
00042 #ifndef _MORK_
00043 #include "mork.h"
00044 #endif
00045 
00046 #ifndef _MORKBLOB_
00047 #include "morkBlob.h"
00048 #endif
00049 
00050 #ifndef _MORKNODE_
00051 #include "morkNode.h"
00052 #endif
00053 
00054 #ifndef _MORKENV_
00055 #include "morkEnv.h"
00056 #endif
00057 
00058 #ifndef _MORKARRAY_
00059 #include "morkWriter.h"
00060 #endif
00061 
00062 // #ifndef _MORKFILE_
00063 // #include "morkFile.h"
00064 // #endif
00065 
00066 #ifndef _MORKSTREAM_
00067 #include "morkStream.h"
00068 #endif
00069 
00070 #ifndef _MORKSTORE_
00071 #include "morkStore.h"
00072 #endif
00073 
00074 #ifndef _MORKATOMSPACE_
00075 #include "morkAtomSpace.h"
00076 #endif
00077 
00078 #ifndef _MORKROWSPACE_
00079 #include "morkRowSpace.h"
00080 #endif
00081 
00082 #ifndef _MORKROWMAP_
00083 #include "morkRowMap.h"
00084 #endif
00085 
00086 #ifndef _MORKATOMMAP_
00087 #include "morkAtomMap.h"
00088 #endif
00089 
00090 #ifndef _MORKROW_
00091 #include "morkRow.h"
00092 #endif
00093 
00094 #ifndef _MORKTABLE_
00095 #include "morkTable.h"
00096 #endif
00097 
00098 #ifndef _MORKCELL_
00099 #include "morkCell.h"
00100 #endif
00101 
00102 #ifndef _MORKATOM_
00103 #include "morkAtom.h"
00104 #endif
00105 
00106 #ifndef _MORKCH_
00107 #include "morkCh.h"
00108 #endif
00109 
00110 //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
00111 
00112 // ````` ````` ````` ````` ````` 
00113 // { ===== begin morkNode interface =====
00114 
00115 /*public virtual*/ void
00116 morkWriter::CloseMorkNode(morkEnv* ev) // CloseTable() only if open
00117 {
00118   if ( this->IsOpenNode() )
00119   {
00120     this->MarkClosing();
00121     this->CloseWriter(ev);
00122     this->MarkShut();
00123   }
00124 }
00125 
00126 /*public virtual*/
00127 morkWriter::~morkWriter() // assert CloseTable() executed earlier
00128 {
00129   MORK_ASSERT(this->IsShutNode());
00130   MORK_ASSERT(mWriter_Store==0);
00131 }
00132 
00133 /*public non-poly*/
00134 morkWriter::morkWriter(morkEnv* ev, const morkUsage& inUsage,
00135     nsIMdbHeap* ioHeap, morkStore* ioStore, nsIMdbFile* ioFile,
00136     nsIMdbHeap* ioSlotHeap)
00137 : morkNode(ev, inUsage, ioHeap)
00138 , mWriter_Store( 0 )
00139 , mWriter_File( 0 )
00140 , mWriter_Bud( 0 )
00141 , mWriter_Stream( 0 )
00142 , mWriter_SlotHeap( 0 )
00143 
00144 , mWriter_CommitGroupIdentity( 0 ) // see mStore_CommitGroupIdentity
00145 , mWriter_GroupBufFill( 0 )
00146 
00147 , mWriter_TotalCount( morkWriter_kCountNumberOfPhases )
00148 , mWriter_DoneCount( 0 )
00149 
00150 , mWriter_LineSize( 0 )
00151 , mWriter_MaxIndent( morkWriter_kMaxIndent )
00152 , mWriter_MaxLine( morkWriter_kMaxLine )
00153   
00154 , mWriter_TableForm( 0 )
00155 , mWriter_TableAtomScope( 'v' )
00156 , mWriter_TableRowScope( 0 )
00157 , mWriter_TableKind( 0 )
00158   
00159 , mWriter_RowForm( 0 )
00160 , mWriter_RowAtomScope( 0 )
00161 , mWriter_RowScope( 0 )
00162   
00163 , mWriter_DictForm( 0 )
00164 , mWriter_DictAtomScope( 'v' )
00165 
00166 , mWriter_NeedDirtyAll( morkBool_kFalse )
00167 , mWriter_Incremental( morkBool_kTrue ) // opposite of mWriter_NeedDirtyAll
00168 , mWriter_DidStartDict( morkBool_kFalse )
00169 , mWriter_DidEndDict( morkBool_kTrue )
00170 
00171 , mWriter_SuppressDirtyRowNewline( morkBool_kFalse )
00172 , mWriter_DidStartGroup( morkBool_kFalse )
00173 , mWriter_DidEndGroup( morkBool_kTrue )
00174 , mWriter_Phase( morkWriter_kPhaseNothingDone )
00175 
00176 , mWriter_BeVerbose( ev->mEnv_BeVerbose )
00177 
00178 , mWriter_TableRowArrayPos( 0 )
00179 
00180 // empty constructors for map iterators:
00181 , mWriter_StoreAtomSpacesIter( )
00182 , mWriter_AtomSpaceAtomAidsIter( )
00183   
00184 , mWriter_StoreRowSpacesIter( )
00185 , mWriter_RowSpaceTablesIter( )
00186 , mWriter_RowSpaceRowsIter( )
00187 {
00188   mWriter_GroupBuf[ 0 ] = 0;
00189 
00190   mWriter_SafeNameBuf[ 0 ] = 0;
00191   mWriter_SafeNameBuf[ morkWriter_kMaxColumnNameSize * 2 ] = 0;
00192   mWriter_ColNameBuf[ 0 ] = 0;
00193   mWriter_ColNameBuf[ morkWriter_kMaxColumnNameSize ] = 0;
00194   
00195   mdbYarn* y = &mWriter_ColYarn;
00196   y->mYarn_Buf = mWriter_ColNameBuf; // where to put col bytes
00197   y->mYarn_Fill = 0; // set later by writer
00198   y->mYarn_Size = morkWriter_kMaxColumnNameSize; // our buf size
00199   y->mYarn_More = 0; // set later by writer
00200   y->mYarn_Form = 0; // set later by writer
00201   y->mYarn_Grow = 0; // do not allow buffer growth
00202   
00203   y = &mWriter_SafeYarn;
00204   y->mYarn_Buf = mWriter_SafeNameBuf; // where to put col bytes
00205   y->mYarn_Fill = 0; // set later by writer
00206   y->mYarn_Size = morkWriter_kMaxColumnNameSize * 2; // our buf size
00207   y->mYarn_More = 0; // set later by writer
00208   y->mYarn_Form = 0; // set later by writer
00209   y->mYarn_Grow = 0; // do not allow buffer growth
00210   
00211   if ( ev->Good() )
00212   {
00213     if ( ioSlotHeap && ioFile && ioStore )
00214     {
00215       morkStore::SlotWeakStore(ioStore, ev, &mWriter_Store);
00216       nsIMdbFile_SlotStrongFile(ioFile, ev, &mWriter_File);
00217       nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mWriter_SlotHeap);
00218       if ( ev->Good() )
00219       {
00220         mNode_Derived = morkDerived_kWriter;
00221       }
00222     }
00223     else
00224       ev->NilPointerError();
00225   }
00226 }
00227 
00228 
00229 void
00230 morkWriter::MakeWriterStream(morkEnv* ev) // give writer a suitable stream
00231 {
00232   mWriter_Incremental = !mWriter_NeedDirtyAll; // opposites
00233   
00234   if ( !mWriter_Stream && ev->Good() )
00235   {
00236     if ( mWriter_File )
00237     {
00238       morkStream* stream = 0;
00239       mork_bool frozen = morkBool_kFalse; // need to modify
00240       nsIMdbHeap* heap = mWriter_SlotHeap;
00241     
00242       if ( mWriter_Incremental )
00243       {
00244         stream = new(*heap, ev)
00245           morkStream(ev, morkUsage::kHeap, heap, mWriter_File,
00246             morkWriter_kStreamBufSize, frozen);
00247       }
00248       else // compress commit
00249       {
00250         nsIMdbFile* bud = 0;
00251         mWriter_File->AcquireBud(ev->AsMdbEnv(), heap, &bud);
00252         if ( bud )
00253         {
00254           if ( ev->Good() )
00255           {
00256             mWriter_Bud = bud;
00257             stream = new(*heap, ev)
00258               morkStream(ev, morkUsage::kHeap, heap, bud,
00259                 morkWriter_kStreamBufSize, frozen);
00260           }
00261           else
00262             bud->Release();
00263         }
00264       }
00265         
00266       if ( stream )
00267       {
00268         if ( ev->Good() )
00269           mWriter_Stream = stream;
00270         else
00271           stream->CutStrongRef(ev->AsMdbEnv());
00272       }
00273     }
00274     else
00275       this->NilWriterFileError(ev);
00276   }
00277 }
00278 
00279 /*public non-poly*/ void
00280 morkWriter::CloseWriter(morkEnv* ev) // called by CloseMorkNode();
00281 {
00282   if ( this )
00283   {
00284     if ( this->IsNode() )
00285     {
00286       morkStore::SlotWeakStore((morkStore*) 0, ev, &mWriter_Store);
00287       nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mWriter_File);
00288       nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mWriter_Bud);
00289       morkStream::SlotStrongStream((morkStream*) 0, ev, &mWriter_Stream);
00290       nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mWriter_SlotHeap);
00291       this->MarkShut();
00292     }
00293     else
00294       this->NonNodeError(ev);
00295   }
00296   else
00297     ev->NilPointerError();
00298 }
00299 
00300 // } ===== end morkNode methods =====
00301 // ````` ````` ````` ````` ````` 
00302 
00303 /*static*/ void
00304 morkWriter::NonWriterTypeError(morkEnv* ev)
00305 {
00306   ev->NewError("non morkWriter");
00307 }
00308 
00309 /*static*/ void
00310 morkWriter::NilWriterStoreError(morkEnv* ev)
00311 {
00312   ev->NewError("nil mWriter_Store");
00313 }
00314 
00315 /*static*/ void
00316 morkWriter::NilWriterBudError(morkEnv* ev)
00317 {
00318   ev->NewError("nil mWriter_Bud");
00319 }
00320 
00321 /*static*/ void
00322 morkWriter::NilWriterFileError(morkEnv* ev)
00323 {
00324   ev->NewError("nil mWriter_File");
00325 }
00326 
00327 /*static*/ void
00328 morkWriter::NilWriterStreamError(morkEnv* ev)
00329 {
00330   ev->NewError("nil mWriter_Stream");
00331 }
00332 
00333 /*static*/ void
00334 morkWriter::UnsupportedPhaseError(morkEnv* ev)
00335 {
00336   ev->NewError("unsupported mWriter_Phase");
00337 }
00338 
00339 mork_bool
00340 morkWriter::WriteMore(morkEnv* ev) // call until IsWritingDone() is true
00341 {
00342   if ( this->IsOpenNode() )
00343   {
00344     if ( this->IsWriter() )
00345     {
00346       if ( !mWriter_Stream )
00347         this->MakeWriterStream(ev);
00348         
00349       if ( mWriter_Stream )
00350       {
00351         if ( ev->Bad() )
00352         {
00353           ev->NewWarning("writing stops on error");
00354           mWriter_Phase = morkWriter_kPhaseWritingDone;
00355         }
00356         switch( mWriter_Phase )
00357         {
00358           case morkWriter_kPhaseNothingDone:
00359             OnNothingDone(ev); break;
00360           
00361           case morkWriter_kPhaseDirtyAllDone:
00362             OnDirtyAllDone(ev); break;
00363           
00364           case morkWriter_kPhasePutHeaderDone:
00365             OnPutHeaderDone(ev); break;
00366           
00367           case morkWriter_kPhaseRenumberAllDone:
00368             OnRenumberAllDone(ev); break;
00369           
00370           case morkWriter_kPhaseStoreAtomSpaces:
00371             OnStoreAtomSpaces(ev); break;
00372           
00373           case morkWriter_kPhaseAtomSpaceAtomAids:
00374             OnAtomSpaceAtomAids(ev); break;
00375           
00376           case morkWriter_kPhaseStoreRowSpacesTables:
00377             OnStoreRowSpacesTables(ev); break;
00378           
00379           case morkWriter_kPhaseRowSpaceTables:
00380             OnRowSpaceTables(ev); break;
00381           
00382           case morkWriter_kPhaseTableRowArray:
00383             OnTableRowArray(ev); break;
00384           
00385           case morkWriter_kPhaseStoreRowSpacesRows:
00386             OnStoreRowSpacesRows(ev); break;
00387           
00388           case morkWriter_kPhaseRowSpaceRows:
00389             OnRowSpaceRows(ev); break;
00390           
00391           case morkWriter_kPhaseContentDone:
00392             OnContentDone(ev); break;
00393           
00394           case morkWriter_kPhaseWritingDone:
00395             OnWritingDone(ev); break;
00396           
00397           default:
00398             this->UnsupportedPhaseError(ev);
00399         }
00400       }
00401       else
00402         this->NilWriterStreamError(ev);
00403     }
00404     else
00405       this->NonWriterTypeError(ev);
00406   }
00407   else
00408     this->NonOpenNodeError(ev);
00409     
00410   return ev->Good();
00411 }
00412 
00413 static const char morkWriter_kHexDigits[] = "0123456789ABCDEF";
00414 
00415 mork_size
00416 morkWriter::WriteYarn(morkEnv* ev, const mdbYarn* inYarn)
00417   // return number of atom bytes written on the current line (which
00418   // implies that escaped line breaks will make the size value smaller
00419   // than the entire yarn's size, since only part goes on a last line).
00420 {
00421 
00422 
00423   mork_size outSize = 0;
00424   mork_size lineSize = mWriter_LineSize;
00425   morkStream* stream = mWriter_Stream;
00426 
00427   const mork_u1* b = (const mork_u1*) inYarn->mYarn_Buf;
00428   if ( b )
00429   {
00430     register int c;
00431     mork_fill fill = inYarn->mYarn_Fill;
00432 
00433     const mork_u1* end = b + fill;
00434     while ( b < end && ev->Good() )
00435     {
00436       if ( lineSize + outSize >= mWriter_MaxLine ) // continue line?
00437       {
00438         stream->PutByteThenNewline(ev, '\\');
00439         mWriter_LineSize = lineSize = outSize = 0;
00440       }
00441       
00442       c = *b++; // next byte to print
00443       if ( morkCh_IsValue(c) )
00444       {
00445         stream->Putc(ev, c);
00446         ++outSize; // c
00447       }
00448       else if ( c == ')' || c == '$' || c == '\\' )
00449       {
00450         stream->Putc(ev, '\\');
00451         stream->Putc(ev, c);
00452         outSize += 2; // '\' c
00453       }
00454       else
00455       {
00456         outSize += 3; // '$' hex hex
00457         stream->Putc(ev, '$');
00458         stream->Putc(ev, morkWriter_kHexDigits[ (c >> 4) & 0x0F ]);
00459         stream->Putc(ev, morkWriter_kHexDigits[ c & 0x0F ]);
00460       }
00461     }
00462   }
00463   mWriter_LineSize += outSize;
00464     
00465   return outSize;
00466 }
00467 
00468 mork_size
00469 morkWriter::WriteAtom(morkEnv* ev, const morkAtom* inAtom)
00470   // return number of atom bytes written on the current line (which
00471   // implies that escaped line breaks will make the size value smaller
00472   // than the entire atom's size, since only part goes on a last line).
00473 {
00474   mork_size outSize = 0;
00475   mdbYarn yarn; // to ref content inside atom
00476 
00477   if ( inAtom->AliasYarn(&yarn) )
00478   {
00479     if ( mWriter_DidStartDict && yarn.mYarn_Form != mWriter_DictForm )
00480       this->ChangeDictForm(ev, yarn.mYarn_Form);  
00481       
00482     outSize = this->WriteYarn(ev, &yarn);
00483     // mWriter_LineSize += stream->Write(ev, inYarn->mYarn_Buf, outSize);
00484   }
00485   else
00486     inAtom->BadAtomKindError(ev);
00487     
00488   return outSize;
00489 }
00490 
00491 void
00492 morkWriter::WriteAtomSpaceAsDict(morkEnv* ev, morkAtomSpace* ioSpace)
00493 {
00494   morkStream* stream = mWriter_Stream;
00495   nsIMdbEnv *mdbev = ev->AsMdbEnv();
00496   mork_scope scope = ioSpace->SpaceScope();
00497   if ( scope < 0x80 )
00498   {
00499     if ( mWriter_LineSize )
00500       stream->PutLineBreak(ev);
00501     stream->PutString(ev, "< <(a=");
00502     stream->Putc(ev, (int) scope);
00503     ++mWriter_LineSize;
00504     stream->PutString(ev, ")> // (f=iso-8859-1)");
00505     mWriter_LineSize = stream->PutIndent(ev, morkWriter_kDictAliasDepth);
00506   }
00507   else
00508     ioSpace->NonAsciiSpaceScopeName(ev);
00509 
00510   if ( ev->Good() )
00511   {
00512     mdbYarn yarn; // to ref content inside atom
00513     char buf[ 64 ]; // buffer for staging the dict alias hex ID
00514     char* idBuf = buf + 1; // where the id always starts
00515     buf[ 0 ] = '('; // we always start with open paren
00516     morkBookAtom* atom = 0;
00517     morkAtomAidMapIter* ai = &mWriter_AtomSpaceAtomAidsIter;
00518     ai->InitAtomAidMapIter(ev, &ioSpace->mAtomSpace_AtomAids);
00519     mork_change* c = 0;
00520     
00521     for ( c = ai->FirstAtom(ev, &atom); c && ev->Good();
00522           c = ai->NextAtom(ev, &atom) )
00523     {
00524       if ( atom )
00525       {
00526         if ( atom->IsAtomDirty() )
00527         {
00528           atom->SetAtomClean(); // neutralize change
00529           
00530           atom->AliasYarn(&yarn);
00531           mork_size size = ev->TokenAsHex(idBuf, atom->mBookAtom_Id);
00532           
00533           if ( yarn.mYarn_Form != mWriter_DictForm )
00534             this->ChangeDictForm(ev, yarn.mYarn_Form);
00535 
00536           mork_size pending = yarn.mYarn_Fill + size + 
00537             morkWriter_kYarnEscapeSlop + 4;
00538           this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth);
00539           mork_size bytesWritten;
00540           stream->Write(mdbev, buf, size+1, &bytesWritten); //  + '('
00541           mWriter_LineSize += bytesWritten;
00542           
00543           pending -= ( size + 1 );
00544           this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasValueDepth);
00545           stream->Putc(ev, '='); // start alias
00546           ++mWriter_LineSize;
00547           
00548           this->WriteYarn(ev, &yarn);
00549           stream->Putc(ev, ')'); // end alias
00550           ++mWriter_LineSize;
00551           
00552           ++mWriter_DoneCount;
00553         }
00554       }
00555       else
00556         ev->NilPointerError();
00557     }
00558     ai->CloseMapIter(ev);
00559   }
00560   
00561   if ( ev->Good() )
00562   {
00563     ioSpace->SetAtomSpaceClean();
00564     // this->IndentAsNeeded(ev, 0);
00565     // stream->PutByteThenNewline(ev, '>'); // end dict
00566     
00567     stream->Putc(ev, '>'); // end dict
00568     ++mWriter_LineSize;
00569   }
00570 }
00571 
00572 /*
00573 (I'm putting the text of this message in file morkWriter.cpp.)
00574 
00575 I'm making a change which should cause rows and tables to go away
00576 when a Mork db is compress committed, when the rows and tables
00577 are no longer needed.  Because this is subtle, I'm describing it
00578 here in case misbehavior is ever observed.  Otherwise you'll have
00579 almost no hope of fixing a related bug.
00580 
00581 This is done entirely in morkWriter.cpp: morkWriter::DirtyAll(),
00582 which currently marks all rows and tables dirty so they will be
00583 written in a later phase of the commit.  My change is to merely
00584 selectively not mark certain rows and tables dirty, when they seem
00585 to be superfluous.
00586 
00587 A row is no longer needed when the mRow_GcUses slot hits zero, and
00588 this is used by the following inline morkRow method:
00589 
00590   mork_bool IsRowUsed() const { return mRow_GcUses != 0; }
00591 
00592 Naturally disaster ensues if mRow_GcUses is ever smaller than right.
00593 
00594 Similarly, we should drop tables when mTable_GcUses hits zero, but
00595 only when a table contains no row members.  We consider tables to
00596 self reference (and prevent collection) when they contain content.
00597 Again, disaster ensues if mTable_GcUses is ever smaller than right.
00598 
00599   mork_count GetRowCount() const
00600   { return mTable_RowArray.mArray_Fill; }
00601 
00602   mork_bool IsTableUsed() const
00603   { return (mTable_GcUses != 0 || this->GetRowCount() != 0); }
00604 
00605 Now let's question why the design involves filtering what gets set
00606 to dirty.  Why not apply a filter in the later phase when we write
00607 content?  Because I'm afraid of missing some subtle interaction in
00608 updating table and row relationships.  It seems safer to write a row
00609 or table when it starts out dirty, before morkWriter::DirtyAll() is
00610 called.  So this design calls for writing out rows and tables when
00611 they are still clearly used, and additionally, <i>when we have just
00612 been actively writing to them right before this commit</i>.
00613 
00614 Presumably if they are truly useless, they will no longer be dirtied
00615 in later sessions and will get collected during the next compress
00616 commit.  So we wait to collect them until they become all dead, and
00617 not just mostly dead.  (At which time you can feel free to go through
00618 their pockets looking for loose change.)
00619 */
00620 
00621 mork_bool
00622 morkWriter::DirtyAll(morkEnv* ev)
00623   // DirtyAll() visits every store sub-object and marks 
00624   // them dirty, including every table, row, cell, and atom.  The return
00625   // equals ev->Good(), to show whether any error happened.  This method is
00626   // intended for use in the beginning of a "compress commit" which writes
00627   // all store content, whether dirty or not.  We dirty everything first so
00628   // that later iterations over content can mark things clean as they are
00629   // written, and organize the process of serialization so that objects are
00630   // written only at need (because of being dirty).  Note the method can 
00631   // stop early when any error happens, since this will abort any commit.
00632 {
00633   morkStore* store = mWriter_Store;
00634   if ( store )
00635   {
00636     store->SetStoreDirty();
00637     mork_change* c = 0;
00638 
00639     if ( ev->Good() )
00640     {
00641       morkAtomSpaceMapIter* asi = &mWriter_StoreAtomSpacesIter;
00642       asi->InitAtomSpaceMapIter(ev, &store->mStore_AtomSpaces);
00643 
00644       mork_scope* key = 0; // ignore keys in map
00645       morkAtomSpace* space = 0; // old val node in the map
00646       
00647       for ( c = asi->FirstAtomSpace(ev, key, &space); c && ev->Good();
00648             c = asi->NextAtomSpace(ev, key, &space) )
00649       {
00650         if ( space )
00651         {
00652           if ( space->IsAtomSpace() )
00653           {
00654             space->SetAtomSpaceDirty();
00655             morkBookAtom* atom = 0;
00656             morkAtomAidMapIter* ai = &mWriter_AtomSpaceAtomAidsIter;
00657             ai->InitAtomAidMapIter(ev, &space->mAtomSpace_AtomAids);
00658             
00659             for ( c = ai->FirstAtom(ev, &atom); c && ev->Good();
00660                   c = ai->NextAtom(ev, &atom) )
00661             {
00662               if ( atom )
00663               {
00664                 atom->SetAtomDirty();
00665                 ++mWriter_TotalCount;
00666               }
00667               else
00668                 ev->NilPointerError();
00669             }
00670             
00671             ai->CloseMapIter(ev);
00672           }
00673           else
00674             space->NonAtomSpaceTypeError(ev);
00675         }
00676         else
00677           ev->NilPointerError();
00678       }
00679     }
00680     
00681     if ( ev->Good() )
00682     {
00683       morkRowSpaceMapIter* rsi = &mWriter_StoreRowSpacesIter;
00684       rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces);
00685 
00686       mork_scope* key = 0; // ignore keys in map
00687       morkRowSpace* space = 0; // old val node in the map
00688       
00689       for ( c = rsi->FirstRowSpace(ev, key, &space); c && ev->Good();
00690             c = rsi->NextRowSpace(ev, key, &space) )
00691       {
00692         if ( space )
00693         {
00694           if ( space->IsRowSpace() )
00695           {
00696             space->SetRowSpaceDirty();
00697             if ( ev->Good() )
00698             {
00699 #ifdef MORK_ENABLE_PROBE_MAPS
00700               morkRowProbeMapIter* ri = &mWriter_RowSpaceRowsIter;
00701 #else /*MORK_ENABLE_PROBE_MAPS*/
00702               morkRowMapIter* ri = &mWriter_RowSpaceRowsIter;
00703 #endif /*MORK_ENABLE_PROBE_MAPS*/
00704               ri->InitRowMapIter(ev, &space->mRowSpace_Rows);
00705 
00706               morkRow* row = 0; // old key row in the map
00707                 
00708               for ( c = ri->FirstRow(ev, &row); c && ev->Good();
00709                     c = ri->NextRow(ev, &row) )
00710               {
00711                 if ( row && row->IsRow() ) // need to dirty row?
00712                 {
00713                      if ( row->IsRowUsed() || row->IsRowDirty() )
00714                      {
00715                          row->DirtyAllRowContent(ev);
00716                          ++mWriter_TotalCount;
00717                      }
00718                 }
00719                 else
00720                   row->NonRowTypeWarning(ev);
00721               }
00722               ri->CloseMapIter(ev);
00723             }
00724             
00725             if ( ev->Good() )
00726             {
00727               morkTableMapIter* ti = &mWriter_RowSpaceTablesIter;
00728               ti->InitTableMapIter(ev, &space->mRowSpace_Tables);
00729 
00730 #ifdef MORK_BEAD_OVER_NODE_MAPS
00731               morkTable* table = ti->FirstTable(ev);
00732                 
00733               for ( ; table && ev->Good(); table = ti->NextTable(ev) )
00734 #else /*MORK_BEAD_OVER_NODE_MAPS*/
00735               mork_tid* tableKey = 0; // ignore keys in table map
00736               morkTable* table = 0; // old key row in the map
00737                 
00738               for ( c = ti->FirstTable(ev, tableKey, &table); c && ev->Good();
00739                     c = ti->NextTable(ev, tableKey, &table) )
00740 #endif /*MORK_BEAD_OVER_NODE_MAPS*/
00741               {
00742                 if ( table && table->IsTable() ) // need to dirty table?
00743                 {
00744                      if ( table->IsTableUsed() || table->IsTableDirty() )
00745                      {
00746                          // table->DirtyAllTableContent(ev);
00747                          // only necessary to mark table itself dirty:
00748                          table->SetTableDirty();
00749                          table->SetTableRewrite();
00750                          ++mWriter_TotalCount;
00751                      }
00752                 }
00753                 else
00754                   table->NonTableTypeWarning(ev);
00755               }
00756               ti->CloseMapIter(ev);
00757             }
00758           }
00759           else
00760             space->NonRowSpaceTypeError(ev);
00761         }
00762         else
00763           ev->NilPointerError();
00764       }
00765     }
00766   }
00767   else
00768     this->NilWriterStoreError(ev);
00769   
00770   return ev->Good();
00771 }
00772 
00773 
00774 mork_bool
00775 morkWriter::OnNothingDone(morkEnv* ev)
00776 {
00777   mWriter_Incremental = !mWriter_NeedDirtyAll; // opposites
00778   
00779   if (!mWriter_Store->IsStoreDirty() && !mWriter_NeedDirtyAll)
00780   {
00781     mWriter_Phase = morkWriter_kPhaseWritingDone;
00782     return morkBool_kTrue;
00783   }
00784 
00785   // morkStream* stream = mWriter_Stream;
00786   if ( mWriter_NeedDirtyAll )
00787     this->DirtyAll(ev);
00788     
00789   if ( ev->Good() )
00790     mWriter_Phase = morkWriter_kPhaseDirtyAllDone;
00791   else
00792     mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
00793     
00794   return ev->Good();
00795 }
00796 
00797 mork_bool
00798 morkWriter::StartGroup(morkEnv* ev)
00799 {
00800   nsIMdbEnv *mdbev = ev->AsMdbEnv();
00801   morkStream* stream = mWriter_Stream;
00802   mWriter_DidStartGroup = morkBool_kTrue;
00803   mWriter_DidEndGroup = morkBool_kFalse;
00804 
00805   char buf[ 64 ];
00806   char* p = buf;
00807   *p++ = '@';
00808   *p++ = '$';
00809   *p++ = '$';
00810   *p++ = '{';
00811   
00812   mork_token groupID = mWriter_CommitGroupIdentity;
00813   mork_fill idFill = ev->TokenAsHex(p, groupID);
00814   mWriter_GroupBufFill = 0;
00815   // ev->TokenAsHex(mWriter_GroupBuf, groupID);
00816   if ( idFill < morkWriter_kGroupBufSize )
00817   {
00818     MORK_MEMCPY(mWriter_GroupBuf, p, idFill + 1);
00819     mWriter_GroupBufFill = idFill;
00820   }
00821   else
00822     *mWriter_GroupBuf = 0;
00823     
00824   p += idFill;
00825   *p++ = '{';
00826   *p++ = '@';
00827   *p = 0;
00828 
00829   stream->PutLineBreak(ev);
00830   
00831   morkStore* store = mWriter_Store;
00832   if ( store ) // might need to capture commit group position?
00833   {
00834     mork_pos groupPos;
00835     stream->Tell(mdbev, &groupPos);
00836     if ( !store->mStore_FirstCommitGroupPos )
00837       store->mStore_FirstCommitGroupPos = groupPos;
00838     else if ( !store->mStore_SecondCommitGroupPos )
00839       store->mStore_SecondCommitGroupPos = groupPos;
00840   }
00841   
00842   mork_size bytesWritten;
00843   stream->Write(mdbev, buf, idFill + 6, &bytesWritten); // '@$${' + idFill + '{@'
00844   stream->PutLineBreak(ev);
00845   mWriter_LineSize = 0;
00846   
00847   return ev->Good();
00848 }
00849 
00850 mork_bool
00851 morkWriter::CommitGroup(morkEnv* ev)
00852 {
00853   if ( mWriter_DidStartGroup )
00854   {
00855     nsIMdbEnv *mdbev = ev->AsMdbEnv();
00856     mork_size bytesWritten;
00857     morkStream* stream = mWriter_Stream;
00858   
00859     if ( mWriter_LineSize )
00860       stream->PutLineBreak(ev);
00861       
00862     stream->Putc(ev, '@');
00863     stream->Putc(ev, '$');
00864     stream->Putc(ev, '$');
00865     stream->Putc(ev, '}');
00866     
00867     mork_fill bufFill = mWriter_GroupBufFill;
00868     if ( bufFill )
00869       stream->Write(mdbev, mWriter_GroupBuf, bufFill, &bytesWritten);
00870 
00871     stream->Putc(ev, '}');
00872     stream->Putc(ev, '@');
00873     stream->PutLineBreak(ev);
00874 
00875     mWriter_LineSize = 0;
00876   }
00877 
00878   mWriter_DidStartGroup = morkBool_kFalse;
00879   mWriter_DidEndGroup = morkBool_kTrue;
00880   
00881   return ev->Good();
00882 }
00883 
00884 mork_bool
00885 morkWriter::AbortGroup(morkEnv* ev)
00886 {
00887   if ( mWriter_DidStartGroup )
00888   {
00889     morkStream* stream = mWriter_Stream;
00890     stream->PutLineBreak(ev);
00891     stream->PutStringThenNewline(ev, "@$$}~~}@");
00892     mWriter_LineSize = 0;
00893   }
00894   
00895   mWriter_DidStartGroup = morkBool_kFalse;
00896   mWriter_DidEndGroup = morkBool_kTrue;
00897 
00898   return ev->Good();
00899 }
00900 
00901 
00902 mork_bool
00903 morkWriter::OnDirtyAllDone(morkEnv* ev)
00904 {
00905   if ( ev->Good() )
00906   {
00907     nsIMdbEnv *mdbev = ev->AsMdbEnv();
00908     morkStream* stream = mWriter_Stream;
00909     mork_pos resultPos;
00910     if ( mWriter_NeedDirtyAll ) // compress commit
00911     {
00912 
00913       stream->Seek(mdbev, 0, &resultPos); // beginning of stream
00914       stream->PutStringThenNewline(ev, morkWriter_kFileHeader);
00915       mWriter_LineSize = 0;
00916     }
00917     else // else mWriter_Incremental
00918     {
00919       mork_pos eos = stream->Length(ev); // length is end of stream
00920       if ( ev->Good() )
00921       {
00922         stream->Seek(mdbev, eos, &resultPos); // goto end of stream
00923         if ( eos < 128 ) // maybe need file header?
00924         {
00925           stream->PutStringThenNewline(ev, morkWriter_kFileHeader);
00926           mWriter_LineSize = 0;
00927         }
00928         this->StartGroup(ev); // begin incremental transaction
00929       }
00930     }
00931   }
00932     
00933   if ( ev->Good() )
00934     mWriter_Phase = morkWriter_kPhasePutHeaderDone;
00935   else
00936     mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
00937     
00938   return ev->Good();
00939 }
00940 
00941 mork_bool
00942 morkWriter::OnPutHeaderDone(morkEnv* ev)
00943 {
00944   morkStream* stream = mWriter_Stream;
00945   if ( mWriter_LineSize )
00946     stream->PutLineBreak(ev);
00947   
00948   // if ( mWriter_NeedDirtyAll )
00949   //   stream->PutStringThenNewline(ev, "// OnPutHeaderDone()");
00950   mWriter_LineSize = 0;
00951   
00952   if ( mWriter_NeedDirtyAll ) // compress commit
00953   {
00954     morkStore* store = mWriter_Store;
00955     if ( store )
00956       store->RenumberAllCollectableContent(ev);
00957     else
00958       this->NilWriterStoreError(ev);
00959   }
00960     
00961   if ( ev->Good() )
00962     mWriter_Phase = morkWriter_kPhaseRenumberAllDone;
00963   else
00964     mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
00965     
00966   return ev->Good();
00967 }
00968 
00969 mork_bool
00970 morkWriter::OnRenumberAllDone(morkEnv* ev)
00971 {
00972   morkStream* stream = mWriter_Stream;
00973   if ( mWriter_LineSize )
00974     stream->PutLineBreak(ev);
00975     
00976   // if ( mWriter_NeedDirtyAll )
00977   //  stream->PutStringThenNewline(ev, "// OnRenumberAllDone()");
00978   mWriter_LineSize = 0;
00979   
00980   if ( mWriter_NeedDirtyAll ) // compress commit
00981   {
00982   }
00983     
00984   if ( ev->Good() )
00985     mWriter_Phase = morkWriter_kPhaseStoreAtomSpaces;
00986   else
00987     mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
00988 
00989   return ev->Good();
00990 }
00991 
00992 mork_bool
00993 morkWriter::OnStoreAtomSpaces(morkEnv* ev)
00994 {
00995   morkStream* stream = mWriter_Stream;
00996   if ( mWriter_LineSize )
00997     stream->PutLineBreak(ev);
00998 
00999   // if ( mWriter_NeedDirtyAll )
01000   //   stream->PutStringThenNewline(ev, "// OnStoreAtomSpaces()");
01001   mWriter_LineSize = 0;
01002   
01003   if ( mWriter_NeedDirtyAll ) // compress commit
01004   {
01005   }
01006   
01007   if ( ev->Good() )
01008   {
01009     morkStore* store = mWriter_Store;
01010     if ( store )
01011     {
01012       morkAtomSpace* space = store->LazyGetGroundColumnSpace(ev);
01013       if ( space && space->IsAtomSpaceDirty() )
01014       {
01015         // stream->PutStringThenNewline(ev, "// ground column space dict:");
01016         
01017         if ( mWriter_LineSize )
01018         {
01019           stream->PutLineBreak(ev);
01020           mWriter_LineSize = 0;
01021         }
01022         this->WriteAtomSpaceAsDict(ev, space);
01023         space->SetAtomSpaceClean();
01024       }
01025     }
01026     else
01027       this->NilWriterStoreError(ev);
01028   }
01029     
01030   if ( ev->Good() )
01031     mWriter_Phase = morkWriter_kPhaseStoreRowSpacesTables;
01032   else
01033     mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
01034 
01035   return ev->Good();
01036 }
01037 
01038 mork_bool
01039 morkWriter::OnAtomSpaceAtomAids(morkEnv* ev)
01040 {
01041   morkStream* stream = mWriter_Stream;
01042   if ( mWriter_LineSize )
01043     stream->PutLineBreak(ev);
01044 
01045   // if ( mWriter_NeedDirtyAll )
01046   //   stream->PutStringThenNewline(ev, "// OnAtomSpaceAtomAids()");
01047   mWriter_LineSize = 0;
01048   
01049   if ( mWriter_NeedDirtyAll ) // compress commit
01050   {
01051   }
01052     
01053   if ( ev->Good() )
01054     mWriter_Phase = morkWriter_kPhaseStoreRowSpacesTables;
01055   else
01056     mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
01057 
01058   return ev->Good();
01059 }
01060 
01061 void
01062 morkWriter::WriteAllStoreTables(morkEnv* ev)
01063 {
01064   morkStore* store = mWriter_Store;
01065   if ( store && ev->Good() )
01066   {
01067     morkRowSpaceMapIter* rsi = &mWriter_StoreRowSpacesIter;
01068     rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces);
01069 
01070     mork_scope* key = 0; // ignore keys in map
01071     morkRowSpace* space = 0; // old val node in the map
01072     mork_change* c = 0;
01073     
01074     for ( c = rsi->FirstRowSpace(ev, key, &space); c && ev->Good();
01075           c = rsi->NextRowSpace(ev, key, &space) )
01076     {
01077       if ( space )
01078       {
01079         if ( space->IsRowSpace() )
01080         {
01081           space->SetRowSpaceClean();
01082           if ( ev->Good() )
01083           {
01084             morkTableMapIter* ti = &mWriter_RowSpaceTablesIter;
01085             ti->InitTableMapIter(ev, &space->mRowSpace_Tables);
01086 
01087 #ifdef MORK_BEAD_OVER_NODE_MAPS
01088             morkTable* table = ti->FirstTable(ev);
01089               
01090             for ( ; table && ev->Good(); table = ti->NextTable(ev) )
01091 #else /*MORK_BEAD_OVER_NODE_MAPS*/
01092             mork_tid* key2 = 0; // ignore keys in table map
01093             morkTable* table = 0; // old key row in the map
01094               
01095             for ( c = ti->FirstTable(ev, key2, &table); c && ev->Good();
01096                   c = ti->NextTable(ev, key2, &table) )
01097 #endif /*MORK_BEAD_OVER_NODE_MAPS*/
01098             {
01099               if ( table && table->IsTable() )
01100               {
01101                 if ( table->IsTableDirty() )
01102                 {
01103                   mWriter_BeVerbose =
01104                     ( ev->mEnv_BeVerbose || table->IsTableVerbose() );
01105                     
01106                   if ( this->PutTableDict(ev, table) )
01107                     this->PutTable(ev, table);
01108 
01109                   table->SetTableClean(ev);
01110                   mWriter_BeVerbose = ev->mEnv_BeVerbose;
01111                 }
01112               }
01113               else
01114                 table->NonTableTypeWarning(ev);
01115             }
01116             ti->CloseMapIter(ev);
01117           }
01118           if ( ev->Good() )
01119           {
01120             mWriter_TableRowScope = 0; // ensure no table context now
01121             
01122 #ifdef MORK_ENABLE_PROBE_MAPS
01123             morkRowProbeMapIter* ri = &mWriter_RowSpaceRowsIter;
01124 #else /*MORK_ENABLE_PROBE_MAPS*/
01125             morkRowMapIter* ri = &mWriter_RowSpaceRowsIter;
01126 #endif /*MORK_ENABLE_PROBE_MAPS*/
01127             ri->InitRowMapIter(ev, &space->mRowSpace_Rows);
01128 
01129             morkRow* row = 0; // old row in the map
01130               
01131             for ( c = ri->FirstRow(ev, &row); c && ev->Good();
01132                   c = ri->NextRow(ev, &row) )
01133             {
01134               if ( row && row->IsRow() )
01135               {
01136                 // later we should also check that table use count is nonzero:
01137                 if ( row->IsRowDirty() ) // && row->IsRowUsed() ??
01138                 {
01139                   mWriter_BeVerbose = ev->mEnv_BeVerbose;
01140                   if ( this->PutRowDict(ev, row) )
01141                   {
01142                     if ( ev->Good() && mWriter_DidStartDict )
01143                     {
01144                       this->EndDict(ev);
01145                       if ( mWriter_LineSize < 32 && ev->Good() )
01146                         mWriter_SuppressDirtyRowNewline = morkBool_kTrue;
01147                     }
01148                       
01149                     if ( ev->Good() )
01150                       this->PutRow(ev, row);
01151                   }
01152                   mWriter_BeVerbose = ev->mEnv_BeVerbose;
01153                 }
01154               }
01155               else
01156                 row->NonRowTypeWarning(ev);
01157             }
01158             ri->CloseMapIter(ev);
01159           }
01160         }
01161         else
01162           space->NonRowSpaceTypeError(ev);
01163       }
01164       else
01165         ev->NilPointerError();
01166     }
01167   }
01168 }
01169 
01170 mork_bool
01171 morkWriter::OnStoreRowSpacesTables(morkEnv* ev)
01172 {
01173   morkStream* stream = mWriter_Stream;
01174   if ( mWriter_LineSize )
01175     stream->PutLineBreak(ev);
01176 
01177   // if ( mWriter_NeedDirtyAll )
01178   //   stream->PutStringThenNewline(ev, "// OnStoreRowSpacesTables()");
01179   mWriter_LineSize = 0;
01180   
01181   if ( mWriter_NeedDirtyAll ) // compress commit
01182   {
01183   }
01184   
01185   // later we'll break this up, but today we'll write all in one shot:
01186   this->WriteAllStoreTables(ev);
01187     
01188   if ( ev->Good() )
01189     mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows;
01190   else
01191     mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
01192 
01193   return ev->Good();
01194 }
01195 
01196 mork_bool
01197 morkWriter::OnRowSpaceTables(morkEnv* ev)
01198 {
01199   morkStream* stream = mWriter_Stream;
01200   if ( mWriter_LineSize )
01201     stream->PutLineBreak(ev);
01202 
01203   // if ( mWriter_NeedDirtyAll )
01204   //   stream->PutStringThenNewline(ev, "// OnRowSpaceTables()");
01205   mWriter_LineSize = 0;
01206   
01207   if ( mWriter_NeedDirtyAll ) // compress commit
01208   {
01209   }
01210     
01211   if ( ev->Good() )
01212     mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows;
01213   else
01214     mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
01215 
01216   return ev->Good();
01217 }
01218 
01219 mork_bool
01220 morkWriter::OnTableRowArray(morkEnv* ev)
01221 {
01222   morkStream* stream = mWriter_Stream;
01223   if ( mWriter_LineSize )
01224     stream->PutLineBreak(ev);
01225 
01226   // if ( mWriter_NeedDirtyAll )
01227   //   stream->PutStringThenNewline(ev, "// OnTableRowArray()");
01228   mWriter_LineSize = 0;
01229   
01230   if ( mWriter_NeedDirtyAll ) // compress commit
01231   {
01232   }
01233     
01234   if ( ev->Good() )
01235     mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows;
01236   else
01237     mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
01238 
01239   return ev->Good();
01240 }
01241 
01242 mork_bool
01243 morkWriter::OnStoreRowSpacesRows(morkEnv* ev)
01244 {
01245   morkStream* stream = mWriter_Stream;
01246   if ( mWriter_LineSize )
01247     stream->PutLineBreak(ev);
01248 
01249   // if ( mWriter_NeedDirtyAll )
01250   //   stream->PutStringThenNewline(ev, "// OnStoreRowSpacesRows()");
01251   mWriter_LineSize = 0;
01252   
01253   if ( mWriter_NeedDirtyAll ) // compress commit
01254   {
01255   }
01256     
01257   if ( ev->Good() )
01258     mWriter_Phase = morkWriter_kPhaseContentDone;
01259   else
01260     mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
01261 
01262   return ev->Good();
01263 }
01264 
01265 mork_bool
01266 morkWriter::OnRowSpaceRows(morkEnv* ev)
01267 {
01268   morkStream* stream = mWriter_Stream;
01269   if ( mWriter_LineSize )
01270     stream->PutLineBreak(ev);
01271 
01272   // if ( mWriter_NeedDirtyAll )
01273   //   stream->PutStringThenNewline(ev, "// OnRowSpaceRows()");
01274   mWriter_LineSize = 0;
01275   
01276   if ( mWriter_NeedDirtyAll ) // compress commit
01277   {
01278   }
01279     
01280   if ( ev->Good() )
01281     mWriter_Phase = morkWriter_kPhaseContentDone;
01282   else
01283     mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
01284 
01285   return ev->Good();
01286 }
01287 
01288 mork_bool
01289 morkWriter::OnContentDone(morkEnv* ev)
01290 {
01291   morkStream* stream = mWriter_Stream;
01292   if ( mWriter_LineSize )
01293     stream->PutLineBreak(ev);
01294 
01295   // if ( mWriter_NeedDirtyAll )
01296   //   stream->PutStringThenNewline(ev, "// OnContentDone()");
01297   mWriter_LineSize = 0;
01298   
01299   if ( mWriter_Incremental )
01300   {
01301     if ( ev->Good() )
01302       this->CommitGroup(ev);
01303     else
01304       this->AbortGroup(ev);
01305   }
01306   else if ( mWriter_Store && ev->Good() )
01307   {
01308     // after rewriting everything, there are no transaction groups:
01309     mWriter_Store->mStore_FirstCommitGroupPos = 0;
01310     mWriter_Store->mStore_SecondCommitGroupPos = 0;
01311   }
01312   
01313   stream->Flush(ev->AsMdbEnv());
01314   nsIMdbFile* bud = mWriter_Bud;
01315   if ( bud )
01316   {
01317     bud->Flush(ev->AsMdbEnv());
01318     bud->BecomeTrunk(ev->AsMdbEnv());
01319     nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mWriter_Bud);
01320   }
01321   else if ( !mWriter_Incremental ) // should have a bud?
01322     this->NilWriterBudError(ev);
01323     
01324   mWriter_Phase = morkWriter_kPhaseWritingDone; // stop always
01325   mWriter_DoneCount = mWriter_TotalCount;
01326   
01327   return ev->Good();
01328 }
01329 
01330 mork_bool
01331 morkWriter::OnWritingDone(morkEnv* ev)
01332 {
01333   mWriter_DoneCount = mWriter_TotalCount;
01334   ev->NewWarning("writing is done");
01335   return ev->Good();
01336 }
01337 
01338 mork_bool
01339 morkWriter::PutTableChange(morkEnv* ev, const morkTableChange* inChange)
01340 {
01341   nsIMdbEnv *mdbev = ev->AsMdbEnv();
01342   if ( inChange->IsAddRowTableChange() )
01343   {
01344     this->PutRow(ev, inChange->mTableChange_Row ); // row alone means add
01345   }
01346   else if ( inChange->IsCutRowTableChange() )
01347   {
01348     mWriter_Stream->Putc(ev, '-'); // prefix '-' indicates cut row
01349     ++mWriter_LineSize;
01350     this->PutRow(ev, inChange->mTableChange_Row );
01351   }
01352   else if ( inChange->IsMoveRowTableChange() )
01353   {
01354     this->PutRow(ev, inChange->mTableChange_Row );
01355     char buf[ 64 ];
01356     char* p = buf;
01357     *p++ = '!'; // for moves, position is indicated by prefix '!'
01358     mork_size posSize = ev->TokenAsHex(p, inChange->mTableChange_Pos);
01359     p += posSize;
01360     *p++ = ' ';
01361     mork_size bytesWritten;
01362     mWriter_Stream->Write(mdbev, buf, posSize + 2, &bytesWritten);
01363     mWriter_LineSize += bytesWritten;
01364   }
01365   else
01366     inChange->UnknownChangeError(ev);
01367   
01368   return ev->Good();
01369 }
01370 
01371 mork_bool
01372 morkWriter::PutTable(morkEnv* ev, morkTable* ioTable)
01373 {
01374   if ( ev->Good() )
01375     this->StartTable(ev, ioTable);
01376     
01377   if ( ev->Good() )
01378   {
01379     if ( ioTable->IsTableRewrite() || mWriter_NeedDirtyAll )
01380     {
01381       morkArray* array = &ioTable->mTable_RowArray; // vector of rows
01382       mork_fill fill = array->mArray_Fill; // count of rows
01383       morkRow** rows = (morkRow**) array->mArray_Slots;
01384       if ( rows && fill )
01385       {
01386         morkRow** end = rows + fill;
01387         while ( rows < end && ev->Good() )
01388         {
01389           morkRow* r = *rows++; // next row to consider
01390           this->PutRow(ev, r);
01391         }
01392       }
01393     }
01394     else // incremental write only table changes
01395     {
01396       morkList* list = &ioTable->mTable_ChangeList;
01397       morkNext* next = list->GetListHead();
01398       while ( next && ev->Good() )
01399       {
01400         this->PutTableChange(ev, (morkTableChange*) next);
01401         next = next->GetNextLink();
01402       }
01403     }
01404   }
01405     
01406   if ( ev->Good() )
01407     this->EndTable(ev);
01408   
01409   ioTable->SetTableClean(ev); // note this also cleans change list
01410   mWriter_TableRowScope = 0;
01411 
01412   ++mWriter_DoneCount;
01413   return ev->Good();
01414 }
01415 
01416 mork_bool
01417 morkWriter::PutTableDict(morkEnv* ev, morkTable* ioTable)
01418 {
01419   morkRowSpace* space = ioTable->mTable_RowSpace;
01420   mWriter_TableRowScope = space->SpaceScope();
01421   mWriter_TableForm = 0;     // (f=iso-8859-1)
01422   mWriter_TableAtomScope = 'v'; // (a=v)
01423   mWriter_TableKind = ioTable->mTable_Kind;
01424   
01425   mWriter_RowForm = mWriter_TableForm;
01426   mWriter_RowAtomScope = mWriter_TableAtomScope;
01427   mWriter_RowScope = mWriter_TableRowScope;
01428   
01429   mWriter_DictForm = mWriter_TableForm;
01430   mWriter_DictAtomScope = mWriter_TableAtomScope;
01431   
01432   // if ( ev->Good() )
01433   //  this->StartDict(ev); // delay as long as possible
01434 
01435   if ( ev->Good() )
01436   {
01437     morkRow* r = ioTable->mTable_MetaRow;
01438     if ( r )
01439     {
01440       if ( r->IsRow() )
01441         this->PutRowDict(ev, r);
01442       else
01443         r->NonRowTypeError(ev);
01444     }
01445     morkArray* array = &ioTable->mTable_RowArray; // vector of rows
01446     mork_fill fill = array->mArray_Fill; // count of rows
01447     morkRow** rows = (morkRow**) array->mArray_Slots;
01448     if ( rows && fill )
01449     {
01450       morkRow** end = rows + fill;
01451       while ( rows < end && ev->Good() )
01452       {
01453         r = *rows++; // next row to consider
01454         if ( r && r->IsRow() )
01455           this->PutRowDict(ev, r);
01456         else
01457           r->NonRowTypeError(ev);
01458       }
01459     }
01460     // we may have a change for a row which is no longer in the
01461     // table, but contains a cell with something not in the dictionary.
01462     // So, loop through the rows in the change log, writing out any
01463     // dirty dictionary elements.
01464     morkList* list = &ioTable->mTable_ChangeList;
01465     morkNext* next = list->GetListHead();
01466     while ( next && ev->Good() )
01467     {
01468       r = ((morkTableChange*) next)->mTableChange_Row;
01469       if  ( r && r->IsRow() )
01470         this->PutRowDict(ev, r);
01471       next = next->GetNextLink();
01472     }
01473   }
01474   if ( ev->Good() )
01475     this->EndDict(ev);
01476   
01477   return ev->Good();
01478 }
01479   
01480 void
01481 morkWriter::WriteTokenToTokenMetaCell(morkEnv* ev,
01482   mork_token inCol, mork_token inValue)
01483 {
01484   morkStream* stream = mWriter_Stream;
01485   mork_bool isKindCol = ( morkStore_kKindColumn == inCol );
01486   mork_u1 valSep = (mork_u1) (( isKindCol )? '^' : '=');
01487   
01488   char buf[ 128 ]; // buffer for staging the two hex IDs
01489   char* p = buf;
01490 
01491   mork_size bytesWritten;
01492   if ( inCol < 0x80 )
01493   {
01494     stream->Putc(ev, '(');
01495     stream->Putc(ev, (char) inCol);
01496     stream->Putc(ev, valSep);
01497   }
01498   else
01499   {
01500     *p++ = '('; // we always start with open paren
01501     
01502     *p++ = '^'; // indicates col is hex ID
01503     mork_size colSize = ev->TokenAsHex(p, inCol);
01504     p += colSize;
01505     *p++ = (char) valSep;
01506     stream->Write(ev->AsMdbEnv(), buf, colSize + 3, &bytesWritten);
01507     
01508     mWriter_LineSize += bytesWritten;
01509   }
01510 
01511   if ( isKindCol )
01512   {
01513     p = buf;
01514     mork_size valSize = ev->TokenAsHex(p, inValue);
01515     p += valSize;
01516     *p++ = ':';
01517     *p++ = 'c';
01518     *p++ = ')';
01519     stream->Write(ev->AsMdbEnv(), buf, valSize + 3, &bytesWritten);
01520     mWriter_LineSize += bytesWritten;
01521   }
01522   else
01523   {
01524     this->IndentAsNeeded(ev, morkWriter_kTableMetaCellValueDepth);
01525     mdbYarn* yarn = &mWriter_ColYarn;
01526     // mork_u1* yarnBuf = (mork_u1*) yarn->mYarn_Buf;
01527     mWriter_Store->TokenToString(ev, inValue, yarn);
01528     this->WriteYarn(ev, yarn);
01529     stream->Putc(ev, ')');
01530     ++mWriter_LineSize;
01531   }
01532   
01533   // mork_fill fill = yarn->mYarn_Fill;
01534   // yarnBuf[ fill ] = ')'; // append terminator
01535   // mWriter_LineSize += stream->Write(ev, yarnBuf, fill + 1); // +1 for ')'
01536 }
01537   
01538 void
01539 morkWriter::WriteStringToTokenDictCell(morkEnv* ev,
01540   const char* inCol, mork_token inValue)
01541   // Note inCol should begin with '(' and end with '=', with col in between.
01542 {
01543   morkStream* stream = mWriter_Stream;
01544   mWriter_LineSize += stream->PutString(ev, inCol);
01545 
01546   this->IndentAsNeeded(ev, morkWriter_kDictMetaCellValueDepth);
01547   mdbYarn* yarn = &mWriter_ColYarn;
01548   // mork_u1* yarnBuf = (mork_u1*) yarn->mYarn_Buf;
01549   mWriter_Store->TokenToString(ev, inValue, yarn);
01550   this->WriteYarn(ev, yarn);
01551   stream->Putc(ev, ')');
01552   ++mWriter_LineSize;
01553   
01554   // mork_fill fill = yarn->mYarn_Fill;
01555   // yarnBuf[ fill ] = ')'; // append terminator
01556   // mWriter_LineSize += stream->Write(ev, yarnBuf, fill + 1); // +1 for ')'
01557 }
01558 
01559 void
01560 morkWriter::ChangeDictAtomScope(morkEnv* ev, mork_scope inScope)
01561 {
01562   if ( inScope != mWriter_DictAtomScope )
01563   {
01564     ev->NewWarning("unexpected atom scope change");
01565     
01566     morkStream* stream = mWriter_Stream;
01567     if ( mWriter_LineSize )
01568       stream->PutLineBreak(ev);
01569     mWriter_LineSize = 0;
01570 
01571     char buf[ 128 ]; // buffer for staging the two hex IDs
01572     char* p = buf;
01573     *p++ = '<'; // we always start with open paren
01574     *p++ = '('; // we always start with open paren
01575     *p++ = (char) morkStore_kAtomScopeColumn;
01576 
01577     mork_size scopeSize = 1; // default to one byte
01578     if ( inScope >= 0x80 )
01579     {
01580       *p++ = '^'; // indicates col is hex ID
01581       scopeSize = ev->TokenAsHex(p, inScope);
01582       p += scopeSize;
01583     }
01584     else
01585     {
01586       *p++ = '='; // indicates col is imm byte
01587       *p++ = (char) (mork_u1) inScope;
01588     }
01589 
01590     *p++ = ')';
01591     *p++ = '>';
01592     *p = 0;
01593 
01594     mork_size pending = scopeSize + 6;
01595     this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth);
01596     mork_size bytesWritten;
01597 
01598     stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten);
01599     mWriter_LineSize += bytesWritten;
01600       
01601     mWriter_DictAtomScope = inScope;
01602   }
01603 }
01604 
01605 void
01606 morkWriter::ChangeRowForm(morkEnv* ev, mork_cscode inNewForm)
01607 {
01608   if ( inNewForm != mWriter_RowForm )
01609   {
01610     morkStream* stream = mWriter_Stream;
01611     if ( mWriter_LineSize )
01612       stream->PutLineBreak(ev);
01613     mWriter_LineSize = 0;
01614 
01615     char buf[ 128 ]; // buffer for staging the two hex IDs
01616     char* p = buf;
01617     *p++ = '['; // we always start with open bracket
01618     *p++ = '('; // we always start with open paren
01619     *p++ = (char) morkStore_kFormColumn;
01620 
01621     mork_size formSize = 1; // default to one byte
01622     if (! morkCh_IsValue(inNewForm))
01623     {
01624       *p++ = '^'; // indicates col is hex ID
01625       formSize = ev->TokenAsHex(p, inNewForm);
01626       p += formSize;
01627     }
01628     else
01629     {
01630       *p++ = '='; // indicates col is imm byte
01631       *p++ = (char) (mork_u1) inNewForm;
01632     }
01633     
01634     *p++ = ')';
01635     *p++ = ']';
01636     *p = 0;
01637 
01638     mork_size pending = formSize + 6;
01639     this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth);
01640     mork_size bytesWritten;
01641     stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten);
01642     mWriter_LineSize += bytesWritten;
01643       
01644     mWriter_RowForm = inNewForm;
01645   }
01646 }
01647 
01648 void
01649 morkWriter::ChangeDictForm(morkEnv* ev, mork_cscode inNewForm)
01650 {
01651   if ( inNewForm != mWriter_DictForm )
01652   {
01653     morkStream* stream = mWriter_Stream;
01654     if ( mWriter_LineSize )
01655       stream->PutLineBreak(ev);
01656     mWriter_LineSize = 0;
01657 
01658     char buf[ 128 ]; // buffer for staging the two hex IDs
01659     char* p = buf;
01660     *p++ = '<'; // we always start with open angle
01661     *p++ = '('; // we always start with open paren
01662     *p++ = (char) morkStore_kFormColumn;
01663 
01664     mork_size formSize = 1; // default to one byte
01665     if (! morkCh_IsValue(inNewForm))
01666     {
01667       *p++ = '^'; // indicates col is hex ID
01668       formSize = ev->TokenAsHex(p, inNewForm);
01669       p += formSize;
01670     }
01671     else
01672     {
01673       *p++ = '='; // indicates col is imm byte
01674       *p++ = (char) (mork_u1) inNewForm;
01675     }
01676     
01677     *p++ = ')';
01678     *p++ = '>';
01679     *p = 0;
01680 
01681     mork_size pending = formSize + 6;
01682     this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth);
01683     
01684     mork_size bytesWritten;
01685     stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten);
01686     mWriter_LineSize += bytesWritten;
01687       
01688     mWriter_DictForm = inNewForm;
01689   }
01690 }
01691 
01692 void
01693 morkWriter::StartDict(morkEnv* ev)
01694 {
01695   morkStream* stream = mWriter_Stream;
01696   if ( mWriter_DidStartDict )
01697   {
01698     stream->Putc(ev, '>'); // end dict
01699     ++mWriter_LineSize;
01700   }
01701   mWriter_DidStartDict = morkBool_kTrue;
01702   mWriter_DidEndDict = morkBool_kFalse;
01703   
01704   if ( mWriter_LineSize )
01705     stream->PutLineBreak(ev);
01706   mWriter_LineSize = 0;
01707   
01708   if ( mWriter_TableRowScope ) // blank line before table's dict?
01709     stream->PutLineBreak(ev);
01710     
01711   if ( mWriter_DictForm || mWriter_DictAtomScope != 'v' )
01712   {
01713     stream->Putc(ev, '<');
01714     stream->Putc(ev, ' ');
01715     stream->Putc(ev, '<');
01716     mWriter_LineSize = 3;
01717     if ( mWriter_DictForm )
01718       this->WriteStringToTokenDictCell(ev, "(f=", mWriter_DictForm);
01719     if ( mWriter_DictAtomScope != 'v' )
01720       this->WriteStringToTokenDictCell(ev, "(a=", mWriter_DictAtomScope);
01721   
01722     stream->Putc(ev, '>');
01723     ++mWriter_LineSize;
01724 
01725     mWriter_LineSize = stream->PutIndent(ev, morkWriter_kDictAliasDepth);
01726   }
01727   else
01728   {
01729     stream->Putc(ev, '<');
01730     // stream->Putc(ev, ' ');
01731     ++mWriter_LineSize;
01732   }
01733 }
01734 
01735 void
01736 morkWriter::EndDict(morkEnv* ev)
01737 {
01738   morkStream* stream = mWriter_Stream;
01739   if ( mWriter_DidStartDict )
01740   {
01741     stream->Putc(ev, '>'); // end dict
01742     ++mWriter_LineSize;
01743   }
01744   mWriter_DidStartDict = morkBool_kFalse;
01745   mWriter_DidEndDict = morkBool_kTrue;
01746 }
01747 
01748 void
01749 morkWriter::StartTable(morkEnv* ev, morkTable* ioTable)
01750 {
01751   mdbOid toid; // to receive table oid
01752   ioTable->GetTableOid(ev, &toid);
01753   
01754   if ( ev->Good() )
01755   {
01756     morkStream* stream = mWriter_Stream;
01757     if ( mWriter_LineSize )
01758       stream->PutLineBreak(ev);
01759     mWriter_LineSize = 0;
01760     // stream->PutLineBreak(ev);
01761 
01762     char buf[ 64 + 16 ]; // buffer for staging hex
01763     char* p = buf;
01764     *p++ = '{'; // punct 1
01765     mork_size punctSize = (mWriter_BeVerbose) ? 10 : 3; // counting "{ {/*r=*/ "
01766 
01767     if ( ioTable->IsTableRewrite() && mWriter_Incremental )
01768     {
01769       *p++ = '-';
01770       ++punctSize; // counting '-' // punct ++
01771       ++mWriter_LineSize;
01772     }
01773     mork_size oidSize = ev->OidAsHex(p, toid);
01774     p += oidSize;
01775     *p++ = ' '; // punct 2
01776     *p++ = '{'; // punct 3
01777     if (mWriter_BeVerbose)
01778     {
01779     
01780       *p++ = '/'; // punct=4
01781       *p++ = '*'; // punct=5
01782       *p++ = 'r'; // punct=6
01783       *p++ = '='; // punct=7
01784 
01785       mork_token tableUses = (mork_token) ioTable->mTable_GcUses;
01786       mork_size usesSize = ev->TokenAsHex(p, tableUses);
01787       punctSize += usesSize;
01788       p += usesSize;
01789     
01790       *p++ = '*'; // punct=8
01791       *p++ = '/'; // punct=9
01792       *p++ = ' '; // punct=10
01793     }
01794     mork_size bytesWritten;
01795 
01796     stream->Write(ev->AsMdbEnv(), buf, oidSize + punctSize, &bytesWritten);
01797     mWriter_LineSize += bytesWritten;
01798 
01799     mork_kind tk = mWriter_TableKind;
01800     if ( tk )
01801     {
01802       this->IndentAsNeeded(ev, morkWriter_kTableMetaCellDepth);
01803       this->WriteTokenToTokenMetaCell(ev, morkStore_kKindColumn, tk);
01804     }
01805       
01806     stream->Putc(ev, '('); // start 's' col cell
01807     stream->Putc(ev, 's'); // column
01808     stream->Putc(ev, '='); // column
01809     mWriter_LineSize += 3;
01810 
01811     int prio = (int) ioTable->mTable_Priority;
01812     if ( prio > 9 ) // need to force down to max decimal digit?
01813       prio = 9;
01814     prio += '0'; // add base digit zero
01815     stream->Putc(ev, prio); // priority: (s=0
01816     ++mWriter_LineSize;
01817     
01818     if ( ioTable->IsTableUnique() )
01819     {
01820       stream->Putc(ev, 'u'); // (s=0u
01821       ++mWriter_LineSize;
01822     }
01823     if ( ioTable->IsTableVerbose() )
01824     {
01825       stream->Putc(ev, 'v'); // (s=0uv
01826       ++mWriter_LineSize;
01827     }
01828     
01829     // stream->Putc(ev, ':'); // (s=0uv:
01830     // stream->Putc(ev, 'c'); // (s=0uv:c
01831     stream->Putc(ev, ')'); // end 's' col cell (s=0uv:c)
01832     mWriter_LineSize += 1; // maybe 3 if we add ':' and 'c'
01833 
01834     morkRow* r = ioTable->mTable_MetaRow;
01835     if ( r )
01836     {
01837       if ( r->IsRow() )
01838       {
01839         mWriter_SuppressDirtyRowNewline = morkBool_kTrue;
01840         this->PutRow(ev, r);
01841       }
01842       else
01843         r->NonRowTypeError(ev);
01844     }
01845     
01846     stream->Putc(ev, '}'); // end meta
01847     ++mWriter_LineSize;
01848     
01849     if ( mWriter_LineSize < mWriter_MaxIndent )
01850     {
01851       stream->Putc(ev, ' '); // nice white space
01852       ++mWriter_LineSize;
01853     }
01854   }
01855 }
01856 
01857 void
01858 morkWriter::EndTable(morkEnv* ev)
01859 {
01860   morkStream* stream = mWriter_Stream;
01861   stream->Putc(ev, '}'); // end table
01862   ++mWriter_LineSize;
01863 
01864   mWriter_TableAtomScope = 'v'; // (a=v)
01865 }
01866 
01867 mork_bool
01868 morkWriter::PutRowDict(morkEnv* ev, morkRow* ioRow)
01869 {
01870   mWriter_RowForm = mWriter_TableForm;
01871 
01872   morkCell* cells = ioRow->mRow_Cells;
01873   if ( cells )
01874   {
01875     morkStream* stream = mWriter_Stream;
01876     mdbYarn yarn; // to ref content inside atom
01877     char buf[ 64 ]; // buffer for staging the dict alias hex ID
01878     char* idBuf = buf + 1; // where the id always starts
01879     buf[ 0 ] = '('; // we always start with open paren
01880 
01881     morkCell* end = cells + ioRow->mRow_Length;
01882     --cells; // prepare for preincrement:
01883     while ( ++cells < end && ev->Good() )
01884     {
01885       morkAtom* atom = cells->GetAtom();
01886       if ( atom && atom->IsAtomDirty() )
01887       {
01888         if ( atom->IsBook() ) // is it possible to write atom ID?
01889         {
01890           if ( !this->DidStartDict() )
01891           {
01892             this->StartDict(ev);
01893             if ( ev->Bad() )
01894               break;
01895           }
01896           atom->SetAtomClean(); // neutralize change
01897           
01898           this->IndentAsNeeded(ev, morkWriter_kDictAliasDepth);
01899           morkBookAtom* ba = (morkBookAtom*) atom;
01900           mork_size size = ev->TokenAsHex(idBuf, ba->mBookAtom_Id);
01901           mork_size bytesWritten;
01902           stream->Write(ev->AsMdbEnv(), buf, size+1, &bytesWritten); // '('
01903           mWriter_LineSize += bytesWritten;
01904 
01905           if ( atom->AliasYarn(&yarn) )
01906           {
01907             mork_scope atomScope = atom->GetBookAtomSpaceScope(ev);
01908             if ( atomScope && atomScope != mWriter_DictAtomScope )
01909               this->ChangeDictAtomScope(ev, atomScope);
01910             
01911             if ( mWriter_DidStartDict && yarn.mYarn_Form != mWriter_DictForm )
01912               this->ChangeDictForm(ev, yarn.mYarn_Form);  
01913       
01914             mork_size pending = yarn.mYarn_Fill + morkWriter_kYarnEscapeSlop + 1;
01915             this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasValueDepth);
01916               
01917             stream->Putc(ev, '='); // start value
01918             ++mWriter_LineSize;
01919       
01920             this->WriteYarn(ev, &yarn);
01921 
01922             stream->Putc(ev, ')'); // end value
01923             ++mWriter_LineSize;
01924           }
01925           else
01926             atom->BadAtomKindError(ev);
01927                       
01928           ++mWriter_DoneCount;
01929         }
01930       }
01931     }
01932   }
01933   return ev->Good();
01934 }
01935 
01936 mork_bool
01937 morkWriter::IsYarnAllValue(const mdbYarn* inYarn)
01938 {
01939   mork_fill fill = inYarn->mYarn_Fill;
01940   const mork_u1* buf = (const mork_u1*) inYarn->mYarn_Buf;
01941   const mork_u1* end = buf + fill;
01942   --buf; // prepare for preincrement
01943   while ( ++buf < end )
01944   {
01945     mork_ch c = *buf;
01946     if ( !morkCh_IsValue(c) )
01947       return morkBool_kFalse;
01948   }
01949   return morkBool_kTrue;
01950 }
01951 
01952 mork_bool
01953 morkWriter::PutVerboseCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal)
01954 {
01955   morkStream* stream = mWriter_Stream;
01956   morkStore* store = mWriter_Store;
01957 
01958   mdbYarn* colYarn = &mWriter_ColYarn;
01959   
01960   morkAtom* atom = (inWithVal)? ioCell->GetAtom() : (morkAtom*) 0;
01961   
01962   mork_column col = ioCell->GetColumn();
01963   store->TokenToString(ev, col, colYarn);
01964   
01965   mdbYarn yarn; // to ref content inside atom
01966   atom->AliasYarn(&yarn); // works even when atom==nil
01967   
01968   if ( yarn.mYarn_Form != mWriter_RowForm )
01969     this->ChangeRowForm(ev, yarn.mYarn_Form);
01970 
01971   mork_size pending = yarn.mYarn_Fill + colYarn->mYarn_Fill +
01972      morkWriter_kYarnEscapeSlop + 3;
01973   this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth);
01974 
01975   stream->Putc(ev, '('); // start cell
01976   ++mWriter_LineSize;
01977 
01978   this->WriteYarn(ev, colYarn); // column
01979   
01980   pending = yarn.mYarn_Fill + morkWriter_kYarnEscapeSlop;
01981   this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellValueDepth);
01982   stream->Putc(ev, '=');
01983   ++mWriter_LineSize;
01984   
01985   this->WriteYarn(ev, &yarn); // value
01986   
01987   stream->Putc(ev, ')'); // end cell
01988   ++mWriter_LineSize;
01989 
01990   return ev->Good();
01991 }
01992 
01993 mork_bool
01994 morkWriter::PutVerboseRowCells(morkEnv* ev, morkRow* ioRow)
01995 {
01996   morkCell* cells = ioRow->mRow_Cells;
01997   if ( cells )
01998   {
01999 
02000     morkCell* end = cells + ioRow->mRow_Length;
02001     --cells; // prepare for preincrement:
02002     while ( ++cells < end && ev->Good() )
02003     {
02004       // note we prefer to avoid writing cells here with no value:
02005       if ( cells->GetAtom() ) // does cell have any value?
02006         this->PutVerboseCell(ev, cells, /*inWithVal*/ morkBool_kTrue);
02007     }
02008   }
02009   return ev->Good();
02010 }
02011 
02012 
02013 mork_bool
02014 morkWriter::PutCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal)
02015 {
02016   morkStream* stream = mWriter_Stream;
02017   char buf[ 128 ]; // buffer for staging hex ids
02018   char* idBuf = buf + 2; // where the id always starts
02019   buf[ 0 ] = '('; // we always start with open paren
02020   buf[ 1 ] = '^'; // column is always a hex ID
02021   
02022   mork_size colSize = 0; // the size of col hex ID
02023   mork_size bytesWritten;
02024   
02025   morkAtom* atom = (inWithVal)? ioCell->GetAtom() : (morkAtom*) 0;
02026   
02027   mork_column col = ioCell->GetColumn();
02028   char* p = idBuf;
02029   colSize = ev->TokenAsHex(p, col);
02030   p += colSize;
02031 
02032   mdbYarn yarn; // to ref content inside atom
02033   atom->AliasYarn(&yarn); // works even when atom==nil
02034   
02035   if ( yarn.mYarn_Form != mWriter_RowForm )
02036     this->ChangeRowForm(ev, yarn.mYarn_Form);
02037   
02038   if ( atom && atom->IsBook() ) // is it possible to write atom ID?
02039   {
02040     this->IndentAsNeeded(ev, morkWriter_kRowCellDepth);
02041     *p++ = '^';
02042     morkBookAtom* ba = (morkBookAtom*) atom;
02043 
02044     mork_size valSize = ev->TokenAsHex(p, ba->mBookAtom_Id);
02045     mork_fill yarnFill = yarn.mYarn_Fill;
02046     mork_bool putImmYarn = ( yarnFill <= valSize );
02047     if ( putImmYarn )
02048       putImmYarn = this->IsYarnAllValue(&yarn);
02049     
02050     if ( putImmYarn ) // value no bigger than id?
02051     {
02052       p[ -1 ] = '='; // go back and clobber '^' with '=' instead
02053       if ( yarnFill )
02054       {
02055         MORK_MEMCPY(p, yarn.mYarn_Buf, yarnFill);
02056         p += yarnFill;
02057       }
02058       *p++ = ')';
02059       mork_size distance = (mork_size) (p - buf);
02060       stream->Write(ev->AsMdbEnv(), buf, distance, &bytesWritten);
02061       mWriter_LineSize += bytesWritten;
02062     }
02063     else
02064     {
02065       p += valSize;
02066       *p = ')';
02067       stream->Write(ev->AsMdbEnv(), buf, colSize + valSize + 4, &bytesWritten);
02068       mWriter_LineSize += bytesWritten;
02069     }
02070 
02071     if ( atom->IsAtomDirty() )
02072     {
02073       atom->SetAtomClean();
02074       ++mWriter_DoneCount;
02075     }
02076   }
02077   else // must write an anonymous atom
02078   {
02079     mork_size pending = yarn.mYarn_Fill + colSize +
02080       morkWriter_kYarnEscapeSlop + 2;
02081     this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth);
02082 
02083     mork_size bytesWritten;
02084     stream->Write(ev->AsMdbEnv(), buf, colSize + 2, &bytesWritten);
02085     mWriter_LineSize += bytesWritten;
02086 
02087     pending -= ( colSize + 2 );
02088     this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth);
02089     stream->Putc(ev, '=');
02090     ++mWriter_LineSize;
02091     
02092     this->WriteYarn(ev, &yarn);
02093     stream->Putc(ev, ')'); // end cell
02094     ++mWriter_LineSize;
02095   }
02096   return ev->Good();
02097 }
02098 
02099 mork_bool
02100 morkWriter::PutRowCells(morkEnv* ev, morkRow* ioRow)
02101 {
02102   morkCell* cells = ioRow->mRow_Cells;
02103   if ( cells )
02104   {
02105     morkCell* end = cells + ioRow->mRow_Length;
02106     --cells; // prepare for preincrement:
02107     while ( ++cells < end && ev->Good() )
02108     {
02109       // note we prefer to avoid writing cells here with no value:
02110       if ( cells->GetAtom() ) // does cell have any value?
02111         this->PutCell(ev, cells, /*inWithVal*/ morkBool_kTrue);
02112     }
02113   }
02114   return ev->Good();
02115 }
02116 
02117 mork_bool
02118 morkWriter::PutRow(morkEnv* ev, morkRow* ioRow)
02119 {
02120   if ( ioRow && ioRow->IsRow() )
02121   {
02122     mWriter_RowForm = mWriter_TableForm;
02123 
02124     mork_size bytesWritten;
02125     morkStream* stream = mWriter_Stream;
02126     char buf[ 128 + 16 ]; // buffer for staging hex
02127     char* p = buf;
02128     mdbOid* roid = &ioRow->mRow_Oid;
02129     mork_size ridSize = 0;
02130     
02131     mork_scope tableScope = mWriter_TableRowScope;
02132 
02133     if ( ioRow->IsRowDirty() )
02134     {
02135       if ( mWriter_SuppressDirtyRowNewline || !mWriter_LineSize )
02136         mWriter_SuppressDirtyRowNewline = morkBool_kFalse;
02137       else
02138       {
02139         if ( tableScope ) // in a table?
02140           mWriter_LineSize = stream->PutIndent(ev, morkWriter_kRowDepth);
02141         else
02142           mWriter_LineSize = stream->PutIndent(ev, 0); // no indent
02143       }
02144       
02145 //      mork_rid rid = roid->mOid_Id;
02146       *p++ = '['; // start row punct=1
02147       mork_size punctSize = (mWriter_BeVerbose) ? 9 : 1; // counting "[ /*r=*/ "
02148       
02149       mork_bool rowRewrite = ioRow->IsRowRewrite();
02150             
02151       if ( rowRewrite && mWriter_Incremental )
02152       {
02153         *p++ = '-';
02154         ++punctSize; // counting '-'
02155         ++mWriter_LineSize;
02156       }
02157 
02158       if ( tableScope && roid->mOid_Scope == tableScope )
02159         ridSize = ev->TokenAsHex(p, roid->mOid_Id);
02160       else
02161         ridSize = ev->OidAsHex(p, *roid);
02162       
02163       p += ridSize;
02164       
02165       if (mWriter_BeVerbose)
02166       {
02167         *p++ = ' '; // punct=2
02168         *p++ = '/'; // punct=3
02169         *p++ = '*'; // punct=4
02170         *p++ = 'r'; // punct=5
02171         *p++ = '='; // punct=6
02172 
02173         mork_size usesSize = ev->TokenAsHex(p, (mork_token) ioRow->mRow_GcUses);
02174         punctSize += usesSize;
02175         p += usesSize;
02176       
02177         *p++ = '*'; // punct=7
02178         *p++ = '/'; // punct=8
02179         *p++ = ' '; // punct=9
02180       }
02181       stream->Write(ev->AsMdbEnv(), buf, ridSize + punctSize, &bytesWritten);
02182       mWriter_LineSize += bytesWritten;
02183 
02184       // special case situation where row puts exactly one column:
02185       if ( !rowRewrite && mWriter_Incremental && ioRow->HasRowDelta() )
02186       {
02187         mork_column col = ioRow->GetDeltaColumn();
02188         morkCell dummy(col, morkChange_kNil, (morkAtom*) 0);
02189         morkCell* cell = 0;
02190         
02191         mork_bool withVal = ( ioRow->GetDeltaChange() != morkChange_kCut );
02192         
02193         if ( withVal )
02194         {
02195           mork_pos cellPos = 0; // dummy pos
02196           cell = ioRow->GetCell(ev, col, &cellPos);
02197         }
02198         if ( !cell )
02199           cell = &dummy;
02200           
02201         if ( mWriter_BeVerbose )
02202           this->PutVerboseCell(ev, cell, withVal);
02203         else
02204           this->PutCell(ev, cell, withVal);
02205       }
02206       else // put entire row?
02207       {
02208         if ( mWriter_BeVerbose )
02209           this->PutVerboseRowCells(ev, ioRow); // write all, verbosely
02210         else
02211           this->PutRowCells(ev, ioRow); // write all, hex notation
02212       }
02213         
02214       stream->Putc(ev, ']'); // end row
02215       ++mWriter_LineSize;
02216     }
02217     else
02218     {
02219       this->IndentAsNeeded(ev, morkWriter_kRowDepth);
02220 
02221       if ( tableScope && roid->mOid_Scope == tableScope )
02222         ridSize = ev->TokenAsHex(p, roid->mOid_Id);
02223       else
02224         ridSize = ev->OidAsHex(p, *roid);
02225 
02226       stream->Write(ev->AsMdbEnv(), buf, ridSize, &bytesWritten);
02227       mWriter_LineSize += bytesWritten;
02228       stream->Putc(ev, ' ');
02229       ++mWriter_LineSize;
02230     }
02231 
02232     ++mWriter_DoneCount;
02233 
02234     ioRow->SetRowClean(); // try to do this at the very last
02235   }
02236   else
02237     ioRow->NonRowTypeWarning(ev);
02238   
02239   return ev->Good();
02240 }
02241 
02242 //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
02243