Back to index

lightning-sunbird  0.9+nobinonly
morkRowSpace.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 _MORKNODE_
00047 #include "morkNode.h"
00048 #endif
00049 
00050 #ifndef _MORKMAP_
00051 #include "morkMap.h"
00052 #endif
00053 
00054 #ifndef _MORKSPACE_
00055 #include "morkSpace.h"
00056 #endif
00057 
00058 #ifndef _MORKNODEMAP_
00059 #include "morkNodeMap.h"
00060 #endif
00061 
00062 #ifndef _MORKROWMAP_
00063 #include "morkRowMap.h"
00064 #endif
00065 
00066 #ifndef _MORKENV_
00067 #include "morkEnv.h"
00068 #endif
00069 
00070 #ifndef _MORKROWSPACE_
00071 #include "morkRowSpace.h"
00072 #endif
00073 
00074 #ifndef _MORKPOOL_
00075 #include "morkPool.h"
00076 #endif
00077 
00078 #ifndef _MORKSTORE_
00079 #include "morkStore.h"
00080 #endif
00081 
00082 #ifndef _MORKTABLE_
00083 #include "morkTable.h"
00084 #endif
00085 
00086 #ifndef _MORKROW_
00087 #include "morkRow.h"
00088 #endif
00089 
00090 #ifndef _MORKATOMMAP_
00091 #include "morkAtomMap.h"
00092 #endif
00093 
00094 #ifndef _MORKROWOBJECT_
00095 #include "morkRowObject.h"
00096 #endif
00097 
00098 //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
00099 
00100 // ````` ````` ````` ````` ````` 
00101 // { ===== begin morkNode interface =====
00102 
00103 /*public virtual*/ void
00104 morkRowSpace::CloseMorkNode(morkEnv* ev) // CloseRowSpace() only if open
00105 {
00106   if ( this->IsOpenNode() )
00107   {
00108     this->MarkClosing();
00109     this->CloseRowSpace(ev);
00110     this->MarkShut();  
00111   }
00112 }
00113 
00114 /*public virtual*/
00115 morkRowSpace::~morkRowSpace() // assert CloseRowSpace() executed earlier
00116 {
00117   MORK_ASSERT(this->IsShutNode());
00118 }
00119 
00120 /*public non-poly*/
00121 morkRowSpace::morkRowSpace(morkEnv* ev, 
00122   const morkUsage& inUsage, mork_scope inScope, morkStore* ioStore,
00123   nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
00124 : morkSpace(ev, inUsage, inScope, ioStore, ioHeap, ioSlotHeap)
00125 , mRowSpace_SlotHeap( ioSlotHeap )
00126 , mRowSpace_Rows(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap,
00127   morkRowSpace_kStartRowMapSlotCount)
00128 , mRowSpace_Tables(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap)
00129 , mRowSpace_NextTableId( 1 )
00130 , mRowSpace_NextRowId( 1 )
00131 
00132 , mRowSpace_IndexCount( 0 )
00133 {
00134   morkAtomRowMap** cache = mRowSpace_IndexCache;
00135   morkAtomRowMap** cacheEnd = cache + morkRowSpace_kPrimeCacheSize;
00136   while ( cache < cacheEnd )
00137     *cache++ = 0; // put nil into every slot of cache table
00138     
00139   if ( ev->Good() )
00140   {
00141     if ( ioSlotHeap )
00142     {
00143       mNode_Derived = morkDerived_kRowSpace;
00144       
00145       // the morkSpace base constructor handles any dirty propagation
00146     }
00147     else
00148       ev->NilPointerError();
00149   }
00150 }
00151 
00152 /*public non-poly*/ void
00153 morkRowSpace::CloseRowSpace(morkEnv* ev) // called by CloseMorkNode();
00154 {
00155   if ( this )
00156   {
00157     if ( this->IsNode() )
00158     {
00159       morkAtomRowMap** cache = mRowSpace_IndexCache;
00160       morkAtomRowMap** cacheEnd = cache + morkRowSpace_kPrimeCacheSize;
00161       --cache; // prepare for preincrement:
00162       while ( ++cache < cacheEnd )
00163       {
00164         if ( *cache )
00165           morkAtomRowMap::SlotStrongAtomRowMap(0, ev, cache);
00166       }
00167       
00168       mRowSpace_Tables.CloseMorkNode(ev);
00169       
00170       morkStore* store = mSpace_Store;
00171       if ( store )
00172         this->CutAllRows(ev, &store->mStore_Pool);
00173       
00174       mRowSpace_Rows.CloseMorkNode(ev);
00175       this->CloseSpace(ev);
00176     }
00177     else
00178       this->NonNodeError(ev);
00179   }
00180   else
00181     ev->NilPointerError();
00182 }
00183 
00184 // } ===== end morkNode methods =====
00185 // ````` ````` ````` ````` ````` 
00186 
00187 /*static*/ void
00188 morkRowSpace::NonRowSpaceTypeError(morkEnv* ev)
00189 {
00190   ev->NewError("non morkRowSpace");
00191 }
00192 
00193 /*static*/ void
00194 morkRowSpace::ZeroKindError(morkEnv* ev)
00195 {
00196   ev->NewError("zero table kind");
00197 }
00198 
00199 /*static*/ void
00200 morkRowSpace::ZeroScopeError(morkEnv* ev)
00201 {
00202   ev->NewError("zero row scope");
00203 }
00204 
00205 /*static*/ void
00206 morkRowSpace::ZeroTidError(morkEnv* ev)
00207 {
00208   ev->NewError("zero table ID");
00209 }
00210 
00211 /*static*/ void
00212 morkRowSpace::MinusOneRidError(morkEnv* ev)
00213 {
00214   ev->NewError("row ID is -1");
00215 }
00216 
00218 //morkRowSpace::ExpectAutoIdOnlyError(morkEnv* ev)
00219 //{
00220 //  ev->NewError("zero row ID");
00221 //}
00222 
00224 //morkRowSpace::ExpectAutoIdNeverError(morkEnv* ev)
00225 //{
00226 //}
00227 
00228 mork_num
00229 morkRowSpace::CutAllRows(morkEnv* ev, morkPool* ioPool)
00230 {
00231   if ( this->IsRowSpaceClean() )
00232     this->MaybeDirtyStoreAndSpace();
00233   
00234   mork_num outSlots = mRowSpace_Rows.MapFill();
00235 
00236 #ifdef MORK_ENABLE_ZONE_ARENAS
00237   MORK_USED_2(ev, ioPool);
00238   return 0;
00239 #else /*MORK_ENABLE_ZONE_ARENAS*/
00240   morkZone* zone = &mSpace_Store->mStore_Zone;
00241   morkRow* r = 0; // old key row in the map
00242   mork_change* c = 0;
00243 
00244 #ifdef MORK_ENABLE_PROBE_MAPS
00245   morkRowProbeMapIter i(ev, &mRowSpace_Rows);
00246 #else /*MORK_ENABLE_PROBE_MAPS*/
00247   morkRowMapIter i(ev, &mRowSpace_Rows);
00248 #endif /*MORK_ENABLE_PROBE_MAPS*/
00249   
00250   for ( c = i.FirstRow(ev, &r); c && ev->Good();
00251         c = i.NextRow(ev, &r) )
00252   {
00253     if ( r )
00254     {
00255       if ( r->IsRow() )
00256       {
00257         if ( r->mRow_Object )
00258         {
00259           morkRowObject::SlotWeakRowObject((morkRowObject*) 0, ev,
00260             &r->mRow_Object);
00261         }
00262         ioPool->ZapRow(ev, r, zone);
00263       }
00264       else
00265         r->NonRowTypeWarning(ev);
00266     }
00267     else
00268       ev->NilPointerError();
00269     
00270 #ifdef MORK_ENABLE_PROBE_MAPS
00271     // cut nothing from the map
00272 #else /*MORK_ENABLE_PROBE_MAPS*/
00273     i.CutHereRow(ev, /*key*/ (morkRow**) 0);
00274 #endif /*MORK_ENABLE_PROBE_MAPS*/
00275   }
00276 #endif /*MORK_ENABLE_ZONE_ARENAS*/
00277   
00278   
00279   return outSlots;
00280 }
00281 
00282 morkTable*
00283 morkRowSpace::FindTableByKind(morkEnv* ev, mork_kind inTableKind)
00284 {
00285   if ( inTableKind )
00286   {
00287 #ifdef MORK_BEAD_OVER_NODE_MAPS
00288 
00289     morkTableMapIter i(ev, &mRowSpace_Tables);
00290     morkTable* table = i.FirstTable(ev);
00291     for ( ; table && ev->Good(); table = i.NextTable(ev) )
00292           
00293 #else /*MORK_BEAD_OVER_NODE_MAPS*/
00294     mork_tid* key = 0; // nil pointer to suppress key access
00295     morkTable* table = 0; // old table in the map
00296 
00297     mork_change* c = 0;
00298     morkTableMapIter i(ev, &mRowSpace_Tables);
00299     for ( c = i.FirstTable(ev, key, &table); c && ev->Good();
00300           c = i.NextTable(ev, key, &table) )
00301 #endif /*MORK_BEAD_OVER_NODE_MAPS*/
00302     {
00303       if ( table->mTable_Kind == inTableKind )
00304         return table;
00305     }
00306   }
00307   else
00308     this->ZeroKindError(ev);
00309     
00310   return (morkTable*) 0;
00311 }
00312 
00313 morkTable*
00314 morkRowSpace::NewTableWithTid(morkEnv* ev, mork_tid inTid,
00315   mork_kind inTableKind,
00316   const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying 
00317 {
00318   morkTable* outTable = 0;
00319   morkStore* store = mSpace_Store;
00320   
00321   if ( inTableKind && store )
00322   {
00323     mdb_bool mustBeUnique = morkBool_kFalse;
00324     nsIMdbHeap* heap = store->mPort_Heap;
00325     morkTable* table = new(*heap, ev)
00326       morkTable(ev, morkUsage::kHeap, heap, store, heap, this,
00327         inOptionalMetaRowOid, inTid, inTableKind, mustBeUnique);
00328     if ( table )
00329     {
00330       if ( mRowSpace_Tables.AddTable(ev, table) )
00331       {
00332         outTable = table;
00333         if ( mRowSpace_NextTableId <= inTid )
00334           mRowSpace_NextTableId = inTid + 1;
00335       }
00336         
00337       if ( this->IsRowSpaceClean() && store->mStore_CanDirty )
00338         this->MaybeDirtyStoreAndSpace(); // morkTable does already
00339 
00340     }
00341   }
00342   else if ( store )
00343     this->ZeroKindError(ev);
00344   else
00345     this->NilSpaceStoreError(ev);
00346     
00347   return outTable;
00348 }
00349 
00350 morkTable*
00351 morkRowSpace::NewTable(morkEnv* ev, mork_kind inTableKind,
00352   mdb_bool inMustBeUnique,
00353   const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying 
00354 {
00355   morkTable* outTable = 0;
00356   morkStore* store = mSpace_Store;
00357   
00358   if ( inTableKind && store )
00359   {
00360     if ( inMustBeUnique ) // need to look for existing table first?
00361       outTable = this->FindTableByKind(ev, inTableKind);
00362       
00363     if ( !outTable && ev->Good() )
00364     {
00365       mork_tid id = this->MakeNewTableId(ev);
00366       if ( id )
00367       {
00368         nsIMdbHeap* heap = mSpace_Store->mPort_Heap;
00369         morkTable* table = new(*heap, ev)
00370           morkTable(ev, morkUsage::kHeap, heap, mSpace_Store, heap, this,
00371             inOptionalMetaRowOid, id, inTableKind, inMustBeUnique);
00372         if ( table )
00373         {
00374           if ( mRowSpace_Tables.AddTable(ev, table) )
00375             outTable = table;
00376           else
00377             table->Release();
00378 
00379           if ( this->IsRowSpaceClean() && store->mStore_CanDirty )
00380             this->MaybeDirtyStoreAndSpace(); // morkTable does already
00381         }
00382       }
00383     }
00384   }
00385   else if ( store )
00386     this->ZeroKindError(ev);
00387   else
00388     this->NilSpaceStoreError(ev);
00389     
00390   return outTable;
00391 }
00392 
00393 mork_tid
00394 morkRowSpace::MakeNewTableId(morkEnv* ev)
00395 {
00396   mork_tid outTid = 0;
00397   mork_tid id = mRowSpace_NextTableId;
00398   mork_num count = 9; // try up to eight times
00399   
00400   while ( !outTid && --count ) // still trying to find an unused table ID?
00401   {
00402     if ( !mRowSpace_Tables.GetTable(ev, id) )
00403       outTid = id;
00404     else
00405     {
00406       MORK_ASSERT(morkBool_kFalse); // alert developer about ID problems
00407       ++id;
00408     }
00409   }
00410   
00411   mRowSpace_NextTableId = id + 1;
00412   return outTid;
00413 }
00414 
00415 mork_rid
00416 morkRowSpace::MakeNewRowId(morkEnv* ev)
00417 {
00418   mork_rid outRid = 0;
00419   mork_rid id = mRowSpace_NextRowId;
00420   mork_num count = 9; // try up to eight times
00421   mdbOid oid;
00422   oid.mOid_Scope = this->SpaceScope();
00423   
00424   while ( !outRid && --count ) // still trying to find an unused row ID?
00425   {
00426     oid.mOid_Id = id;
00427     if ( !mRowSpace_Rows.GetOid(ev, &oid) )
00428       outRid = id;
00429     else
00430     {
00431       MORK_ASSERT(morkBool_kFalse); // alert developer about ID problems
00432       ++id;
00433     }
00434   }
00435   
00436   mRowSpace_NextRowId = id + 1;
00437   return outRid;
00438 }
00439 
00440 morkAtomRowMap*
00441 morkRowSpace::make_index(morkEnv* ev, mork_column inCol)
00442 {
00443   morkAtomRowMap* outMap = 0;
00444   nsIMdbHeap* heap = mRowSpace_SlotHeap;
00445   if ( heap ) // have expected heap for allocations?
00446   {
00447     morkAtomRowMap* map = new(*heap, ev)
00448       morkAtomRowMap(ev, morkUsage::kHeap, heap, heap, inCol);
00449     
00450     if ( map ) // able to create new map index?
00451     {
00452       if ( ev->Good() ) // no errors during construction?
00453       {
00454 #ifdef MORK_ENABLE_PROBE_MAPS
00455         morkRowProbeMapIter i(ev, &mRowSpace_Rows);
00456 #else /*MORK_ENABLE_PROBE_MAPS*/
00457         morkRowMapIter i(ev, &mRowSpace_Rows);
00458 #endif /*MORK_ENABLE_PROBE_MAPS*/
00459         mork_change* c = 0;
00460         morkRow* row = 0;
00461         mork_aid aidKey = 0;
00462         
00463         for ( c = i.FirstRow(ev, &row); c && ev->Good();
00464               c = i.NextRow(ev, &row) ) // another row in space?
00465         {
00466           aidKey = row->GetCellAtomAid(ev, inCol);
00467           if ( aidKey ) // row has indexed attribute?
00468             map->AddAid(ev, aidKey, row); // include in map
00469         }
00470       }
00471       if ( ev->Good() ) // no errors constructing index?
00472         outMap = map; // return from function
00473       else
00474         map->CutStrongRef(ev); // discard map on error
00475     }
00476   }
00477   else
00478     ev->NilPointerError();
00479   
00480   return outMap;
00481 }
00482 
00483 morkAtomRowMap*
00484 morkRowSpace::ForceMap(morkEnv* ev, mork_column inCol)
00485 {
00486   morkAtomRowMap* outMap = this->FindMap(ev, inCol);
00487   
00488   if ( !outMap && ev->Good() ) // no such existing index?
00489   {
00490     if ( mRowSpace_IndexCount < morkRowSpace_kMaxIndexCount )
00491     {
00492       morkAtomRowMap* map = this->make_index(ev, inCol);
00493       if ( map ) // created a new index for col?
00494       {
00495         mork_count wrap = 0; // count times wrap-around occurs
00496         morkAtomRowMap** slot = mRowSpace_IndexCache; // table
00497         morkAtomRowMap** end = slot + morkRowSpace_kPrimeCacheSize;
00498         slot += ( inCol % morkRowSpace_kPrimeCacheSize ); // hash
00499         while ( *slot ) // empty slot not yet found?
00500         {
00501           if ( ++slot >= end ) // wrap around?
00502           {
00503             slot = mRowSpace_IndexCache; // back to table start
00504             if ( ++wrap > 1 ) // wrapped more than once?
00505             {
00506               ev->NewError("no free cache slots"); // disaster
00507               break; // end while loop
00508             }
00509           }
00510         }
00511         if ( ev->Good() ) // everything went just fine?
00512         {
00513           ++mRowSpace_IndexCount; // note another new map
00514           *slot = map; // install map in the hash table
00515           outMap = map; // return the new map from function
00516         }
00517         else
00518           map->CutStrongRef(ev); // discard map on error
00519       }
00520     }
00521     else
00522       ev->NewError("too many indexes"); // why so many indexes?
00523   }
00524   return outMap;
00525 }
00526 
00527 morkAtomRowMap*
00528 morkRowSpace::FindMap(morkEnv* ev, mork_column inCol)
00529 {
00530   if ( mRowSpace_IndexCount && ev->Good() )
00531   {
00532     mork_count wrap = 0; // count times wrap-around occurs
00533     morkAtomRowMap** slot = mRowSpace_IndexCache; // table
00534     morkAtomRowMap** end = slot + morkRowSpace_kPrimeCacheSize;
00535     slot += ( inCol % morkRowSpace_kPrimeCacheSize ); // hash
00536     morkAtomRowMap* map = *slot;
00537     while ( map ) // another used slot to examine?
00538     {
00539       if ( inCol == map->mAtomRowMap_IndexColumn ) // found col?
00540         return map;
00541       if ( ++slot >= end ) // wrap around?
00542       {
00543         slot = mRowSpace_IndexCache;
00544         if ( ++wrap > 1 ) // wrapped more than once?
00545           return (morkAtomRowMap*) 0; // stop searching
00546       }
00547       map = *slot;
00548     }
00549   }
00550   return (morkAtomRowMap*) 0;
00551 }
00552 
00553 morkRow*
00554 morkRowSpace::FindRow(morkEnv* ev, mork_column inCol, const mdbYarn* inYarn)
00555 {
00556   morkRow* outRow = 0;
00557 
00558   // if yarn hasn't been atomized, there can't be a corresponding row,
00559   // so pass in PR_FALSE to not create the row - should help history bloat
00560   morkAtom* atom = mSpace_Store->YarnToAtom(ev, inYarn, PR_FALSE);
00561   if ( atom ) // have or created an atom corresponding to input yarn?
00562   {
00563     mork_aid atomAid = atom->GetBookAtomAid();
00564     if ( atomAid ) // atom has an identity for use in hash table?
00565     {
00566       morkAtomRowMap* map = this->ForceMap(ev, inCol);
00567       if ( map ) // able to find or create index for col?
00568       {
00569         outRow = map->GetAid(ev, atomAid); // search for row
00570       }
00571     }
00572   }
00573   
00574   return outRow;
00575 }
00576 
00577 morkRow*
00578 morkRowSpace::NewRowWithOid(morkEnv* ev, const mdbOid* inOid)
00579 {
00580   morkRow* outRow = mRowSpace_Rows.GetOid(ev, inOid);
00581   MORK_ASSERT(outRow==0);
00582   if ( !outRow && ev->Good() )
00583   {
00584     morkStore* store = mSpace_Store;
00585     if ( store )
00586     {
00587       morkPool* pool = this->GetSpaceStorePool();
00588       morkRow* row = pool->NewRow(ev, &store->mStore_Zone);
00589       if ( row )
00590       {
00591         row->InitRow(ev, inOid, this, /*length*/ 0, pool);
00592         
00593         if ( ev->Good() && mRowSpace_Rows.AddRow(ev, row) )
00594         {
00595           outRow = row;
00596           mork_rid rid = inOid->mOid_Id;
00597           if ( mRowSpace_NextRowId <= rid )
00598             mRowSpace_NextRowId = rid + 1;
00599         }
00600         else
00601           pool->ZapRow(ev, row, &store->mStore_Zone);
00602 
00603         if ( this->IsRowSpaceClean() && store->mStore_CanDirty )
00604           this->MaybeDirtyStoreAndSpace(); // InitRow() does already
00605       }
00606     }
00607     else
00608       this->NilSpaceStoreError(ev);
00609   }
00610   return outRow;
00611 }
00612 
00613 morkRow*
00614 morkRowSpace::NewRow(morkEnv* ev)
00615 {
00616   morkRow* outRow = 0;
00617   if ( ev->Good() )
00618   {
00619     mork_rid id = this->MakeNewRowId(ev);
00620     if ( id )
00621     {
00622       morkStore* store = mSpace_Store;
00623       if ( store )
00624       {
00625         mdbOid oid;
00626         oid.mOid_Scope = this->SpaceScope();
00627         oid.mOid_Id = id;
00628         morkPool* pool = this->GetSpaceStorePool();
00629         morkRow* row = pool->NewRow(ev, &store->mStore_Zone);
00630         if ( row )
00631         {
00632           row->InitRow(ev, &oid, this, /*length*/ 0, pool);
00633           
00634           if ( ev->Good() && mRowSpace_Rows.AddRow(ev, row) )
00635             outRow = row;
00636           else
00637             pool->ZapRow(ev, row, &store->mStore_Zone);
00638 
00639           if ( this->IsRowSpaceClean() && store->mStore_CanDirty )
00640             this->MaybeDirtyStoreAndSpace(); // InitRow() does already
00641         }
00642       }
00643       else
00644         this->NilSpaceStoreError(ev);
00645     }
00646   }
00647   return outRow;
00648 }
00649 
00650 
00651 //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
00652 
00653 
00654 morkRowSpaceMap::~morkRowSpaceMap()
00655 {
00656 }
00657 
00658 morkRowSpaceMap::morkRowSpaceMap(morkEnv* ev, const morkUsage& inUsage,
00659   nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
00660   : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap)
00661 {
00662   if ( ev->Good() )
00663     mNode_Derived = morkDerived_kRowSpaceMap;
00664 }
00665 
00666 //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789