comparison foosdk/sdk/foobar2000/helpers/readers.h @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
comparison
equal deleted inserted replaced
0:e9bb126753e7 1:20d02a178406
1 #pragma once
2
3 class NOVTABLE reader_membuffer_base : public file_readonly {
4 public:
5 reader_membuffer_base() : m_offset(0) {}
6
7 t_size read(void * p_buffer, t_size p_bytes, abort_callback & p_abort) override;
8
9 void write(const void * p_buffer, t_size p_bytes, abort_callback & p_abort) override { throw exception_io_denied(); }
10
11 t_filesize get_size(abort_callback & p_abort) override { return get_buffer_size(); }
12 t_filesize get_position(abort_callback & p_abort) override { return m_offset; }
13 void seek(t_filesize position, abort_callback & p_abort) override;
14 void reopen(abort_callback & p_abort) override { seek(0, p_abort); }
15
16 bool can_seek() override { return true; }
17 bool is_in_memory() override { return true; }
18
19 protected:
20 virtual const void * get_buffer() = 0;
21 virtual t_size get_buffer_size() = 0;
22 //virtual t_filetimestamp get_timestamp(abort_callback & p_abort) = 0;
23 bool get_content_type(pfc::string_base &) override { return false; }
24 inline void seek_internal(t_size p_offset) { if (p_offset > get_buffer_size()) throw exception_io_seek_out_of_range(); m_offset = p_offset; }
25 private:
26 t_size m_offset;
27 };
28
29 class reader_membuffer_simple : public reader_membuffer_base {
30 public:
31 reader_membuffer_simple(pfc::mem_block&& block, t_filetimestamp ts = filetimestamp_invalid, bool is_remote = false) : m_data(std::move(block)), m_ts(ts), m_isRemote(is_remote) {}
32 reader_membuffer_simple(const void * ptr, t_size size, t_filetimestamp ts = filetimestamp_invalid, bool is_remote = false) : m_ts(ts), m_isRemote(is_remote) {
33 m_data.resize(size);
34 if ( ptr != nullptr ) memcpy(m_data.get_ptr(), ptr, size);
35 }
36 const void * get_buffer() { return m_data.get_ptr(); }
37 void* _get_write_buffer() { return m_data.get_ptr(); }
38 t_size get_buffer_size() { return m_data.get_size(); }
39 t_filetimestamp get_timestamp(abort_callback & p_abort) { return m_ts; }
40 bool is_remote() { return m_isRemote; }
41
42 private:
43 pfc::mem_block m_data;
44 t_filetimestamp m_ts;
45 bool m_isRemote;
46 };
47
48 class reader_membuffer_mirror : public reader_membuffer_base
49 {
50 public:
51 t_filetimestamp get_timestamp(abort_callback & p_abort) { return m_timestamp; }
52 bool is_remote() { return m_remote; }
53
54 //! Returns false when the object could not be mirrored (too big) or did not need mirroring.
55 static bool g_create(service_ptr_t<file> & p_out, const service_ptr_t<file> & p_src, abort_callback & p_abort) {
56 service_ptr_t<reader_membuffer_mirror> ptr = new service_impl_t<reader_membuffer_mirror>();
57 if (!ptr->init(p_src, p_abort)) return false;
58 p_out = ptr.get_ptr();
59 return true;
60 }
61 bool get_content_type(pfc::string_base & out) {
62 if (m_contentType.is_empty()) return false;
63 out = m_contentType; return true;
64 }
65 private:
66 const void * get_buffer() { return m_buffer.get_ptr(); }
67 t_size get_buffer_size() { return m_buffer.get_size(); }
68
69 bool init(const service_ptr_t<file> & p_src, abort_callback & p_abort) {
70 if (p_src->is_in_memory()) return false;//already buffered
71 if (!p_src->get_content_type(m_contentType)) m_contentType.reset();
72 m_remote = p_src->is_remote();
73
74 t_size size = pfc::downcast_guarded<t_size>(p_src->get_size(p_abort));
75
76 m_buffer.set_size(size);
77
78 p_src->reopen(p_abort);
79
80 p_src->read_object(m_buffer.get_ptr(), size, p_abort);
81
82 m_timestamp = p_src->get_timestamp(p_abort);
83
84 return true;
85 }
86
87
88 t_filetimestamp m_timestamp;
89 pfc::array_t<char> m_buffer;
90 bool m_remote;
91 pfc::string8 m_contentType;
92
93 };
94
95 class reader_limited : public file_readonly_t<file_v2> {
96 service_ptr_t<file> r;
97 t_filesize begin;
98 t_filesize end;
99
100 public:
101 static file::ptr g_create(file::ptr base, t_filesize offset, t_filesize size, abort_callback & abort) {
102 service_ptr_t<reader_limited> r = new service_impl_t<reader_limited>();
103 if (offset + size < offset) throw pfc::exception_overflow();
104 r->init(base, offset, offset + size, abort);
105 return r;
106 }
107 reader_limited() { begin = 0;end = 0; }
108 reader_limited(const service_ptr_t<file> & p_r, t_filesize p_begin, t_filesize p_end, abort_callback & p_abort) {
109 r = p_r;
110 begin = p_begin;
111 end = p_end;
112 reopen(p_abort);
113 }
114
115 void init(const service_ptr_t<file> & p_r, t_filesize p_begin, t_filesize p_end, abort_callback & p_abort) {
116 r = p_r;
117 begin = p_begin;
118 end = p_end;
119 reopen(p_abort);
120 }
121
122 service_ptr get_metadata(abort_callback& a) override {return r->get_metadata_(a);}
123 t_filestats2 get_stats2(uint32_t f, abort_callback& a) override {
124 auto ret = r->get_stats2_(f, a);
125 ret.m_size = this->get_size(a);
126 return ret;
127 }
128 t_filetimestamp get_timestamp(abort_callback & p_abort) override { return r->get_timestamp(p_abort); }
129
130 t_size read(void *p_buffer, t_size p_bytes, abort_callback & p_abort) override {
131 t_filesize pos;
132 pos = r->get_position(p_abort);
133 if (p_bytes > end - pos) p_bytes = (t_size)(end - pos);
134 return r->read(p_buffer, p_bytes, p_abort);
135 }
136
137 t_filesize get_size(abort_callback & p_abort) override { return end - begin; }
138
139 t_filesize get_position(abort_callback & p_abort) override {
140 return r->get_position(p_abort) - begin;
141 }
142
143 void seek(t_filesize position, abort_callback & p_abort) override {
144 if (position > end) throw exception_io_seek_out_of_range();
145 r->seek(position + begin, p_abort);
146 }
147 bool can_seek() override { return r->can_seek(); }
148 bool is_remote() override { return r->is_remote(); }
149
150 bool get_content_type(pfc::string_base & out) override { return r->get_content_type(out); }
151
152 void reopen(abort_callback & p_abort) override {
153 seekInternal(begin, p_abort);
154 }
155 private:
156 void seekInternal(t_filesize position, abort_callback & abort) {
157 if (r->can_seek()) {
158 r->seek(position, abort);
159 } else {
160 t_filesize positionWas = r->get_position(abort);
161 if (positionWas == filesize_invalid || positionWas > position) {
162 r->reopen(abort);
163 try { r->skip_object(position, abort); }
164 catch (exception_io_data const &) { throw exception_io_seek_out_of_range(); }
165 } else {
166 t_filesize skipMe = position - positionWas;
167 if (skipMe > 0) {
168 try { r->skip_object(skipMe, abort); }
169 catch (exception_io_data const &) { throw exception_io_seek_out_of_range(); }
170 }
171 }
172 }
173 }
174 };
175
176
177
178 // A more clever version of reader_membuffer_*.
179 // Behaves more nicely with large files within 32bit address space.
180 class reader_bigmem : public file_readonly_t<file_v2> {
181 public:
182 reader_bigmem() : m_offset() {}
183 t_size read(void * p_buffer, t_size p_bytes, abort_callback & p_abort) override {
184 pfc::min_acc(p_bytes, remaining());
185 m_mem.read(p_buffer, p_bytes, m_offset);
186 m_offset += p_bytes;
187 return p_bytes;
188 }
189 void read_object(void * p_buffer, t_size p_bytes, abort_callback & p_abort) override {
190 if (p_bytes > remaining()) throw exception_io_data_truncation();
191 m_mem.read(p_buffer, p_bytes, m_offset);
192 m_offset += p_bytes;
193 }
194 t_filesize skip(t_filesize p_bytes, abort_callback & p_abort) override {
195 pfc::min_acc(p_bytes, (t_filesize)remaining());
196 m_offset += (size_t)p_bytes;
197 return p_bytes;
198 }
199 void skip_object(t_filesize p_bytes, abort_callback & p_abort) override {
200 if (p_bytes > remaining()) throw exception_io_data_truncation();
201 m_offset += (size_t)p_bytes;
202 }
203
204 t_filesize get_size(abort_callback & p_abort) override { p_abort.check(); return m_mem.size(); }
205 t_filesize get_position(abort_callback & p_abort) override { p_abort.check(); return m_offset; }
206 void seek(t_filesize p_position, abort_callback & p_abort) override {
207 if (p_position > m_mem.size()) throw exception_io_seek_out_of_range();
208 m_offset = (size_t)p_position;
209 }
210 bool can_seek() override { return true; }
211 bool is_in_memory() override { return true; }
212 void reopen(abort_callback & p_abort) override { seek(0, p_abort); }
213
214 // To be overridden by individual derived classes
215 bool get_content_type(pfc::string_base & p_out) override { return false; }
216 bool is_remote() override { return false; }
217 service_ptr get_metadata(abort_callback&) override { return nullptr; }
218
219 t_filestats2 get_stats2(uint32_t f, abort_callback& a) override {
220 t_filestats2 ret;
221 ret.set_file();
222 ret.m_size = get_size(a);
223 if ( f & stats2_timestamp ) ret.m_timestamp = this->get_timestamp(a);
224 return ret;
225 }
226 protected:
227 void resize(size_t newSize) {
228 m_offset = 0;
229 m_mem.resize(newSize);
230 }
231 size_t remaining() const { return m_mem.size() - m_offset; }
232 pfc::bigmem m_mem;
233 size_t m_offset;
234 };
235
236 class reader_bigmem_mirror : public reader_bigmem {
237 public:
238 reader_bigmem_mirror() {}
239
240 void init(file::ptr source, abort_callback & abort) {
241 source->reopen(abort);
242 m_metadata = source->get_metadata_(abort);
243 t_filesize fs = source->get_size(abort);
244 if (fs > 1024 * 1024 * 1024) { // reject > 1GB
245 throw std::bad_alloc();
246 }
247 size_t s = (size_t)fs;
248 resize(s);
249 for (size_t walk = 0; walk < m_mem._sliceCount(); ++walk) {
250 source->read(m_mem._slicePtr(walk), m_mem._sliceSize(walk), abort);
251 }
252
253 if (!source->get_content_type(m_contentType)) m_contentType.reset();
254 m_isRemote = source->is_remote();
255 m_stats2 = source->get_stats2_(stats2_all, abort);
256 }
257
258 bool get_content_type(pfc::string_base & p_out) override {
259 if (m_contentType.is_empty()) return false;
260 p_out = m_contentType; return true;
261 }
262 t_filetimestamp get_timestamp(abort_callback & p_abort) override { return m_stats2.m_timestamp; }
263 bool is_remote() override { return m_isRemote; }
264 service_ptr get_metadata(abort_callback&) override { return m_metadata; }
265 t_filestats2 get_stats2(uint32_t f, abort_callback& a) override {
266 a.check(); (void)f; return m_stats2;
267 }
268 private:
269 service_ptr m_metadata;
270 t_filestats2 m_stats2;
271 pfc::string8 m_contentType;
272 bool m_isRemote = false;
273 };
274
275 class file_chain : public file_v2 {
276 public:
277 service_ptr get_metadata(abort_callback& a) override {
278 return m_file->get_metadata_(a);
279 }
280 t_filestats2 get_stats2(uint32_t f, abort_callback& a) override {
281 return m_file->get_stats2_(f, a);
282 }
283 t_size read(void * p_buffer, t_size p_bytes, abort_callback & p_abort) override {
284 return m_file->read(p_buffer, p_bytes, p_abort);
285 }
286 void read_object(void * p_buffer, t_size p_bytes, abort_callback & p_abort) override {
287 m_file->read_object(p_buffer, p_bytes, p_abort);
288 }
289 t_filesize skip(t_filesize p_bytes, abort_callback & p_abort) override {
290 return m_file->skip(p_bytes, p_abort);
291 }
292 void skip_object(t_filesize p_bytes, abort_callback & p_abort) override {
293 m_file->skip_object(p_bytes, p_abort);
294 }
295 void write(const void * p_buffer, t_size p_bytes, abort_callback & p_abort) override {
296 m_file->write(p_buffer, p_bytes, p_abort);
297 }
298
299 t_filesize get_size(abort_callback & p_abort) override {
300 return m_file->get_size(p_abort);
301 }
302
303 t_filesize get_position(abort_callback & p_abort) override {
304 return m_file->get_position(p_abort);
305 }
306
307 void resize(t_filesize p_size, abort_callback & p_abort) override {
308 m_file->resize(p_size, p_abort);
309 }
310
311 void seek(t_filesize p_position, abort_callback & p_abort) override {
312 m_file->seek(p_position, p_abort);
313 }
314
315 void seek_ex(t_sfilesize p_position, t_seek_mode p_mode, abort_callback & p_abort) override {
316 m_file->seek_ex(p_position, p_mode, p_abort);
317 }
318
319 bool can_seek() override { return m_file->can_seek(); }
320 bool get_content_type(pfc::string_base & p_out) override { return m_file->get_content_type(p_out); }
321 bool is_in_memory() override { return m_file->is_in_memory(); }
322 void on_idle(abort_callback & p_abort) override { m_file->on_idle(p_abort); }
323 t_filetimestamp get_timestamp(abort_callback & p_abort) override { return m_file->get_timestamp(p_abort); }
324 void reopen(abort_callback & p_abort) override { m_file->reopen(p_abort); }
325 bool is_remote() override { return m_file->is_remote(); }
326
327 file_chain(file::ptr chain) : m_file(chain) {}
328 private:
329 file::ptr m_file;
330 };
331
332 class file_chain_readonly : public file_chain {
333 public:
334 void write(const void * p_buffer, t_size p_bytes, abort_callback & p_abort) override { throw exception_io_denied(); }
335 void resize(t_filesize p_size, abort_callback & p_abort) override { throw exception_io_denied(); }
336 file_chain_readonly(file::ptr chain) : file_chain(chain) {}
337 static file::ptr create(file::ptr chain) { return new service_impl_t< file_chain_readonly >(chain); }
338 };