foobar2000 SDK  2015-08-03
filesystem_helper.cpp
Go to the documentation of this file.
1 #include "foobar2000.h"
2 
3 void stream_writer_chunk::write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
4  t_size remaining = p_bytes, written = 0;
5  while(remaining > 0) {
6  t_size delta = sizeof(m_buffer) - m_buffer_state;
7  if (delta > remaining) delta = remaining;
8  memcpy(m_buffer,(const t_uint8*)p_buffer + written,delta);
9  written += delta;
10  remaining -= delta;
11 
12  if (m_buffer_state == sizeof(m_buffer)) {
13  m_writer->write_lendian_t((t_uint8)m_buffer_state,p_abort);
14  m_writer->write_object(m_buffer,m_buffer_state,p_abort);
15  m_buffer_state = 0;
16  }
17  }
18 }
19 
21 {
22  m_writer->write_lendian_t((t_uint8)m_buffer_state,p_abort);
23  if (m_buffer_state > 0) {
24  m_writer->write_object(m_buffer,m_buffer_state,p_abort);
25  m_buffer_state = 0;
26  }
27 }
28 
29 /*
30  stream_writer * m_writer;
31  unsigned m_buffer_state;
32  unsigned char m_buffer[255];
33 */
34 
35 t_size stream_reader_chunk::read(void * p_buffer,t_size p_bytes,abort_callback & p_abort)
36 {
37  t_size todo = p_bytes, done = 0;
38  while(todo > 0) {
39  if (m_buffer_size == m_buffer_state) {
40  if (m_eof) break;
41  t_uint8 temp;
42  m_reader->read_lendian_t(temp,p_abort);
43  m_buffer_size = temp;
44  if (temp != sizeof(m_buffer)) m_eof = true;
45  m_buffer_state = 0;
46  if (m_buffer_size>0) {
47  m_reader->read_object(m_buffer,m_buffer_size,p_abort);
48  }
49  }
50 
51 
52  t_size delta = m_buffer_size - m_buffer_state;
53  if (delta > todo) delta = todo;
54  if (delta > 0) {
55  memcpy((unsigned char*)p_buffer + done,m_buffer + m_buffer_state,delta);
56  todo -= delta;
57  done += delta;
58  m_buffer_state += delta;
59  }
60  }
61  return done;
62 }
63 
65  while(!m_eof) {
66  p_abort.check_e();
67  t_uint8 temp;
68  m_reader->read_lendian_t(temp,p_abort);
69  m_buffer_size = temp;
70  if (temp != sizeof(m_buffer)) m_eof = true;
71  m_buffer_state = 0;
72  if (m_buffer_size>0) {
73  m_reader->skip_object(m_buffer_size,p_abort);
74  }
75  }
76 }
77 
78 /*
79  stream_reader * m_reader;
80  unsigned m_buffer_state, m_buffer_size;
81  bool m_eof;
82  unsigned char m_buffer[255];
83 */
84 
86  stream_reader_chunk(p_stream).flush(p_abort);
87 }
88 
89 t_size reader_membuffer_base::read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
90  p_abort.check_e();
91  t_size max = get_buffer_size();
92  if (max < m_offset) uBugCheck();
93  max -= m_offset;
94  t_size delta = p_bytes;
95  if (delta > max) delta = max;
96  memcpy(p_buffer,(char*)get_buffer() + m_offset,delta);
97  m_offset += delta;
98  return delta;
99 }
100 
102  p_abort.check_e();
103  t_filesize max = get_buffer_size();
104  if (position == filesize_invalid || position > max) throw exception_io_seek_out_of_range();
105  m_offset = (t_size)position;
106 }
107 
108 
109 
110 
111 static void fileSanitySeek(file::ptr f, pfc::array_t<uint8_t> const & content, size_t offset, abort_callback & aborter) {
112  const size_t readAmount = 64 * 1024;
114  f->seek(offset, aborter);
115  t_filesize positionGot = f->get_position(aborter);
116  if (positionGot != offset) {
117  FB2K_console_formatter() << "File sanity: at " << offset << " reported position became " << positionGot;
118  throw std::runtime_error("Seek test failure");
119  }
120  size_t did = f->read(buf.get_ptr(), readAmount, aborter);
121  size_t expected = pfc::min_t<size_t>(readAmount, content.get_size() - offset);
122  if (expected != did) {
123  FB2K_console_formatter() << "File sanity: at " << offset << " bytes, expected read size of " << expected << ", got " << did;
124  if (did > expected) FB2K_console_formatter() << "Read past EOF";
125  else FB2K_console_formatter() << "Premature EOF";
126  throw std::runtime_error("Seek test failure");
127  }
128  if (memcmp(buf.get_ptr(), content.get_ptr() + offset, did) != 0) {
129  FB2K_console_formatter() << "File sanity: data mismatch at " << offset << " - " << (offset + did) << " bytes";
130  throw std::runtime_error("Seek test failure");
131  }
132  positionGot = f->get_position(aborter);
133  if (positionGot != offset + did) {
134  FB2K_console_formatter() << "File sanity: at " << offset << "+" << did << "=" << (offset + did) << " reported position became " << positionGot;
135  throw std::runtime_error("Seek test failure");
136  }
137 }
138 
139 bool fb2kFileSelfTest(file::ptr f, abort_callback & aborter) {
140  try {
141  pfc::array_t<uint8_t> fileContent;
142  f->reopen(aborter);
143  f->read_till_eof(fileContent, aborter);
144 
145  {
146  t_filesize sizeClaimed = f->get_size(aborter);
147  if (sizeClaimed == filesize_invalid) {
148  FB2K_console_formatter() << "File sanity: file reports unknown size, actual size read is " << fileContent.get_size();
149  }
150  else {
151  if (sizeClaimed != fileContent.get_size()) {
152  FB2K_console_formatter() << "File sanity: file reports size of " << sizeClaimed << ", actual size read is " << fileContent.get_size();
153  throw std::runtime_error("File size mismatch");
154  }
155  else {
156  FB2K_console_formatter() << "File sanity: file size check OK: " << sizeClaimed;
157  }
158  }
159  }
160 
161  {
162  FB2K_console_formatter() << "File sanity: testing N-first-bytes reads...";
163  const size_t sizeUpTo = pfc::min_t<size_t>(fileContent.get_size(), 1024 * 1024);
165  buf1.set_size_discard(sizeUpTo);
166 
167  for (size_t w = 1; w <= sizeUpTo; w <<= 1) {
168  f->reopen(aborter);
169  size_t did = f->read(buf1.get_ptr(), w, aborter);
170  if (did != w) {
171  FB2K_console_formatter() << "File sanity: premature EOF reading first " << w << " bytes, got " << did;
172  throw std::runtime_error("Premature EOF");
173  }
174  if (memcmp(fileContent.get_ptr(), buf1.get_ptr(), did) != 0) {
175  FB2K_console_formatter() << "File sanity: file content mismatch reading first " << w << " bytes";
176  throw std::runtime_error("File content mismatch");
177  }
178  }
179  }
180  if (f->can_seek()) {
181  FB2K_console_formatter() << "File sanity: testing random access...";
182 
183  {
184  size_t sizeUpTo = pfc::min_t<size_t>(fileContent.get_size(), 1024 * 1024);
185  for (size_t w = 1; w < sizeUpTo; w <<= 1) {
186  fileSanitySeek(f, fileContent, w, aborter);
187  }
188  fileSanitySeek(f, fileContent, fileContent.get_size(), aborter);
189  for (size_t w = 1; w < sizeUpTo; w <<= 1) {
190  fileSanitySeek(f, fileContent, fileContent.get_size() - w, aborter);
191  }
192  fileSanitySeek(f, fileContent, fileContent.get_size() / 2, aborter);
193  }
194  }
195  FB2K_console_formatter() << "File sanity test: all OK";
196  return true;
197  }
198  catch (std::exception const & e) {
199  FB2K_console_formatter() << "File sanity test failure: " << e.what();
200  return false;
201  }
202 }
203 
204 
205 
206 namespace {
207  class listDirectoryCallback : public directory_callback {
208  public:
209  bool on_entry(filesystem * p_owner,abort_callback & p_abort,const char * p_url,bool p_is_subdirectory,const t_filestats & p_stats) {
210  m_func( p_url, p_stats, p_is_subdirectory );
211  return true;
212  }
213  listDirectoryFunc_t m_func;
214  };
215 
216 }
217 namespace foobar2000_io {
218  void listDirectory( const char * path, abort_callback & aborter, listDirectoryFunc_t func) {
219  listDirectoryCallback cb; cb.m_func = func;
220  filesystem::g_list_directory(path, cb, aborter);
221  }
222 }
const t_item * get_ptr() const
Definition: array.h:52
const t_item * get_ptr() const
Definition: array.h:213
uint8_t t_uint8
Definition: int_types.h:9
This class is used to signal underlying worker code whether user has decided to abort a potentially t...
Special simplififed version of array class that avoids stepping on landmines with classes without pub...
Definition: array.h:11
void flush(abort_callback &p_abort)
void listDirectory(const char *path, abort_callback &aborter, listDirectoryFunc_t func)
static const t_filesize filesize_invalid
Invalid/unknown file size constant. Also see: t_filesize.
Definition: filesystem.h:16
void seek(t_filesize position, abort_callback &p_abort)
unsigned char m_buffer[255]
void write(const void *p_buffer, t_size p_bytes, abort_callback &p_abort)
Contains various I/O related structures and interfaces.
Definition: filetimetools.h:1
size_t t_size
Definition: int_types.h:48
t_size read(void *p_buffer, t_size p_bytes, abort_callback &p_abort)
bool fb2kFileSelfTest(file::ptr f, abort_callback &aborter)
Debug self-test function for testing a file object implementation, performs various behavior validity...
void flush(abort_callback &p_abort)
static void g_skip(stream_reader *p_stream, abort_callback &p_abort)
t_size read(void *p_buffer, t_size p_bytes, abort_callback &p_abort)
std::exception exception
Definition: primitives.h:193
std::function< void(const char *, t_filestats const &, bool) > listDirectoryFunc_t
t_uint64 t_filesize
Type used for file size related variables.
Definition: filesystem.h:8
static void fileSanitySeek(file::ptr f, pfc::array_t< uint8_t > const &content, size_t offset, abort_callback &aborter)
stream_writer * m_writer
void set_size_discard(t_size p_size)
Definition: array.h:36
PFC_NORETURN void SHARED_EXPORT uBugCheck()
t_size get_size() const
Definition: array.h:130