Sparse Virtual File System  0.4.0
A Sparse Virtual File System.
_cSVF.cpp
Go to the documentation of this file.
1 
33 #include "cp_svfs.h"
34 
35 #include <ctime>
36 #include <memory>
37 
38 #include "svf.h"
39 #include "svfs_util.h"
40 
57 #define SVFS_SVF_METHOD_SIZE_T_WRAPPER(method_name, docstring) \
58 PyDoc_STRVAR( \
59  cp_SparseVirtualFile_##method_name##_docstring, \
60  #method_name"(self) -> int\n\n" \
61  docstring \
62 );\
63 static PyObject * \
64 cp_SparseVirtualFile_##method_name(cp_SparseVirtualFile *self) { \
65  ASSERT_FUNCTION_ENTRY_SVF(pSvf); \
66  PyObject *ret = NULL; \
67  try { \
68  ret = PyLong_FromLong(self->pSvf->method_name()); \
69  if (!ret) { \
70  PyErr_Format(PyExc_RuntimeError, "%s: Can not create integer from size_t.", __FUNCTION__); \
71  goto except; \
72  } \
73  } catch (const std::exception &err) { \
74  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what()); \
75  goto except; \
76  } \
77  assert(! PyErr_Occurred()); \
78  assert(ret); \
79  goto finally; \
80 except: \
81  assert(PyErr_Occurred()); \
82  Py_XDECREF(ret); \
83  ret = NULL; \
84 finally: \
85  return ret; \
86 }
87 
92 #define SVFS_SVF_METHOD_SIZE_T_REGISTER(method_name) \
93  { \
94  #method_name, \
95  (PyCFunction) cp_SparseVirtualFile_##method_name, \
96  METH_NOARGS, \
97  cp_SparseVirtualFile_##method_name##_docstring \
98  }
99 
105 typedef struct {
106  PyObject_HEAD
108 #ifdef PY_THREAD_SAFE
109  PyThread_type_lock lock;
110 #endif
112 
113 #ifdef PY_THREAD_SAFE
114 
121 public:
127  explicit AcquireLockSVF(cp_SparseVirtualFile *pSVF) : _pSVF(pSVF) {
128  assert(_pSVF);
129  assert(_pSVF->lock);
130  if (!PyThread_acquire_lock(_pSVF->lock, NOWAIT_LOCK)) {
131  Py_BEGIN_ALLOW_THREADS
132  PyThread_acquire_lock(_pSVF->lock, WAIT_LOCK);
133  Py_END_ALLOW_THREADS
134  }
135  }
136 
143  assert(_pSVF);
144  assert(_pSVF->lock);
145  PyThread_release_lock(_pSVF->lock);
146  }
147 
148 private:
150 };
151 
152 #else
154 class AcquireLockSVF {
155 public:
157 };
158 #endif
159 
165 #define ASSERT_FUNCTION_ENTRY_SVF(member) do { \
166  assert(self); \
167  assert(((cp_SparseVirtualFile *)self)->member); \
168  assert(! PyErr_Occurred()); \
169 } while (0)
170 
171 
172 // Construction and destruction
173 #pragma mark Construction and destruction
174 
184 static PyObject *
185 cp_SparseVirtualFile_new(PyTypeObject *type, PyObject *Py_UNUSED(args), PyObject *Py_UNUSED(kwds)) {
186  assert(!PyErr_Occurred());
187  cp_SparseVirtualFile *self;
188  self = (cp_SparseVirtualFile *) type->tp_alloc(type, 0);
189  if (self != NULL) {
190  self->pSvf = nullptr;
191 #ifdef PY_THREAD_SAFE
192  self->lock = NULL;
193 #endif
194  }
195 // PyObject_Print((PyObject *)self, stdout);
196 // fprintf(stdout, "cp_SparseVirtualFile_new() self %p\n", (void *)self);
197  assert(!PyErr_Occurred());
198  return (PyObject *) self;
199 }
200 
218 static int
219 cp_SparseVirtualFile_init(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs) {
220  assert(!PyErr_Occurred());
221 
222  char *c_id = NULL;
223  double mod_time = 0.0;
224  static const char *kwlist[] = {"id", "mod_time", "overwrite_on_exit", "compare_for_diff", NULL};
226 
227 // TRACE_SELF_ARGS_KWARGS;
228 // fprintf(stdout, "Config was compare_for_diff=%d overwrite_on_exit=%d\n", config.compare_for_diff,
229 // config.overwrite_on_exit);
230 
231  // NOTE: With format unit 'p' we need to pass in an int.
232  int overwrite_on_exit = config.overwrite_on_exit ? 1 : 0;
233  int compare_for_diff = config.compare_for_diff ? 1 : 0;
234 
235  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|dpp", (char **) kwlist, &c_id, &mod_time,
236  &overwrite_on_exit, &compare_for_diff)) {
237  assert(PyErr_Occurred());
238  return -1;
239  }
240  config.overwrite_on_exit = overwrite_on_exit != 0;
241  config.compare_for_diff = compare_for_diff != 0;
242 
243 // fprintf(stdout, "Config now compare_for_diff=%d overwrite_on_exit=%d\n", config.compare_for_diff,
244 // config.overwrite_on_exit);
245 
246  try {
247  self->pSvf = new SVFS::SparseVirtualFile(c_id, mod_time, config);
248  } catch (const std::exception &err) {
249  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
250  return -1;
251  }
252 #ifdef PY_THREAD_SAFE
253  self->lock = PyThread_allocate_lock();
254  if (self->lock == NULL) {
255  delete self->pSvf;
256  PyErr_SetString(PyExc_MemoryError, "Unable to allocate thread lock.");
257  return -2;
258  }
259 #endif
260 // fprintf(stdout, "cp_SparseVirtualFile_init() self->pSvf %p\n", (void *)self->pSvf);
261  assert(!PyErr_Occurred());
262  return 0;
263 }
264 
269 static void
271  delete self->pSvf;
272 #ifdef PY_THREAD_SAFE
273  if (self->lock) {
274  PyThread_free_lock(self->lock);
275  self->lock = NULL;
276  }
277 #endif
278  Py_TYPE(self)->tp_free((PyObject *) self);
279 }
280 
281 // END: Construction and destruction
282 #pragma mark END: Construction and destruction
283 
284 // SVFS functions
285 #pragma mark SVF functions
286 
288  cp_SparseVirtualFile_id_docstring,
289  "id(self) -> str\n\n"
290  "Returns the ID of the Sparse Virtual File."
291 );
292 
293 static PyObject *
296 
297  PyObject * ret = NULL;
298  ret = PyUnicode_FromKindAndData(PyUnicode_1BYTE_KIND, self->pSvf->id().c_str(), self->pSvf->id().size());
299  if (!ret) {
300  PyErr_Format(PyExc_RuntimeError, "%s: Can not create id for %s", __FUNCTION__, self->pSvf->id().c_str());
301  goto except;
302  }
303  assert(!PyErr_Occurred());
304  assert(ret);
305  goto finally;
306  except:
307  assert(PyErr_Occurred());
308  Py_XDECREF(ret);
309  ret = NULL;
310  finally:
311  return ret;
312 }
313 
314 
316  size_of,
317  "Returns the estimate of total memory usage of the Sparse Virtual File."
318 );
319 
320 
322  num_bytes,
323  "Returns the total number of file bytes held by the Sparse Virtual File."
324 );
325 
326 
328  num_blocks,
329  "Returns the total number of blocks of data held by the Sparse Virtual File System."
330 );
331 
332 
334  last_file_position,
335  "Returns the file position immediately past the last block."
336 );
337 
339  cp_SparseVirtualFile_has_data_docstring,
340  "has_data(self, file_position: int, length: int) -> bool\n\n"
341  "Checks if the Sparse Virtual File of the ID has data at the given ``file_position`` and ``length``.\n\n"
342  "Parameters\n\n"
343  "file_position: int\n"
344  " The absolute file position of the start of the data.\n\n"
345  "length: int\n"
346  " The length of the required data in bytes.\n\n"
347  "Returns\n\n"
348  "bool: True if the SVF contains the data, False otherwise."
349 );
350 
351 static PyObject *
352 cp_SparseVirtualFile_has_data(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs) {
354 
355  PyObject * ret = NULL;
356  unsigned long long fpos = 0;
357  unsigned long long len = 0;
358  static const char *kwlist[] = {"file_position", "length", NULL};
359 
360  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "KK", (char **) kwlist, &fpos, &len)) {
361  goto except;
362  }
363  try {
364  if (self->pSvf->has(fpos, len)) {
365  Py_INCREF(Py_True);
366  ret = Py_True;
367  } else {
368  Py_INCREF(Py_False);
369  ret = Py_False;
370  }
371  } catch (const std::exception &err) {
372  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
373  goto except;
374  }
375  assert(!PyErr_Occurred());
376  assert(ret);
377  goto finally;
378  except:
379  assert(PyErr_Occurred());
380  Py_XDECREF(ret);
381  ret = NULL;
382  finally:
383  return ret;
384 }
385 
387  cp_SparseVirtualFile_write_docstring,
388  "write(self, file_position: int, data: bytes) -> None\n\n"
389  "Writes the data to the Sparse Virtual File of the given ID at ``file_position`` and ``data`` as a ``bytes`` object."
390  " This will raise an ``IOError`` if ``self.compare_for_diff`` is True and given data is different than"
391  " that seen before and only new data up to this point will be written."
392  " If the ``byte`` data is empty nothing will be done."
393  " This will raise a RuntimeError if the data can not be written for any other reason"
394 );
395 
396 static PyObject *
397 cp_SparseVirtualFile_write(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs) {
399 
400  PyObject * ret = NULL;
401  unsigned long long fpos = 0;
402  PyObject * py_bytes_data = NULL;
403  static const char *kwlist[] = {"file_position", "data", NULL};
404  AcquireLockSVF _lock(self);
405 
406  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "KS", (char **) kwlist, &fpos, &py_bytes_data)) {
407  goto except;
408  }
409  if (PyBytes_GET_SIZE(py_bytes_data) > 0) {
410  try {
411 // fprintf(stdout, "TRACE: %s Writing fpos %llu length %zd\n", __FUNCTION__, fpos, PyBytes_Size(py_bytes_data));
412  self->pSvf->write(fpos, PyBytes_AS_STRING(py_bytes_data), PyBytes_Size(py_bytes_data));
414  PyErr_Format(PyExc_IOError,
415  "%s: Can not write to a SVF as the given data is different from what is there. ERROR: %s",
416  __FUNCTION__, err.message().c_str());
417  goto except;
418  } catch (const SVFS::Exceptions::ExceptionSparseVirtualFile &err) {
419  PyErr_Format(PyExc_RuntimeError, "%s: Can not write to a SVF. ERROR: %s",
420  __FUNCTION__, err.message().c_str());
421  goto except;
422  } catch (const std::exception &err) {
423  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
424  goto except;
425  }
426  }
427  Py_INCREF(Py_None);
428  ret = Py_None;
429  assert(!PyErr_Occurred());
430  assert(ret);
431  goto finally;
432  except:
433  assert(PyErr_Occurred());
434  Py_XDECREF(ret);
435  ret = NULL;
436  finally:
437  return ret;
438 }
439 
441  cp_SparseVirtualFile_read_docstring,
442  "read(self, file_position: int, length: int) -> bytes\n\n"
443  "Read the data from the Sparse Virtual File at ``file_position`` and ``length`` returning a ``bytes`` object."
444  " This takes a file position and a length."
445  " This will raise an ``IOError`` if any data is not present"
446  " This will raise a ``RuntimeError`` if the data can not be read for any other reason"
447 );
448 
449 static PyObject *
451  unsigned long long len) {
452  // Create a bytes object
453  PyObject * ret = PyBytes_FromStringAndSize(NULL, len);
454  if (!ret) {
455  PyErr_Format(PyExc_RuntimeError, "%s()#%d: Could not create bytes object.", __FUNCTION__, __LINE__);
456  return NULL;
457  }
458  try {
459  self->pSvf->read(fpos, len, PyBytes_AS_STRING(ret));
461  PyErr_Format(PyExc_IOError, "%s()#%d: Can not read from a SVF. ERROR: %s",
462  __FUNCTION__, __LINE__, err.message().c_str());
463  Py_DECREF(ret);
464  return NULL;
465  } catch (const SVFS::Exceptions::ExceptionSparseVirtualFile &err) {
466  PyErr_Format(PyExc_RuntimeError, "%s()#%d: Fatal error reading from a SVF. ERROR: %s",
467  __FUNCTION__, __LINE__, err.message().c_str());
468  Py_DECREF(ret);
469  return NULL;
470  } catch (const std::exception &err) {
471  PyErr_Format(PyExc_RuntimeError, "%s()#%d: FATAL caught std::exception %s", __FUNCTION__, __LINE__,
472  err.what());
473  Py_DECREF(ret);
474  return NULL;
475  }
476  return ret;
477 }
478 
479 static PyObject *
480 cp_SparseVirtualFile_read(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs) {
482 
483  PyObject * ret = NULL;
484  unsigned long long fpos = 0;
485  unsigned long long len = 0;
486  static const char *kwlist[] = {"file_position", "length", NULL};
487  AcquireLockSVF _lock(self);
488 
489  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "KK", (char **) kwlist, &fpos, &len)) {
490  goto except;
491  }
492  // Create a bytes object
493  ret = private_SparseVirtualFile_svf_read_as_py_bytes(self, fpos, len);
494  if (ret == NULL) {
495  goto except;
496  }
497  assert(!PyErr_Occurred());
498  assert(ret);
499  goto finally;
500  except:
501  assert(PyErr_Occurred());
502  Py_XDECREF(ret);
503  ret = NULL;
504  finally:
505  return ret;
506 }
507 
508 
510  cp_SparseVirtualFile_erase_docstring,
511  "erase(self, file_position: int) -> None\n\n"
512  "Erase the data from the Sparse Virtual File at the given ``file_position``"
513  " which must be the beginning of a block.\n"
514  "This will raise an ``IOError`` if a block is not present at that file position."
515 );
516 
517 static PyObject *
518 cp_SparseVirtualFile_erase(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs) {
520 
521  unsigned long long fpos = 0;
522  static const char *kwlist[] = {"file_position", NULL};
523  AcquireLockSVF _lock(self);
524 
525  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "K", (char **) kwlist, &fpos)) {
526  return NULL;
527  }
528  try {
529  self->pSvf->erase(fpos);
531  /* Do not include line as this can vary between versions. */
532  PyErr_Format(PyExc_IOError, "%s(): Can not erase from a SVF. ERROR: %s",
533  __FUNCTION__, err.message().c_str());
534  return NULL;
535  } catch (const SVFS::Exceptions::ExceptionSparseVirtualFile &err) {
536  /* Do not include line as this can vary between versions. */
537  PyErr_Format(PyExc_RuntimeError, "%s(): Fatal error reading from a SVF. ERROR: %s",
538  __FUNCTION__, err.message().c_str());
539  return NULL;
540  } catch (const std::exception &err) {
541  /* Do not include line as this can vary between versions. */
542  PyErr_Format(PyExc_RuntimeError, "%s(): FATAL caught std::exception %s", __FUNCTION__, __LINE__,
543  err.what());
544  return NULL;
545  }
546  Py_RETURN_NONE;
547 }
548 
550  cp_SparseVirtualFile_need_docstring,
551  "need(self, file_position: int, length: int, greedy_length: int = 0) -> typing.Tuple[typing.Tuple[int, int], ...]\n\n"
552  "Given a file_position and length this returns a ordered list ``[(file_position, length), ...]`` of seek/read"
553  " instructions of data that is required to be written to the Sparse Virtual File so that a subsequent read will"
554  " succeed.\n"
555  " If greedy_length is > 0 then, if possible, blocks will be coalesced to reduce the size of the return value."
556  "\n\n"
557  ".. note::\n"
558  " If a greedy_length is given this will be the *minimum* size of the length of the required block."
559  " If the length of the required block is so large (because of existing blocks likely to be coalesced)"
560  " then the user might want to split the length, for example, into multiple (smaller) GET requests which"
561  " will then be coalesced on ``write()``"
562  "\n\n"
563  ".. warning::\n"
564  " The SVF has no knowledge of the the actual file size so when using a greedy length the need list might"
565  " include positions beyond EOF.\n\n"
566  " For example a file 1024 bytes long and a greedy length of 256 then ``need(1000, 24, 256)`` will create"
567  " a need list of ``[(1000, 256),]``."
568  " This should generate a ``write(1000, 24)`` not a ``write(1000, 256)``.\n\n"
569  " It is up to the caller to handle this, however, ``reads()`` in C/C++/Python will ignore read lengths past"
570  " EOF so the caller does not have to do anything.\n\n"
571  "\n\nUsage::\n\n"
572  " if not svf.has_data(position, length):\n"
573  " for read_fpos, read_length in svf.need(position, length):\n"
574  " # Somehow get data as a bytes object at read_fpos, read_length...\n"
575  " svf.write(read_fpos, data)\n"
576  " return svf.read(position, length):\n"
577 );
578 
579 
580 static PyObject *
582  unsigned long long fpos,
583  unsigned long long len,
584  unsigned long long greedy_len) {
585  PyObject * ret = NULL; // PyListObject
586  PyObject * list_item = NULL; // PyTupleObject
587 
588  SVFS::t_seek_reads seek_read = pSvf->need(fpos, len, greedy_len);
589  ret = PyList_New(seek_read.size());
590  if (!ret) {
591  PyErr_Format(PyExc_MemoryError, "%s: Can not create list.", __FUNCTION__);
592  goto except;
593  }
594  for (size_t i = 0; i < seek_read.size(); ++i) {
595  list_item = Py_BuildValue("KK", seek_read[i].first, seek_read[i].second);
596  if (!list_item) {
597  PyErr_Format(PyExc_MemoryError, "%s: Can not create tuple as list element.", __FUNCTION__);
598  goto except;
599  }
600  PyList_SET_ITEM(ret, i, list_item);
601  list_item = NULL;
602  }
603  assert(!PyErr_Occurred());
604  assert(ret);
605  goto finally;
606  except:
607  assert(PyErr_Occurred());
608  if (ret) {
609  for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
610  Py_XDECREF(PyList_GET_ITEM(ret, i));
611  }
612  }
613  Py_XDECREF(ret);
614  ret = NULL;
615  finally:
616  return ret;
617 }
618 
627 static PyObject *
628 cp_SparseVirtualFile_need(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs) {
630 
631  PyObject * ret = NULL; // PyListObject
632  unsigned long long fpos = 0;
633  unsigned long long len = 0;
634  unsigned long long greedy_len = 0;
635  static const char *kwlist[] = {"file_position", "length", "greedy_length", NULL};
636  AcquireLockSVF _lock(self);
637 
638  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "KK|K", (char **) kwlist, &fpos, &len, &greedy_len)) {
639  goto except;
640  }
641  try {
642  ret = cp_SparseVirtualFile_need_internal(self->pSvf, fpos, len, greedy_len);
643  if (!ret) {
644  goto except;
645  }
646  } catch (const std::exception &err) {
647  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
648  goto except;
649  }
650  assert(!PyErr_Occurred());
651  assert(ret);
652  goto finally;
653  except:
654  assert(PyErr_Occurred());
655  Py_XDECREF(ret);
656  ret = NULL;
657  finally:
658  return ret;
659 }
660 
662  cp_SparseVirtualFile_need_many_docstring,
663  "need_many(self, seek_reads: typing.List[typing.Tuple[int, int]], greedy_length: int = 0) -> typing.Tuple[typing.Tuple[int, int], ...]\n\n"
664  "Given a list of (file_position, length) this returns a ordered list ``[(file_position, length), ...]`` of seek/read"
665  " instructions of data that is required to be written to the Sparse Virtual File so that a subsequent read will"
666  " succeed.\n\n"
667  "If greedy_length is > 0 then, if possible, blocks will be coalesced to reduce the size of the return value."
668  "\n\n"
669  "See also :py:meth:`svfsc.cSVF.need`"
670 );
671 
672 
676 static PyObject *
678  const SVFS::SparseVirtualFile *pSvf,
679  unsigned long long greedy_len) {
680  PyObject * ret = NULL; // PyListObject
681  Py_ssize_t i = 0;
682  SVFS::t_seek_reads cpp_seek_reads;
683  /* Check that we have a list of tuples of the right size.*/
684  if (!PyList_Check(py_seek_reads)) {
685  PyErr_Format(PyExc_TypeError, "%s: seek_reads is not a list.", __FUNCTION__);
686  goto except;
687  }
688  for (i = 0; i < PyList_Size(py_seek_reads); ++i) {
689  if (!PyTuple_Check(PyList_GetItem(py_seek_reads, i))) {
690  PyErr_Format(PyExc_TypeError, "%s: seek_reads[%ld] is not a tuple.", __FUNCTION__, i);
691  goto except;
692  }
693  if (PyTuple_Size(PyList_GetItem(py_seek_reads, i)) != 2) {
694  PyErr_Format(
695  PyExc_TypeError,
696  "%s: seek_reads[%ld] length %ld is not a tuple of length 2.",
697  __FUNCTION__, i, PyTuple_Size(PyList_GetItem(py_seek_reads, i))
698  );
699  goto except;
700  }
701  }
702  /* Create a std::vector<std::pair<fpos, length>> */
703  for (i = 0; i < PyList_Size(py_seek_reads); ++i) {
704  PyObject * py_fpos_len = PyList_GetItem(py_seek_reads, i);
705  SVFS::t_fpos fpos;
706  size_t length;
707  if (!PyArg_ParseTuple(py_fpos_len, "KK", &fpos, &length)) {
708  PyErr_Format(PyExc_TypeError, "%s: can not parse list element[%ld].", __FUNCTION__, i);
709  goto except;
710  }
711  cpp_seek_reads.push_back({fpos, length});
712  }
713  /* Create the new seek-reads vector. */
714  cpp_seek_reads = pSvf->need_many(cpp_seek_reads, greedy_len);
715  /* Create the Python list */
716  ret = PyList_New(cpp_seek_reads.size());
717  if (!ret) {
718  PyErr_Format(PyExc_MemoryError, "%s: Can not create list", __FUNCTION__);
719  goto except;
720  }
721  i = 0;
722  for (const auto &iter: cpp_seek_reads) {
723  PyObject * list_item = Py_BuildValue("KK", iter.first, iter.second);
724  if (!list_item) {
725  PyErr_Format(PyExc_MemoryError, "%s: Can not create tuple as a list element", __FUNCTION__);
726  goto except;
727  }
728  PyList_SET_ITEM(ret, i, list_item);
729  i++;
730  }
731  assert(!PyErr_Occurred());
732  assert(ret);
733  goto finally;
734  except:
735  assert(PyErr_Occurred());
736  if (ret) {
737  for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
738  Py_XDECREF(PyList_GET_ITEM(ret, i));
739  }
740  }
741  Py_XDECREF(ret);
742  ret = NULL;
743  finally:
744  return ret;
745 }
746 
755 static PyObject *
756 cp_SparseVirtualFile_need_many(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs) {
758 
759  PyObject * ret = NULL; // PyListObject
760  PyObject * py_seek_reads = NULL;
761  unsigned long long greedy_len = 0;
762  static const char *kwlist[] = {"seek_reads", "greedy_length", NULL};
763  AcquireLockSVF _lock(self);
764 
765  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|K", (char **) kwlist, &py_seek_reads, &greedy_len)) {
766  goto except;
767  }
768  try {
769  ret = cp_SparseVirtualFile_need_many_internal(py_seek_reads, self->pSvf, greedy_len);
770  if (!ret) {
771  goto except;
772  }
773  } catch (const std::exception &err) {
774  PyErr_Format(PyExc_RuntimeError, "%s#%d: FATAL caught std::exception %s", __FUNCTION__, __LINE__, err.what());
775  goto except;
776  }
777  assert(!PyErr_Occurred());
778  assert(ret);
779  goto finally;
780  except:
781  assert(PyErr_Occurred());
782  Py_XDECREF(ret);
783  ret = NULL;
784  finally:
785  return ret;
786 }
787 
812  cp_SparseVirtualFile_blocks_docstring,
813  "blocks(self) -> typing.Tuple[typing.Tuple[int, int], ...]\n\n"
814  "This returns a ordered tuple ``((file_position, length), ...)``"
815  " of the shape of the blocks held by the SVF in file position order."
816 );
817 
818 static PyObject *
821 
822  PyObject * ret = NULL; // PyTupleObject
823  PyObject * insert_item = NULL; // PyTupleObject
824  AcquireLockSVF _lock(self);
825 
826  try {
827  SVFS::t_seek_reads seek_read = self->pSvf->blocks();
828  ret = PyTuple_New(seek_read.size());
829  if (!ret) {
830  PyErr_Format(PyExc_MemoryError, "%s: Can not create tuple for return", __FUNCTION__);
831  goto except;
832  }
833  for (size_t i = 0; i < seek_read.size(); ++i) {
834  insert_item = Py_BuildValue("KK", seek_read[i].first, seek_read[i].second);
835  if (!insert_item) {
836  PyErr_Format(PyExc_MemoryError, "%s: Can not create tuple", __FUNCTION__);
837  goto except;
838  }
839  PyTuple_SET_ITEM(ret, i, insert_item);
840  insert_item = NULL;
841  }
842  } catch (const std::exception &err) {
843  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
844  goto except;
845  }
846  assert(!PyErr_Occurred());
847  assert(ret);
848  goto finally;
849  except:
850  assert(PyErr_Occurred());
851  if (ret) {
852  for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
853  Py_XDECREF(PyList_GET_ITEM(ret, i));
854  }
855  }
856  Py_XDECREF(ret);
857  ret = NULL;
858  finally:
859  return ret;
860 }
861 
863  block_touch,
864  "Return the latest value of the monotonically increasing block_touch value."
865 );
866 
868  cp_SparseVirtualFile_block_touches_docstring,
869  "block_touches(self) -> typing.Dict[int, int]\n\n"
870  "This returns a dict ``{touch_int: file_position, ...}``"
871  " of the touch integer of each block mapped to the file position.\n"
872  "The caller can decide what older blocks can be used the erase(file_position)."
873 );
874 
875 static PyObject *
878 
879  PyObject * ret = NULL; // PyDictObject
880  AcquireLockSVF _lock(self);
881 
882  try {
883  ret = PyDict_New();
884  if (!ret) {
885  PyErr_Format(PyExc_MemoryError, "%s: Can not create dict for return", __FUNCTION__);
886  goto except;
887  }
888  for (const auto &iter: self->pSvf->block_touches()) {
889  PyObject * key = PyLong_FromLong(iter.first);
890  if (!key) {
891  PyErr_Format(PyExc_MemoryError, "%s: Can not create key", __FUNCTION__);
892  goto except;
893  }
894  PyObject * val = PyLong_FromLong(iter.second);
895  if (!val) {
896  PyErr_Format(PyExc_MemoryError, "%s: Can not create value", __FUNCTION__);
897  goto except;
898  }
899  PyDict_SetItem(ret, key, val);
900  Py_DECREF(key);
901  Py_DECREF(val);
902  }
903  } catch (const std::exception &err) {
904  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
905  goto except;
906  }
907  assert(!PyErr_Occurred());
908  assert(ret);
909  goto finally;
910  except:
911  assert(PyErr_Occurred());
912  if (ret) {
913  for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
914  Py_XDECREF(PyList_GET_ITEM(ret, i));
915  }
916  }
917  Py_XDECREF(ret);
918  ret = NULL;
919  finally:
920  return ret;
921 }
922 
924  cp_SparseVirtualFile_lru_punt_docstring,
925  "lru_punt(self, cache_size_upper_bound: int) -> int\n\n"
926  "Reduces the size of the cache to < the given size by removing older blocks, at least one block will be left.\n"
927  "There are limitations to this tactic, see the documentation in Technical Notes -> Cache Punting."
928 );
929 
938 static PyObject *
939 cp_SparseVirtualFile_lru_punt(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs) {
941 
942  PyObject * ret = NULL; // Long
943  size_t cache_size_upper_bound;
944  static const char *kwlist[] = {"cache_size_upper_bound", NULL};
945  AcquireLockSVF _lock(self);
946 
947  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "K", (char **) kwlist, &cache_size_upper_bound)) {
948  goto except;
949  }
950  try {
951  ret = Py_BuildValue("K", self->pSvf->lru_punt(cache_size_upper_bound));
952  if (!ret) {
953  PyErr_Format(PyExc_MemoryError, "%s: Can not create long", __FUNCTION__);
954  goto except;
955  }
956  } catch (const std::exception &err) {
957  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
958  goto except;
959  }
960  assert(!PyErr_Occurred());
961  assert(ret);
962  goto finally;
963  except:
964  assert(PyErr_Occurred());
965  Py_XDECREF(ret);
966  ret = NULL;
967  finally:
968  return ret;
969 }
970 
972  cp_SparseVirtualFile_file_mod_time_matches_docstring,
973  "file_mod_time_matches(self, file_mod_time: float) -> bool\n\n"
974  "Returns True if the file modification time of the Sparse Virtual File matches the given time as a float."
975 );
976 
977 static PyObject *
978 cp_SparseVirtualFile_file_mod_time_matches(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs) {
980 
981  PyObject * ret = NULL;
982  double file_mod_time;
983  static const char *kwlist[] = {"file_mod_time", NULL};
984 
985  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d", (char **) kwlist, &file_mod_time)) {
986  goto except;
987  }
988  try {
989  if (self->pSvf->file_mod_time_matches(file_mod_time)) {
990  Py_INCREF(Py_True);
991  ret = Py_True;
992  } else {
993  Py_INCREF(Py_False);
994  ret = Py_False;
995  }
996  } catch (const std::exception &err) {
997  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
998  goto except;
999  }
1000  assert(!PyErr_Occurred());
1001  assert(ret);
1002  goto finally;
1003  except:
1004  assert(PyErr_Occurred());
1005  Py_XDECREF(ret);
1006  ret = NULL;
1007  finally:
1008  return ret;
1009 }
1010 
1026  cp_SparseVirtualFile_file_mod_time_docstring,
1027  "file_mod_time(self) -> float\n\n"
1028  "Returns the file modification time as a float in UNIX time of the Sparse Virtual File."
1029 );
1030 
1031 static PyObject *
1034 
1035  PyObject * ret = NULL;
1036  try {
1037  ret = PyFloat_FromDouble(self->pSvf->file_mod_time());
1038  } catch (const std::exception &err) {
1039  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
1040  goto except;
1041  }
1042  assert(!PyErr_Occurred());
1043  assert(ret);
1044  goto finally;
1045  except:
1046  assert(PyErr_Occurred());
1047  Py_XDECREF(ret);
1048  ret = NULL;
1049  finally:
1050  return ret;
1051 }
1052 
1054  count_write,
1055  "Returns the count of write operations on the Sparse Virtual File."
1056 );
1057 
1059  count_read,
1060  "Returns the count of read operations on the Sparse Virtual File."
1061 );
1062 
1064  bytes_write,
1065  "Returns the count of the number of bytes writen to the Sparse Virtual File."
1066 );
1067 
1069  bytes_read,
1070  "Returns the count of the number of bytes read from the Sparse Virtual File."
1071 );
1072 
1074  blocks_erased,
1075  "Returns the The total count of blocks that have been erased either directly or by punting."
1076 );
1077 
1079  bytes_erased,
1080  "Returns the The total count of bytes that have been erased either directly or by punting."
1081 );
1082 
1084  blocks_punted,
1085  "Returns the The total count of blocks that have been erased by punting."
1086 );
1087 
1089  bytes_punted,
1090  "Returns the The total count of bytes that have been erased by punting."
1091 );
1092 
1093 // NOTE: time_read and time_write functions are very similar.
1094 
1096  cp_SparseVirtualFile_time_write_docstring,
1097  "time_write(self) -> typing.Optional[datetime.datetime]\n\n"
1098  "Returns the timestamp of the last write to the Sparse Virtual File as a ``datetime.datetime``"
1099  " or ``None`` if no write has taken place."
1100 );
1101 //SVFS_SVF_METHOD_DATETIME_WRAPPER(time_write)
1102 
1103 static PyObject *
1106  PyObject * ret = NULL;
1107  try {
1108  if (self->pSvf->count_write()) {
1109  auto time = self->pSvf->time_write();
1110  const long seconds = std::chrono::time_point_cast<std::chrono::seconds>(
1111  time).time_since_epoch().count();
1112  int micro_seconds =
1113  std::chrono::time_point_cast<std::chrono::microseconds>(time).time_since_epoch().count() %
1114  1000000;
1115  const std::tm *p_struct_tm = std::gmtime(&seconds);
1116  ret = datetime_from_struct_tm(p_struct_tm, micro_seconds);
1117  if (!ret) {
1118  goto except;
1119  }
1120  } else {
1121  Py_INCREF(Py_None);
1122  ret = Py_None;
1123  }
1124  } catch (const std::exception &err) {
1125  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
1126  goto except;
1127  }
1128  assert(!PyErr_Occurred());
1129  assert(ret);
1130  goto finally;
1131  except:
1132  assert(PyErr_Occurred());
1133  Py_XDECREF(ret);
1134  ret = NULL;
1135  finally:
1136  return ret;
1137 }
1138 
1139 
1141  cp_SparseVirtualFile_time_read_docstring,
1142  "time_read(self) -> typing.Optional[datetime.datetime]\n\n"
1143  "Returns the timestamp of the last read from the Sparse Virtual File as a ``datetime.datetime``"
1144  " or ``None`` if no read has taken place."
1145 );
1146 //SVFS_SVF_METHOD_DATETIME_WRAPPER(time_read)
1147 
1148 static PyObject *
1151 
1152  PyObject * ret = NULL;
1153  try {
1154  if (self->pSvf->count_read()) {
1155  auto time = self->pSvf->time_read();
1156  const long seconds = std::chrono::time_point_cast<std::chrono::seconds>(
1157  time).time_since_epoch().count();
1158  int micro_seconds =
1159  std::chrono::time_point_cast<std::chrono::microseconds>(time).time_since_epoch().count() %
1160  1000000;
1161  const std::tm *p_struct_tm = std::gmtime(&seconds);
1162  ret = datetime_from_struct_tm(p_struct_tm, micro_seconds);
1163  if (!ret) {
1164  goto except;
1165  }
1166  } else {
1167  Py_INCREF(Py_None);
1168  ret = Py_None;
1169  }
1170  } catch (const std::exception &err) {
1171  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
1172  goto except;
1173  }
1174  assert(!PyErr_Occurred());
1175  assert(ret);
1176  goto finally;
1177  except:
1178  assert(PyErr_Occurred());
1179  Py_XDECREF(ret);
1180  ret = NULL;
1181  finally:
1182  return ret;
1183 }
1184 
1186  cp_SparseVirtualFile_config_docstring,
1187  "config(self) -> typing.Dict[str, bool]\n\n"
1188  "Returns the SVF configuration as a dict."
1189 );
1190 
1191 static PyObject *
1194 
1195  PyObject * ret = Py_BuildValue(
1196  "{"
1197  "s:N" /* compare_for_diff */
1198  ",s:N" /* overwrite_on_exit */
1199  "}",
1200  "compare_for_diff", PyBool_FromLong(self->pSvf->config().compare_for_diff ? 1 : 0),
1201  "overwrite_on_exit", PyBool_FromLong(self->pSvf->config().overwrite_on_exit ? 1 : 0)
1202  );
1203  return ret;
1204 }
1205 
1206 /* Pickle the object */
1207 #pragma mark Pickling
1208 
1209 static const char *PICKLE_ID_KEY = "id";
1210 static const char *PICKLE_FILE_MOD_TIME_KEY = "file_mod_time";
1211 static const char *PICKLE_BLOCKS_KEY = "blocks";
1212 static const char *PICKLE_VERSION_KEY = "pickle_version";
1213 static int PICKLE_VERSION = 1;
1214 
1223 static PyObject *
1224 cp_SparseVirtualFile___getstate__(cp_SparseVirtualFile *self, PyObject *Py_UNUSED(ignored)) {
1226 
1227  SVFS::t_seek_reads blocks_fpos_len = self->pSvf->blocks();
1228  /* Build a tuple of ((fpos, bytes), ...) */
1229  PyObject * blocks_fpos_bytes = PyTuple_New(blocks_fpos_len.size());
1230  if (!blocks_fpos_bytes) {
1231  PyErr_Format(PyExc_RuntimeError, "%s()#d Can not create blocks tuple.", __FUNCTION__, __LINE__);
1232  return NULL;
1233  }
1234  Py_ssize_t index = 0;
1235  for (SVFS::t_seek_reads::const_iterator iter = blocks_fpos_len.cbegin(); iter != blocks_fpos_len.cend(); ++iter) {
1236  PyObject * bytes_object = private_SparseVirtualFile_svf_read_as_py_bytes(self, iter->first, iter->second);
1237  if (bytes_object == NULL) {
1238  Py_DECREF(blocks_fpos_bytes);
1239  PyErr_Format(PyExc_RuntimeError, "%s()#d Can not create a bytes object.", __FUNCTION__, __LINE__);
1240  return NULL;
1241  }
1242  /* value is (fpos, bytes) */
1243  PyObject * fpos_bytes = Py_BuildValue("KN", iter->first, bytes_object);
1244  if (fpos_bytes == NULL) {
1245  Py_DECREF(bytes_object);
1246  Py_DECREF(blocks_fpos_bytes);
1247  PyErr_Format(PyExc_RuntimeError, "%s()#d Can not build a value.", __FUNCTION__, __LINE__);
1248  return NULL;
1249  }
1250  PyTuple_SET_ITEM(blocks_fpos_bytes, index, fpos_bytes);
1251  ++index;
1252  }
1253  /* Now build the pickle dict. */
1254  PyObject * ret = Py_BuildValue(
1255  "{"
1256  "s:N" /* id */
1257  ",s:d" /* file_mod_time */
1258  ",s:N" /* blocks */
1259  ",s:i" /* pickle_version */
1260  "}",
1261  PICKLE_ID_KEY, PyUnicode_FromKindAndData(PyUnicode_1BYTE_KIND,
1262  self->pSvf->id().c_str(),
1263  self->pSvf->id().size()
1264  ),
1265  PICKLE_FILE_MOD_TIME_KEY, self->pSvf->file_mod_time(),
1266  PICKLE_BLOCKS_KEY, blocks_fpos_bytes,
1268  );
1269  if (!ret) {
1270  Py_DECREF(blocks_fpos_bytes);
1271  }
1272  return ret;
1273 }
1274 
1275 static PyObject *
1277 // PyObject * key, *value;
1278 // Py_ssize_t pos = 0;
1279 
1280  if (!PyDict_CheckExact(state)) {
1281  PyErr_Format(PyExc_ValueError, "%s()#%d: Pickled object is not a dict.", __FUNCTION__, __LINE__);
1282  return NULL;
1283  }
1284  /* Version check. */
1285  /* Borrowed reference but no need to increment as we create a C long from it. */
1286  PyObject * temp = PyDict_GetItemString(state, PICKLE_VERSION_KEY);
1287  if (temp == NULL) {
1288  /* PyDict_GetItemString does not set any error state so we have to. */
1289  PyErr_Format(PyExc_KeyError, "%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__, __LINE__, PICKLE_VERSION_KEY);
1290  return NULL;
1291  }
1292  int pickle_version = (int) PyLong_AsLong(temp);
1293  if (pickle_version != PICKLE_VERSION) {
1294  PyErr_Format(PyExc_ValueError, "%s()#d Pickle version mismatch. Got version %d but expected version %d.",
1295  __FUNCTION__, __LINE__, pickle_version, PICKLE_VERSION);
1296  return NULL;
1297  }
1298  /* Create a tuple to pass in as args.*/
1299  PyObject * id = PyDict_GetItemString(state, PICKLE_ID_KEY); /* Borrowed reference. */
1300  if (id == NULL) {
1301  PyErr_Format(PyExc_KeyError, "%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__, __LINE__, PICKLE_ID_KEY);
1302  return NULL;
1303  }
1304  if (!PyUnicode_Check(id)) {
1305  PyErr_Format(PyExc_TypeError, "%s()#%d: \"%s\" is not string.", __FUNCTION__, __LINE__, PICKLE_ID_KEY);
1306  return NULL;
1307  }
1308  Py_INCREF(id);
1309  PyObject * file_mod_time = PyDict_GetItemString(state, PICKLE_FILE_MOD_TIME_KEY); /* Borrowed reference. */
1310  if (file_mod_time == NULL) {
1311  Py_DECREF(id);
1312  PyErr_Format(PyExc_KeyError, "%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__,
1313  __LINE__, PICKLE_FILE_MOD_TIME_KEY);
1314  return NULL;
1315  }
1316  if (!PyFloat_Check(file_mod_time)) {
1317  Py_DECREF(id);
1318  PyErr_Format(PyExc_TypeError, "%s()#%d: \"%s\" is not a double.", __FUNCTION__,
1319  __LINE__, PICKLE_FILE_MOD_TIME_KEY);
1320  return NULL;
1321  }
1322  Py_INCREF(file_mod_time);
1323  PyObject * args = Py_BuildValue("OO", id, file_mod_time); /* New reference. */
1324  if (args == NULL) {
1325  Py_DECREF(file_mod_time);
1326  Py_DECREF(id);
1327  PyErr_Format(PyExc_RuntimeError, "%s()#d Can not create arguments.", __FUNCTION__, __LINE__);
1328  return NULL;
1329  }
1330  delete self->pSvf;
1331  self->pSvf = NULL;
1332  if (cp_SparseVirtualFile_init(self, args, NULL)) {
1333  Py_DECREF(file_mod_time);
1334  Py_DECREF(id);
1335  PyErr_Format(PyExc_RuntimeError, "%s()#%d: Can not create new SVF object.", __FUNCTION__, __LINE__);
1336  return NULL;
1337 
1338  }
1339  Py_DECREF(args);
1340  args = NULL;
1341  Py_DECREF(file_mod_time);
1342  file_mod_time = NULL;
1343  Py_DECREF(id);
1344  id = NULL;
1345  /* Play back the blocks. */
1346  PyObject * blocks = PyDict_GetItemString(state, PICKLE_BLOCKS_KEY); /* Borrowed reference. */
1347  if (blocks == NULL) {
1348  PyErr_Format(PyExc_KeyError, "%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__, __LINE__, PICKLE_BLOCKS_KEY);
1349  return NULL;
1350  }
1351  if (!PyTuple_Check(blocks)) {
1352  PyErr_Format(PyExc_TypeError, "%s()#%d: \"%s\" is not a tuple.", __FUNCTION__, __LINE__, PICKLE_BLOCKS_KEY);
1353  return NULL;
1354  }
1355  Py_INCREF(blocks);
1356  for (Py_ssize_t i = 0; i < PyTuple_Size(blocks); ++i) {
1357  PyObject * fpos_bytes = PyTuple_GetItem(blocks, i); /* Borrowed reference. */
1358  Py_INCREF(fpos_bytes);
1359  unsigned long long fpos;
1360  PyObject * block_bytes = NULL; /* Borrowed reference. */
1361  if (!PyArg_ParseTuple(fpos_bytes, "KO", &fpos, &block_bytes)) {
1362  PyErr_Format(PyExc_ValueError, "%s()#%d: Can not parse block (fpos, bytes) tuple.", __FUNCTION__,
1363  __LINE__, PICKLE_BLOCKS_KEY);
1364  Py_DECREF(fpos_bytes);
1365  return NULL;
1366  }
1367  Py_INCREF(block_bytes);
1368  if (!PyBytes_Check(block_bytes)) {
1369  PyErr_Format(PyExc_TypeError, "%s()#%d: Second item of \"%s\" is not a bytes object.", __FUNCTION__,
1370  __LINE__, PICKLE_BLOCKS_KEY);
1371  Py_DECREF(block_bytes);
1372  Py_DECREF(fpos_bytes);
1373  return NULL;
1374  }
1375  self->pSvf->write(fpos, PyBytes_AS_STRING(block_bytes), PyBytes_GET_SIZE(block_bytes));
1376  Py_DECREF(block_bytes);
1377  Py_DECREF(fpos_bytes);
1378  }
1379  Py_DECREF(blocks);
1380  blocks = NULL;
1381  Py_RETURN_NONE;
1382 }
1383 
1384 #pragma mark END Pickling
1385 
1386 #pragma mark END: SVF functions
1387 
1388 static PyMemberDef cp_SparseVirtualFile_members[] = {
1389 // {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,
1390 // "first name"},
1391  {NULL, 0, 0, 0, NULL} /* Sentinel */
1392 };
1393 
1394 static PyMethodDef cp_SparseVirtualFile_methods[] = {
1395  {
1396  "id", (PyCFunction) cp_SparseVirtualFile_id, METH_NOARGS,
1397  cp_SparseVirtualFile_id_docstring
1398  },
1401  SVFS_SVF_METHOD_SIZE_T_REGISTER(num_blocks),
1402  SVFS_SVF_METHOD_SIZE_T_REGISTER(last_file_position),
1403  {
1404  "has_data", (PyCFunction) cp_SparseVirtualFile_has_data, METH_VARARGS |
1405  METH_KEYWORDS,
1406  cp_SparseVirtualFile_has_data_docstring
1407  },
1408  {
1409  "write", (PyCFunction) cp_SparseVirtualFile_write, METH_VARARGS |
1410  METH_KEYWORDS,
1411  cp_SparseVirtualFile_write_docstring
1412  },
1413  {
1414  "read", (PyCFunction) cp_SparseVirtualFile_read, METH_VARARGS |
1415  METH_KEYWORDS,
1416  cp_SparseVirtualFile_read_docstring
1417  },
1418  {
1419  "erase", (PyCFunction) cp_SparseVirtualFile_erase, METH_VARARGS |
1420  METH_KEYWORDS,
1421  cp_SparseVirtualFile_erase_docstring
1422  },
1423  {
1424  "need", (PyCFunction) cp_SparseVirtualFile_need, METH_VARARGS |
1425  METH_KEYWORDS,
1426  cp_SparseVirtualFile_need_docstring
1427  },
1428  {
1429  "need_many", (PyCFunction) cp_SparseVirtualFile_need_many, METH_VARARGS |
1430  METH_KEYWORDS,
1431  cp_SparseVirtualFile_need_many_docstring
1432  },
1433  // ---- Meta information about the specific SVF ----
1434  {
1435  "blocks", (PyCFunction) cp_SparseVirtualFile_blocks, METH_NOARGS,
1436  cp_SparseVirtualFile_blocks_docstring
1437  },
1438  SVFS_SVF_METHOD_SIZE_T_REGISTER(block_touch),
1439  {
1440  "block_touches", (PyCFunction) cp_SparseVirtualFile_block_touches, METH_NOARGS,
1441  cp_SparseVirtualFile_block_touches_docstring
1442  },
1443  {
1444  "lru_punt", (PyCFunction) cp_SparseVirtualFile_lru_punt,
1445  METH_VARARGS |
1446  METH_KEYWORDS,
1447  cp_SparseVirtualFile_lru_punt_docstring
1448  },
1449  {
1450  "file_mod_time_matches", (PyCFunction) cp_SparseVirtualFile_file_mod_time_matches,
1451  METH_VARARGS |
1452  METH_KEYWORDS,
1453  cp_SparseVirtualFile_file_mod_time_matches_docstring
1454  },
1455  // ---- Attribute access ----
1456  {
1457  "file_mod_time", (PyCFunction) cp_SparseVirtualFile_file_mod_time, METH_NOARGS,
1458  cp_SparseVirtualFile_file_mod_time_docstring
1459  },
1460  SVFS_SVF_METHOD_SIZE_T_REGISTER(count_write),
1461  SVFS_SVF_METHOD_SIZE_T_REGISTER(count_read),
1462  SVFS_SVF_METHOD_SIZE_T_REGISTER(bytes_write),
1463  SVFS_SVF_METHOD_SIZE_T_REGISTER(bytes_read),
1464  SVFS_SVF_METHOD_SIZE_T_REGISTER(blocks_erased),
1465  SVFS_SVF_METHOD_SIZE_T_REGISTER(bytes_erased),
1466  SVFS_SVF_METHOD_SIZE_T_REGISTER(blocks_punted),
1467  SVFS_SVF_METHOD_SIZE_T_REGISTER(bytes_punted),
1468  {
1469  "time_write", (PyCFunction) cp_SparseVirtualFile_time_write, METH_NOARGS,
1470  cp_SparseVirtualFile_time_write_docstring
1471  },
1472  {
1473  "config", (PyCFunction) cp_SparseVirtualFile_config, METH_NOARGS,
1474  cp_SparseVirtualFile_config_docstring
1475  },
1476  {
1477  "time_read", (PyCFunction) cp_SparseVirtualFile_time_read, METH_NOARGS,
1478  cp_SparseVirtualFile_time_read_docstring
1479  },
1480  { "__getstate__", (PyCFunction) cp_SparseVirtualFile___getstate__, METH_NOARGS,
1481  "Return the state for pickling."
1482  },
1483  { "__setstate__", (PyCFunction) cp_SparseVirtualFile___setstate__, METH_O,
1484  "Set the state from a pickled object."
1485  },
1486  {NULL, NULL, 0, NULL} /* Sentinel */
1487 };
1488 
1489 // clang-format off
1490 // @formatter:off
1492  svfs_cSVF_doc,
1493  "This class implements a Sparse Virtual File (SVF)."
1494  " This is an in-memory file that has fragments of a real file."
1495  " It has read/write operations and can describe what file fragments are needed, if any, before any read operation."
1496  "\n\n"
1497  "The constructor takes a string as an ID and optionally:\n"
1498  " - A file modification time as a float (default 0.0)."
1499  " This can be used for checking if the actual file might been changed which might invalidate the SVF.\n"
1500  " - ``overwrite_on_exit``, a boolean that will overwrite the memory on destruction (default ``False``)."
1501  " If ``True`` then ``clear()`` on a 1Mb SVF typically takes 35 µs, if ``False`` 1.5 µs.\n"
1502  " - ``compare_for_diff``, a boolean that will check that overlapping writes match (default ``True``)."
1503  " If ``True`` this adds about 25% time to an overlapping write but gives better chance of catching changes to the"
1504  " original file.\n"
1505  "\n\n"
1506  "For example::"
1507  "\n\n"
1508  " import svfsc\n"
1509  " \n"
1510  " svf = svfsc.cSVF('some ID')\n"
1511  " svf.write(12, b'ABCD')\n"
1512  " svf.read(13, 2) # Returns b'BC'\n"
1513  " svf.need(10, 12) # Returns ((10, 2), 16, 6)), the file positions and lengths the the SVF needs\n"
1514  " svf.read(1024, 18) # SVF raises an error as it has no data here.\n"
1515  "\n"
1516  "Signature:\n\n``svfsc.cSVF(id: str, mod_time: float = 0.0, overwrite_on_exit: bool = False, compare_for_diff: bool = True)``"
1517 );
1518 // @formatter:on
1519 // clang-format on
1520 
1521 static PyTypeObject svfsc_cSVF = {
1522  PyVarObject_HEAD_INIT(NULL, 0)
1523  .tp_name = "svfsc.cSVF",
1524  .tp_basicsize = sizeof(cp_SparseVirtualFile),
1525  .tp_itemsize = 0,
1526  .tp_dealloc = (destructor) cp_SparseVirtualFile_dealloc,
1527  .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1528  .tp_doc = svfs_cSVF_doc,
1529  .tp_methods = cp_SparseVirtualFile_methods,
1530  .tp_members = cp_SparseVirtualFile_members,
1531  .tp_init = (initproc) cp_SparseVirtualFile_init,
1532  .tp_new = cp_SparseVirtualFile_new,
1533 };
1534 
1535 #pragma mark - END SVF
static PyObject * private_SparseVirtualFile_svf_read_as_py_bytes(cp_SparseVirtualFile *self, unsigned long long fpos, unsigned long long len)
Definition: _cSVF.cpp:450
static void cp_SparseVirtualFile_dealloc(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:270
static const char * PICKLE_VERSION_KEY
Definition: _cSVF.cpp:1212
static PyObject * cp_SparseVirtualFile_need_many_internal(PyObject *py_seek_reads, const SVFS::SparseVirtualFile *pSvf, unsigned long long greedy_len)
Definition: _cSVF.cpp:677
static PyObject * cp_SparseVirtualFile_write(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
Definition: _cSVF.cpp:397
static PyTypeObject svfsc_cSVF
Definition: _cSVF.cpp:1521
static PyObject * cp_SparseVirtualFile_file_mod_time_matches(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
Definition: _cSVF.cpp:978
static PyObject * cp_SparseVirtualFile_erase(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
Definition: _cSVF.cpp:518
static PyMemberDef cp_SparseVirtualFile_members[]
Definition: _cSVF.cpp:1388
static PyObject * cp_SparseVirtualFile_time_write(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:1104
static PyObject * cp_SparseVirtualFile_new(PyTypeObject *type, PyObject *Py_UNUSED(args), PyObject *Py_UNUSED(kwds))
Definition: _cSVF.cpp:185
static PyObject * cp_SparseVirtualFile_id(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:294
static PyObject * cp_SparseVirtualFile_read(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
Definition: _cSVF.cpp:480
static const char * PICKLE_ID_KEY
Definition: _cSVF.cpp:1209
static PyObject * cp_SparseVirtualFile_config(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:1192
static PyObject * cp_SparseVirtualFile___setstate__(cp_SparseVirtualFile *self, PyObject *state)
Definition: _cSVF.cpp:1276
static PyObject * cp_SparseVirtualFile_need_internal(const SVFS::SparseVirtualFile *pSvf, unsigned long long fpos, unsigned long long len, unsigned long long greedy_len)
Definition: _cSVF.cpp:581
static PyObject * cp_SparseVirtualFile_time_read(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:1149
static int PICKLE_VERSION
Definition: _cSVF.cpp:1213
static PyObject * cp_SparseVirtualFile_block_touches(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:876
static PyObject * cp_SparseVirtualFile_need_many(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
Definition: _cSVF.cpp:756
#define ASSERT_FUNCTION_ENTRY_SVF(member)
Definition: _cSVF.cpp:165
static PyObject * cp_SparseVirtualFile_lru_punt(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
Definition: _cSVF.cpp:939
static PyObject * cp_SparseVirtualFile_file_mod_time(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:1032
static PyObject * cp_SparseVirtualFile_has_data(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
Definition: _cSVF.cpp:352
static int cp_SparseVirtualFile_init(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
Definition: _cSVF.cpp:219
#define SVFS_SVF_METHOD_SIZE_T_REGISTER(method_name)
Definition: _cSVF.cpp:92
static const char * PICKLE_BLOCKS_KEY
Definition: _cSVF.cpp:1211
static PyObject * cp_SparseVirtualFile___getstate__(cp_SparseVirtualFile *self, PyObject *Py_UNUSED(ignored))
Definition: _cSVF.cpp:1224
static PyObject * cp_SparseVirtualFile_need(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
Definition: _cSVF.cpp:628
static PyObject * cp_SparseVirtualFile_blocks(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:819
static PyMethodDef cp_SparseVirtualFile_methods[]
Definition: _cSVF.cpp:1394
#define SVFS_SVF_METHOD_SIZE_T_WRAPPER(method_name, docstring)
Definition: _cSVF.cpp:57
PyDoc_STRVAR(cp_SparseVirtualFile_id_docstring, "id(self) -> str\n\n" "Returns the ID of the Sparse Virtual File.")
static const char * PICKLE_FILE_MOD_TIME_KEY
Definition: _cSVF.cpp:1210
A RAII wrapper around the PyThread_type_lock for the CPython SVF.
Definition: _cSVF.cpp:120
AcquireLockSVF(cp_SparseVirtualFile *pSVF)
Definition: _cSVF.cpp:127
cp_SparseVirtualFile * _pSVF
Definition: _cSVF.cpp:149
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
Exception specialisation for the SparseVirtualFile.
Definition: svf.h:207
const std::string & message() const
Definition: svf.h:211
Might be thrown during a write operation where the data differs.
Definition: svf.h:231
Implementation of a Sparse Virtual File.
Definition: svf.h:288
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
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::vector< t_seek_read > t_seek_reads
Definition: svf.h:251
size_t t_fpos
Definition: svf.h:247
Configuration for the Sparse Virtual File.
Definition: svf.h:262
Python wrapper around a C++ SparseVirtualFile.
Definition: _cSVF.cpp:105
PyThread_type_lock lock
Definition: _cSVF.cpp:109
PyObject_HEAD SVFS::SparseVirtualFile * pSvf
Definition: _cSVF.cpp:107
PyObject * datetime_from_struct_tm(const std::tm *bdt, int usecond)
Definition: svfs_util.cpp:63