Sparse Virtual File System  0.4.0
A Sparse Virtual File System.
svf.cpp
Go to the documentation of this file.
1 
32 #include <algorithm>
33 #include <cstring>
34 #include <iostream>
35 #include <iterator>
36 #include <sstream>
37 #include <set>
38 
39 #include "svf.h"
40 
41 namespace SVFS {
45  static const char OVERWRITE_CHAR = '0';
46 
56  bool SparseVirtualFile::has(t_fpos fpos, size_t len) const noexcept {
57  SVF_ASSERT(integrity() == ERROR_NONE);
58 #ifdef SVF_THREAD_SAFE
59  std::lock_guard<std::mutex> mutex(m_mutex);
60 #endif
61 
62  if (m_svf.empty()) {
63  return false;
64  }
65  t_map::const_iterator iter = m_svf.upper_bound(fpos);
66  if (iter != m_svf.begin()) {
67  --iter;
68  }
69  t_fpos fpos_end = _file_position_immediatly_after_block(iter);
70  if (fpos >= iter->first && (fpos + len) <= fpos_end) {
71  return true;
72  }
73  return false;
74  }
75 
84  void
85  SparseVirtualFile::_throw_diff(t_fpos fpos, const char *data, t_map::const_iterator iter, size_t index_iter) const {
86  assert(data);
87  assert(iter != m_svf.end());
88  assert(m_config.compare_for_diff);
89 
90  if (*data != iter->second.data[index_iter]) {
91  std::ostringstream os;
92  os << "SparseVirtualFile::write():";
93  os << " Difference at position " << fpos;
94  os << " '" << *(data) << "' != '" << iter->second.data[index_iter] << "'";
95  os << " Ordinal " << static_cast<int>(*data) << " != " << static_cast<int>(iter->second.data[index_iter]);
96  std::string str = os.str();
98  }
99  // Assert as this should now never be called is there is _not_ a diff.
100  assert(0);
101  }
102 
113  void SparseVirtualFile::_write_new_block(t_fpos fpos, const char *data, size_t len, t_map::const_iterator hint) {
114  assert(m_svf.count(fpos) == 0);
115 
116  t_val new_value;
117  new_value.data.reserve(len);
118  new_value.block_touch = m_block_touch++;
119  while (len) {
120  new_value.data.push_back(*data);
121  --len;
122  ++data;
123  ++m_bytes_total;
124  }
125  auto size_before_insert = m_svf.size();
126  m_svf.insert(hint, {fpos, std::move(new_value)});
127  // Sanity check that we really have added a new block (rather than replacing one).
128  if (m_svf.size() != 1 + size_before_insert) {
129  std::ostringstream os;
130  os << "SparseVirtualFile::_write_new_block():";
131  os << " Unable to insert new block at " << fpos;
133  }
134  }
135 
179  void SparseVirtualFile::_write_new_append_old(t_fpos fpos, const char *data, size_t len, t_map::iterator iter) {
181  assert(data);
182  assert(len > 0);
183  assert(iter != m_svf.end());
184  assert(iter->first > fpos);
185 
186  size_t fpos_start = fpos;
187  size_t fpos_end = fpos + len;
188  t_val new_value;
189  new_value.block_touch = m_block_touch++;
190 
191  while (true) {
192  while (len && fpos < iter->first) {
193  // Copy new data 'X' up to start of iter.
194  // ^===========| |=====|
195  // %XXXX+++++++++++++XX++|
196  new_value.data.push_back(*data);
197  ++data;
198  ++fpos;
199  --len;
200  ++m_bytes_total;
201  }
202  size_t index_iter = 0;
203  size_t delta = std::min(len, iter->second.data.size());
204  // Check overlapped data matches 'Y'
205  // ^===========| |=====|
206  // %++++YYYYYYYYYYYYY++++|
208  if (std::memcmp(iter->second.data.data(), data, delta) != 0) {
209  _throw_diff(fpos, data, iter, 0);
210  }
211  }
212  for (size_t i = 0; i < delta; ++i) {
213  new_value.data.push_back(*data);
214  ++data;
215  }
216  fpos += delta;
217  len -= delta;
218  index_iter += delta;
219  if (_file_position_immediatly_after_block(iter) > fpos_end) {
220  // Copy rest of iter 'Z'
221  assert(len == 0);
222  // ^=========ZZZ
223  // %+++++++++++++|
224  // So append up to the end of iter and (maybe) go round again.
225  while (index_iter < iter->second.data.size()) {
226  new_value.data.push_back(iter->second.data[index_iter]);
227  ++index_iter;
228  }
230  iter->second.data.assign(iter->second.data.size(), OVERWRITE_CHAR);
231  }
232  m_svf.erase(iter);
233  break;
234  }
235  // Remove copied and checked old block and move on.
237  iter->second.data.assign(iter->second.data.size(), OVERWRITE_CHAR);
238  }
239  iter = m_svf.erase(iter);
240  if (iter == m_svf.end() || iter->first > fpos + len) {
241  // Copy rest of new and break
242  while (len) {
243  new_value.data.push_back(*data);
244  ++data;
245  ++fpos;
246  --len;
247  ++m_bytes_total;
248  }
249  break;
250  }
251  }
252  auto size_before_insert = m_svf.size();
253  m_svf.insert({fpos_start, std::move(new_value)});
254  if (m_svf.size() != 1 + size_before_insert) {
255  std::ostringstream os;
256  os << "SparseVirtualFile::write():";
257  os << " Unable to insert new block at " << fpos_start;
259  }
260  assert(len == 0);
261  assert(fpos == fpos_end);
263  }
264 
313  void SparseVirtualFile::_write_append_new_to_old(t_fpos fpos, const char *new_data, size_t new_data_len,
314  t_map::iterator base_block_iter) {
316  assert(new_data);
317  assert(new_data_len > 0);
318  assert(base_block_iter != m_svf.end());
319  assert(fpos >= base_block_iter->first);
320  assert(fpos <= _file_position_immediatly_after_block(base_block_iter));
321 
322 #ifdef DEBUG
323  size_t fpos_end = fpos + new_data_len;
324 #endif
325  // Diff check against base_block_iter
326  // Do the check to end of new_data_len or end of base_block_iter which ever comes first.
327  // Do not increment m_bytes_total as this is existing new_data.
328  // Diff check against base_block_iter
329  size_t write_index_from_block_start = fpos - base_block_iter->first;
330  // Do the check to end of new_data_len or end of base_block_iter which ever comes first.
331  size_t len_check_or_copy = std::min(new_data_len,
332  base_block_iter->second.data.size() - write_index_from_block_start);
334  if (std::memcmp(base_block_iter->second.data.data() + write_index_from_block_start, new_data,
335  len_check_or_copy) != 0) {
336  _throw_diff(fpos, new_data, base_block_iter, write_index_from_block_start);
337  }
338  }
339  new_data += len_check_or_copy;
340  fpos += len_check_or_copy;
341  new_data_len -= len_check_or_copy;
342  t_map::iterator next_block_iter = std::next(base_block_iter);
343  while (new_data_len) {
344  if (next_block_iter == m_svf.end()) {
345  // Termination case, copy remainder
346  while (new_data_len) {
347  base_block_iter->second.data.push_back(*new_data);
348  ++new_data;
349  ++fpos;
350  --new_data_len;
351  m_bytes_total += 1;
352  }
353  break; // Done. Needed because we are going to do erase(next_block_iter) otherwise.
354  } else {
355  // Copy the new_data up to start of next_block_iter or, we have exhausted the new_data.
356  while (new_data_len && fpos < next_block_iter->first) {
357  base_block_iter->second.data.push_back(*new_data);
358  ++new_data;
359  ++fpos;
360  --new_data_len;
361  m_bytes_total += 1;
362  }
363  }
364  if (new_data_len == 0 and fpos < next_block_iter->first) {
365  // We have exhausted new data and not reached the next block so we are done
366  break;
367  }
368  // Still data to copy, and we are up against the next block which we will coalesce into the base_block_iter.
369  write_index_from_block_start = 0;
370  // Diff check, but also append to base_block_iter.
371  // Do not increment m_bytes_total as this is existing data.
372  len_check_or_copy = std::min(new_data_len, _file_position_immediatly_after_block(next_block_iter) - fpos);
373  if (len_check_or_copy) {
375  if (std::memcmp(next_block_iter->second.data.data(), new_data, len_check_or_copy) != 0) {
376  _throw_diff(fpos, new_data, base_block_iter, 0);
377  }
378  }
379  // We could push_back either the new_data or the existing next block data. We choose the former.
380  for (size_t i = 0; i < len_check_or_copy; ++i) {
381  base_block_iter->second.data.push_back(*new_data);
382  ++new_data;
383  ++fpos;
384  --new_data_len;
385  ++write_index_from_block_start;
386  }
387  }
388  // Here either new_data is exhausted or next_block_iter is.
389  // If new_data is exhausted then copy remaining from next_block_iter to base_block_iter.
390  // Do not increment m_bytes_total as this is existing new_data.
391  if (new_data_len == 0) {
392  while (write_index_from_block_start < next_block_iter->second.data.size()) {
393  base_block_iter->second.data.push_back(next_block_iter->second.data[write_index_from_block_start]);
394  ++write_index_from_block_start;
395  }
396  }
397  // New data is not exhausted so erase next_block_iter as we have copied it and move on to the next block.
399  next_block_iter->second.data.assign(next_block_iter->second.data.size(), OVERWRITE_CHAR);
400  }
401  next_block_iter = m_svf.erase(next_block_iter);
402  }
403  base_block_iter->second.block_touch = m_block_touch++;
404  assert(new_data_len == 0);
405 #ifdef DEBUG
406  assert(fpos == fpos_end);
407 #endif
409  }
410 
453  void SparseVirtualFile::write(t_fpos fpos, const char *data, size_t len) {
455 #ifdef SVF_THREAD_SAFE
456  std::lock_guard<std::mutex> mutex(m_mutex);
457 #endif
458  // TODO: throw if !data, len == 0
459  if (m_svf.empty() || fpos > _file_position_immediatly_after_end()) {
460  // Simple insert of new data into empty map or a node beyond the end (common case).
461  _write_new_block(fpos, data, len, m_svf.begin());
462  } else {
463  t_map::iterator iter = m_svf.upper_bound(fpos);
464  if (iter != m_svf.begin()) {
465  --iter;
466  }
467  if (iter->first > fpos) {
468  // Insert new block, possibly coalescing existing blocks.
469  // New comes earlier so either create a new block or copy existing block on to it.
470  if (iter->first <= fpos + len) {
471  // Need to coalesce
472  _write_new_append_old(fpos, data, len, iter);
473  } else {
474  // The new block precedes the old one
475  _write_new_block(fpos, data, len, iter);
476  }
477  } else {
478  // Existing block.first is <= fpos
479  if (fpos > _file_position_immediatly_after_block(iter)) {
480  // No overlap so just write new block after the last one
481  _write_new_block(fpos, data, len, m_svf.end());
482  } else {
483  // Append new to existing block, possibly coalescing existing blocks.
484  _write_append_new_to_old(fpos, data, len, iter);
485  }
486  }
487  }
488  // Update internals.
489  // NOTE: m_block_touch is incremented in one of the three actual write methods.
490  m_count_write += 1;
491  m_bytes_write += len;
492  m_time_write = std::chrono::system_clock::now();
494  }
495 
504  void SparseVirtualFile::read(t_fpos fpos, size_t len, char *p) {
505 #ifdef SVF_THREAD_SAFE
506  std::lock_guard<std::mutex> mutex(m_mutex);
507 #endif
509 
510  if (m_svf.empty()) {
512  "SparseVirtualFile::read(): Sparse virtual file is empty.");
513  }
514  t_map::iterator iter = m_svf.lower_bound(fpos);
515  if (iter == m_svf.begin() && iter->first != fpos) {
516  std::ostringstream os;
517  os << "SparseVirtualFile::read():";
518  os << " Requested file position " << fpos << " precedes first block at " << iter->first;
520  }
521  size_t offset_into_block = 0;
522  if (iter == m_svf.end() || iter->first != fpos) {
523  --iter;
524  offset_into_block = fpos - iter->first;
525  }
526  if (offset_into_block + len > iter->second.data.size()) {
527  std::ostringstream os;
528  os << "SparseVirtualFile::read():";
529  os << " Requested position " << fpos << " length " << len;
530  os << " (end " << fpos + len << ")";
531  os << " overruns block that starts at " << iter->first << " has size " << iter->second.data.size();
532  os << " (end " << iter->first + iter->second.data.size() << ").";
533  os << " Offset into block is " << offset_into_block;
534  os << " overrun is " << offset_into_block + len - iter->second.data.size() << " bytes";
536  }
537  if (memcpy(p, iter->second.data.data() + offset_into_block, len) != p) {
538  std::ostringstream os;
539  os << "SparseVirtualFile::read():";
540  os << " memcpy failed " << fpos << " length " << len;
542  }
543  // Adjust non-const members
544  iter->second.block_touch = m_block_touch++;
545  m_bytes_read += len;
546  m_count_read += 1;
547  m_time_read = std::chrono::system_clock::now();
548  }
549 
582  t_seek_reads SparseVirtualFile::need(t_fpos fpos, size_t len, size_t greedy_length) const noexcept {
583  SVF_ASSERT(integrity() == ERROR_NONE);
584 #ifdef SVF_THREAD_SAFE
585  std::lock_guard<std::mutex> mutex(m_mutex);
586 #endif
587  return _need_no_lock(fpos, len, greedy_length);
588  }
589 
590  t_seek_reads SparseVirtualFile::_need_no_lock(t_fpos fpos, size_t len, size_t greedy_length) const noexcept {
591  SVF_ASSERT(integrity() == ERROR_NONE);
592  if (m_svf.empty()) {
593  return {{fpos, greedy_length > len ? greedy_length : len}};
594  }
595  size_t original_len = len;
596  t_fpos fpos_to = fpos + len;
597  t_seek_reads ret;
598  t_map::const_iterator iter = m_svf.upper_bound(fpos);
599  if (iter == m_svf.begin()) {
600  if (fpos + len <= iter->first) {
601  // ^==|
602  // |+++|
603  ret.push_back({fpos, len});
604  fpos += len;
605  // Mark that we are done.
606  len = 0;
607  }
608  } else {
609  // Otherwise check the previous node with std::prev.
610  auto last_fpos = _file_position_immediatly_after_block(std::prev(iter));
611  if (fpos < last_fpos) {
612  // Example, change:
613  // |==| ^==|
614  // |++++++++++++|
615  // to:
616  // |+++++++++|
617  len -= std::min(len, last_fpos - fpos);
618  fpos = std::min(fpos_to, last_fpos);
619  }
620  }
621  // Now walk through the remaining nodes until we have exhausted the read.
622  while (len) {
623  if (iter == m_svf.end() || fpos + len <= iter->first) {
624  // Either:
625  // |==|
626  // |++++++++++++|
627  // or:
628  // |==|
629  // |+++++|
630  ret.emplace_back(fpos, len);
631  fpos += len;
632  len = 0;
633  break;
634  }
635  // We are in this state:
636  // ^=====|
637  // |++++++++++++++|
638  // or:
639  // ^=====|
640  // |++++++++|
641  if (fpos < iter->first) {
642  assert(len >= iter->first - fpos);
643  auto bytes_added = iter->first - fpos;
644  ret.emplace_back(fpos, bytes_added);
645  len -= bytes_added;
646  fpos += bytes_added;
647  }
648  // We are now in this state:
649  // ^=====|
650  // |+++++++|
651  // or:
652  // ^=====|
653  // |++++|
654  assert(fpos == iter->first);
655  if (fpos + len <= _file_position_immediatly_after_block(iter)) {
656  // ^======|
657  // |++++|
658  fpos += len;
659  len = 0;
660  break;
661  } else {
662  // ^======|
663  // |+++++++++|
664  fpos += iter->second.data.size();
665  len -= iter->second.data.size();
666  }
667  ++iter;
668  }
669  assert(fpos == fpos_to);
670  assert(len == 0);
671  if (greedy_length && greedy_length > original_len && !ret.empty()) {
672  ret = _minimise_seek_reads(ret, greedy_length);
673  }
674  return ret;
675  }
676 
708  t_seek_reads SparseVirtualFile::need_many(t_seek_reads &seek_reads, size_t greedy_length) const noexcept {
709  SVF_ASSERT(integrity() == ERROR_NONE);
710 #ifdef SVF_THREAD_SAFE
711  std::lock_guard<std::mutex> mutex(m_mutex);
712 #endif
713 
714  std::sort(seek_reads.begin(), seek_reads.end());
715  if (m_svf.empty()) {
716  return _minimise_seek_reads(seek_reads, greedy_length);
717  }
718  t_seek_reads ret;
719  for (const auto &iter_seek_read: seek_reads) {
720  for (const auto &iter_need: _need_no_lock(iter_seek_read.first, iter_seek_read.second, 0)) {
721  ret.emplace_back(iter_need);
722  }
723  }
724  return _minimise_seek_reads(ret, greedy_length);
725  }
726 
734  size_t SparseVirtualFile::_amount_to_read(t_seek_read iter, size_t greedy_length) noexcept {
735  return iter.second > greedy_length ? iter.second : greedy_length;
736  }
737 
748  SparseVirtualFile::_minimise_seek_reads(const t_seek_reads &seek_reads, size_t greedy_length) noexcept {
749 
750  t_seek_reads new_seek_reads;
751  for (const t_seek_read &seek_read: seek_reads) {
752  if (new_seek_reads.empty()) {
753  // Add the first greedy block
754  new_seek_reads.emplace_back(seek_read.first, _amount_to_read(seek_read, greedy_length));
755  } else {
756  // Compare with last new block
757  auto last_iter_of_new = new_seek_reads.end();
758  --last_iter_of_new;
759  if (seek_read.first > last_iter_of_new->first + last_iter_of_new->second) {
760  // Add a new greedy block
761  new_seek_reads.emplace_back(seek_read.first, _amount_to_read(seek_read, greedy_length));
762  } else if (seek_read.first + seek_read.second > last_iter_of_new->first + last_iter_of_new->second) {
763  // Extend last block
764  last_iter_of_new->second +=
765  (seek_read.first + seek_read.second) - (last_iter_of_new->first + last_iter_of_new->second);
766  } // Otherwise do nothing, it is covered by greedy.
767  }
768  }
769  return new_seek_reads;
770  }
771 
779 #ifdef SVF_THREAD_SAFE
780  std::lock_guard<std::mutex> mutex(m_mutex);
781 #endif
782 
783  t_seek_reads ret;
784  for (const auto &iter: m_svf) {
785  ret.emplace_back(iter.first, iter.second.data.size());
786  }
787  return ret;
788  }
789 
800 #ifdef SVF_THREAD_SAFE
801  std::lock_guard<std::mutex> mutex(m_mutex);
802 #endif
803 
804  if (m_svf.empty()) {
806  "SparseVirtualFile::block_size(): Sparse virtual file is empty.");
807  }
808  t_map::const_iterator iter = m_svf.find(fpos);
809  if (iter == m_svf.end()) {
810  std::ostringstream os;
811  os << "SparseVirtualFile::block_size():";
812  os << " Requested file position " << fpos << " is not at the start of a block";
814  }
815  return iter->second.data.size();
816  }
817 
823  size_t SparseVirtualFile::size_of() const noexcept {
825 #ifdef SVF_THREAD_SAFE
826  std::lock_guard<std::mutex> mutex(m_mutex);
827 #endif
828  size_t ret = sizeof(SparseVirtualFile);
829 
830  // Add heap referenced data sizes.
831  ret += m_id.size();
832  for (const auto &iter: m_svf) {
833  ret += sizeof(iter.first);
834  ret += sizeof(iter.second);
835  ret += iter.second.data.size();
836  }
837  return ret;
838  }
839 
849  void SparseVirtualFile::clear() noexcept {
851 #ifdef SVF_THREAD_SAFE
852  std::lock_guard<std::mutex> mutex(m_mutex);
853 #endif
854  // Maintain ID and constructor arguments.
855 // m_time_write = std::chrono::time_point<std::chrono::system_clock>::min();
856 // m_time_read = std::chrono::time_point<std::chrono::system_clock>::min();
858  for (auto &iter: m_svf) {
859  iter.second.data.assign(iter.second.data.size(), OVERWRITE_CHAR);
860  }
861  }
862  m_svf.clear();
863  m_bytes_total = 0;
864  m_count_write = 0;
865  m_count_read = 0;
866  m_bytes_write = 0;
867  m_bytes_read = 0;
869  }
870 
881 
882  auto iter = m_svf.find(fpos);
883  if (iter == m_svf.end()) {
884  std::ostringstream os;
885  os << "SparseVirtualFile::erase():";
886  os << " Non-existent file position " << fpos << " at start of block.";
888  }
889  size_t ret = iter->second.data.size();
890  m_bytes_total -= ret;
891  m_svf.erase(iter);
892  m_blocks_erased++;
893  m_bytes_erased += ret;
894  return ret;
895  }
896 
907 #ifdef SVF_THREAD_SAFE
908  std::lock_guard<std::mutex> mutex(m_mutex);
909 #endif
910  return _erase_no_lock(fpos);
911  }
912 
927  SparseVirtualFile::integrity() const noexcept {
928  t_fpos prev_fpos = 0;
929  size_t prev_size = 0;
930  t_map::const_iterator iter = m_svf.begin();
931  size_t byte_count = 0;
932  std::set<t_block_touch> block_touches;
933 
934  while (iter != m_svf.end()) {
935  if (iter->second.data.empty()) {
936  return ERROR_EMPTY_BLOCK;
937  }
938  if (iter != m_svf.begin()) {
939  if (prev_fpos == iter->first && prev_size == iter->second.data.size()) {
940  return ERROR_DUPLICATE_BLOCK;
941  }
942  if (prev_fpos + prev_size == iter->first) {
943  return ERROR_ADJACENT_BLOCKS;
944  }
945  if (prev_fpos + prev_size > iter->first) {
946  return ERROR_BLOCKS_OVERLAP;
947  }
948  }
949  if (block_touches.find(iter->second.block_touch) != block_touches.end()) {
950  // Duplicate block_touches value
952  }
953  prev_fpos = iter->first;
954  prev_size = iter->second.data.size();
955  byte_count += prev_size;
956  ++iter;
957  }
958  if (byte_count != m_bytes_total) {
960  }
961  return ERROR_NONE;
962  }
963 
971  t_fpos
974 #ifdef SVF_THREAD_SAFE
975  std::lock_guard<std::mutex> mutex(m_mutex);
976 #endif
978  }
979 
989  t_block_touches ret;
990  for (const auto &iter: m_svf) {
991  // The block_touch should not be in the return value, yet.
992  assert(ret.find(iter.second.block_touch) == ret.end());
993  ret[iter.second.block_touch] = iter.first;
994  }
995  return ret;
996  }
997 
1005  [[nodiscard]] t_block_touches SparseVirtualFile::block_touches() const noexcept {
1007 #ifdef SVF_THREAD_SAFE
1008  std::lock_guard<std::mutex> mutex(m_mutex);
1009 #endif
1010  return _block_touches_no_lock();
1011  }
1012 
1021  size_t SparseVirtualFile::lru_punt(size_t cache_size_upper_bound) {
1023 #ifdef SVF_THREAD_SAFE
1024  std::lock_guard<std::mutex> mutex(m_mutex);
1025 #endif
1026  size_t ret = 0;
1027  if (m_svf.size() > 1 and m_bytes_total >= cache_size_upper_bound) {
1028  auto touch_fpos_map = _block_touches_no_lock();
1029  for (const auto &iter: touch_fpos_map) {
1030  if (m_svf.size() > 1 and m_bytes_total >= cache_size_upper_bound) {
1031  ret += _erase_no_lock(iter.second);
1032  m_blocks_punted++;
1033  } else {
1034  break;
1035  }
1036  }
1037  }
1038  m_bytes_punted += ret;
1039  return ret;
1040  }
1041 
1050  t_fpos
1052  if (m_svf.empty()) {
1053  return 0;
1054  } else {
1055  auto iter = m_svf.end();
1056  --iter;
1058  }
1059  }
1060 
1069  t_fpos
1070  SparseVirtualFile::_file_position_immediatly_after_block(t_map::const_iterator iter) const noexcept {
1071  // NOTE: do not SVF_ASSERT(integrity() == ERROR_NONE); as integrity() calls this so infinite recursion.
1072  assert(iter != m_svf.end());
1073 
1074  auto ret = iter->first + iter->second.data.size();
1075  return ret;
1076  }
1077 
1078 } // namespace SVFS
Might be thrown during a write operation where the data differs.
Definition: svf.h:224
Might be thrown during a erase operation where the file position is not at the exact beginning of a b...
Definition: svf.h:237
Might be thrown during a write operation where the data differs.
Definition: svf.h:231
Might be thrown during a write operation which fails.
Definition: svf.h:218
ERROR_CONDITION
Check result of internal integrity.
Definition: svf.h:510
@ ERROR_BLOCKS_OVERLAP
Blocks overlap.
Definition: svf.h:518
@ ERROR_ADJACENT_BLOCKS
Blocks are adjacent and have not been coalesced.
Definition: svf.h:516
@ ERROR_NONE
No error.
Definition: svf.h:512
@ ERROR_BYTE_COUNT_MISMATCH
Missmatch in byte count where the count of the bytes in all the blocks does not match m_bytes_total.
Definition: svf.h:520
@ ERROR_DUPLICATE_BLOCK
Duplicate blocks of the same length and at the same file positions.
Definition: svf.h:522
@ ERROR_EMPTY_BLOCK
A block is empty.
Definition: svf.h:514
@ ERROR_DUPLICATE_BLOCK_TOUCH
Two or more blocks have the same block touch value.
Definition: svf.h:524
t_fpos _file_position_immediatly_after_end() const noexcept
Returns the file position immediately after the last block.
Definition: svf.cpp:1051
t_seek_reads need_many(t_seek_reads &seek_reads, size_t greedy_length=0) const noexcept
Given many [(file position, lengths), ...] what data do I need that I don't yet have?
Definition: svf.cpp:708
size_t m_bytes_punted
The count of bytes that have been erased by punting.
Definition: svf.h:483
static t_seek_reads _minimise_seek_reads(const t_seek_reads &seek_reads, size_t greedy_length) noexcept
May reduce the list of file position/lengths by coalescing them if possible up to a limit greedy_leng...
Definition: svf.cpp:748
std::chrono::time_point< std::chrono::system_clock > m_time_write
Last access real-time timestamp for a write.
Definition: svf.h:457
size_t size_of() const noexcept
size_of() gives best guess of total memory usage.
Definition: svf.cpp:823
size_t m_bytes_total
Total number of bytes in this SVF.
Definition: svf.h:445
size_t m_bytes_read
Definition: svf.h:455
size_t m_blocks_punted
The count of blocks that have been erased by punting.
Definition: svf.h:481
size_t lru_punt(size_t cache_size_upper_bound)
Definition: svf.cpp:1021
void _write_append_new_to_old(t_fpos fpos, const char *new_data, size_t new_data_len, t_map::iterator base_block_iter)
From file position, write the new_data to the block identified by base_block_iter....
Definition: svf.cpp:313
tSparseVirtualFileConfig m_config
The SVF configuration.
Definition: svf.h:443
t_block_touches block_touches() const noexcept
Returns a std::map of latest touch value key and file position value.
Definition: svf.cpp:1005
size_t m_bytes_write
Definition: svf.h:452
void _throw_diff(t_fpos fpos, const char *data, t_map::const_iterator iter, size_t index_iter) const
Throws a ExceptionSparseVirtualFileDiff with an explanation of the data difference.
Definition: svf.cpp:85
size_t m_bytes_erased
The total count of bytes that have been erased either directly or by punting.
Definition: svf.h:479
size_t m_blocks_erased
The total count of blocks that have been erased either directly or by punting.
Definition: svf.h:477
SparseVirtualFile(const std::string &id, double mod_time, const tSparseVirtualFileConfig &config=tSparseVirtualFileConfig())
Create a Sparse Virtual File.
Definition: svf.h:297
size_t m_count_write
Access statistics: count of write operations.
Definition: svf.h:447
std::chrono::time_point< std::chrono::system_clock > m_time_read
Last access real-time timestamp for a read.
Definition: svf.h:459
void write(t_fpos fpos, const char *data, size_t len)
Write the data a the given file position.
Definition: svf.cpp:453
void clear() noexcept
Executes the data deletion strategy.
Definition: svf.cpp:849
t_fpos _file_position_immediatly_after_block(t_map::const_iterator iter) const noexcept
Returns the file position immediately after the particular block.
Definition: svf.cpp:1070
void _write_new_append_old(t_fpos fpos, const char *data, size_t len, t_map::iterator iter)
Write a new block and append existing blocks to it.
Definition: svf.cpp:179
t_block_touches _block_touches_no_lock() const noexcept
Returns a std::map of latest touch value key and file position value.
Definition: svf.cpp:987
static size_t _amount_to_read(t_seek_read iter, size_t greedy_length) noexcept
Returns the maximal length to read given a greedy length.
Definition: svf.cpp:734
size_t erase(t_fpos fpos)
Remove a particular block.
Definition: svf.cpp:905
bool has(t_fpos fpos, size_t len) const noexcept
Do I have the data at the given file position and length?
Definition: svf.cpp:56
size_t block_size(t_fpos fpos) const
The length of the block at a specific file position.
Definition: svf.cpp:798
size_t _erase_no_lock(t_fpos fpos)
Remove a particular block.
Definition: svf.cpp:879
void _write_new_block(t_fpos fpos, const char *data, size_t len, t_map::const_iterator hint)
Write a brand new block into either an empty SVF or beyond the current blocks.
Definition: svf.cpp:113
ERROR_CONDITION integrity() const noexcept
Internal integrity check.
Definition: svf.cpp:927
std::mutex m_mutex
Thread mutex. This adds about 5-10% execution time compared with a single threaded version.
Definition: svf.h:474
t_map m_svf
The actual SVF.
Definition: svf.h:469
t_fpos last_file_position() const noexcept
The position of the last byte.
Definition: svf.cpp:972
void read(t_fpos fpos, size_t len, char *p)
Read data and write to the buffer provided by the caller. This is non-const as it updates the non-con...
Definition: svf.cpp:504
t_seek_reads _need_no_lock(t_fpos fpos, size_t len, size_t greedy_length=0) const noexcept
Definition: svf.cpp:590
size_t m_count_read
Access statistics: count of read operations.
Definition: svf.h:449
t_seek_reads need(t_fpos fpos, size_t len, size_t greedy_length=0) const noexcept
Create a new fragmentation list of seek/read instructions.
Definition: svf.cpp:582
std::string m_id
The SVF ID.
Definition: svf.h:439
t_seek_reads blocks() const noexcept
The existing blocks as a list of (file_position, size) pairs.
Definition: svf.cpp:777
t_block_touch m_block_touch
A monotonically increasing integer that indicates the age of a block, smaller is older.
Definition: svf.h:471
The namespace for all svfsc code.
Definition: svf.cpp:41
static const char OVERWRITE_CHAR
Used to overwrite the memory before discarding it (if required).
Definition: svf.cpp:45
std::vector< t_seek_read > t_seek_reads
Definition: svf.h:251
std::map< t_block_touch, t_fpos > t_block_touches
Definition: svf.h:255
std::pair< t_fpos, size_t > t_seek_read
Definition: svf.h:249
size_t t_fpos
Definition: svf.h:247
Typedef for the data. This allows for extra per-block fields in the future.
Definition: svf.h:461
std::vector< char > data
Definition: svf.h:462
t_block_touch block_touch
Definition: svf.h:464
#define SVF_ASSERT(x)
Definition: svf.h:194