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
|
The class tt(std::iostream)hi(iostream) offers reading and writing from/to
tt(std::streambuf) objects. In practice the tt(iostream) is provided with a
class which is derived from tt(std::streambuf), overriding the tt(streambuf's)
reading and writing virtual members. This derived class is referred to as
tt(StreamBuf) below. This section covers the commonly encountered
characteristics of tt(StreamBuf).
itemization(
it() The tt(iostream's) seek operations for reading and writing are
synchronized: using tt(seekg) or tt(seekp) updates both tt(tellg) and
tt(tellp).
it() Seek operations do not reload tt(StreamBuf's) data buffer. For
example: assume tt(StreamBuf's) data buffer contains subsequent blocks of
1000 characters from tt(StreamBuf's) device, and the
last read or write operation resulted in tt(tellg) returning 1100, so the 2nd
block of 1000 characters is available in tt(StreamBuf's) buffer. Then
issuing tt(seekp(250)) won't result in loading the 1st block of 1000
characters. Neither will, assuming that the device's size is 10,000 bytes,
tt(seekg(12000)) have any effect, other than tt(tellp) or tt(tellg) returning
12,000. Then, after issuing tt(tellg(1200)), both read or
write operations will use the currently loaded buffer, starting the
operation at the buffer's offset 200.
it() tt(tellg) and tt(tellp) functions call tt(StreamBuf's seekoff(0,
ios::cur)) member, returning the device's current position managed by
tt(StreamBuf)
)
tt(StreamBuf), therefore, should keep track of the current position in the
device, and also of which area of the device is currently loaded in its
buffer. Once a block has been loaded the tt(iostream's) functions tt(setg) and
tt(setp) are used to define the begin and end positions of the loaded buffer,
and tt(std::streambuf's) members tt(pbump) and tt(gbump) can be used to
relocate the position in the buffer where tt(gptr()) and tt(pptr()) refer to.
Once the tt(iostream) is used for reading or writing the tt(StreamBuf's)
members tt(underflow) and tt(overflow) are called when, respectively,
tt(gptr() == eback()) and tt(pptr() == epptr()).
The complexity here is that reading and writing updates tt(gptr()) and
tt(pptr()) but, since these positions are modified by the tt(iostream) object,
the current device position maintained by the tt(StreamBuf) object isn't
updated as well. Somehow tt(StreamBuf) must manage that situation.
The current read/write position, however, is primarily relevant when
performing seek operations relative to the currently used device
position. When the requested seek position is relative to the device's begin
or end position then the resulting position is simply the sum of those
positions and the requested addition. But when the requested seek position is
relative to the current position then the current position must be
used. However, tt(seekpos) and tt(seekoff) can also be called when no buffer
has as yet been loaded from the device.
Here is one way to solve this complexity (for implementation details refer to
the tt(MmapBuf) class of the
url(Bobcat library)(http://fbb-git.gitlab.io/bobcat/)).
hi(Bobcat library)hi(http://fbb-git.gitlab.io/bobcat/)
itemization(
it() The class tt(StreamBuf) uses the following members to manage the
current situation:
itemization(
itt(d_pos) keeps track of the current position in the device;
itt(d_activeBuffer) is tt(true) if a buffer was loaded from the device
and is actively being used (by read/write operations). It's
tt(false) if not;
itt(d_buffer) is 0 if no buffer was loaded from the device. Otherwise
it points to the location of the allocated buffer;
itt(d_bufSize) contains the size of the allocated buffer;
itt(d_offset) corresponds to the physical offset in the device
corresponding to the buffer's first character position;
itt(d_sync) is set to true when the content of the currently loaded
buffer was modified by a write-operation.
)
)
At a tt(seek) request:
itemization(
it() At the end of a seek request tt(setp) and tt(setg) are called with
0 arguments (forcing tt(underflow) and tt(overflow) calls at the next
read/write operation), and tt(d_activeBuffer) is set to false
(tt(d_buffer) may be defined, but it's not actively used following a
seek operation).
it() Seek requests relative to the device's begin and end positions simply
update tt(d_pos). With tt(ios::cur) specifications: if currently
there's no active buffer then no read/write position has been
requested since the last seek operation, so tt(d_pos) holds the latest
postion, and is updated with the requested tt(pos) argument. However,
em(if) there's an active buffer, then the current position is
tt(pos + d_offset) plus the maximum of the current read (tt(gptr()))
or write (tt(pptr())) position in the loaded buffer.
it() Finally tt(d_pos) is returned.
)
The members tt(underflow) and tt(overflow) are called when, respectively, the
read and write buffer pointers are equal to the buffer's end-address.
tt(StreamBuf::underflow):
itemization(
it() the last action may have been a write action (tt(d_sync == true)). If
so the currently loaded buffer is written to the device.
it() if there's a currently active buffer the buffer was exhaused and
tt(d_pos) is set to the device's position beyond the buffer (i.e.,
tt(d_offset + d_bufSize));
it() if a buffer is available and tt(d_pos) corresponds to a position in
the currently loaded buffer then the buffer's tt(gptr()) is set to
that position. Otherwise:
it() If tt(d_pos) exceeds the device's size tt(EOF) is returned. Otherwise
tt(d_offset) is updated (tt(d_offset = d_pos / d_bufSize * d_bufSize))
and a new buffer is loaded from the device.
)
tt(StreamBuf::overflow):
itemization(
it() if there's a currently active buffer the buffer was exhaused and
tt(d_pos) is set to the device's position beyond the
buffer. Otherwise:
it() if a buffer is available and tt(d_pos) corresponds to a position in
the currently loaded buffer then the buffer's tt(pptr()) is set to
that position and the overflowing character is written into the buffer
and returned by tt(overflow). Otherwise:
it() if a buffer is available it's flushed to the device. Then
tt(d_offset) is updated (tt(d_offset = d_pos / d_bufSize * d_bufSize))
and a new buffer is loaded from the device.
)
|