commonlib
disk_buffer.h
Go to the documentation of this file.
1 
12 // TODO validate capacity of disk
13 
14 #pragma once
15 
16 #include "memory.h"
17 #include <string.h>
18 #include <mutex>
19 #include "fifo_buffer_base.h"
20 #include <fstream>
21 #include "risingdelay.h"
22 
23 #if __has_include(<filesystem>)
24 #include <filesystem>
25 #else
26 #include <experimental/filesystem>
27 #endif
28 
29 BEGIN_NAMESPACE_MEMORY
30 
34 template <typename T>
36 {
37 public:
43  explicit FileHandler(const std::string& filename, bool deleteFileAfterClose = true)
44  : filename(filename)
45  , deleteFileAfterClose(deleteFileAfterClose)
46  {
47  }
48 
49  virtual ~FileHandler()
50  {
51  close();
52  }
53 
57  void open()
58  {
59  open("w");
60  }
61 
66  void open(char const* mode)
67  {
68  if (strcmp(mode, "w") == 0)
69  {
70  file.open(filename.c_str(), std::fstream::app | std::fstream::out);
71  file.close();
72  file.open(filename.c_str(), std::fstream::binary | std::fstream::in | std::fstream::out);
73  }
74  else if (strcmp(mode, "r") == 0)
75  {
76  file = std::fstream(filename.c_str(), std::fstream::binary | std::fstream::in);
77  }
78  else
79  throw std::invalid_argument("Opening disk buffer file with argument: " + std::string(mode));
80 
81  if (!file)
82  throw std::runtime_error("Error opening disk buffer file");
83  }
84 
89  bool close()
90  {
91  if (!file || !file.is_open())
92  return true;
93  file.close();
94  if (file.fail())
95  throw std::runtime_error("File closing error");
96  assert(!file.is_open());
97  if (deleteFileAfterClose)
98  removeFile();
99  return true;
100  }
101 
105  void removeFile()
106  {
107  const size_t MAX_RETRIES = 5;
108 
109  auto start = std::chrono::steady_clock::now();
110 
111  Threading::RisingDelay risingDelay{100, 1000};
112  size_t removeResult;
113  size_t it = 0;
114 
115  do
116  {
117  removeResult = remove(filename.c_str());
118  if (removeResult == 0)
119  return;
120 
121  risingDelay.wait();
122  } while (++it < MAX_RETRIES);
123 
124  auto end = std::chrono::steady_clock::now();
125  auto durationInMs = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
126  std::stringstream ss;
127  ss << "File closing error; file:" << this->filename << "; error code:" << removeResult << "; retries: " << MAX_RETRIES
128  << "; elapsed[ms]:" << durationInMs;
129  throw std::runtime_error(ss.str().c_str());
130  }
131 
135  void rewind()
136  {
137  file.clear();
138  file.seekg(0, std::fstream::beg);
139  }
140 
148  size_t write(const T* data, size_t count)
149  {
150  return write(data, sizeof(T), count);
151  }
152 
160  size_t writeAt(const T* data, size_t count, size_t pos)
161  {
162  seekPos(pos);
163  return write(data, count);
164  }
165 
172  size_t read(T* data, size_t count)
173  {
174  return read(data, sizeof(T), count);
175  }
176 
184  size_t readAt(T* data, size_t count, size_t pos)
185  {
186  seekPos(pos);
187  return read(data, count);
188  }
189 
195  void seekPos(size_t offset)
196  {
197  file.seekg(offset * sizeof(T));
198  if (file.fail())
199  throw std::runtime_error("Failed to change read position in file");
200  }
201 
206  size_t getSizeInBytes()
207  {
208  file.seekg(0, std::fstream::end);
209  if (file.fail())
210  throw std::runtime_error("Failed to determine size of a file in bytes");
211  size_t size = size_t(file.tellg());
212  return size;
213  }
214 
219  size_t getSize()
220  {
221  return getSizeInBytes() / sizeof(T);
222  }
223 
227  void reset()
228  {
229  close();
230  open();
231  }
232 
233 protected:
234  std::fstream file;
235  std::string filename;
236  bool deleteFileAfterClose;
237 
238  size_t write(const void* data, size_t size, size_t count)
239  {
240  file.write(static_cast<const char*>(data), size * count);
241  if (file.fail())
242  throw std::runtime_error("Error writing data to file");
243  return count;
244  }
245 
246  size_t read(void* data, size_t size, size_t count)
247  {
248  file.read(static_cast<char*>(data), size * count);
249  if (file.fail())
250  throw std::runtime_error("Error reading data from file");
251  return count;
252  }
253 };
254 
258 template <typename T>
259 class DiskBuffer : public FifoBufferBase<T>
260 {
261 public:
267  DiskBuffer(size_t capacity, const std::string& fileName, bool deleteFileAfterClose = true);
268  virtual ~DiskBuffer() = default;
269 
274  void reset() override;
275 
281 
289  size_t write(const T* samples, size_t samplesCount, size_t pos) override;
290 
298  size_t read(T* samples, size_t samplesCount, const size_t pos) override;
299 
304  T* getData() const override;
305 
310  bool validateReadPos(size_t readPos);
311 
312 private:
313  FileHandler<T> fileHandler;
314 };
315 
316 template <typename T>
317 inline DiskBuffer<T>::DiskBuffer(size_t capacity, const std::string& fileName, bool deleteFileAfterClose)
318  : fileHandler(fileName, deleteFileAfterClose)
319 {
320  DiskBuffer<T>::setCapacity(capacity);
321 }
322 
323 template <typename T>
324 inline void DiskBuffer<T>::reset()
325 {
326  std::lock_guard<std::recursive_mutex> guard(this->getLock());
327  fileHandler.reset();
329 }
330 
331 template <typename T>
333 {
334  return fileHandler;
335 }
336 
337 template <typename T>
338 size_t DiskBuffer<T>::write(const T* samples, size_t samplesCount, size_t pos)
339 {
340  return fileHandler.writeAt(samples, samplesCount, pos);
341 }
342 
343 template <typename T>
344 size_t Dewesoft::Utils::Memory::DiskBuffer<T>::read(T* samples, size_t samplesCount, const size_t pos)
345 {
346  return fileHandler.readAt(samples, samplesCount, pos);
347 }
348 
349 template <typename T>
351 {
352  throw std::runtime_error("Not supported");
353 }
354 
355 template <typename T>
356 bool DiskBuffer<T>::validateReadPos(size_t readPos)
357 {
358  std::lock_guard<std::recursive_mutex> guard(this->getLock());
359 
360  auto lower = this->getReadPos();
361  auto upper = this->getWritePos();
362  auto capacity = this->getCapacity();
363 
364  return (readPos < capacity) &&
365  ((lower < upper && readPos < upper && readPos >= lower) || (upper < lower && !(readPos >= upper && readPos < lower)));
366 }
367 
376 template <typename T>
378  DiskBuffer<T>& target,
379  bool incrementPosition = true,
380  size_t samplesCount = (std::numeric_limits<size_t>::max)())
381 {
382  return transferBuffer(
383  static_cast<IDirectArraySourceBuffer<T>&>(source), static_cast<ITargetBuffer<T>&>(target), incrementPosition, samplesCount, true);
384 }
385 
394 template <typename T>
397  bool incrementPosition = true,
398  size_t samplesCount = (std::numeric_limits<size_t>::max)())
399 {
400  return transferBuffer(
401  static_cast<ISourceBuffer<T>&>(source), static_cast<IDirectArrayTargetBuffer<T>&>(target), incrementPosition, samplesCount);
402 }
403 
404 END_NAMESPACE_MEMORY
FileHandler< T > & getHandler()
Returns buffer (file) handler.
Definition: disk_buffer.h:332
void seekPos(size_t offset)
Repositions file pointer relative to beginning of the file.
Definition: disk_buffer.h:195
void removeFile()
Deletes the file.
Definition: disk_buffer.h:105
virtual void reset()
Function resets all positions to default value.
Definition: fifo_buffer_base.h:167
Basic source buffer interface.
Definition: buffer.h:25
The class FifoBufferBase is used to implement first-in-first-out (FIFO) buffers.
Definition: fifo_buffer_base.h:21
void rewind()
Places file pointer at start of the file.
Definition: disk_buffer.h:135
void reset()
Clears file.
Definition: disk_buffer.h:227
size_t read(T *data, size_t count)
Reads data from disk at the current file pointer position.
Definition: disk_buffer.h:172
T * getData() const override
Function returns pointer to the internal buffer.
Definition: disk_buffer.h:350
size_t getSizeInBytes()
Returns the size of the file in bytes.
Definition: disk_buffer.h:206
void open(char const *mode)
Opens file as binary in given file access mode.
Definition: disk_buffer.h:66
virtual void setCapacity(size_t aCapacity)
Function allocates memory at the heap.
Definition: fifo_buffer_base.h:145
void open()
Opens file as binary in write/update mode.
Definition: disk_buffer.h:57
bool close()
Closes the file and frees the file pointer.
Definition: disk_buffer.h:89
FileHandler(const std::string &filename, bool deleteFileAfterClose=true)
The constructor creates an instance.
Definition: disk_buffer.h:43
The class DiskBuffer implements a first-in-first-out (FIFO) data structure on physical disc...
Definition: disk_buffer.h:259
size_t writeAt(const T *data, size_t count, size_t pos)
Writes data on disk.
Definition: disk_buffer.h:160
void reset() override
Function resets all positions to default value.
Definition: disk_buffer.h:324
size_t write(const T *samples, size_t samplesCount, size_t pos) override
Writes samples into buffer.
Definition: disk_buffer.h:338
Interface to array-backed target buffers.
Definition: direct_array_buffer.h:131
size_t read(T *samples, size_t samplesCount, const size_t pos) override
Reads from buffer into samples.
RisingDelay enables quadratic increment of delay with each iteration.
Wrapper of operations on file.
Definition: disk_buffer.h:35
bool validateReadPos(size_t readPos)
Function validates whether reading position is inside buffer boundaries.
Definition: disk_buffer.h:356
size_t readAt(T *data, size_t count, size_t pos)
Reads data from disk.
Definition: disk_buffer.h:184
Basic target buffer interface.
Definition: buffer.h:63
size_t getSize()
Returns the size of the file in type T elements.
Definition: disk_buffer.h:219
size_t write(const T *data, size_t count)
Writes data on disk at the current file pointer position.
Definition: disk_buffer.h:148
size_t transferBuffer(IDirectArraySourceBuffer< T > &source, DiskBuffer< T > &target, bool incrementPosition=true, size_t samplesCount=(std::numeric_limits< size_t >::max)())
Transferring data from source buffer to disk buffer without intermediate buffer.
Definition: disk_buffer.h:377
DiskBuffer(size_t capacity, const std::string &fileName, bool deleteFileAfterClose=true)
The constructor prepares file on disk and creates an instance.
Definition: disk_buffer.h:317