Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _MORKSINK_
#define _MORKSINK_ 1
#ifndef _MORK_
# include "mork.h"
#endif
#ifndef _MORKBLOB_
# include "morkBlob.h"
#endif
// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
/*| morkSink is intended to be a very cheap buffered i/o sink which
**| writes to bufs and other strings a single byte at a time. The
**| basic idea is that writing a single byte has a very cheap average
**| cost, because a polymophic function call need only occur when the
**| space between At and End is exhausted. The rest of the time a
**| very cheap inline method will write a byte, and then bump a pointer.
**|
**|| At: the current position in some sequence of bytes at which to
**| write the next byte put into the sink. Presumably At points into
**| the private storage of some space which is not yet filled (except
**| when At reaches End, and the overflow must then spill). Note both
**| At and End are zeroed in the destructor to help show that a sink
**| is no longer usable; this is safe because At==End causes the case
**| where SpillPutc() is called to handled an exhausted buffer space.
**|
**|| End: an address one byte past the last byte which can be written
**| without needing to make a buffer larger than previously. When At
**| and End are equal, this means there is no space to write a byte,
**| and that some underlying buffer space must be grown before another
**| byte can be written. Note At must always be less than or equal to
**| End, and otherwise an important invariant has failed severely.
**|
**|| Buf: this original class slot has been commented out in the new
**| and more abstract version of this sink class, but the general idea
**| behind this slot should be explained to help design subclasses.
**| Each subclass should provide space into which At and End can point,
**| where End is beyond the last writable byte, and At is less than or
**| equal to this point inside the same buffer. With some kinds of
**| medium, such as writing to an instance of morkBlob, it is feasible
**| to point directly into the final resting place for all the content
**| written to the medium. Other mediums such as files, which write
**| only through function calls, will typically need a local buffer
**| to efficiently accumulate many bytes between such function calls.
**|
**|| FlushSink: this flush method should move any buffered content to
**| its final destination. For example, for buffered writes to a
**| string medium, where string methods are function calls and not just
**| inline macros, it is faster to accumulate many bytes in a small
**| local buffer and then move these en masse later in a single call.
**|
**|| SpillPutc: when At is greater than or equal to End, this means an
**| underlying buffer has become full, so the buffer must be flushed
**| before a new byte can be written. The intention is that SpillPutc()
**| will be equivalent to calling FlushSink() followed by another call
**| to Putc(), where the flush is expected to make At less then End once
**| again. Except that FlushSink() need not make the underlying buffer
**| any larger, and SpillPutc() typically must make room for more bytes.
**| Note subclasses might want to guard against the case that both At
**| and End are null, which happens when a sink is destroyed, which sets
**| both these pointers to null as an indication the sink is disabled.
|*/
class morkSink {
// ````` ````` ````` ````` ````` ````` ````` `````
public: // public sink virtual methods
virtual void FlushSink(morkEnv* ev) = 0;
virtual void SpillPutc(morkEnv* ev, int c) = 0;
// ````` ````` ````` ````` ````` ````` ````` `````
public: // member variables
mork_u1* mSink_At; // pointer into mSink_Buf
mork_u1* mSink_End; // one byte past last content byte
// define morkSink_kBufSize 256 /* small enough to go on stack */
// mork_u1 mSink_Buf[ morkSink_kBufSize + 4 ];
// want plus one for any needed end null byte; use plus 4 for alignment
// ````` ````` ````` ````` ````` ````` ````` `````
public: // public non-poly morkSink methods
virtual ~morkSink(); // zero both At and End; virtual for subclasses
morkSink() {} // does nothing; subclasses must set At and End suitably
void Putc(morkEnv* ev, int c) {
if (mSink_At < mSink_End)
*mSink_At++ = (mork_u1)c;
else
this->SpillPutc(ev, c);
}
};
/*| morkSpool: an output sink that efficiently writes individual bytes
**| or entire byte sequences to a coil instance, which grows as needed by
**| using the heap instance in the coil to grow the internal buffer.
**|
**|| Note we do not "own" the coil referenced by mSpool_Coil, and
**| the lifetime of the coil is expected to equal or exceed that of this
**| sink by some external means. Typical usage might involve keeping an
**| instance of morkCoil and an instance of morkSpool in the same
**| owning parent object, which uses the spool with the associated coil.
|*/
class morkSpool : public morkSink { // for buffered i/o to a morkCoil
// ````` ````` ````` ````` ````` ````` ````` `````
public: // public sink virtual methods
// when morkSink::Putc() moves mSink_At, mSpool_Coil->mBuf_Fill is wrong:
virtual void FlushSink(morkEnv* ev); // sync mSpool_Coil->mBuf_Fill
virtual void SpillPutc(morkEnv* ev, int c); // grow coil and write byte
// ````` ````` ````` ````` ````` ````` ````` `````
public: // member variables
morkCoil* mSpool_Coil; // destination medium for written bytes
// ````` ````` ````` ````` ````` ````` ````` `````
public: // public non-poly morkSink methods
static void BadSpoolCursorOrderError(morkEnv* ev);
static void NilSpoolCoilError(morkEnv* ev);
virtual ~morkSpool();
// Zero all slots to show this sink is disabled, but destroy no memory.
// Note it is typically unnecessary to flush this coil sink, since all
// content is written directly to the coil without any buffering.
morkSpool(morkEnv* ev, morkCoil* ioCoil);
// After installing the coil, calls Seek(ev, 0) to prepare for writing.
// ----- All boolean return values below are equal to ev->Good(): -----
mork_bool Seek(morkEnv* ev, mork_pos inPos);
// Changed the current write position in coil's buffer to inPos.
// For example, to start writing the coil from scratch, use inPos==0.
mork_bool Write(morkEnv* ev, const void* inBuf, mork_size inSize);
// write inSize bytes of inBuf to current position inside coil's buffer
mork_bool PutBuf(morkEnv* ev, const morkBuf& inBuffer) {
return this->Write(ev, inBuffer.mBuf_Body, inBuffer.mBuf_Fill);
}
mork_bool PutString(morkEnv* ev, const char* inString);
// call Write() with inBuf=inString and inSize=strlen(inString),
// unless inString is null, in which case we then do nothing at all.
};
// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
#endif /* _MORKSINK_ */