Sparse Virtual File System  0.4.1
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_clear_docstring,
551  "clear(self) -> None\n\n"
552  "Clear all the data from the Sparse Virtual File."
553  " This removes all data and resets the internal counters."
554  " Modification times are maintained."
555 );
556 
557 static PyObject *
560 
561  AcquireLockSVF _lock(self);
562 
563  self->pSvf->clear();
564 
565  Py_RETURN_NONE;
566 }
567 
569  cp_SparseVirtualFile_need_docstring,
570  "need(self, file_position: int, length: int, greedy_length: int = 0) -> typing.Tuple[typing.Tuple[int, int], ...]\n\n"
571  "Given a file_position and length this returns a ordered list ``[(file_position, length), ...]`` of seek/read"
572  " instructions of data that is required to be written to the Sparse Virtual File so that a subsequent read will"
573  " succeed.\n"
574  " If greedy_length is > 0 then, if possible, blocks will be coalesced to reduce the size of the return value."
575  "\n\n"
576  ".. note::\n"
577  " If a greedy_length is given this will be the *minimum* size of the length of the required block."
578  " If the length of the required block is so large (because of existing blocks likely to be coalesced)"
579  " then the user might want to split the length, for example, into multiple (smaller) GET requests which"
580  " will then be coalesced on ``write()``"
581  "\n\n"
582  ".. warning::\n"
583  " The SVF has no knowledge of the the actual file size so when using a greedy length the need list might"
584  " include positions beyond EOF.\n\n"
585  " For example a file 1024 bytes long and a greedy length of 256 then ``need(1000, 24, 256)`` will create"
586  " a need list of ``[(1000, 256),]``."
587  " This should generate a ``write(1000, 24)`` not a ``write(1000, 256)``.\n\n"
588  " It is up to the caller to handle this, however, ``reads()`` in C/C++/Python will ignore read lengths past"
589  " EOF so the caller does not have to do anything.\n\n"
590  "\n\nUsage::\n\n"
591  " if not svf.has_data(position, length):\n"
592  " for read_fpos, read_length in svf.need(position, length):\n"
593  " # Somehow get data as a bytes object at read_fpos, read_length...\n"
594  " svf.write(read_fpos, data)\n"
595  " return svf.read(position, length):\n"
596 );
597 
598 
599 static PyObject *
601  unsigned long long fpos,
602  unsigned long long len,
603  unsigned long long greedy_len) {
604  PyObject * ret = NULL; // PyListObject
605  PyObject * list_item = NULL; // PyTupleObject
606 
607  SVFS::t_seek_reads seek_read = pSvf->need(fpos, len, greedy_len);
608  ret = PyList_New(seek_read.size());
609  if (!ret) {
610  PyErr_Format(PyExc_MemoryError, "%s: Can not create list.", __FUNCTION__);
611  goto except;
612  }
613  for (size_t i = 0; i < seek_read.size(); ++i) {
614  list_item = Py_BuildValue("KK", seek_read[i].first, seek_read[i].second);
615  if (!list_item) {
616  PyErr_Format(PyExc_MemoryError, "%s: Can not create tuple as list element.", __FUNCTION__);
617  goto except;
618  }
619  PyList_SET_ITEM(ret, i, list_item);
620  list_item = NULL;
621  }
622  assert(!PyErr_Occurred());
623  assert(ret);
624  goto finally;
625  except:
626  assert(PyErr_Occurred());
627  if (ret) {
628  for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
629  Py_XDECREF(PyList_GET_ITEM(ret, i));
630  }
631  }
632  Py_XDECREF(ret);
633  ret = NULL;
634  finally:
635  return ret;
636 }
637 
646 static PyObject *
647 cp_SparseVirtualFile_need(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs) {
649 
650  PyObject * ret = NULL; // PyListObject
651  unsigned long long fpos = 0;
652  unsigned long long len = 0;
653  unsigned long long greedy_len = 0;
654  static const char *kwlist[] = {"file_position", "length", "greedy_length", NULL};
655  AcquireLockSVF _lock(self);
656 
657  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "KK|K", (char **) kwlist, &fpos, &len, &greedy_len)) {
658  goto except;
659  }
660  try {
661  ret = cp_SparseVirtualFile_need_internal(self->pSvf, fpos, len, greedy_len);
662  if (!ret) {
663  goto except;
664  }
665  } catch (const std::exception &err) {
666  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
667  goto except;
668  }
669  assert(!PyErr_Occurred());
670  assert(ret);
671  goto finally;
672  except:
673  assert(PyErr_Occurred());
674  Py_XDECREF(ret);
675  ret = NULL;
676  finally:
677  return ret;
678 }
679 
681  cp_SparseVirtualFile_need_many_docstring,
682  "need_many(self, seek_reads: typing.List[typing.Tuple[int, int]], greedy_length: int = 0) -> typing.Tuple[typing.Tuple[int, int], ...]\n\n"
683  "Given a list of (file_position, length) this returns a ordered list ``[(file_position, length), ...]`` of seek/read"
684  " instructions of data that is required to be written to the Sparse Virtual File so that a subsequent read will"
685  " succeed.\n\n"
686  "If greedy_length is > 0 then, if possible, blocks will be coalesced to reduce the size of the return value."
687  "\n\n"
688  "See also :py:meth:`svfsc.cSVF.need`"
689 );
690 
691 
695 static PyObject *
697  const SVFS::SparseVirtualFile *pSvf,
698  unsigned long long greedy_len) {
699  PyObject * ret = NULL; // PyListObject
700  Py_ssize_t i = 0;
701  SVFS::t_seek_reads cpp_seek_reads;
702  /* Check that we have a list of tuples of the right size.*/
703  if (!PyList_Check(py_seek_reads)) {
704  PyErr_Format(PyExc_TypeError, "%s: seek_reads is not a list.", __FUNCTION__);
705  goto except;
706  }
707  for (i = 0; i < PyList_Size(py_seek_reads); ++i) {
708  if (!PyTuple_Check(PyList_GetItem(py_seek_reads, i))) {
709  PyErr_Format(PyExc_TypeError, "%s: seek_reads[%ld] is not a tuple.", __FUNCTION__, i);
710  goto except;
711  }
712  if (PyTuple_Size(PyList_GetItem(py_seek_reads, i)) != 2) {
713  PyErr_Format(
714  PyExc_TypeError,
715  "%s: seek_reads[%ld] length %ld is not a tuple of length 2.",
716  __FUNCTION__, i, PyTuple_Size(PyList_GetItem(py_seek_reads, i))
717  );
718  goto except;
719  }
720  }
721  /* Create a std::vector<std::pair<fpos, length>> */
722  for (i = 0; i < PyList_Size(py_seek_reads); ++i) {
723  PyObject * py_fpos_len = PyList_GetItem(py_seek_reads, i);
724  SVFS::t_fpos fpos;
725  size_t length;
726  if (!PyArg_ParseTuple(py_fpos_len, "KK", &fpos, &length)) {
727  PyErr_Format(PyExc_TypeError, "%s: can not parse list element[%ld].", __FUNCTION__, i);
728  goto except;
729  }
730  cpp_seek_reads.push_back({fpos, length});
731  }
732  /* Create the new seek-reads vector. */
733  cpp_seek_reads = pSvf->need_many(cpp_seek_reads, greedy_len);
734  /* Create the Python list */
735  ret = PyList_New(cpp_seek_reads.size());
736  if (!ret) {
737  PyErr_Format(PyExc_MemoryError, "%s: Can not create list", __FUNCTION__);
738  goto except;
739  }
740  i = 0;
741  for (const auto &iter: cpp_seek_reads) {
742  PyObject * list_item = Py_BuildValue("KK", iter.first, iter.second);
743  if (!list_item) {
744  PyErr_Format(PyExc_MemoryError, "%s: Can not create tuple as a list element", __FUNCTION__);
745  goto except;
746  }
747  PyList_SET_ITEM(ret, i, list_item);
748  i++;
749  }
750  assert(!PyErr_Occurred());
751  assert(ret);
752  goto finally;
753  except:
754  assert(PyErr_Occurred());
755  if (ret) {
756  for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
757  Py_XDECREF(PyList_GET_ITEM(ret, i));
758  }
759  }
760  Py_XDECREF(ret);
761  ret = NULL;
762  finally:
763  return ret;
764 }
765 
774 static PyObject *
775 cp_SparseVirtualFile_need_many(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs) {
777 
778  PyObject * ret = NULL; // PyListObject
779  PyObject * py_seek_reads = NULL;
780  unsigned long long greedy_len = 0;
781  static const char *kwlist[] = {"seek_reads", "greedy_length", NULL};
782  AcquireLockSVF _lock(self);
783 
784  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|K", (char **) kwlist, &py_seek_reads, &greedy_len)) {
785  goto except;
786  }
787  try {
788  ret = cp_SparseVirtualFile_need_many_internal(py_seek_reads, self->pSvf, greedy_len);
789  if (!ret) {
790  goto except;
791  }
792  } catch (const std::exception &err) {
793  PyErr_Format(PyExc_RuntimeError, "%s#%d: FATAL caught std::exception %s", __FUNCTION__, __LINE__, err.what());
794  goto except;
795  }
796  assert(!PyErr_Occurred());
797  assert(ret);
798  goto finally;
799  except:
800  assert(PyErr_Occurred());
801  Py_XDECREF(ret);
802  ret = NULL;
803  finally:
804  return ret;
805 }
806 
831  cp_SparseVirtualFile_blocks_docstring,
832  "blocks(self) -> typing.Tuple[typing.Tuple[int, int], ...]\n\n"
833  "This returns a ordered tuple ``((file_position, length), ...)``"
834  " of the shape of the blocks held by the SVF in file position order."
835 );
836 
837 static PyObject *
840 
841  PyObject * ret = NULL; // PyTupleObject
842  PyObject * insert_item = NULL; // PyTupleObject
843  AcquireLockSVF _lock(self);
844 
845  try {
846  SVFS::t_seek_reads seek_read = self->pSvf->blocks();
847  ret = PyTuple_New(seek_read.size());
848  if (!ret) {
849  PyErr_Format(PyExc_MemoryError, "%s: Can not create tuple for return", __FUNCTION__);
850  goto except;
851  }
852  for (size_t i = 0; i < seek_read.size(); ++i) {
853  insert_item = Py_BuildValue("KK", seek_read[i].first, seek_read[i].second);
854  if (!insert_item) {
855  PyErr_Format(PyExc_MemoryError, "%s: Can not create tuple", __FUNCTION__);
856  goto except;
857  }
858  PyTuple_SET_ITEM(ret, i, insert_item);
859  insert_item = NULL;
860  }
861  } catch (const std::exception &err) {
862  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
863  goto except;
864  }
865  assert(!PyErr_Occurred());
866  assert(ret);
867  goto finally;
868  except:
869  assert(PyErr_Occurred());
870  if (ret) {
871  for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
872  Py_XDECREF(PyList_GET_ITEM(ret, i));
873  }
874  }
875  Py_XDECREF(ret);
876  ret = NULL;
877  finally:
878  return ret;
879 }
880 
882  block_touch,
883  "Return the latest value of the monotonically increasing block_touch value."
884 );
885 
887  cp_SparseVirtualFile_block_touches_docstring,
888  "block_touches(self) -> typing.Dict[int, int]\n\n"
889  "This returns a dict ``{touch_int: file_position, ...}``"
890  " of the touch integer of each block mapped to the file position.\n"
891  "The caller can decide what older blocks can be used the erase(file_position)."
892 );
893 
894 static PyObject *
897 
898  PyObject * ret = NULL; // PyDictObject
899  AcquireLockSVF _lock(self);
900 
901  try {
902  ret = PyDict_New();
903  if (!ret) {
904  PyErr_Format(PyExc_MemoryError, "%s: Can not create dict for return", __FUNCTION__);
905  goto except;
906  }
907  for (const auto &iter: self->pSvf->block_touches()) {
908  PyObject * key = PyLong_FromLong(iter.first);
909  if (!key) {
910  PyErr_Format(PyExc_MemoryError, "%s: Can not create key", __FUNCTION__);
911  goto except;
912  }
913  PyObject * val = PyLong_FromLong(iter.second);
914  if (!val) {
915  PyErr_Format(PyExc_MemoryError, "%s: Can not create value", __FUNCTION__);
916  goto except;
917  }
918  PyDict_SetItem(ret, key, val);
919  Py_DECREF(key);
920  Py_DECREF(val);
921  }
922  } catch (const std::exception &err) {
923  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
924  goto except;
925  }
926  assert(!PyErr_Occurred());
927  assert(ret);
928  goto finally;
929  except:
930  assert(PyErr_Occurred());
931  if (ret) {
932  for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
933  Py_XDECREF(PyList_GET_ITEM(ret, i));
934  }
935  }
936  Py_XDECREF(ret);
937  ret = NULL;
938  finally:
939  return ret;
940 }
941 
943  cp_SparseVirtualFile_lru_punt_docstring,
944  "lru_punt(self, cache_size_upper_bound: int) -> int\n\n"
945  "Reduces the size of the cache to < the given size by removing older blocks, at least one block will be left.\n"
946  "There are limitations to this tactic, see the documentation in Technical Notes -> Cache Punting."
947 );
948 
957 static PyObject *
958 cp_SparseVirtualFile_lru_punt(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs) {
960 
961  PyObject * ret = NULL; // Long
962  size_t cache_size_upper_bound;
963  static const char *kwlist[] = {"cache_size_upper_bound", NULL};
964  AcquireLockSVF _lock(self);
965 
966  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "K", (char **) kwlist, &cache_size_upper_bound)) {
967  goto except;
968  }
969  try {
970  ret = Py_BuildValue("K", self->pSvf->lru_punt(cache_size_upper_bound));
971  if (!ret) {
972  PyErr_Format(PyExc_MemoryError, "%s: Can not create long", __FUNCTION__);
973  goto except;
974  }
975  } catch (const std::exception &err) {
976  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
977  goto except;
978  }
979  assert(!PyErr_Occurred());
980  assert(ret);
981  goto finally;
982  except:
983  assert(PyErr_Occurred());
984  Py_XDECREF(ret);
985  ret = NULL;
986  finally:
987  return ret;
988 }
989 
991  cp_SparseVirtualFile_file_mod_time_matches_docstring,
992  "file_mod_time_matches(self, file_mod_time: float) -> bool\n\n"
993  "Returns True if the file modification time of the Sparse Virtual File matches the given time as a float."
994 );
995 
996 static PyObject *
997 cp_SparseVirtualFile_file_mod_time_matches(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs) {
999 
1000  PyObject * ret = NULL;
1001  double file_mod_time;
1002  static const char *kwlist[] = {"file_mod_time", NULL};
1003 
1004  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d", (char **) kwlist, &file_mod_time)) {
1005  goto except;
1006  }
1007  try {
1008  if (self->pSvf->file_mod_time_matches(file_mod_time)) {
1009  Py_INCREF(Py_True);
1010  ret = Py_True;
1011  } else {
1012  Py_INCREF(Py_False);
1013  ret = Py_False;
1014  }
1015  } catch (const std::exception &err) {
1016  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
1017  goto except;
1018  }
1019  assert(!PyErr_Occurred());
1020  assert(ret);
1021  goto finally;
1022  except:
1023  assert(PyErr_Occurred());
1024  Py_XDECREF(ret);
1025  ret = NULL;
1026  finally:
1027  return ret;
1028 }
1029 
1045  cp_SparseVirtualFile_file_mod_time_docstring,
1046  "file_mod_time(self) -> float\n\n"
1047  "Returns the file modification time as a float in UNIX time of the Sparse Virtual File."
1048 );
1049 
1050 static PyObject *
1053 
1054  PyObject * ret = NULL;
1055  try {
1056  ret = PyFloat_FromDouble(self->pSvf->file_mod_time());
1057  } catch (const std::exception &err) {
1058  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
1059  goto except;
1060  }
1061  assert(!PyErr_Occurred());
1062  assert(ret);
1063  goto finally;
1064  except:
1065  assert(PyErr_Occurred());
1066  Py_XDECREF(ret);
1067  ret = NULL;
1068  finally:
1069  return ret;
1070 }
1071 
1073  count_write,
1074  "Returns the count of write operations on the Sparse Virtual File."
1075 );
1076 
1078  count_read,
1079  "Returns the count of read operations on the Sparse Virtual File."
1080 );
1081 
1083  bytes_write,
1084  "Returns the count of the number of bytes writen to the Sparse Virtual File."
1085 );
1086 
1088  bytes_read,
1089  "Returns the count of the number of bytes read from the Sparse Virtual File."
1090 );
1091 
1093  blocks_erased,
1094  "Returns the The total count of blocks that have been erased either directly or by punting."
1095 );
1096 
1098  bytes_erased,
1099  "Returns the The total count of bytes that have been erased either directly or by punting."
1100 );
1101 
1103  blocks_punted,
1104  "Returns the The total count of blocks that have been erased by punting."
1105 );
1106 
1108  bytes_punted,
1109  "Returns the The total count of bytes that have been erased by punting."
1110 );
1111 
1112 // NOTE: time_read and time_write functions are very similar.
1113 
1115  cp_SparseVirtualFile_time_write_docstring,
1116  "time_write(self) -> typing.Optional[datetime.datetime]\n\n"
1117  "Returns the timestamp of the last write to the Sparse Virtual File as a ``datetime.datetime``"
1118  " or ``None`` if no write has taken place."
1119 );
1120 //SVFS_SVF_METHOD_DATETIME_WRAPPER(time_write)
1121 
1122 static PyObject *
1125  PyObject * ret = NULL;
1126  try {
1127  if (self->pSvf->count_write()) {
1128  auto time = self->pSvf->time_write();
1129  const long seconds = std::chrono::time_point_cast<std::chrono::seconds>(
1130  time).time_since_epoch().count();
1131  int micro_seconds =
1132  std::chrono::time_point_cast<std::chrono::microseconds>(time).time_since_epoch().count() %
1133  1000000;
1134  const std::tm *p_struct_tm = std::gmtime(&seconds);
1135  ret = datetime_from_struct_tm(p_struct_tm, micro_seconds);
1136  if (!ret) {
1137  goto except;
1138  }
1139  } else {
1140  Py_INCREF(Py_None);
1141  ret = Py_None;
1142  }
1143  } catch (const std::exception &err) {
1144  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
1145  goto except;
1146  }
1147  assert(!PyErr_Occurred());
1148  assert(ret);
1149  goto finally;
1150  except:
1151  assert(PyErr_Occurred());
1152  Py_XDECREF(ret);
1153  ret = NULL;
1154  finally:
1155  return ret;
1156 }
1157 
1158 
1160  cp_SparseVirtualFile_time_read_docstring,
1161  "time_read(self) -> typing.Optional[datetime.datetime]\n\n"
1162  "Returns the timestamp of the last read from the Sparse Virtual File as a ``datetime.datetime``"
1163  " or ``None`` if no read has taken place."
1164 );
1165 //SVFS_SVF_METHOD_DATETIME_WRAPPER(time_read)
1166 
1167 static PyObject *
1170 
1171  PyObject * ret = NULL;
1172  try {
1173  if (self->pSvf->count_read()) {
1174  auto time = self->pSvf->time_read();
1175  const long seconds = std::chrono::time_point_cast<std::chrono::seconds>(
1176  time).time_since_epoch().count();
1177  int micro_seconds =
1178  std::chrono::time_point_cast<std::chrono::microseconds>(time).time_since_epoch().count() %
1179  1000000;
1180  const std::tm *p_struct_tm = std::gmtime(&seconds);
1181  ret = datetime_from_struct_tm(p_struct_tm, micro_seconds);
1182  if (!ret) {
1183  goto except;
1184  }
1185  } else {
1186  Py_INCREF(Py_None);
1187  ret = Py_None;
1188  }
1189  } catch (const std::exception &err) {
1190  PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
1191  goto except;
1192  }
1193  assert(!PyErr_Occurred());
1194  assert(ret);
1195  goto finally;
1196  except:
1197  assert(PyErr_Occurred());
1198  Py_XDECREF(ret);
1199  ret = NULL;
1200  finally:
1201  return ret;
1202 }
1203 
1205  cp_SparseVirtualFile_config_docstring,
1206  "config(self) -> typing.Dict[str, bool]\n\n"
1207  "Returns the SVF configuration as a dict."
1208 );
1209 
1210 static PyObject *
1213 
1214  PyObject * ret = Py_BuildValue(
1215  "{"
1216  "s:N" /* compare_for_diff */
1217  ",s:N" /* overwrite_on_exit */
1218  "}",
1219  "compare_for_diff", PyBool_FromLong(self->pSvf->config().compare_for_diff ? 1 : 0),
1220  "overwrite_on_exit", PyBool_FromLong(self->pSvf->config().overwrite_on_exit ? 1 : 0)
1221  );
1222  return ret;
1223 }
1224 
1225 /* Pickle the object */
1226 #pragma mark Pickling
1227 
1228 static const char *PICKLE_ID_KEY = "id";
1229 static const char *PICKLE_FILE_MOD_TIME_KEY = "file_mod_time";
1230 static const char *PICKLE_BLOCKS_KEY = "blocks";
1231 static const char *PICKLE_VERSION_KEY = "pickle_version";
1232 static int PICKLE_VERSION = 1;
1233 
1242 static PyObject *
1243 cp_SparseVirtualFile___getstate__(cp_SparseVirtualFile *self, PyObject *Py_UNUSED(ignored)) {
1245 
1246  SVFS::t_seek_reads blocks_fpos_len = self->pSvf->blocks();
1247  /* Build a tuple of ((fpos, bytes), ...) */
1248  PyObject * blocks_fpos_bytes = PyTuple_New(blocks_fpos_len.size());
1249  if (!blocks_fpos_bytes) {
1250  PyErr_Format(PyExc_RuntimeError, "%s()#d Can not create blocks tuple.", __FUNCTION__, __LINE__);
1251  return NULL;
1252  }
1253  Py_ssize_t index = 0;
1254  for (SVFS::t_seek_reads::const_iterator iter = blocks_fpos_len.cbegin(); iter != blocks_fpos_len.cend(); ++iter) {
1255  PyObject * bytes_object = private_SparseVirtualFile_svf_read_as_py_bytes(self, iter->first, iter->second);
1256  if (bytes_object == NULL) {
1257  Py_DECREF(blocks_fpos_bytes);
1258  PyErr_Format(PyExc_RuntimeError, "%s()#d Can not create a bytes object.", __FUNCTION__, __LINE__);
1259  return NULL;
1260  }
1261  /* value is (fpos, bytes) */
1262  PyObject * fpos_bytes = Py_BuildValue("KN", iter->first, bytes_object);
1263  if (fpos_bytes == NULL) {
1264  Py_DECREF(bytes_object);
1265  Py_DECREF(blocks_fpos_bytes);
1266  PyErr_Format(PyExc_RuntimeError, "%s()#d Can not build a value.", __FUNCTION__, __LINE__);
1267  return NULL;
1268  }
1269  PyTuple_SET_ITEM(blocks_fpos_bytes, index, fpos_bytes);
1270  ++index;
1271  }
1272  /* Now build the pickle dict. */
1273  PyObject * ret = Py_BuildValue(
1274  "{"
1275  "s:N" /* id */
1276  ",s:d" /* file_mod_time */
1277  ",s:N" /* blocks */
1278  ",s:i" /* pickle_version */
1279  "}",
1280  PICKLE_ID_KEY, PyUnicode_FromKindAndData(PyUnicode_1BYTE_KIND,
1281  self->pSvf->id().c_str(),
1282  self->pSvf->id().size()
1283  ),
1284  PICKLE_FILE_MOD_TIME_KEY, self->pSvf->file_mod_time(),
1285  PICKLE_BLOCKS_KEY, blocks_fpos_bytes,
1287  );
1288  if (!ret) {
1289  Py_DECREF(blocks_fpos_bytes);
1290  }
1291  return ret;
1292 }
1293 
1294 static PyObject *
1296 // PyObject * key, *value;
1297 // Py_ssize_t pos = 0;
1298 
1299  if (!PyDict_CheckExact(state)) {
1300  PyErr_Format(PyExc_ValueError, "%s()#%d: Pickled object is not a dict.", __FUNCTION__, __LINE__);
1301  return NULL;
1302  }
1303  /* Version check. */
1304  /* Borrowed reference but no need to increment as we create a C long from it. */
1305  PyObject * temp = PyDict_GetItemString(state, PICKLE_VERSION_KEY);
1306  if (temp == NULL) {
1307  /* PyDict_GetItemString does not set any error state so we have to. */
1308  PyErr_Format(PyExc_KeyError, "%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__, __LINE__, PICKLE_VERSION_KEY);
1309  return NULL;
1310  }
1311  int pickle_version = (int) PyLong_AsLong(temp);
1312  if (pickle_version != PICKLE_VERSION) {
1313  PyErr_Format(PyExc_ValueError, "%s()#d Pickle version mismatch. Got version %d but expected version %d.",
1314  __FUNCTION__, __LINE__, pickle_version, PICKLE_VERSION);
1315  return NULL;
1316  }
1317  /* Create a tuple to pass in as args.*/
1318  PyObject * id = PyDict_GetItemString(state, PICKLE_ID_KEY); /* Borrowed reference. */
1319  if (id == NULL) {
1320  PyErr_Format(PyExc_KeyError, "%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__, __LINE__, PICKLE_ID_KEY);
1321  return NULL;
1322  }
1323  if (!PyUnicode_Check(id)) {
1324  PyErr_Format(PyExc_TypeError, "%s()#%d: \"%s\" is not string.", __FUNCTION__, __LINE__, PICKLE_ID_KEY);
1325  return NULL;
1326  }
1327  Py_INCREF(id);
1328  PyObject * file_mod_time = PyDict_GetItemString(state, PICKLE_FILE_MOD_TIME_KEY); /* Borrowed reference. */
1329  if (file_mod_time == NULL) {
1330  Py_DECREF(id);
1331  PyErr_Format(PyExc_KeyError, "%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__,
1332  __LINE__, PICKLE_FILE_MOD_TIME_KEY);
1333  return NULL;
1334  }
1335  if (!PyFloat_Check(file_mod_time)) {
1336  Py_DECREF(id);
1337  PyErr_Format(PyExc_TypeError, "%s()#%d: \"%s\" is not a double.", __FUNCTION__,
1338  __LINE__, PICKLE_FILE_MOD_TIME_KEY);
1339  return NULL;
1340  }
1341  Py_INCREF(file_mod_time);
1342  PyObject * args = Py_BuildValue("OO", id, file_mod_time); /* New reference. */
1343  if (args == NULL) {
1344  Py_DECREF(file_mod_time);
1345  Py_DECREF(id);
1346  PyErr_Format(PyExc_RuntimeError, "%s()#d Can not create arguments.", __FUNCTION__, __LINE__);
1347  return NULL;
1348  }
1349  delete self->pSvf;
1350  self->pSvf = NULL;
1351  if (cp_SparseVirtualFile_init(self, args, NULL)) {
1352  Py_DECREF(file_mod_time);
1353  Py_DECREF(id);
1354  PyErr_Format(PyExc_RuntimeError, "%s()#%d: Can not create new SVF object.", __FUNCTION__, __LINE__);
1355  return NULL;
1356 
1357  }
1358  Py_DECREF(args);
1359  args = NULL;
1360  Py_DECREF(file_mod_time);
1361  file_mod_time = NULL;
1362  Py_DECREF(id);
1363  id = NULL;
1364  /* Play back the blocks. */
1365  PyObject * blocks = PyDict_GetItemString(state, PICKLE_BLOCKS_KEY); /* Borrowed reference. */
1366  if (blocks == NULL) {
1367  PyErr_Format(PyExc_KeyError, "%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__, __LINE__, PICKLE_BLOCKS_KEY);
1368  return NULL;
1369  }
1370  if (!PyTuple_Check(blocks)) {
1371  PyErr_Format(PyExc_TypeError, "%s()#%d: \"%s\" is not a tuple.", __FUNCTION__, __LINE__, PICKLE_BLOCKS_KEY);
1372  return NULL;
1373  }
1374  Py_INCREF(blocks);
1375  for (Py_ssize_t i = 0; i < PyTuple_Size(blocks); ++i) {
1376  PyObject * fpos_bytes = PyTuple_GetItem(blocks, i); /* Borrowed reference. */
1377  Py_INCREF(fpos_bytes);
1378  unsigned long long fpos;
1379  PyObject * block_bytes = NULL; /* Borrowed reference. */
1380  if (!PyArg_ParseTuple(fpos_bytes, "KO", &fpos, &block_bytes)) {
1381  PyErr_Format(PyExc_ValueError, "%s()#%d: Can not parse block (fpos, bytes) tuple.", __FUNCTION__,
1382  __LINE__, PICKLE_BLOCKS_KEY);
1383  Py_DECREF(fpos_bytes);
1384  return NULL;
1385  }
1386  Py_INCREF(block_bytes);
1387  if (!PyBytes_Check(block_bytes)) {
1388  PyErr_Format(PyExc_TypeError, "%s()#%d: Second item of \"%s\" is not a bytes object.", __FUNCTION__,
1389  __LINE__, PICKLE_BLOCKS_KEY);
1390  Py_DECREF(block_bytes);
1391  Py_DECREF(fpos_bytes);
1392  return NULL;
1393  }
1394  self->pSvf->write(fpos, PyBytes_AS_STRING(block_bytes), PyBytes_GET_SIZE(block_bytes));
1395  Py_DECREF(block_bytes);
1396  Py_DECREF(fpos_bytes);
1397  }
1398  Py_DECREF(blocks);
1399  blocks = NULL;
1400  Py_RETURN_NONE;
1401 }
1402 
1403 #pragma mark END Pickling
1404 
1405 #pragma mark END: SVF functions
1406 
1407 static PyMemberDef cp_SparseVirtualFile_members[] = {
1408 // {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,
1409 // "first name"},
1410  {NULL, 0, 0, 0, NULL} /* Sentinel */
1411 };
1412 
1413 static PyMethodDef cp_SparseVirtualFile_methods[] = {
1414  {
1415  "id", (PyCFunction) cp_SparseVirtualFile_id, METH_NOARGS,
1416  cp_SparseVirtualFile_id_docstring
1417  },
1420  SVFS_SVF_METHOD_SIZE_T_REGISTER(num_blocks),
1421  SVFS_SVF_METHOD_SIZE_T_REGISTER(last_file_position),
1422  {
1423  "has_data", (PyCFunction) cp_SparseVirtualFile_has_data, METH_VARARGS |
1424  METH_KEYWORDS,
1425  cp_SparseVirtualFile_has_data_docstring
1426  },
1427  {
1428  "write", (PyCFunction) cp_SparseVirtualFile_write, METH_VARARGS |
1429  METH_KEYWORDS,
1430  cp_SparseVirtualFile_write_docstring
1431  },
1432  {
1433  "read", (PyCFunction) cp_SparseVirtualFile_read, METH_VARARGS |
1434  METH_KEYWORDS,
1435  cp_SparseVirtualFile_read_docstring
1436  },
1437  {
1438  "erase", (PyCFunction) cp_SparseVirtualFile_erase, METH_VARARGS |
1439  METH_KEYWORDS,
1440  cp_SparseVirtualFile_erase_docstring
1441  },
1442  {
1443  "clear", (PyCFunction) cp_SparseVirtualFile_clear, METH_NOARGS,
1444  cp_SparseVirtualFile_clear_docstring
1445  },
1446  {
1447  "need", (PyCFunction) cp_SparseVirtualFile_need, METH_VARARGS |
1448  METH_KEYWORDS,
1449  cp_SparseVirtualFile_need_docstring
1450  },
1451  {
1452  "need_many", (PyCFunction) cp_SparseVirtualFile_need_many, METH_VARARGS |
1453  METH_KEYWORDS,
1454  cp_SparseVirtualFile_need_many_docstring
1455  },
1456  // ---- Meta information about the specific SVF ----
1457  {
1458  "blocks", (PyCFunction) cp_SparseVirtualFile_blocks, METH_NOARGS,
1459  cp_SparseVirtualFile_blocks_docstring
1460  },
1461  SVFS_SVF_METHOD_SIZE_T_REGISTER(block_touch),
1462  {
1463  "block_touches", (PyCFunction) cp_SparseVirtualFile_block_touches, METH_NOARGS,
1464  cp_SparseVirtualFile_block_touches_docstring
1465  },
1466  {
1467  "lru_punt", (PyCFunction) cp_SparseVirtualFile_lru_punt,
1468  METH_VARARGS |
1469  METH_KEYWORDS,
1470  cp_SparseVirtualFile_lru_punt_docstring
1471  },
1472  {
1473  "file_mod_time_matches", (PyCFunction) cp_SparseVirtualFile_file_mod_time_matches,
1474  METH_VARARGS |
1475  METH_KEYWORDS,
1476  cp_SparseVirtualFile_file_mod_time_matches_docstring
1477  },
1478  // ---- Attribute access ----
1479  {
1480  "file_mod_time", (PyCFunction) cp_SparseVirtualFile_file_mod_time, METH_NOARGS,
1481  cp_SparseVirtualFile_file_mod_time_docstring
1482  },
1483  SVFS_SVF_METHOD_SIZE_T_REGISTER(count_write),
1484  SVFS_SVF_METHOD_SIZE_T_REGISTER(count_read),
1485  SVFS_SVF_METHOD_SIZE_T_REGISTER(bytes_write),
1486  SVFS_SVF_METHOD_SIZE_T_REGISTER(bytes_read),
1487  SVFS_SVF_METHOD_SIZE_T_REGISTER(blocks_erased),
1488  SVFS_SVF_METHOD_SIZE_T_REGISTER(bytes_erased),
1489  SVFS_SVF_METHOD_SIZE_T_REGISTER(blocks_punted),
1490  SVFS_SVF_METHOD_SIZE_T_REGISTER(bytes_punted),
1491  {
1492  "time_write", (PyCFunction) cp_SparseVirtualFile_time_write, METH_NOARGS,
1493  cp_SparseVirtualFile_time_write_docstring
1494  },
1495  {
1496  "config", (PyCFunction) cp_SparseVirtualFile_config, METH_NOARGS,
1497  cp_SparseVirtualFile_config_docstring
1498  },
1499  {
1500  "time_read", (PyCFunction) cp_SparseVirtualFile_time_read, METH_NOARGS,
1501  cp_SparseVirtualFile_time_read_docstring
1502  },
1503  { "__getstate__", (PyCFunction) cp_SparseVirtualFile___getstate__, METH_NOARGS,
1504  "Return the state for pickling."
1505  },
1506  { "__setstate__", (PyCFunction) cp_SparseVirtualFile___setstate__, METH_O,
1507  "Set the state from a pickled object."
1508  },
1509  {NULL, NULL, 0, NULL} /* Sentinel */
1510 };
1511 
1512 // clang-format off
1513 // @formatter:off
1515  svfs_cSVF_doc,
1516  "This class implements a Sparse Virtual File (SVF)."
1517  " This is an in-memory file that has fragments of a real file."
1518  " It has read/write operations and can describe what file fragments are needed, if any, before any read operation."
1519  "\n\n"
1520  "The constructor takes a string as an ID and optionally:\n"
1521  " - A file modification time as a float (default 0.0)."
1522  " This can be used for checking if the actual file might been changed which might invalidate the SVF.\n"
1523  " - ``overwrite_on_exit``, a boolean that will overwrite the memory on destruction (default ``False``)."
1524  " If ``True`` then ``clear()`` on a 1Mb SVF typically takes 35 µs, if ``False`` 1.5 µs.\n"
1525  " - ``compare_for_diff``, a boolean that will check that overlapping writes match (default ``True``)."
1526  " If ``True`` this adds about 25% time to an overlapping write but gives better chance of catching changes to the"
1527  " original file.\n"
1528  "\n\n"
1529  "For example::"
1530  "\n\n"
1531  " import svfsc\n"
1532  " \n"
1533  " svf = svfsc.cSVF('some ID')\n"
1534  " svf.write(12, b'ABCD')\n"
1535  " svf.read(13, 2) # Returns b'BC'\n"
1536  " svf.need(10, 12) # Returns ((10, 2), 16, 6)), the file positions and lengths the the SVF needs\n"
1537  " svf.read(1024, 18) # SVF raises an error as it has no data here.\n"
1538  "\n"
1539  "Signature:\n\n``svfsc.cSVF(id: str, mod_time: float = 0.0, overwrite_on_exit: bool = False, compare_for_diff: bool = True)``"
1540 );
1541 // @formatter:on
1542 // clang-format on
1543 
1544 static PyTypeObject svfsc_cSVF = {
1545  PyVarObject_HEAD_INIT(NULL, 0)
1546  .tp_name = "svfsc.cSVF",
1547  .tp_basicsize = sizeof(cp_SparseVirtualFile),
1548  .tp_itemsize = 0,
1549  .tp_dealloc = (destructor) cp_SparseVirtualFile_dealloc,
1550  .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1551  .tp_doc = svfs_cSVF_doc,
1552  .tp_methods = cp_SparseVirtualFile_methods,
1553  .tp_members = cp_SparseVirtualFile_members,
1554  .tp_init = (initproc) cp_SparseVirtualFile_init,
1555  .tp_new = cp_SparseVirtualFile_new,
1556 };
1557 
1558 #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:1231
static PyObject * cp_SparseVirtualFile_need_many_internal(PyObject *py_seek_reads, const SVFS::SparseVirtualFile *pSvf, unsigned long long greedy_len)
Definition: _cSVF.cpp:696
static PyObject * cp_SparseVirtualFile_write(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
Definition: _cSVF.cpp:397
static PyTypeObject svfsc_cSVF
Definition: _cSVF.cpp:1544
static PyObject * cp_SparseVirtualFile_file_mod_time_matches(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
Definition: _cSVF.cpp:997
static PyObject * cp_SparseVirtualFile_erase(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
Definition: _cSVF.cpp:518
static PyMemberDef cp_SparseVirtualFile_members[]
Definition: _cSVF.cpp:1407
static PyObject * cp_SparseVirtualFile_time_write(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:1123
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:1228
static PyObject * cp_SparseVirtualFile_config(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:1211
static PyObject * cp_SparseVirtualFile___setstate__(cp_SparseVirtualFile *self, PyObject *state)
Definition: _cSVF.cpp:1295
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:600
static PyObject * cp_SparseVirtualFile_time_read(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:1168
static int PICKLE_VERSION
Definition: _cSVF.cpp:1232
static PyObject * cp_SparseVirtualFile_block_touches(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:895
static PyObject * cp_SparseVirtualFile_need_many(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
Definition: _cSVF.cpp:775
#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:958
static PyObject * cp_SparseVirtualFile_file_mod_time(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:1051
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:1230
static PyObject * cp_SparseVirtualFile___getstate__(cp_SparseVirtualFile *self, PyObject *Py_UNUSED(ignored))
Definition: _cSVF.cpp:1243
static PyObject * cp_SparseVirtualFile_need(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
Definition: _cSVF.cpp:647
static PyObject * cp_SparseVirtualFile_clear(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:558
static PyObject * cp_SparseVirtualFile_blocks(cp_SparseVirtualFile *self)
Definition: _cSVF.cpp:838
static PyMethodDef cp_SparseVirtualFile_methods[]
Definition: _cSVF.cpp:1413
#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:1229
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:707
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:581
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