JASSv2
file.h
Go to the documentation of this file.
1 /*
2  FILE.H
3  ------
4  Copyright (c) 2016 Andrew Trotman
5  Released under the 2-clause BSD license (See:https://en.wikipedia.org/wiki/BSD_licenses)
6 
7  Originally from the ATIRE codebase (where it was also written by Andrew Trotman)
8 */
15 #pragma once
16 
17 #include <stdio.h>
18 #include <errno.h>
19 #include <stddef.h>
20 #include <string.h>
21 
22 #ifdef _MSC_VER
23  #include <windows.h>
24 #endif
25 
26 #include <string>
27 #include <vector>
28 #include <memory>
29 #include <stdexcept>
30 
31 namespace JASS
32  {
33  /*
34  CLASS FILE
35  ----------
36  */
45  class file
46  {
47  public:
48  /*
49  CLASS FILE::FILE_READ_ONLY
50  --------------------------
51  */
56  {
57  friend class file;
58  private:
59 #ifdef _MSC_VER
60  HANDLE hFile;
61  HANDLE hMapFile;
62 #endif
63  const void *file_contents;
64  size_t size;
65 
66  public:
67  /*
68  FILE::FILE_READ_ONLY::FILE_READ_ONLY()
69  --------------------------------------
70  */
75  file_contents(nullptr),
76  size(0)
77  {
78  /* Nothing */
79  }
80 
81  /*
82  FILE::FILE_READ_ONLY::OPEN()
83  ----------------------------
84  */
90  size_t open(const std::string &filename);
91 
92  /*
93  FILE::FILE_READ_ONLY::~FILE_READ_ONLY()
94  ---------------------------------------
95  */
100 
101  /*
102  FILE::FILE_READ_ONLY::READ_ENTIRE_FILE()
103  ----------------------------------------
104  */
110  size_t read_entire_file(const uint8_t *&into) const
111  {
112  into = reinterpret_cast<const uint8_t *>(file_contents);
113  return size;
114  }
115  };
116 
117  protected:
118  FILE *fp;
119  size_t file_position;
120  size_t buffer_size;
121  size_t buffer_used;
122  std::unique_ptr<uint8_t []> buffer;
123  size_t bytes_written;
124  size_t bytes_read;
125 
126  public:
127  /*
128  FILE::FILE()
129  ------------
130  */
134  file() = delete;
135 
136  /*
137  FILE::FILE()
138  ------------
139  */
144  file(FILE *fp) :
145  fp(fp),
146  file_position(0),
147  buffer_size(10 * 1024 * 1024), // start with a buffer of this size
148  buffer_used(0),
149  buffer(std::make_unique<uint8_t []>(buffer_size)),
150  bytes_written(0),
151  bytes_read(0)
152  {
153  /* Nothing */
154  }
155 
156  /*
157  FILE::FILE()
158  ------------
159  */
165  file(const char *filename, const char *mode) :
166  file(nullptr)
167  {
168  /*
169  Open the given file in the given mode
170  */
171  #if defined(__STDC_LIB_EXT1__)
172  fopen_s(&fp, filename, mode);
173  #else
174  fp = fopen(filename, mode);
175  #endif
176  }
177 
178  /*
179  FILE::FILE()
180  ------------
181  */
187  file(const std::string &filename, const std::string &mode) :
188  file(filename.c_str(), mode.c_str())
189  {
190  /* Nothing */
191  }
192 
193  /*
194  FILE::~FILE()
195  -------------
196  */
201  {
202  flush();
203  if (fp != nullptr && fp != stdin && fp != stdout && fp != stderr)
204  fclose(fp);
205  }
206 
207  /*
208  FILE::SETVBUF()
209  ---------------
210  */
220  size_t setvbuf(size_t size)
221  {
222  buffer_size = size;
223  buffer = std::make_unique<uint8_t []>(buffer_size);
224  return buffer == NULL ? 0 : 1;
225  }
226 
227  /*
228  FILE::FLUSH()
229  -------------
230  */
234  void flush(void)
235  {
236  if (buffer_used > 0)
237  {
238  /*
239  All physical output to the disk happens with this one line of code. If this is
240  done with blocking I/O then this causes a bottleneck as we wait for the OS
241  to write to disk.
242  */
243  ::fwrite(buffer.get(), 1, buffer_used, fp);
244  buffer_used = 0;
245  }
246  }
247 
248  /*
249  FILE::READ()
250  ------------
251  */
258  size_t read(void *data, size_t size)
259  {
260  /*
261  Keep track of the number of bytes we've been asked to write
262  */
263  bytes_read += size;
264 
265  /*
266  Flush the write cache
267  */
268  flush();
269 
270  /*
271  Take note of the new file position (at the end of the read)
272  */
273  file_position += size; // this is where we'll be at the end of the read
274 
275  /*
276  And now perform the read
277  */
278  return ::fread(data, 1, size, fp);
279  }
280 
281  /*
282  FILE::READ()
283  ------------
284  */
289  void read(std::vector<uint8_t> &buffer)
290  {
291  /*
292  Read from the file
293  */
294  size_t bytes_read = read(&buffer[0], buffer.size());
295 
296  /*
297  If we got a short read then resize the buffer to signal back to the caller that we failed to read (probably EOF).
298  */
299  if (bytes_read == 0)
300  buffer.resize(0);
301  else if (bytes_read != buffer.size())
302  buffer.resize(bytes_read);
303  }
304 
305  /*
306  FILE::WRITE()
307  -------------
308  */
315  size_t write(const void *data, size_t size)
316  {
317  uint8_t *from;
318  size_t block_size;
319 
320  /*
321  Keep track of the total number of bytes we've been asked to write to the file
322  */
323  bytes_written += size;
324 
325  /*
326  Update the file pointer
327  */
328  file_position += size;
329 
330  if (buffer_used + size < buffer_size)
331  {
332  /*
333  The data fits in the internal buffers
334  */
335  memcpy(buffer.get() + buffer_used, data, (size_t)size);
336  buffer_used += size;
337  }
338  else
339  {
340  /*
341  The data does not fit in the internal buffers so it is
342  necessary to flush the buffers and then do the write
343  */
344  from = (uint8_t *)data;
345  do
346  {
347  flush();
348  block_size = size <= buffer_size ? size : buffer_size;
349  memcpy(buffer.get(), from, (size_t)block_size);
350  buffer_used += block_size;
351  from += block_size;
352  size -= block_size;
353  }
354  while (size > 0);
355  }
356  return 1;
357  }
358 
359  /*
360  FILE::WRITE()
361  -------------
362  */
368  size_t write(const std::string &buffer)
369  {
370  return write(buffer.c_str(), buffer.size());
371  }
372 
373  /*
374  FILE::SIZE()
375  ------------
376  */
381  size_t size(void) const;
382 
383  /*
384  FILE::TELL()
385  ------------
386  */
391  size_t tell(void)
392  {
393  return file_position;
394  }
395 
396  /*
397  FILE::SEEK()
398  ------------
399  */
405  void seek(size_t offset)
406  {
407  /*
408  Empty the write buffer
409  */
410  flush();
411 
412  /*
413  Seek
414  */
415  #ifdef _MSC_VER
416  auto error = _fseeki64(fp, offset, SEEK_SET);
417  #else
418  auto error = fseeko(fp, offset, SEEK_SET);
419  #endif
420 
421  /*
422  Store the file file position
423  */
424  file_position = offset;
425 
426  /*
427  if error is non-zero then seek failed - which if a fatal error.
428  */
429  if (error != 0)
430  throw std::out_of_range("file::seek() failure"); // LCOV_EXCL_LINE // This is highly unlikely to happen - its not clear why seek() can fail.
431  }
432 
433  /*
434  FILE::READ_ENTIRE_FILE()
435  ------------------------
436  */
444  static size_t read_entire_file(const std::string &filename, std::string &into);
445 
446  /*
447  FILE::READ_ENTIRE_FILE()
448  ------------------------
449  */
457  static size_t read_entire_file(const std::string &filename, file_read_only &into)
458  {
459  return into.open(filename);
460  }
461 
462  /*
463  FILE::WRITE_ENTIRE_FILE()
464  -------------------------
465  */
473  static bool write_entire_file(const std::string &filename, const std::string &buffer);
474 
475  /*
476  FILE::BUFFER_TO_LIST()
477  ----------------------
478  */
487  static void buffer_to_list(std::vector<uint8_t *> &line_list, std::string &buffer);
488 
489  /*
490  FILE::IS_DIRECTORY()
491  --------------------
492  */
498  static bool is_directory(const std::string &filename);
499 
500  /*
501  FILE::MKSTEMP()
502  ---------------
503  */
512  static std::string mkstemp(std::string prefix);
513 
514  /*
515  FILE::UNITTEST()
516  ----------------
517  */
521  static void unittest(void);
522  } ;
523  }
static bool is_directory(const std::string &filename)
Determines whether the given file system object is a directoy or not.
Definition: file.cpp:259
static void buffer_to_list(std::vector< uint8_t *> &line_list, std::string &buffer)
Turn a single std::string into a vector of uint8_t * (i.e. "C" strings).
Definition: file.cpp:198
file(const std::string &filename, const std::string &mode)
Constructor used for opening files.
Definition: file.h:187
size_t write(const std::string &buffer)
Write bytes number of bytes to the give file at the current cursor position.
Definition: file.h:368
size_t tell(void)
Return the byte offset of the file pointer in the current file.
Definition: file.h:391
size_t buffer_used
How much of the internal file buffer is being used.
Definition: file.h:121
size_t open(const std::string &filename)
Open and read the file into memory.
Definition: file.cpp:36
const void * file_contents
The contents of the file.
Definition: file.h:63
size_t bytes_read
Number of bytes read from this file.
Definition: file.h:124
~file()
Destructor.
Definition: file.h:200
size_t read_entire_file(const uint8_t *&into) const
Return the contents and length of the file.
Definition: file.h:110
size_t size
The size of the file.
Definition: file.h:64
size_t write(const void *data, size_t size)
Write bytes number of bytes to the give file at the current cursor position.
Definition: file.h:315
~file_read_only()
Destrucgtor.
Definition: file.cpp:115
std::unique_ptr< uint8_t []> buffer
Internal file buffer.
Definition: file.h:122
file(FILE *fp)
Constructor with a C FILE * object.
Definition: file.h:144
void read(std::vector< uint8_t > &buffer)
Read buffer.size() bytes from the give file into the buffer. If at end of file then this method will ...
Definition: file.h:289
size_t setvbuf(size_t size)
change the size of the internal buffer (does not flush() first)
Definition: file.h:220
size_t read(void *data, size_t size)
Read bytes number of bytes from the give file into the buffer.
Definition: file.h:258
static void unittest(void)
Unit test this class.
Definition: file.cpp:350
size_t bytes_written
Number of bytes written to this file.
Definition: file.h:123
void flush(void)
Flush the internal buffers to disk (called automatically on close).
Definition: file.h:234
static size_t read_entire_file(const std::string &filename, file_read_only &into)
Read the contents of file filename into the std::string into.
Definition: file.h:457
static std::string mkstemp(std::string prefix)
Generate a temporary filename containing the given prefix.
Definition: file.cpp:329
file_read_only()
Constructor.
Definition: file.h:74
FILE * fp
The underlying representation is a FILE * from C (as they appear to be fast).
Definition: file.h:118
static bool write_entire_file(const std::string &filename, const std::string &buffer)
Write the contents of buffer to the file specified in filenane.
Definition: file.cpp:176
void seek(size_t offset)
Seek to the given offset in the file.
Definition: file.h:405
File based I/O methods including whole file and partial files.
Definition: file.h:45
size_t file_position
The ftell() position in the file.
Definition: file.h:119
size_t buffer_size
Size of the internal file buffering.
Definition: file.h:120
Definition: compress_integer_elias_delta_simd.c:23
A read_only file object, the memory was probably allocated with mmap() and needs deallocating accordi...
Definition: file.h:55
file(const char *filename, const char *mode)
Constructor used for opening files.
Definition: file.h:165