summaryrefslogtreecommitdiff
path: root/db/mork/src/morkSink.h
blob: 4d501e76c8bd827e60d215c14b6084bb2ec6e2ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/* -*- 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

//3456789_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.
};

//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789

#endif /* _MORKSINK_ */