comparison foosdk/sdk/foobar2000/SDK/filesystem_helper.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 #include <functional>
4 #include "filesystem.h"
5
6 namespace foobar2000_io {
7 typedef std::function< void (const char *, t_filestats const & , bool ) > listDirectoryFunc_t;
8 void listDirectory( const char * path, abort_callback & aborter, listDirectoryFunc_t func);
9
10 #ifdef _WIN32
11 pfc::string8 stripParentFolders( const char * inPath );
12 #endif
13
14 void retryOnSharingViolation( std::function<void () > f, double timeout, abort_callback & a);
15 void retryOnSharingViolation( double timeout, abort_callback & a, std::function<void() > f);
16
17 // **** WINDOWS SUCKS ****
18 // Special version of retryOnSharingViolation with workarounds for known MoveFile() bugs.
19 void retryFileMove( double timeout, abort_callback & a, std::function<void()> f);
20
21 // **** WINDOWS SUCKS ****
22 // Special version of retryOnSharingViolation with workarounds for known idiotic problems with folder removal.
23 void retryFileDelete( double timeout, abort_callback & a, std::function<void()> f);
24
25 class listDirectoryCallbackImpl : public directory_callback {
26 public:
27 listDirectoryCallbackImpl() {}
28 listDirectoryCallbackImpl( listDirectoryFunc_t f ) : m_func(f) {}
29 bool on_entry(filesystem *, abort_callback &, const char * p_url, bool p_is_subdirectory, const t_filestats & p_stats) override {
30 m_func(p_url, p_stats, p_is_subdirectory);
31 return true;
32 }
33 listDirectoryFunc_t m_func;
34 };
35
36 #ifdef _WIN32
37 pfc::string8 winGetVolumePath(const char * fb2kPath );
38 DWORD winVolumeFlags( const char * fb2kPath );
39 #endif
40 }
41
42
43 pfc::string8 file_path_canonical(const char* src);
44 pfc::string8 file_path_display(const char* src);
45
46 namespace fb2k {
47 //! Sane replacement for pfc::string_filename_ext(), which isn't safe to use in cross-platform code.
48 //! @returns Filename with extension extracted from path.
49 pfc::string8 filename_ext( const char * path );
50 pfc::string8 filename_ext( const char * path, filesystem::ptr & fs_reuse);
51 //! Sane replacement for pfc::string_filename(), which isn't safe to use in cross-platform code
52 //! @returns Filename without extension extracted from path.
53 pfc::string8 filename( const char * path );
54 }
55
56 class stream_reader_memblock_ref : public stream_reader
57 {
58 public:
59 template<typename t_array> stream_reader_memblock_ref(const t_array & p_array) : m_data(p_array.get_ptr()), m_data_size(p_array.get_size()), m_pointer(0) {
60 pfc::assert_byte_type<typename t_array::t_item>();
61 }
62 stream_reader_memblock_ref(const void * p_data,t_size p_data_size) : m_data((const unsigned char*)p_data), m_data_size(p_data_size), m_pointer(0) {}
63 stream_reader_memblock_ref() : m_data(NULL), m_data_size(0), m_pointer(0) {}
64
65 template<typename t_array> void set_data(const t_array & data) {
66 pfc::assert_byte_type<typename t_array::t_item>();
67 set_data(data.get_ptr(), data.get_size());
68 }
69
70 void set_data(const void * data, t_size dataSize) {
71 m_pointer = 0;
72 m_data = reinterpret_cast<const unsigned char*>(data);
73 m_data_size = dataSize;
74 }
75
76 t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
77 p_abort.check();
78 t_size delta = pfc::min_t(p_bytes, get_remaining());
79 memcpy(p_buffer,m_data+m_pointer,delta);
80 m_pointer += delta;
81 return delta;
82 }
83 void read_object(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
84 p_abort.check();
85 if (p_bytes > get_remaining()) throw exception_io_data_truncation();
86 memcpy(p_buffer,m_data+m_pointer,p_bytes);
87 m_pointer += p_bytes;
88 }
89 t_filesize skip(t_filesize p_bytes,abort_callback & p_abort) {
90 p_abort.check();
91 t_size remaining = get_remaining();
92 if (p_bytes >= remaining) {
93 m_pointer = m_data_size; return remaining;
94 } else {
95 m_pointer += (t_size)p_bytes; return p_bytes;
96 }
97 }
98 void skip_object(t_filesize p_bytes,abort_callback & p_abort) {
99 p_abort.check();
100 if (p_bytes > get_remaining()) {
101 throw exception_io_data_truncation();
102 } else {
103 m_pointer += (t_size)p_bytes;
104 }
105 }
106 void seek_(t_size offset) {
107 PFC_ASSERT( offset <= m_data_size );
108 m_pointer = offset;
109 }
110 const void * get_ptr_() const {return m_data + m_pointer;}
111 t_size get_remaining() const {return m_data_size - m_pointer;}
112 void reset() {m_pointer = 0;}
113 private:
114 const unsigned char * m_data;
115 t_size m_data_size,m_pointer;
116 };
117
118 template<typename buffer_t>
119 class stream_writer_buffer_simple_t : public stream_writer {
120 public:
121 void write(const void * p_buffer, t_size p_bytes, abort_callback & p_abort) {
122 p_abort.check();
123 t_size base = m_buffer.get_size();
124 if (base + p_bytes < base) throw std::bad_alloc();
125 m_buffer.set_size(base + p_bytes);
126 memcpy((t_uint8*)m_buffer.get_ptr() + base, p_buffer, p_bytes);
127 }
128 typedef buffer_t t_buffer; // other classes reference this
129 t_buffer m_buffer;
130 };
131
132 typedef stream_writer_buffer_simple_t< pfc::array_t<t_uint8, pfc::alloc_fast> > stream_writer_buffer_simple;
133
134 template<class t_storage>
135 class stream_writer_buffer_append_ref_t : public stream_writer
136 {
137 public:
138 stream_writer_buffer_append_ref_t(t_storage & p_output) : m_output(p_output) {}
139 void write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
140 PFC_STATIC_ASSERT( sizeof(m_output[0]) == 1 );
141 p_abort.check();
142 t_size base = m_output.get_size();
143 if (base + p_bytes < base) throw std::bad_alloc();
144 m_output.set_size(base + p_bytes);
145 memcpy( (t_uint8*) m_output.get_ptr() + base, p_buffer, p_bytes );
146 }
147 private:
148 t_storage & m_output;
149 };
150
151 class stream_reader_limited_ref : public stream_reader {
152 public:
153 stream_reader_limited_ref(stream_reader * p_reader,t_filesize p_limit) : m_reader(p_reader), m_remaining(p_limit) {}
154
155 t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
156 if (p_bytes > m_remaining) p_bytes = (t_size)m_remaining;
157
158 t_size done = m_reader->read(p_buffer,p_bytes,p_abort);
159 m_remaining -= done;
160 return done;
161 }
162
163 inline t_filesize get_remaining() const {return m_remaining;}
164
165 t_filesize skip(t_filesize p_bytes,abort_callback & p_abort) {
166 if (p_bytes > m_remaining) p_bytes = m_remaining;
167 t_filesize done = m_reader->skip(p_bytes,p_abort);
168 m_remaining -= done;
169 return done;
170 }
171
172 void flush_remaining(abort_callback & p_abort) {
173 if (m_remaining > 0) skip_object(m_remaining,p_abort);
174 }
175
176 private:
177 stream_reader * m_reader;
178 t_filesize m_remaining;
179 };
180
181 class stream_writer_chunk_dwordheader : public stream_writer
182 {
183 public:
184 stream_writer_chunk_dwordheader(const service_ptr_t<file> & p_writer) : m_writer(p_writer) {}
185
186 void initialize(abort_callback & p_abort) {
187 m_headerposition = m_writer->get_position(p_abort);
188 m_written = 0;
189 m_writer->write_lendian_t((t_uint32)0,p_abort);
190 }
191
192 void finalize(abort_callback & p_abort) {
193 t_filesize end_offset;
194 end_offset = m_writer->get_position(p_abort);
195 m_writer->seek(m_headerposition,p_abort);
196 m_writer->write_lendian_t(pfc::downcast_guarded<t_uint32>(m_written),p_abort);
197 m_writer->seek(end_offset,p_abort);
198 }
199
200 void write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
201 m_writer->write(p_buffer,p_bytes,p_abort);
202 m_written += p_bytes;
203 }
204
205 private:
206 service_ptr_t<file> m_writer;
207 t_filesize m_headerposition;
208 t_filesize m_written;
209 };
210
211 class stream_writer_chunk : public stream_writer
212 {
213 public:
214 stream_writer_chunk(stream_writer * p_writer) : m_writer(p_writer), m_buffer_state(0) {}
215
216 void write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort);
217
218 void flush(abort_callback & p_abort);//must be called after writing before object is destroyed
219
220 private:
221 stream_writer * m_writer;
222 unsigned m_buffer_state;
223 unsigned char m_buffer[255];
224 };
225
226 class stream_reader_chunk : public stream_reader
227 {
228 public:
229 stream_reader_chunk(stream_reader * p_reader) : m_reader(p_reader), m_buffer_state(0), m_buffer_size(0), m_eof(false) {}
230
231 t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort);
232
233 void flush(abort_callback & p_abort);//must be called after reading before object is destroyed
234
235 static void g_skip(stream_reader * p_stream,abort_callback & p_abort);
236
237 private:
238 stream_reader * m_reader;
239 t_size m_buffer_state, m_buffer_size;
240 bool m_eof;
241 unsigned char m_buffer[255];
242 };
243
244 class stream_reader_dummy : public stream_reader { t_size read(void *,t_size,abort_callback &) override {return 0;} };
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263 template<bool isBigEndian = false> class stream_reader_formatter {
264 public:
265 stream_reader_formatter(stream_reader & p_stream,abort_callback & p_abort) : m_stream(p_stream), m_abort(p_abort) {}
266
267 template<typename t_int> void read_int(t_int & p_out) {
268 if (isBigEndian) m_stream.read_bendian_t(p_out,m_abort);
269 else m_stream.read_lendian_t(p_out,m_abort);
270 }
271
272 void read_raw(void * p_buffer,t_size p_bytes) {
273 m_stream.read_object(p_buffer,p_bytes,m_abort);
274 }
275
276 void skip(t_size p_bytes) {m_stream.skip_object(p_bytes,m_abort);}
277
278 template<typename TArray> void read_raw(TArray& data) {
279 pfc::assert_byte_type<typename TArray::t_item>();
280 read_raw(data.get_ptr(),data.get_size());
281 }
282 template<typename TArray> void read_byte_block(TArray & data) {
283 pfc::assert_byte_type<typename TArray::t_item>();
284 t_uint32 size; read_int(size); data.set_size(size);
285 read_raw(data);
286 }
287 template<typename TArray> void read_array(TArray & data) {
288 t_uint32 size; *this >> size; data.set_size(size);
289 for(t_uint32 walk = 0; walk < size; ++walk) *this >> data[walk];
290 }
291 void read_string_nullterm( pfc::string_base & ret ) {
292 m_stream.read_string_nullterm( ret, m_abort );
293 }
294 pfc::string8 read_string_nullterm() {
295 pfc::string8 ret; this->read_string_nullterm(ret); return ret;
296 }
297 pfc::string8 read_string() {
298 return m_stream.read_string(m_abort);
299 }
300 stream_reader & m_stream;
301 abort_callback & m_abort;
302 };
303
304 template<bool isBigEndian = false> class stream_writer_formatter {
305 public:
306 stream_writer_formatter(stream_writer & p_stream,abort_callback & p_abort) : m_stream(p_stream), m_abort(p_abort) {}
307
308 template<typename t_int> void write_int(t_int p_int) {
309 if (isBigEndian) m_stream.write_bendian_t(p_int,m_abort);
310 else m_stream.write_lendian_t(p_int,m_abort);
311 }
312
313 void write_raw(const void * p_buffer,t_size p_bytes) {
314 m_stream.write_object(p_buffer,p_bytes,m_abort);
315 }
316 template<typename TArray> void write_raw(const TArray& data) {
317 pfc::assert_byte_type<typename TArray::t_item>();
318 write_raw(data.get_ptr(),data.get_size());
319 }
320
321 template<typename TArray> void write_byte_block(const TArray& data) {
322 pfc::assert_byte_type<typename TArray::t_item>();
323 write_int( pfc::downcast_guarded<t_uint32>(data.get_size()) );
324 write_raw( data );
325 }
326 template<typename TArray> void write_array(const TArray& data) {
327 const t_uint32 size = pfc::downcast_guarded<t_uint32>(data.get_size());
328 *this << size;
329 for(t_uint32 walk = 0; walk < size; ++walk) *this << data[walk];
330 }
331
332 void write_string(const char * str) {
333 const t_size len = strlen(str);
334 *this << pfc::downcast_guarded<t_uint32>(len);
335 write_raw(str, len);
336 }
337 void write_string(const char * str, t_size len_) {
338 const t_size len = pfc::strlen_max(str, len_);
339 *this << pfc::downcast_guarded<t_uint32>(len);
340 write_raw(str, len);
341 }
342 void write_string_nullterm( const char * str ) {
343 this->write_raw( str, strlen(str)+1 );
344 }
345
346 stream_writer & m_stream;
347 abort_callback & m_abort;
348 };
349
350
351 #define __DECLARE_INT_OVERLOADS(TYPE) \
352 template<bool isBigEndian> inline stream_reader_formatter<isBigEndian> & operator>>(stream_reader_formatter<isBigEndian> & p_stream,TYPE & p_int) {typename pfc::sized_int_t<sizeof(TYPE)>::t_unsigned temp;p_stream.read_int(temp); p_int = (TYPE) temp; return p_stream;} \
353 template<bool isBigEndian> inline stream_writer_formatter<isBigEndian> & operator<<(stream_writer_formatter<isBigEndian> & p_stream,TYPE p_int) {p_stream.write_int((typename pfc::sized_int_t<sizeof(TYPE)>::t_unsigned)p_int); return p_stream;}
354
355 __DECLARE_INT_OVERLOADS(char);
356 __DECLARE_INT_OVERLOADS(signed char);
357 __DECLARE_INT_OVERLOADS(unsigned char);
358 __DECLARE_INT_OVERLOADS(signed short);
359 __DECLARE_INT_OVERLOADS(unsigned short);
360
361 __DECLARE_INT_OVERLOADS(signed int);
362 __DECLARE_INT_OVERLOADS(unsigned int);
363
364 __DECLARE_INT_OVERLOADS(signed long);
365 __DECLARE_INT_OVERLOADS(unsigned long);
366
367 __DECLARE_INT_OVERLOADS(signed long long);
368 __DECLARE_INT_OVERLOADS(unsigned long long);
369
370 __DECLARE_INT_OVERLOADS(wchar_t);
371
372
373 #undef __DECLARE_INT_OVERLOADS
374
375 template<typename TVal> class _IsTypeByte {
376 public:
377 enum {value = pfc::is_same_type<TVal,char>::value || pfc::is_same_type<TVal,unsigned char>::value || pfc::is_same_type<TVal,signed char>::value};
378 };
379
380 template<bool isBigEndian,typename TVal,size_t Count> stream_reader_formatter<isBigEndian> & operator>>(stream_reader_formatter<isBigEndian> & p_stream,TVal (& p_array)[Count]) {
381 if constexpr (_IsTypeByte<TVal>::value) {
382 p_stream.read_raw(p_array,Count);
383 } else {
384 for(t_size walk = 0; walk < Count; ++walk) p_stream >> p_array[walk];
385 }
386 return p_stream;
387 }
388
389 template<bool isBigEndian,typename TVal,size_t Count> stream_writer_formatter<isBigEndian> & operator<<(stream_writer_formatter<isBigEndian> & p_stream,TVal const (& p_array)[Count]) {
390 if constexpr (_IsTypeByte<TVal>::value) {
391 p_stream.write_raw(p_array,Count);
392 } else {
393 for(t_size walk = 0; walk < Count; ++walk) p_stream << p_array[walk];
394 }
395 return p_stream;
396 }
397
398 #define FB2K_STREAM_READER_OVERLOAD(type) \
399 template<bool isBigEndian> stream_reader_formatter<isBigEndian> & operator>>(stream_reader_formatter<isBigEndian> & stream,type & value)
400
401 #define FB2K_STREAM_WRITER_OVERLOAD(type) \
402 template<bool isBigEndian> stream_writer_formatter<isBigEndian> & operator<<(stream_writer_formatter<isBigEndian> & stream,const type & value)
403
404 FB2K_STREAM_READER_OVERLOAD(GUID) {
405 return stream >> value.Data1 >> value.Data2 >> value.Data3 >> value.Data4;
406 }
407
408 FB2K_STREAM_WRITER_OVERLOAD(GUID) {
409 return stream << value.Data1 << value.Data2 << value.Data3 << value.Data4;
410 }
411
412 FB2K_STREAM_READER_OVERLOAD(pfc::string) {
413 t_uint32 len; stream >> len;
414 value = stream.m_stream.read_string_ex(len,stream.m_abort);
415 return stream;
416 }
417
418 FB2K_STREAM_WRITER_OVERLOAD(pfc::string) {
419 stream << pfc::downcast_guarded<t_uint32>(value.length());
420 stream.write_raw(value.ptr(),value.length());
421 return stream;
422 }
423
424 FB2K_STREAM_READER_OVERLOAD(pfc::string_base) {
425 stream.m_stream.read_string(value, stream.m_abort);
426 return stream;
427 }
428 FB2K_STREAM_WRITER_OVERLOAD(pfc::string_base) {
429 const char * val = value.get_ptr();
430 const t_size len = strlen(val);
431 stream << pfc::downcast_guarded<t_uint32>(len);
432 stream.write_raw(val,len);
433 return stream;
434 }
435
436
437 FB2K_STREAM_WRITER_OVERLOAD(float) {
438 union {
439 float f; t_uint32 i;
440 } u; u.f = value;
441 return stream << u.i;
442 }
443
444 FB2K_STREAM_READER_OVERLOAD(float) {
445 union { float f; t_uint32 i;} u;
446 stream >> u.i; value = u.f;
447 return stream;
448 }
449
450 FB2K_STREAM_WRITER_OVERLOAD(double) {
451 union {
452 double f; t_uint64 i;
453 } u; u.f = value;
454 return stream << u.i;
455 }
456
457 FB2K_STREAM_READER_OVERLOAD(double) {
458 union { double f; t_uint64 i;} u;
459 stream >> u.i; value = u.f;
460 return stream;
461 }
462
463 FB2K_STREAM_WRITER_OVERLOAD(bool) {
464 t_uint8 temp = value ? 1 : 0;
465 return stream << temp;
466 }
467 FB2K_STREAM_READER_OVERLOAD(bool) {
468 t_uint8 temp; stream >> temp; value = temp != 0;
469 return stream;
470 }
471
472 template<bool BE = false>
473 class stream_writer_formatter_simple : public stream_writer_formatter<BE> {
474 public:
475 stream_writer_formatter_simple() : stream_writer_formatter<BE>(_m_stream,fb2k::noAbort), m_buffer(_m_stream.m_buffer) {}
476
477 typedef stream_writer_buffer_simple::t_buffer t_buffer;
478 t_buffer & m_buffer;
479 private:
480 stream_writer_buffer_simple _m_stream;
481 };
482
483 template<bool BE = false>
484 class stream_reader_formatter_simple_ref : public stream_reader_formatter<BE> {
485 public:
486 stream_reader_formatter_simple_ref(const void * source, t_size sourceSize) : stream_reader_formatter<BE>(_m_stream,fb2k::noAbort), _m_stream(source,sourceSize) {}
487 template<typename TSource> stream_reader_formatter_simple_ref(const TSource& source) : stream_reader_formatter<BE>(_m_stream,fb2k::noAbort), _m_stream(source) {}
488 stream_reader_formatter_simple_ref() : stream_reader_formatter<BE>(_m_stream,fb2k::noAbort) {}
489
490 void set_data(const void * source, t_size sourceSize) {_m_stream.set_data(source,sourceSize);}
491 template<typename TSource> void set_data(const TSource & source) {_m_stream.set_data(source);}
492
493 void reset() {_m_stream.reset();}
494 t_size get_remaining() {return _m_stream.get_remaining();}
495
496 const void * get_ptr_() const {return _m_stream.get_ptr_();}
497 private:
498 stream_reader_memblock_ref _m_stream;
499 };
500
501 template<bool BE = false>
502 class stream_reader_formatter_simple : public stream_reader_formatter_simple_ref<BE> {
503 public:
504 stream_reader_formatter_simple() {}
505 stream_reader_formatter_simple(const void * source, t_size sourceSize) {set_data(source,sourceSize);}
506 template<typename TSource> stream_reader_formatter_simple(const TSource & source) {set_data(source);}
507
508 void set_data(const void * source, t_size sourceSize) {
509 m_content.set_data_fromptr(reinterpret_cast<const t_uint8*>(source), sourceSize);
510 onContentChange();
511 }
512 template<typename TSource> void set_data(const TSource & source) {
513 m_content = source;
514 onContentChange();
515 }
516 private:
517 void onContentChange() {
518 stream_reader_formatter_simple_ref<BE>::set_data(m_content);
519 }
520 pfc::array_t<t_uint8> m_content;
521 };
522
523
524
525
526
527
528 template<bool isBigEndian> class _stream_reader_formatter_translator {
529 public:
530 _stream_reader_formatter_translator(stream_reader_formatter<isBigEndian> & stream) : m_stream(stream) {}
531 typedef _stream_reader_formatter_translator<isBigEndian> t_self;
532 template<typename t_what> t_self & operator||(t_what & out) {m_stream >> out; return *this;}
533 private:
534 stream_reader_formatter<isBigEndian> & m_stream;
535 };
536 template<bool isBigEndian> class _stream_writer_formatter_translator {
537 public:
538 _stream_writer_formatter_translator(stream_writer_formatter<isBigEndian> & stream) : m_stream(stream) {}
539 typedef _stream_writer_formatter_translator<isBigEndian> t_self;
540 template<typename t_what> t_self & operator||(const t_what & in) {m_stream << in; return *this;}
541 private:
542 stream_writer_formatter<isBigEndian> & m_stream;
543 };
544
545 #define FB2K_STREAM_RECORD_OVERLOAD(type, code) \
546 FB2K_STREAM_READER_OVERLOAD(type) { \
547 _stream_reader_formatter_translator<isBigEndian> streamEx(stream); \
548 streamEx || code; \
549 return stream; \
550 } \
551 FB2K_STREAM_WRITER_OVERLOAD(type) { \
552 _stream_writer_formatter_translator<isBigEndian> streamEx(stream); \
553 streamEx || code; \
554 return stream; \
555 }
556
557
558
559
560 #define FB2K_RETRY_ON_EXCEPTION(OP, ABORT, TIMEOUT, EXCEPTION) \
561 { \
562 pfc::lores_timer timer; timer.start(); \
563 for(;;) { \
564 try { {OP;} break; } \
565 catch(const EXCEPTION &) { if (timer.query() > TIMEOUT) throw;} \
566 ABORT.sleep(0.05); \
567 } \
568 }
569
570 #define FB2K_RETRY_ON_EXCEPTION2(OP, ABORT, TIMEOUT, EXCEPTION1, EXCEPTION2) \
571 { \
572 pfc::lores_timer timer; timer.start(); \
573 for(;;) { \
574 try { {OP;} break; } \
575 catch(const EXCEPTION1 &) { if (timer.query() > TIMEOUT) throw;} \
576 catch(const EXCEPTION2 &) { if (timer.query() > TIMEOUT) throw;} \
577 ABORT.sleep(0.05); \
578 } \
579 }
580
581 #define FB2K_RETRY_ON_EXCEPTION3(OP, ABORT, TIMEOUT, EXCEPTION1, EXCEPTION2, EXCEPTION3) \
582 { \
583 pfc::lores_timer timer; timer.start(); \
584 for(;;) { \
585 try { {OP;} break; } \
586 catch(const EXCEPTION1 &) { if (timer.query() > TIMEOUT) throw;} \
587 catch(const EXCEPTION2 &) { if (timer.query() > TIMEOUT) throw;} \
588 catch(const EXCEPTION3 &) { if (timer.query() > TIMEOUT) throw;} \
589 ABORT.sleep(0.05); \
590 } \
591 }
592
593 #define FB2K_RETRY_ON_SHARING_VIOLATION(OP, ABORT, TIMEOUT) FB2K_RETRY_ON_EXCEPTION(OP, ABORT, TIMEOUT, exception_io_sharing_violation)
594
595 // **** WINDOWS SUCKS ****
596 // File move ops must be retried on all these because you get access-denied when someone is holding open handles to something you're trying to move, or already-exists on something you just told Windows to move away
597 #define FB2K_RETRY_FILE_MOVE(OP, ABORT, TIMEOUT) FB2K_RETRY_ON_EXCEPTION3(OP, ABORT, TIMEOUT, exception_io_sharing_violation, exception_io_denied, exception_io_already_exists)
598
599 class fileRestorePositionScope {
600 public:
601 fileRestorePositionScope(file::ptr f, abort_callback & a) : m_file(f), m_abort(a) {
602 m_offset = f->get_position(a);
603 }
604 ~fileRestorePositionScope() {
605 try {
606 if (!m_abort.is_aborting()) m_file->seek(m_offset, m_abort);
607 } catch(...) {}
608 }
609 private:
610 file::ptr m_file;
611 t_filesize m_offset;
612 abort_callback & m_abort;
613 };
614
615
616 //! Debug self-test function for testing a file object implementation, performs various behavior validity checks, random access etc. Output goes to fb2k console.
617 //! Returns true on success, false on failure (buggy file object implementation).
618 bool fb2kFileSelfTest(file::ptr f, abort_callback & aborter);