1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef _MORKSINK_
7 #define _MORKSINK_ 1
8 
9 #ifndef _MORK_
10 #  include "mork.h"
11 #endif
12 
13 #ifndef _MORKBLOB_
14 #  include "morkBlob.h"
15 #endif
16 
17 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
18 
19 /*| morkSink is intended to be a very cheap buffered i/o sink which
20 **| writes to bufs and other strings a single byte at a time.  The
21 **| basic idea is that writing a single byte has a very cheap average
22 **| cost, because a polymophic function call need only occur when the
23 **| space between At and End is exhausted.  The rest of the time a
24 **| very cheap inline method will write a byte, and then bump a pointer.
25 **|
26 **|| At: the current position in some sequence of bytes at which to
27 **| write the next byte put into the sink.  Presumably At points into
28 **| the private storage of some space which is not yet filled (except
29 **| when At reaches End, and the overflow must then spill).  Note both
30 **| At and End are zeroed in the destructor to help show that a sink
31 **| is no longer usable; this is safe because At==End causes the case
32 **| where SpillPutc() is called to handled an exhausted buffer space.
33 **|
34 **|| End: an address one byte past the last byte which can be written
35 **| without needing to make a buffer larger than previously.  When At
36 **| and End are equal, this means there is no space to write a byte,
37 **| and that some underlying buffer space must be grown before another
38 **| byte can be written.  Note At must always be less than or equal to
39 **| End, and otherwise an important invariant has failed severely.
40 **|
41 **|| Buf: this original class slot has been commented out in the new
42 **| and more abstract version of this sink class, but the general idea
43 **| behind this slot should be explained to help design subclasses.
44 **| Each subclass should provide space into which At and End can point,
45 **| where End is beyond the last writable byte, and At is less than or
46 **| equal to this point inside the same buffer.  With some kinds of
47 **| medium, such as writing to an instance of morkBlob, it is feasible
48 **| to point directly into the final resting place for all the content
49 **| written to the medium.  Other mediums such as files, which write
50 **| only through function calls, will typically need a local buffer
51 **| to efficiently accumulate many bytes between such function calls.
52 **|
53 **|| FlushSink: this flush method should move any buffered content to
54 **| its final destination.  For example, for buffered writes to a
55 **| string medium, where string methods are function calls and not just
56 **| inline macros, it is faster to accumulate many bytes in a small
57 **| local buffer and then move these en masse later in a single call.
58 **|
59 **|| SpillPutc: when At is greater than or equal to End, this means an
60 **| underlying buffer has become full, so the buffer must be flushed
61 **| before a new byte can be written.  The intention is that SpillPutc()
62 **| will be equivalent to calling FlushSink() followed by another call
63 **| to Putc(), where the flush is expected to make At less then End once
64 **| again.  Except that FlushSink() need not make the underlying buffer
65 **| any larger, and SpillPutc() typically must make room for more bytes.
66 **| Note subclasses might want to guard against the case that both At
67 **| and End are null, which happens when a sink is destroyed, which sets
68 **| both these pointers to null as an indication the sink is disabled.
69 |*/
70 class morkSink {
71   // ````` ````` ````` `````   ````` ````` ````` `````
72  public:  // public sink virtual methods
73   virtual void FlushSink(morkEnv* ev) = 0;
74   virtual void SpillPutc(morkEnv* ev, int c) = 0;
75 
76   // ````` ````` ````` `````   ````` ````` ````` `````
77  public:               // member variables
78   mork_u1* mSink_At;   // pointer into mSink_Buf
79   mork_u1* mSink_End;  // one byte past last content byte
80 
81   // define morkSink_kBufSize 256 /* small enough to go on stack */
82 
83   // mork_u1      mSink_Buf[ morkSink_kBufSize + 4 ];
84   // want plus one for any needed end null byte; use plus 4 for alignment
85 
86   // ````` ````` ````` `````   ````` ````` ````` `````
87  public:                // public non-poly morkSink methods
88   virtual ~morkSink();  // zero both At and End; virtual for subclasses
morkSink()89   morkSink() {}         // does nothing; subclasses must set At and End suitably
90 
Putc(morkEnv * ev,int c)91   void Putc(morkEnv* ev, int c) {
92     if (mSink_At < mSink_End)
93       *mSink_At++ = (mork_u1)c;
94     else
95       this->SpillPutc(ev, c);
96   }
97 };
98 
99 /*| morkSpool: an output sink that efficiently writes individual bytes
100 **| or entire byte sequences to a coil instance, which grows as needed by
101 **| using the heap instance in the coil to grow the internal buffer.
102 **|
103 **|| Note we do not "own" the coil referenced by mSpool_Coil, and
104 **| the lifetime of the coil is expected to equal or exceed that of this
105 **| sink by some external means.  Typical usage might involve keeping an
106 **| instance of morkCoil and an instance of morkSpool in the same
107 **| owning parent object, which uses the spool with the associated coil.
108 |*/
109 class morkSpool : public morkSink {  // for buffered i/o to a morkCoil
110 
111   // ````` ````` ````` `````   ````` ````` ````` `````
112  public:  // public sink virtual methods
113   // when morkSink::Putc() moves mSink_At, mSpool_Coil->mBuf_Fill is wrong:
114 
115   virtual void FlushSink(morkEnv* ev);         // sync mSpool_Coil->mBuf_Fill
116   virtual void SpillPutc(morkEnv* ev, int c);  // grow coil and write byte
117 
118   // ````` ````` ````` `````   ````` ````` ````` `````
119  public:                  // member variables
120   morkCoil* mSpool_Coil;  // destination medium for written bytes
121 
122   // ````` ````` ````` `````   ````` ````` ````` `````
123  public:  // public non-poly morkSink methods
124   static void BadSpoolCursorOrderError(morkEnv* ev);
125   static void NilSpoolCoilError(morkEnv* ev);
126 
127   virtual ~morkSpool();
128   // Zero all slots to show this sink is disabled, but destroy no memory.
129   // Note it is typically unnecessary to flush this coil sink, since all
130   // content is written directly to the coil without any buffering.
131 
132   morkSpool(morkEnv* ev, morkCoil* ioCoil);
133   // After installing the coil, calls Seek(ev, 0) to prepare for writing.
134 
135   // ----- All boolean return values below are equal to ev->Good(): -----
136 
137   mork_bool Seek(morkEnv* ev, mork_pos inPos);
138   // Changed the current write position in coil's buffer to inPos.
139   // For example, to start writing the coil from scratch, use inPos==0.
140 
141   mork_bool Write(morkEnv* ev, const void* inBuf, mork_size inSize);
142   // write inSize bytes of inBuf to current position inside coil's buffer
143 
PutBuf(morkEnv * ev,const morkBuf & inBuffer)144   mork_bool PutBuf(morkEnv* ev, const morkBuf& inBuffer) {
145     return this->Write(ev, inBuffer.mBuf_Body, inBuffer.mBuf_Fill);
146   }
147 
148   mork_bool PutString(morkEnv* ev, const char* inString);
149   // call Write() with inBuf=inString and inSize=strlen(inString),
150   // unless inString is null, in which case we then do nothing at all.
151 };
152 
153 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
154 
155 #endif /* _MORKSINK_ */
156