roo_io
API Documentation for roo_io
Loading...
Searching...
No Matches
buffered_multipass_input_stream_iterator.h
Go to the documentation of this file.
1#pragma once
2
3#include <cstring>
4#include <memory>
5
8
9namespace roo_io {
10
12
14 public:
15 /// Creates a detached iterator with `kClosed` status.
17 : input_(nullptr),
18 buffer_(nullptr),
19 offset_(0),
20 length_(0),
21 status_(kClosed) {}
22
23 /// Creates iterator over `input`.
24 ///
25 /// Initializes `status()` from `input.status()`. Allocates internal buffer
26 /// when initial status is `kOk` or `kEndOfStream`.
28 : input_(&input), offset_(0), length_(0), status_(input.status()) {
29 buffer_ = (status_ == kOk || status_ == kEndOfStream)
30 ? std::unique_ptr<byte[]>(
32 : nullptr;
33 }
34
35 /// Move-constructs iterator state.
36 ///
37 /// Source iterator becomes detached with `kClosed` status.
40 : input_(other.input_),
41 buffer_(std::move(other.buffer_)),
42 offset_(other.offset_),
43 length_(other.length_),
44 status_(other.status_) {
45 other.input_ = nullptr;
46 other.offset_ = 0;
47 other.length_ = 0;
48 other.status_ = kClosed;
49 }
50
51 /// Move-assigns iterator state.
52 ///
53 /// Source iterator becomes detached with `kClosed` status.
56 if (this != &other) {
57 input_ = other.input_;
58 buffer_ = std::move(other.buffer_);
59 offset_ = other.offset_;
60 length_ = other.length_;
61 status_ = other.status_;
62 other.input_ = nullptr;
63 other.offset_ = 0;
64 other.length_ = 0;
65 other.status_ = kClosed;
66 }
67 return *this;
68 }
69
70 /// Reads one byte.
71 ///
72 /// If buffered data is available, returns it without changing `status()`.
73 /// If `status() != kOk`, returns zero byte and leaves status unchanged.
74 /// Otherwise reads from underlying stream; when that read returns zero,
75 /// updates `status()` from `input.status()`.
76 ///
77 /// @return Read byte, or zero byte when no byte can be read.
78 byte read() {
79 if (offset_ < length_) {
80 return buffer_[offset_++];
81 }
82 if (status_ != kOk) return byte{0};
83 size_t len =
84 input_->read(buffer_.get(), kMultipassInputStreamIteratorBufferSize);
85 if (len == 0) {
86 offset_ = 0;
87 length_ = 0;
88 status_ = input_->status();
89 return byte{0};
90 }
91 offset_ = 1;
92 length_ = len;
93 return buffer_[0];
94 }
95
96 /// Reads up to `count` bytes into `buf`.
97 ///
98 /// Uses buffered bytes first. If `status() != kOk`, returns zero and leaves
99 /// status unchanged. When delegated stream read returns zero, updates
100 /// `status()` from `input.status()`.
101 ///
102 /// @return Number of bytes read.
103 size_t read(byte* buf, size_t count) {
104 if (offset_ < length_) {
105 // Have some data still in the buffer; just return that.
106 size_t remaining = static_cast<size_t>(length_ - offset_);
108 memcpy(buf, &buffer_[offset_], count);
109 offset_ += count;
110 return count;
111 }
112 if (status_ != kOk) {
113 // Already done.
114 return 0;
115 }
117 // Skip buffering; read directly into the client's buffer.
118 size_t len = input_->read(buf, count);
119 if (len == 0) {
120 offset_ = 0;
121 length_ = 0;
122 status_ = input_->status();
123 }
124 return len;
125 }
126 size_t len =
127 input_->read(buffer_.get(), kMultipassInputStreamIteratorBufferSize);
128 if (len == 0) {
129 offset_ = 0;
130 length_ = 0;
131 status_ = input_->status();
132 return 0;
133 }
134 length_ = len;
135 if (count > static_cast<size_t>(length_))
136 count = static_cast<size_t>(length_);
137 memcpy(buf, buffer_.get(), count);
138 offset_ = count;
139 return count;
140 }
141
142 /// Skips up to `count` bytes.
143 ///
144 /// If skip is satisfied from buffered bytes, `status()` is unchanged.
145 /// Otherwise clears local buffer state and, when `status() == kOk`, delegates
146 /// remaining skip to underlying stream and updates `status()` from
147 /// `input.status()`.
148 void skip(size_t count) {
149 size_t remaining = (length_ - offset_);
150 if (count < remaining) {
151 offset_ += count;
152 } else {
153 offset_ = 0;
154 length_ = 0;
155 if (status_ != kOk) return;
156 input_->skip(count - remaining);
157 status_ = input_->status();
158 }
159 }
160
161 /// Returns current iterator status.
162 ///
163 /// @return Current status value.
164 Status status() const { return status_; }
165
166 /// Returns stream size when iterator status is `kOk` or `kEndOfStream`.
167 ///
168 /// Otherwise returns zero.
169 ///
170 /// @return Stream size, or zero when iterator is in other statuses.
171 uint64_t size() const {
172 return status_ == kOk || status_ == kEndOfStream ? input_->size() : 0;
173 }
174
175 /// Returns current read position when status is `kOk` or `kEndOfStream`.
176 ///
177 /// Otherwise returns zero.
178 ///
179 /// @return Current position, or zero when iterator is in other statuses.
181 return (status_ == kOk || status_ == kEndOfStream)
182 ? input_->position() + offset_ - length_
183 : 0;
184 }
185
186 /// Rewinds to stream start.
187 ///
188 /// If status is neither `kOk` nor `kEndOfStream`, no-op.
189 /// If current position still lies within buffered window, only adjusts
190 /// buffer offset and leaves status unchanged.
191 /// Otherwise delegates rewind to underlying stream, clears buffer, and
192 /// updates `status()` from `input.status()`.
193 void rewind() {
194 if (status_ != kOk && status_ != kEndOfStream) return;
195 uint64_t file_pos = input_->position();
196 if (file_pos <= length_) {
197 // Keep the buffer data and length.
198 offset_ = 0;
199 } else {
200 // Reset the buffer.
201 input_->rewind();
202 offset_ = 0;
203 length_ = 0;
204 status_ = input_->status();
205 }
206 }
207
208 /// Seeks to absolute `position`.
209 ///
210 /// If status is neither `kOk` nor `kEndOfStream`, no-op.
211 /// If target lies within buffered window, adjusts offset only.
212 /// Otherwise delegates seek to underlying stream, clears buffer, and
213 /// synchronizes from `input.status()`.
214 ///
215 /// As implemented, accepted seek requests set iterator status to `kOk`
216 /// at the end of the call.
218 if (status_ != kOk && status_ != kEndOfStream) return;
219 uint64_t file_pos = input_->position();
221 // Seek within the area we have in the buffer.
222 offset_ = position + length_ - file_pos;
223 } else {
224 // Seek outside the buffer. Just seek in the file and reset the buffer.
225 input_->seek(position);
226 offset_ = 0;
227 length_ = 0;
228 status_ = input_->status();
229 }
230 status_ = kOk;
231 }
232
233 /// Returns whether `status() == kOk`.
234 ///
235 /// @return `true` iff current status is `kOk`.
236 bool ok() const { return status() == roo_io::kOk; }
237
238 /// Returns whether `status() == kEndOfStream`.
239 ///
240 /// @return `true` iff current status is `kEndOfStream`.
241 bool eos() const { return status() == roo_io::kEndOfStream; }
242
243 /// Rebinds iterator to `input` and clears buffered state.
244 ///
245 /// Updates `status()` to `input.status()`. Allocates buffer lazily when
246 /// needed and status is `kOk`/`kEndOfStream`.
248 input_ = &input;
249 offset_ = 0;
250 length_ = 0;
251 status_ = input.status();
252 if ((status_ == kOk || status_ == kEndOfStream) && buffer_ == nullptr) {
253 buffer_ = std::unique_ptr<byte[]>(
255 }
256 }
257
258 /// Detaches from stream and releases internal buffer.
259 ///
260 /// Sets status to `kClosed`.
261 void reset() {
262 input_ = nullptr;
263 buffer_ = nullptr;
264 offset_ = 0;
265 length_ = 0;
266 status_ = kClosed;
267 }
268
269 private:
271 std::unique_ptr<byte[]> buffer_;
272 uint8_t offset_;
273 uint8_t length_;
274 Status status_;
275};
276
277} // namespace roo_io
BufferedMultipassInputStreamIterator(roo_io::MultipassInputStream &input)
Creates iterator over input.
size_t read(byte *buf, size_t count)
Reads up to count bytes into buf.
void reset()
Detaches from stream and releases internal buffer.
void reset(roo_io::MultipassInputStream &input)
Rebinds iterator to input and clears buffered state.
void seek(uint64_t position)
Seeks to absolute position.
BufferedMultipassInputStreamIterator(BufferedMultipassInputStreamIterator &&other)
Move-constructs iterator state.
uint64_t size() const
Returns stream size when iterator status is kOk or kEndOfStream.
uint64_t position() const
Returns current read position when status is kOk or kEndOfStream.
BufferedMultipassInputStreamIterator()
Creates a detached iterator with kClosed status.
BufferedMultipassInputStreamIterator & operator=(BufferedMultipassInputStreamIterator &&other)
Move-assigns iterator state.
bool eos() const
Returns whether status() == kEndOfStream.
virtual size_t read(byte *result, size_t count)=0
Attempts to read up to count bytes into result.
virtual void skip(uint64_t count)
Skips over count bytes, updating status().
virtual Status status() const =0
Returns status of the most recent I/O operation.
Virtualizes access to files, memory, and other readable sources.
virtual uint64_t position() const =0
Returns current byte offset from beginning of stream.
virtual uint64_t size()=0
Returns stream size in bytes from beginning.
virtual void seek(uint64_t offset)=0
Seeks to byte offset from beginning.
virtual void rewind()
Resets stream to starting position.
Definition byte.h:6
roo::basic_string_view< CharT, Traits > basic_string_view
Definition string_view.h:8
size_t count
Definition compare.h:45
static const size_t kMultipassInputStreamIteratorBufferSize
Status
Definition status.h:7
@ kOk
Definition status.h:8
@ kClosed
Definition status.h:10
@ kEndOfStream
Definition status.h:9