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