Back to index

lightning-sunbird  0.9+nobinonly
morkStream.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 _MORKFILE_
00051 #include "morkFile.h"
00052 #endif
00053 
00054 #ifndef _MORKENV_
00055 #include "morkEnv.h"
00056 #endif
00057 
00058 #ifndef _MORKSTREAM_
00059 #include "morkStream.h"
00060 #endif
00061 
00062 //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
00063 
00064 // ````` ````` ````` ````` ````` 
00065 // { ===== begin morkNode interface =====
00066 
00067 /*public virtual*/ void
00068 morkStream::CloseMorkNode(morkEnv* ev) // CloseStream() only if open
00069 {
00070   if ( this->IsOpenNode() )
00071   {
00072     this->MarkClosing();
00073     this->CloseStream(ev);
00074     this->MarkShut();
00075   }
00076 }
00077 
00078 /*public virtual*/
00079 morkStream::~morkStream() // assert CloseStream() executed earlier
00080 {
00081   MORK_ASSERT(mStream_ContentFile==0);
00082   MORK_ASSERT(mStream_Buf==0);
00083 }
00084 
00085 /*public non-poly*/
00086 morkStream::morkStream(morkEnv* ev, const morkUsage& inUsage,
00087   nsIMdbHeap* ioHeap,
00088   nsIMdbFile* ioContentFile, mork_size inBufSize, mork_bool inFrozen)
00089 : morkFile(ev, inUsage, ioHeap, ioHeap)
00090 , mStream_At( 0 )
00091 , mStream_ReadEnd( 0 )
00092 , mStream_WriteEnd( 0 )
00093 
00094 , mStream_ContentFile( 0 )
00095 
00096 , mStream_Buf( 0 )
00097 , mStream_BufSize( inBufSize )
00098 , mStream_BufPos( 0 )
00099 , mStream_Dirty( morkBool_kFalse )
00100 , mStream_HitEof( morkBool_kFalse )
00101 {
00102   if ( ev->Good() )
00103   {
00104     if ( inBufSize < morkStream_kMinBufSize )
00105       mStream_BufSize = inBufSize = morkStream_kMinBufSize;
00106     else if ( inBufSize > morkStream_kMaxBufSize )
00107       mStream_BufSize = inBufSize = morkStream_kMaxBufSize;
00108     
00109     if ( ioContentFile && ioHeap )
00110     {
00111       // if ( ioContentFile->FileFrozen() ) // forced to be readonly?
00112       //   inFrozen = morkBool_kTrue; // override the input value
00113         
00114       nsIMdbFile_SlotStrongFile(ioContentFile, ev, &mStream_ContentFile);
00115       if ( ev->Good() )
00116       {
00117         mork_u1* buf = 0;
00118         ioHeap->Alloc(ev->AsMdbEnv(), inBufSize, (void**) &buf);
00119         if ( buf )
00120         {
00121           mStream_At = mStream_Buf = buf;
00122           
00123           if ( !inFrozen )
00124           {
00125             // physical buffer end never moves:
00126             mStream_WriteEnd = buf + inBufSize;
00127           }
00128           else
00129             mStream_WriteEnd = 0; // no writing is allowed
00130           
00131           if ( inFrozen )
00132           {
00133             // logical buffer end starts at Buf with no content:
00134             mStream_ReadEnd = buf;
00135             this->SetFileFrozen(inFrozen);
00136           }
00137           else
00138             mStream_ReadEnd = 0; // no reading is allowed
00139           
00140           this->SetFileActive(morkBool_kTrue);
00141           this->SetFileIoOpen(morkBool_kTrue);
00142         }
00143         if ( ev->Good() )
00144           mNode_Derived = morkDerived_kStream;
00145       }
00146     }
00147     else ev->NilPointerError();
00148   }
00149 }
00150 
00151 /*public non-poly*/ void
00152 morkStream::CloseStream(morkEnv* ev) // called by CloseMorkNode();
00153 {
00154   if ( this )
00155   {
00156     if ( this->IsNode() )
00157     {
00158       nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mStream_ContentFile);
00159       nsIMdbHeap* heap = mFile_SlotHeap;
00160       mork_u1* buf = mStream_Buf;
00161       mStream_Buf = 0;
00162       
00163       if ( heap && buf )
00164         heap->Free(ev->AsMdbEnv(), buf);
00165 
00166       this->CloseFile(ev);
00167       this->MarkShut();
00168     }
00169     else
00170       this->NonNodeError(ev);
00171   }
00172   else
00173     ev->NilPointerError();
00174 }
00175 
00176 // } ===== end morkNode methods =====
00177 // ````` ````` ````` ````` ````` 
00178   
00179 #define morkStream_kSpacesPerIndent 1 /* one space per indent */
00180 #define morkStream_kMaxIndentDepth 70 /* max indent of 70 space bytes */
00181 static const char morkStream_kSpaces[] // next line to ease length perception
00182 = "                                                                        ";
00183 // 123456789_123456789_123456789_123456789_123456789_123456789_123456789_
00184 // morkStream_kSpaces above must contain (at least) 70 spaces (ASCII 0x20)
00185  
00186 mork_size
00187 morkStream::PutIndent(morkEnv* ev, mork_count inDepth)
00188   // PutIndent() puts a linebreak, and then
00189   // "indents" by inDepth, and returns the line length after indentation.
00190 {
00191   mork_size outLength = 0;
00192   nsIMdbEnv *mev = ev->AsMdbEnv();
00193   if ( ev->Good() )
00194   {
00195     this->PutLineBreak(ev);
00196     if ( ev->Good() )
00197     {
00198       outLength = inDepth;
00199       mdb_size bytesWritten;
00200       if ( inDepth )
00201         this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten);
00202     }
00203   }
00204   return outLength;
00205 }
00206 
00207 mork_size
00208 morkStream::PutByteThenIndent(morkEnv* ev, int inByte, mork_count inDepth)
00209   // PutByteThenIndent() puts the byte, then a linebreak, and then
00210   // "indents" by inDepth, and returns the line length after indentation.
00211 {
00212   mork_size outLength = 0;
00213   nsIMdbEnv *mev = ev->AsMdbEnv();
00214   
00215   if ( inDepth > morkStream_kMaxIndentDepth )
00216     inDepth = morkStream_kMaxIndentDepth;
00217   
00218   this->Putc(ev, inByte);
00219   if ( ev->Good() )
00220   {
00221     this->PutLineBreak(ev);
00222     if ( ev->Good() )
00223     {
00224       outLength = inDepth;
00225       mdb_size bytesWritten;
00226       if ( inDepth )
00227         this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten);
00228     }
00229   }
00230   return outLength;
00231 }
00232   
00233 mork_size
00234 morkStream::PutStringThenIndent(morkEnv* ev,
00235   const char* inString, mork_count inDepth)
00236 // PutStringThenIndent() puts the string, then a linebreak, and then
00237 // "indents" by inDepth, and returns the line length after indentation.
00238 {
00239   mork_size outLength = 0;
00240   mdb_size bytesWritten;
00241   nsIMdbEnv *mev = ev->AsMdbEnv();
00242   
00243   if ( inDepth > morkStream_kMaxIndentDepth )
00244     inDepth = morkStream_kMaxIndentDepth;
00245   
00246   if ( inString )
00247   {
00248     mork_size length = MORK_STRLEN(inString);
00249     if ( length && ev->Good() ) // any bytes to write?
00250       this->Write(mev, inString, length, &bytesWritten);
00251   }
00252   
00253   if ( ev->Good() )
00254   {
00255     this->PutLineBreak(ev);
00256     if ( ev->Good() )
00257     {
00258       outLength = inDepth;
00259       if ( inDepth )
00260         this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten);
00261     }
00262   }
00263   return outLength;
00264 }
00265 
00266 mork_size
00267 morkStream::PutString(morkEnv* ev, const char* inString)
00268 {
00269   nsIMdbEnv *mev = ev->AsMdbEnv();
00270   mork_size outSize = 0;
00271   mdb_size bytesWritten;
00272   if ( inString )
00273   {
00274     outSize = MORK_STRLEN(inString);
00275     if ( outSize && ev->Good() ) // any bytes to write?
00276     {
00277       this->Write(mev, inString, outSize, &bytesWritten);
00278     }
00279   }
00280   return outSize;
00281 }
00282 
00283 mork_size
00284 morkStream::PutStringThenNewline(morkEnv* ev, const char* inString)
00285   // PutStringThenNewline() returns total number of bytes written.
00286 {
00287   nsIMdbEnv *mev = ev->AsMdbEnv();
00288   mork_size outSize = 0;
00289   mdb_size bytesWritten;
00290   if ( inString )
00291   {
00292     outSize = MORK_STRLEN(inString);
00293     if ( outSize && ev->Good() ) // any bytes to write?
00294     {
00295       this->Write(mev, inString, outSize, &bytesWritten);
00296       if ( ev->Good() )
00297         outSize += this->PutLineBreak(ev);
00298     }
00299   }
00300   return outSize;
00301 }
00302 
00303 mork_size
00304 morkStream::PutByteThenNewline(morkEnv* ev, int inByte)
00305   // PutByteThenNewline() returns total number of bytes written.
00306 {
00307   mork_size outSize = 1; // one for the following byte
00308   this->Putc(ev, inByte);
00309   if ( ev->Good() )
00310     outSize += this->PutLineBreak(ev);
00311   return outSize;
00312 }
00313 
00314 mork_size
00315 morkStream::PutLineBreak(morkEnv* ev)
00316 {
00317 #if defined(MORK_MAC)
00318 
00319   this->Putc(ev, mork_kCR);
00320   return 1;
00321   
00322 #else
00323 #  if defined(MORK_WIN) || defined(MORK_OS2)
00324   
00325   this->Putc(ev, mork_kCR);
00326   this->Putc(ev, mork_kLF);
00327   return 2;
00328   
00329 #  else
00330 #    if defined(MORK_UNIX) || defined(MORK_BEOS)
00331   
00332   this->Putc(ev, mork_kLF);
00333   return 1;
00334   
00335 #    endif /* MORK_UNIX || MORK_BEOS */
00336 #  endif /* MORK_WIN */
00337 #endif /* MORK_MAC */
00338 }
00339 // ````` ````` ````` `````   ````` ````` ````` `````  
00340 // public: // virtual morkFile methods
00341 
00342 
00343 NS_IMETHODIMP
00344 morkStream::Steal(nsIMdbEnv* mev, nsIMdbFile* ioThief)
00345   // Steal: tell this file to close any associated i/o stream in the file
00346   // system, because the file ioThief intends to reopen the file in order
00347   // to provide the MDB implementation with more exotic file access than is
00348   // offered by the nsIMdbFile alone.  Presumably the thief knows enough
00349   // from Path() in order to know which file to reopen.  If Steal() is
00350   // successful, this file should probably delegate all future calls to
00351   // the nsIMdbFile interface down to the thief files, so that even after
00352   // the file has been stolen, it can still be read, written, or forcibly
00353   // closed (by a call to CloseMdbObject()).
00354 {
00355   MORK_USED_1(ioThief);
00356   morkEnv *ev = morkEnv::FromMdbEnv(mev);
00357   ev->StubMethodOnlyError();
00358   return NS_ERROR_NOT_IMPLEMENTED;
00359 }
00360 
00361 NS_IMETHODIMP
00362 morkStream::BecomeTrunk(nsIMdbEnv* mev)
00363   // If this file is a file version branch created by calling AcquireBud(),
00364   // BecomeTrunk() causes this file's content to replace the original
00365   // file's content, typically by assuming the original file's identity.
00366 {
00367   morkEnv *ev = morkEnv::FromMdbEnv(mev);
00368   ev->StubMethodOnlyError();
00369   return NS_ERROR_NOT_IMPLEMENTED;
00370 }
00371 
00372 NS_IMETHODIMP
00373 morkStream::AcquireBud(nsIMdbEnv* mev, nsIMdbHeap* ioHeap, nsIMdbFile **acqBud)
00374   // AcquireBud() starts a new "branch" version of the file, empty of content,
00375   // so that a new version of the file can be written.  This new file
00376   // can later be told to BecomeTrunk() the original file, so the branch
00377   // created by budding the file will replace the original file.  Some
00378   // file subclasses might initially take the unsafe but expedient
00379   // approach of simply truncating this file down to zero length, and
00380   // then returning the same morkFile pointer as this, with an extra
00381   // reference count increment.  Note that the caller of AcquireBud() is
00382   // expected to eventually call CutStrongRef() on the returned file
00383   // in order to release the strong reference.  High quality versions
00384   // of morkFile subclasses will create entirely new files which later
00385   // are renamed to become the old file, so that better transactional
00386   // behavior is exhibited by the file, so crashes protect old files.
00387   // Note that AcquireBud() is an illegal operation on readonly files.
00388 {
00389   MORK_USED_1(ioHeap);
00390   morkFile* outFile = 0;
00391   nsIMdbFile* file = mStream_ContentFile;
00392   morkEnv *ev = morkEnv::FromMdbEnv(mev);
00393   if ( this->IsOpenAndActiveFile() && file )
00394   {
00395     // figure out how this interacts with buffering and mStream_WriteEnd:
00396     ev->StubMethodOnlyError();
00397   }
00398   else this->NewFileDownError(ev);
00399   
00400   *acqBud = outFile;
00401   return NS_ERROR_NOT_IMPLEMENTED;
00402 }
00403 
00404 mork_pos 
00405 morkStream::Length(morkEnv* ev) const // eof
00406 {
00407   mork_pos outPos = 0;
00408 
00409   nsIMdbFile* file = mStream_ContentFile;
00410   if ( this->IsOpenAndActiveFile() && file )
00411   {
00412     mork_pos contentEof = 0;
00413     file->Eof(ev->AsMdbEnv(), &contentEof);
00414     if ( ev->Good() )
00415     {
00416       if ( mStream_WriteEnd ) // this stream supports writing?
00417       {
00418         // the local buffer might have buffered content past content eof
00419         if ( ev->Good() ) // no error happened during Length() above?
00420         {
00421           mork_u1* at = mStream_At;
00422           mork_u1* buf = mStream_Buf;
00423           if ( at >= buf ) // expected cursor order?
00424           {
00425             mork_pos localContent = mStream_BufPos + (at - buf);
00426             if ( localContent > contentEof ) // buffered past eof?
00427               contentEof = localContent; // return new logical eof
00428 
00429             outPos = contentEof;
00430           }
00431           else this->NewBadCursorOrderError(ev);
00432         }
00433       }
00434       else
00435         outPos = contentEof; // frozen files get length from content file
00436     }
00437   }
00438   else this->NewFileDownError(ev);
00439 
00440   return outPos;
00441 }
00442 
00443 void morkStream::NewBadCursorSlotsError(morkEnv* ev) const
00444 { ev->NewError("bad stream cursor slots"); }
00445 
00446 void morkStream::NewNullStreamBufferError(morkEnv* ev) const
00447 { ev->NewError("null stream buffer"); }
00448 
00449 void morkStream::NewCantReadSinkError(morkEnv* ev) const
00450 { ev->NewError("cant read stream sink"); }
00451 
00452 void morkStream::NewCantWriteSourceError(morkEnv* ev) const
00453 { ev->NewError("cant write stream source"); }
00454 
00455 void morkStream::NewPosBeyondEofError(morkEnv* ev) const
00456 { ev->NewError("stream pos beyond eof"); }
00457 
00458 void morkStream::NewBadCursorOrderError(morkEnv* ev) const
00459 { ev->NewError("bad stream cursor order"); }
00460 
00461 NS_IMETHODIMP 
00462 morkStream::Tell(nsIMdbEnv* mdbev, mork_pos *aOutPos) const
00463 {
00464   nsresult rv = NS_OK;
00465   morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
00466 
00467   NS_ENSURE_ARG_POINTER(aOutPos);
00468   
00469   nsIMdbFile* file = mStream_ContentFile;
00470   if ( this->IsOpenAndActiveFile() && file )
00471   {
00472     mork_u1* buf = mStream_Buf;
00473     mork_u1* at = mStream_At;
00474     
00475     mork_u1* readEnd = mStream_ReadEnd;   // nonzero only if readonly
00476     mork_u1* writeEnd = mStream_WriteEnd; // nonzero only if writeonly
00477     
00478     if ( writeEnd )
00479     {
00480       if ( buf && at >= buf && at <= writeEnd ) 
00481       {
00482         *aOutPos = mStream_BufPos + (at - buf);
00483       }
00484       else this->NewBadCursorOrderError(ev);
00485     }
00486     else if ( readEnd )
00487     {
00488       if ( buf && at >= buf && at <= readEnd ) 
00489       {
00490         *aOutPos = mStream_BufPos + (at - buf);
00491       }
00492       else this->NewBadCursorOrderError(ev);
00493     }
00494   }
00495   else this->NewFileDownError(ev);
00496 
00497   return rv;
00498 }
00499 
00500 NS_IMETHODIMP 
00501 morkStream::Read(nsIMdbEnv* mdbev, void* outBuf, mork_size inSize, mork_size *aOutSize)
00502 {
00503   NS_ENSURE_ARG_POINTER(aOutSize);
00504   // First we satisfy the request from buffered bytes, if any.  Then
00505   // if additional bytes are needed, we satisfy these by direct reads
00506   // from the content file without any local buffering (but we still need
00507   // to adjust the buffer position to reflect the current i/o point).
00508 
00509   morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
00510   nsresult rv = NS_OK;
00511 
00512   nsIMdbFile* file = mStream_ContentFile;
00513   if ( this->IsOpenAndActiveFile() && file )
00514   {
00515     mork_u1* end = mStream_ReadEnd; // byte after last buffered byte
00516     if ( end ) // file is open for read access?
00517     {
00518       if ( inSize ) // caller wants any output?
00519       {
00520         mork_u1* sink = (mork_u1*) outBuf; // where we plan to write bytes
00521         if ( sink ) // caller passed good buffer address?
00522         {
00523           mork_u1* at = mStream_At;
00524           mork_u1* buf = mStream_Buf;
00525           if ( at >= buf && at <= end ) // expected cursor order?
00526           {
00527             mork_num remaining = (mork_num) (end - at); // bytes left in buffer
00528             
00529             mork_num quantum = inSize; // number of bytes to copy
00530             if ( quantum > remaining ) // more than buffer content?
00531               quantum = remaining; // restrict to buffered bytes
00532               
00533             if ( quantum ) // any bytes left in the buffer?
00534             {
00535               MORK_MEMCPY(sink, at, quantum); // from buffer bytes
00536               
00537               at += quantum; // advance past read bytes
00538               mStream_At = at;
00539               *aOutSize += quantum;  // this much copied so far
00540 
00541               sink += quantum;   // in case we need to copy more
00542               inSize -= quantum; // filled this much of request
00543               mStream_HitEof = morkBool_kFalse;
00544             }
00545             
00546             if ( inSize ) // we still need to read more content?
00547             {
00548               // We need to read more bytes directly from the
00549               // content file, without local buffering.  We have
00550               // exhausted the local buffer, so we need to show
00551               // it is now empty, and adjust the current buf pos.
00552               
00553               mork_num posDelta = (mork_num) (at - buf); // old buf content
00554               mStream_BufPos += posDelta;   // past now empty buf
00555               
00556               mStream_At = mStream_ReadEnd = buf; // empty buffer
00557               
00558               // file->Seek(ev, mStream_BufPos); // set file pos
00559               // if ( ev->Good() ) // no seek error?
00560               // {
00561               // }
00562               
00563               mork_num actual = 0;
00564               nsIMdbEnv* menv = ev->AsMdbEnv();
00565               file->Get(menv, sink, inSize, mStream_BufPos, &actual);
00566               if ( ev->Good() ) // no read error?
00567               {
00568                 if ( actual )
00569                 {
00570                   *aOutSize += actual;
00571                   mStream_BufPos += actual;
00572                   mStream_HitEof = morkBool_kFalse;
00573                 }
00574                 else if ( !*aOutSize )
00575                   mStream_HitEof = morkBool_kTrue;
00576               }
00577             }
00578           }
00579           else this->NewBadCursorOrderError(ev);
00580         }
00581         else this->NewNullStreamBufferError(ev);
00582       }
00583     }
00584     else this->NewCantReadSinkError(ev);
00585   }
00586   else this->NewFileDownError(ev);
00587   
00588   if ( ev->Bad() )
00589     *aOutSize = 0;
00590 
00591   return rv;
00592 }
00593 
00594 NS_IMETHODIMP 
00595 morkStream::Seek(nsIMdbEnv * mdbev, mork_pos inPos, mork_pos *aOutPos)
00596 {
00597   NS_ENSURE_ARG_POINTER(aOutPos);
00598   morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
00599   *aOutPos = 0;
00600   nsresult rv = NS_OK;
00601   nsIMdbFile* file = mStream_ContentFile;
00602   if ( this->IsOpenOrClosingNode() && this->FileActive() && file )
00603   {
00604     mork_u1* at = mStream_At;             // current position in buffer
00605     mork_u1* buf = mStream_Buf;           // beginning of buffer 
00606     mork_u1* readEnd = mStream_ReadEnd;   // nonzero only if readonly
00607     mork_u1* writeEnd = mStream_WriteEnd; // nonzero only if writeonly
00608     
00609     if ( writeEnd ) // file is mutable/writeonly?
00610     {
00611       if ( mStream_Dirty ) // need to commit buffer changes?
00612         this->Flush(mdbev);
00613 
00614       if ( ev->Good() ) // no errors during flush or earlier?
00615       {
00616         if ( at == buf ) // expected post flush cursor value?
00617         {
00618           if ( mStream_BufPos != inPos ) // need to change pos?
00619           {
00620             mork_pos eof = 0;
00621             nsIMdbEnv* menv = ev->AsMdbEnv();
00622             file->Eof(menv, &eof);
00623             if ( ev->Good() ) // no errors getting length?
00624             {
00625               if ( inPos <= eof ) // acceptable new position?
00626               {
00627                 mStream_BufPos = inPos; // new stream position
00628                 *aOutPos = inPos;
00629               }
00630               else this->NewPosBeyondEofError(ev);
00631             }
00632           }
00633         }
00634         else this->NewBadCursorOrderError(ev);
00635       }
00636     }
00637     else if ( readEnd ) // file is frozen/readonly?
00638     {
00639       if ( at >= buf && at <= readEnd ) // expected cursor order?
00640       {
00641         mork_pos eof = 0;
00642         nsIMdbEnv* menv = ev->AsMdbEnv();
00643         file->Eof(menv, &eof);
00644         if ( ev->Good() ) // no errors getting length?
00645         {
00646           if ( inPos <= eof ) // acceptable new position?
00647           {
00648             *aOutPos = inPos;
00649             mStream_BufPos = inPos; // new stream position
00650             mStream_At = mStream_ReadEnd = buf; // empty buffer
00651             if ( inPos == eof ) // notice eof reached?
00652               mStream_HitEof = morkBool_kTrue;
00653           }
00654           else this->NewPosBeyondEofError(ev);
00655         }
00656       }
00657       else this->NewBadCursorOrderError(ev);
00658     }
00659       
00660   }
00661   else this->NewFileDownError(ev);
00662 
00663   return rv;
00664 }
00665 
00666 NS_IMETHODIMP 
00667 morkStream::Write(nsIMdbEnv* menv, const void* inBuf, mork_size inSize, mork_size  *aOutSize)
00668 {
00669   mork_num outActual = 0;
00670   morkEnv *ev = morkEnv::FromMdbEnv(menv);
00671 
00672   nsIMdbFile* file = mStream_ContentFile;
00673   if ( this->IsOpenActiveAndMutableFile() && file )
00674   {
00675     mork_u1* end = mStream_WriteEnd; // byte after last buffered byte
00676     if ( end ) // file is open for write access?
00677     {
00678       if ( inSize ) // caller provided any input?
00679       {
00680         const mork_u1* source = (const mork_u1*) inBuf; // from where
00681         if ( source ) // caller passed good buffer address?
00682         {
00683           mork_u1* at = mStream_At;
00684           mork_u1* buf = mStream_Buf;
00685           if ( at >= buf && at <= end ) // expected cursor order?
00686           {
00687             mork_num space = (mork_num) (end - at); // space left in buffer
00688             
00689             mork_num quantum = inSize; // number of bytes to write
00690             if ( quantum > space ) // more than buffer size?
00691               quantum = space; // restrict to avail space
00692               
00693             if ( quantum ) // any space left in the buffer?
00694             {
00695               mStream_Dirty = morkBool_kTrue; // to ensure later flush
00696               MORK_MEMCPY(at, source, quantum); // into buffer
00697               
00698               mStream_At += quantum; // advance past written bytes
00699               outActual += quantum;  // this much written so far
00700 
00701               source += quantum; // in case we need to write more
00702               inSize -= quantum; // filled this much of request
00703             }
00704             
00705             if ( inSize ) // we still need to write more content?
00706             {
00707               // We need to write more bytes directly to the
00708               // content file, without local buffering.  We have
00709               // exhausted the local buffer, so we need to flush
00710               // it and empty it, and adjust the current buf pos.
00711               // After flushing, if the rest of the write fits
00712               // inside the buffer, we will put bytes into the
00713               // buffer rather than write them to content file.
00714               
00715               if ( mStream_Dirty )
00716                 this->Flush(menv); // will update mStream_BufPos
00717 
00718               at = mStream_At;
00719               if ( at < buf || at > end ) // bad cursor?
00720                 this->NewBadCursorOrderError(ev);
00721                 
00722               if ( ev->Good() ) // no errors?
00723               {
00724                 space = (mork_num) (end - at); // space left in buffer
00725                 if ( space > inSize ) // write to buffer?
00726                 {
00727                   mStream_Dirty = morkBool_kTrue; // ensure flush
00728                   MORK_MEMCPY(at, source, inSize); // copy
00729                   
00730                   mStream_At += inSize; // past written bytes
00731                   outActual += inSize;  // this much written
00732                 }
00733                 else // directly to content file instead
00734                 {
00735                   // file->Seek(ev, mStream_BufPos); // set pos
00736                   // if ( ev->Good() ) // no seek error?
00737                   // {
00738                   // }
00739 
00740                   mork_num actual = 0;
00741                   file->Put(menv, source, inSize, mStream_BufPos, &actual);
00742                   if ( ev->Good() ) // no write error?
00743                   {
00744                     outActual += actual;
00745                     mStream_BufPos += actual;
00746                   }
00747                 }
00748               }
00749             }
00750           }
00751           else this->NewBadCursorOrderError(ev);
00752         }
00753         else this->NewNullStreamBufferError(ev);
00754       }
00755     }
00756     else this->NewCantWriteSourceError(ev);
00757   }
00758   else this->NewFileDownError(ev);
00759   
00760   if ( ev->Bad() )
00761     outActual = 0;
00762 
00763   *aOutSize = outActual;
00764   return ev->AsErr();
00765 }
00766 
00767 NS_IMETHODIMP     
00768 morkStream::Flush(nsIMdbEnv* ev)
00769 {
00770   morkEnv *mev = morkEnv::FromMdbEnv(ev);
00771   nsresult rv = NS_ERROR_FAILURE;
00772   nsIMdbFile* file = mStream_ContentFile;
00773   if ( this->IsOpenOrClosingNode() && this->FileActive() && file )
00774   {
00775     if ( mStream_Dirty )
00776       this->spill_buf(mev);
00777 
00778     rv = file->Flush(ev);
00779   }
00780   else this->NewFileDownError(mev);
00781   return rv;
00782 }
00783 
00784 // ````` ````` ````` `````   ````` ````` ````` `````  
00785 // protected: // protected non-poly morkStream methods (for char io)
00786 
00787 int
00788 morkStream::fill_getc(morkEnv* ev)
00789 {
00790   int c = EOF;
00791   
00792   nsIMdbFile* file = mStream_ContentFile;
00793   if ( this->IsOpenAndActiveFile() && file )
00794   {
00795     mork_u1* buf = mStream_Buf;
00796     mork_u1* end = mStream_ReadEnd; // beyond buf after earlier read
00797     if ( end > buf ) // any earlier read bytes buffered?
00798     {
00799       mStream_BufPos += ( end - buf ); // advance past old read
00800     }
00801       
00802     if ( ev->Good() ) // no errors yet?
00803     {
00804       // file->Seek(ev, mStream_BufPos); // set file pos
00805       // if ( ev->Good() ) // no seek error?
00806       // {
00807       // }
00808 
00809       nsIMdbEnv* menv = ev->AsMdbEnv();
00810       mork_num actual = 0;
00811       file->Get(menv, buf, mStream_BufSize, mStream_BufPos, &actual);
00812       if ( ev->Good() ) // no read errors?
00813       {
00814         if ( actual > mStream_BufSize ) // more than asked for??
00815           actual = mStream_BufSize;
00816         
00817         mStream_At = buf;
00818         mStream_ReadEnd = buf + actual;
00819         if ( actual ) // any bytes actually read?
00820         {
00821           c = *mStream_At++; // return first byte from buffer
00822           mStream_HitEof = morkBool_kFalse;
00823         }
00824         else
00825           mStream_HitEof = morkBool_kTrue;
00826       }
00827     }
00828   }
00829   else this->NewFileDownError(ev);
00830   
00831   return c;
00832 }
00833 
00834 void
00835 morkStream::spill_putc(morkEnv* ev, int c)
00836 {
00837   this->spill_buf(ev);
00838   if ( ev->Good() && mStream_At < mStream_WriteEnd )
00839     this->Putc(ev, c);
00840 }
00841 
00842 void
00843 morkStream::spill_buf(morkEnv* ev) // spill/flush from buffer to file
00844 {
00845   nsIMdbFile* file = mStream_ContentFile;
00846   if ( this->IsOpenOrClosingNode() && this->FileActive() && file )
00847   {
00848     mork_u1* buf = mStream_Buf;
00849     if ( mStream_Dirty )
00850     {
00851       mork_u1* at = mStream_At;
00852       if ( at >= buf && at <= mStream_WriteEnd ) // order?
00853       {
00854         mork_num count = (mork_num) (at - buf); // bytes buffered
00855         if ( count ) // anything to write to the string?
00856         {
00857           if ( count > mStream_BufSize ) // no more than max?
00858           {
00859             count = mStream_BufSize;
00860             mStream_WriteEnd = buf + mStream_BufSize;
00861             this->NewBadCursorSlotsError(ev);
00862           }
00863           if ( ev->Good() )
00864           {
00865             // file->Seek(ev, mStream_BufPos);
00866             // if ( ev->Good() )
00867             // {
00868             // }
00869             nsIMdbEnv* menv = ev->AsMdbEnv();
00870             mork_num actual = 0;
00871             
00872             file->Put(menv, buf, count, mStream_BufPos, &actual);
00873             if ( ev->Good() )
00874             {
00875               mStream_BufPos += actual; // past bytes written
00876               mStream_At = buf; // reset buffer cursor
00877               mStream_Dirty = morkBool_kFalse;
00878             }
00879           }
00880         }
00881       }
00882       else this->NewBadCursorOrderError(ev);
00883     }
00884     else
00885     {
00886 #ifdef MORK_DEBUG
00887       ev->NewWarning("stream:spill:not:dirty");
00888 #endif /*MORK_DEBUG*/
00889     }
00890   }
00891   else this->NewFileDownError(ev);
00892 
00893 }
00894 
00895 
00896 //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789