roo_io
API Documentation for roo_io
Loading...
Searching...
No Matches
file_input_iterator.h
Go to the documentation of this file.
1#pragma once
2
3#ifdef ARDUINO
4
5#include <FS.h>
6
7#include <memory>
8
9#include "roo_backport/byte.h"
11#include "roo_io/status.h"
12
13namespace roo_io {
14
15static const size_t kFileInputIteratorBufferSize = 64;
16
18 public:
20
21 byte read() { return rep_->read(); }
22
23 size_t read(byte* buf, size_t count) { return rep_->read(buf, count); }
24
25 void skip(size_t count) { rep_->skip(count); }
26 Status status() const { return rep_->status(); }
27
28 uint64_t size() const { return rep_->size(); }
29 uint64_t position() const { return rep_->position(); }
30
31 void rewind() { rep_->rewind(); }
32 void seek(uint64_t position) { rep_->seek(position); }
33
34 // void reset(::File file) { rep_->reset(&input); }
35 // void reset() { rep_->reset(nullptr); }
36
37 private:
38 class Rep {
39 public:
40 Rep(::fs::File file);
41 ~Rep();
42 byte read();
43 size_t read(byte* buf, size_t count);
44 void skip(size_t count);
45 Status status() const { return status_; }
46
47 uint64_t size() const;
48 uint64_t position() const;
49
50 void rewind();
51 void seek(uint64_t position);
52
53 private:
54 Rep(const Rep&) = delete;
55 Rep(Rep&&) = delete;
56 Rep& operator=(const Rep&) = delete;
57
58 ::fs::File file_;
59 byte buffer_[kFileInputIteratorBufferSize];
60 uint8_t offset_;
61 uint8_t length_;
62 Status status_;
63 };
64
65 // We keep the content on the heap for the following reasons:
66 // * stack space is very limited, and we need some buffer cache;
67 // * underlying file structures are using heap anyway;
68 // * we want the stream object to be cheaply movable.
69 std::unique_ptr<Rep> rep_;
70};
71
72inline ArduinoFileInputIterator::Rep::Rep(::fs::File file)
73 : file_(std::move(file)),
74 offset_(0),
75 length_(0),
76 status_(file_ ? kOk : kClosed) {}
77
78inline ArduinoFileInputIterator::Rep::~Rep() { file_.close(); }
79
80inline uint64_t ArduinoFileInputIterator::Rep::size() const {
81 return file_.size();
82}
83
84inline uint64_t ArduinoFileInputIterator::Rep::position() const {
85 return file_.position() + offset_ - length_;
86}
87
88inline void ArduinoFileInputIterator::Rep::rewind() {
89 if (status_ != kOk && status_ != kEndOfStream) return;
90 uint64_t file_pos = file_.position();
91 if (file_pos <= length_) {
92 // Keep the buffer data and length.
93 offset_ = 0;
94 status_ = kOk;
95 } else {
96 // Reset the buffer.
97 offset_ = 0;
98 length_ = 0;
99 status_ = file_.seek(0, SeekSet) ? kOk : kSeekError;
100 }
101}
102
103inline void ArduinoFileInputIterator::Rep::seek(uint64_t position) {
104 if (status_ != kOk && status_ != kEndOfStream) return;
105 uint64_t file_pos = file_.position();
106 if (file_pos <= position + length_ && file_pos >= position) {
107 // Seek within the area we have in the buffer.
108 offset_ = position + length_ - file_pos;
109 status_ = kOk;
110 } else {
111 // Seek outside the buffer. Just seek in the file and reset the buffer.
112 offset_ = 0;
113 length_ = 0;
114 status_ = file_.seek(position, SeekSet) ? kOk : kSeekError;
115 }
116}
117
118inline byte ArduinoFileInputIterator::Rep::read() {
119 if (offset_ < length_) {
120 return buffer_[offset_++];
121 }
122 if (status_ != kOk) return byte{0};
123 size_t len = file_.read((uint8_t*)buffer_, kFileInputIteratorBufferSize);
124 if (len == 0) {
125 offset_ = 0;
126 length_ = 0;
127 status_ = kEndOfStream;
128 return byte{0};
129 } else if (len == ((size_t)(-1))) {
130 offset_ = 0;
131 length_ = 0;
132 status_ = kReadError;
133 return byte{0};
134 }
135 offset_ = 1;
136 length_ = len;
137 return buffer_[0];
138}
139
140inline size_t ArduinoFileInputIterator::Rep::read(byte* buf, size_t count) {
141 if (offset_ < length_) {
142 // Have some data still in the buffer; just return that.
143 const size_t available = static_cast<size_t>(length_ - offset_);
144 if (count > available) count = available;
145 memcpy(buf, &buffer_[offset_], count);
146 offset_ += count;
147 return count;
148 }
149 if (status_ != kOk) {
150 // Already done.
151 return 0;
152 }
153 if (count >= kFileInputIteratorBufferSize) {
154 // Skip buffering; read directly into the client's buffer.
155 size_t len = file_.read((uint8_t*)buf, count);
156 if (len == 0) {
157 offset_ = 0;
158 length_ = 0;
159 status_ = kEndOfStream;
160 return 0;
161 } else if (len == ((size_t)(-1))) {
162 offset_ = 0;
163 length_ = 0;
164 status_ = kReadError;
165 return 0;
166 }
167 return len;
168 }
169 size_t len = file_.read((uint8_t*)buffer_, kFileInputIteratorBufferSize);
170 if (len == 0) {
171 offset_ = 0;
172 length_ = 0;
173 status_ = kEndOfStream;
174 return 0;
175 } else if (len == ((size_t)(-1))) {
176 offset_ = 0;
177 length_ = 0;
178 status_ = kReadError;
179 return 0;
180 }
181 length_ = len;
182 if (count > length_) count = length_;
183 memcpy(buf, buffer_, count);
184 offset_ = count;
185 return count;
186}
187
188inline void ArduinoFileInputIterator::Rep::skip(size_t count) {
189 if (status_ != kOk) return;
190 size_t remaining = (length_ - offset_);
191 if (count < remaining) {
192 offset_ += count;
193 } else {
194 offset_ = 0;
195 length_ = 0;
196 if (!file_.seek(count - remaining, SeekCur)) {
197 status_ = kSeekError;
198 }
199 if (file_.position() > file_.size()) {
200 status_ = kEndOfStream;
201 }
202 }
203}
204
205} // namespace roo_io
206
207#endif // ARDUINO
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
Status
Definition status.h:7
@ kOk
Definition status.h:8
@ kSeekError
Definition status.h:14
@ kClosed
Definition status.h:10
@ kReadError
Definition status.h:13
@ kEndOfStream
Definition status.h:9