57 #define SVFS_SVF_METHOD_SIZE_T_WRAPPER(method_name, docstring) \
59 cp_SparseVirtualFile_##method_name##_docstring, \
60 #method_name"(self) -> int\n\n" \
64 cp_SparseVirtualFile_##method_name(cp_SparseVirtualFile *self) { \
65 ASSERT_FUNCTION_ENTRY_SVF(pSvf); \
66 PyObject *ret = NULL; \
68 ret = PyLong_FromLong(self->pSvf->method_name()); \
70 PyErr_Format(PyExc_RuntimeError, "%s: Can not create integer from size_t.", __FUNCTION__); \
73 } catch (const std::exception &err) { \
74 PyErr_Format(PyExc_RuntimeError, "%s: FATAL caught std::exception %s", __FUNCTION__, err.what()); \
77 assert(! PyErr_Occurred()); \
81 assert(PyErr_Occurred()); \
92 #define SVFS_SVF_METHOD_SIZE_T_REGISTER(method_name) \
95 (PyCFunction) cp_SparseVirtualFile_##method_name, \
97 cp_SparseVirtualFile_##method_name##_docstring \
108 #ifdef PY_THREAD_SAFE
113 #ifdef PY_THREAD_SAFE
130 if (!PyThread_acquire_lock(
_pSVF->
lock, NOWAIT_LOCK)) {
131 Py_BEGIN_ALLOW_THREADS
132 PyThread_acquire_lock(
_pSVF->
lock, WAIT_LOCK);
165 #define ASSERT_FUNCTION_ENTRY_SVF(member) do { \
167 assert(((cp_SparseVirtualFile *)self)->member); \
168 assert(! PyErr_Occurred()); \
173 #pragma mark Construction and destruction
186 assert(!PyErr_Occurred());
190 self->
pSvf =
nullptr;
191 #ifdef PY_THREAD_SAFE
197 assert(!PyErr_Occurred());
198 return (PyObject *)
self;
220 assert(!PyErr_Occurred());
223 double mod_time = 0.0;
224 static const char *kwlist[] = {
"id",
"mod_time",
"overwrite_on_exit",
"compare_for_diff", NULL};
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());
248 }
catch (
const std::exception &err) {
249 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
252 #ifdef PY_THREAD_SAFE
253 self->lock = PyThread_allocate_lock();
254 if (self->lock == NULL) {
256 PyErr_SetString(PyExc_MemoryError,
"Unable to allocate thread lock.");
261 assert(!PyErr_Occurred());
272 #ifdef PY_THREAD_SAFE
274 PyThread_free_lock(self->lock);
278 Py_TYPE(
self)->tp_free((PyObject *)
self);
282 #pragma mark END: Construction and destruction
285 #pragma mark SVF functions
288 cp_SparseVirtualFile_id_docstring,
289 "id(self) -> str\n\n"
290 "Returns the ID of the Sparse Virtual File."
297 PyObject * ret = NULL;
298 ret = PyUnicode_FromKindAndData(PyUnicode_1BYTE_KIND, self->pSvf->id().c_str(), self->pSvf->id().size());
300 PyErr_Format(PyExc_RuntimeError,
"%s: Can not create id for %s", __FUNCTION__, self->pSvf->id().c_str());
303 assert(!PyErr_Occurred());
307 assert(PyErr_Occurred());
317 "Returns the estimate of total memory usage of the Sparse Virtual File."
323 "Returns the total number of file bytes held by the Sparse Virtual File."
329 "Returns the total number of blocks of data held by the Sparse Virtual File System."
335 "Returns the file position immediately past the last block."
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"
343 "file_position: int\n"
344 " The absolute file position of the start of the data.\n\n"
346 " The length of the required data in bytes.\n\n"
348 "bool: True if the SVF contains the data, False otherwise."
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};
360 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"KK", (
char **) kwlist, &fpos, &len)) {
364 if (self->pSvf->has(fpos, len)) {
371 }
catch (
const std::exception &err) {
372 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
375 assert(!PyErr_Occurred());
379 assert(PyErr_Occurred());
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"
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};
406 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"KS", (
char **) kwlist, &fpos, &py_bytes_data)) {
409 if (PyBytes_GET_SIZE(py_bytes_data) > 0) {
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());
419 PyErr_Format(PyExc_RuntimeError,
"%s: Can not write to a SVF. ERROR: %s",
420 __FUNCTION__, err.
message().c_str());
422 }
catch (
const std::exception &err) {
423 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
429 assert(!PyErr_Occurred());
433 assert(PyErr_Occurred());
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"
451 unsigned long long len) {
453 PyObject * ret = PyBytes_FromStringAndSize(NULL, len);
455 PyErr_Format(PyExc_RuntimeError,
"%s()#%d: Could not create bytes object.", __FUNCTION__, __LINE__);
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());
466 PyErr_Format(PyExc_RuntimeError,
"%s()#%d: Fatal error reading from a SVF. ERROR: %s",
467 __FUNCTION__, __LINE__, err.
message().c_str());
470 }
catch (
const std::exception &err) {
471 PyErr_Format(PyExc_RuntimeError,
"%s()#%d: FATAL caught std::exception %s", __FUNCTION__, __LINE__,
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};
489 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"KK", (
char **) kwlist, &fpos, &len)) {
497 assert(!PyErr_Occurred());
501 assert(PyErr_Occurred());
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."
521 unsigned long long fpos = 0;
522 static const char *kwlist[] = {
"file_position", NULL};
525 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"K", (
char **) kwlist, &fpos)) {
529 self->pSvf->erase(fpos);
532 PyErr_Format(PyExc_IOError,
"%s(): Can not erase from a SVF. ERROR: %s",
533 __FUNCTION__, err.
message().c_str());
537 PyErr_Format(PyExc_RuntimeError,
"%s(): Fatal error reading from a SVF. ERROR: %s",
538 __FUNCTION__, err.
message().c_str());
540 }
catch (
const std::exception &err) {
542 PyErr_Format(PyExc_RuntimeError,
"%s(): FATAL caught std::exception %s", __FUNCTION__, __LINE__,
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."
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"
574 " If greedy_length is > 0 then, if possible, blocks will be coalesced to reduce the size of the return value."
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()``"
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"
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"
601 unsigned long long fpos,
602 unsigned long long len,
603 unsigned long long greedy_len) {
604 PyObject * ret = NULL;
605 PyObject * list_item = NULL;
608 ret = PyList_New(seek_read.size());
610 PyErr_Format(PyExc_MemoryError,
"%s: Can not create list.", __FUNCTION__);
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);
616 PyErr_Format(PyExc_MemoryError,
"%s: Can not create tuple as list element.", __FUNCTION__);
619 PyList_SET_ITEM(ret, i, list_item);
622 assert(!PyErr_Occurred());
626 assert(PyErr_Occurred());
628 for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
629 Py_XDECREF(PyList_GET_ITEM(ret, i));
650 PyObject * ret = NULL;
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};
657 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"KK|K", (
char **) kwlist, &fpos, &len, &greedy_len)) {
665 }
catch (
const std::exception &err) {
666 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
669 assert(!PyErr_Occurred());
673 assert(PyErr_Occurred());
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"
686 "If greedy_length is > 0 then, if possible, blocks will be coalesced to reduce the size of the return value."
688 "See also :py:meth:`svfsc.cSVF.need`"
698 unsigned long long greedy_len) {
699 PyObject * ret = NULL;
703 if (!PyList_Check(py_seek_reads)) {
704 PyErr_Format(PyExc_TypeError,
"%s: seek_reads is not a list.", __FUNCTION__);
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);
712 if (PyTuple_Size(PyList_GetItem(py_seek_reads, i)) != 2) {
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))
722 for (i = 0; i < PyList_Size(py_seek_reads); ++i) {
723 PyObject * py_fpos_len = PyList_GetItem(py_seek_reads, i);
726 if (!PyArg_ParseTuple(py_fpos_len,
"KK", &fpos, &length)) {
727 PyErr_Format(PyExc_TypeError,
"%s: can not parse list element[%ld].", __FUNCTION__, i);
730 cpp_seek_reads.push_back({fpos, length});
733 cpp_seek_reads = pSvf->
need_many(cpp_seek_reads, greedy_len);
735 ret = PyList_New(cpp_seek_reads.size());
737 PyErr_Format(PyExc_MemoryError,
"%s: Can not create list", __FUNCTION__);
741 for (
const auto &iter: cpp_seek_reads) {
742 PyObject * list_item = Py_BuildValue(
"KK", iter.first, iter.second);
744 PyErr_Format(PyExc_MemoryError,
"%s: Can not create tuple as a list element", __FUNCTION__);
747 PyList_SET_ITEM(ret, i, list_item);
750 assert(!PyErr_Occurred());
754 assert(PyErr_Occurred());
756 for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
757 Py_XDECREF(PyList_GET_ITEM(ret, i));
778 PyObject * ret = NULL;
779 PyObject * py_seek_reads = NULL;
780 unsigned long long greedy_len = 0;
781 static const char *kwlist[] = {
"seek_reads",
"greedy_length", NULL};
784 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"O|K", (
char **) kwlist, &py_seek_reads, &greedy_len)) {
792 }
catch (
const std::exception &err) {
793 PyErr_Format(PyExc_RuntimeError,
"%s#%d: FATAL caught std::exception %s", __FUNCTION__, __LINE__, err.what());
796 assert(!PyErr_Occurred());
800 assert(PyErr_Occurred());
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."
841 PyObject * ret = NULL;
842 PyObject * insert_item = NULL;
847 ret = PyTuple_New(seek_read.size());
849 PyErr_Format(PyExc_MemoryError,
"%s: Can not create tuple for return", __FUNCTION__);
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);
855 PyErr_Format(PyExc_MemoryError,
"%s: Can not create tuple", __FUNCTION__);
858 PyTuple_SET_ITEM(ret, i, insert_item);
861 }
catch (
const std::exception &err) {
862 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
865 assert(!PyErr_Occurred());
869 assert(PyErr_Occurred());
871 for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
872 Py_XDECREF(PyList_GET_ITEM(ret, i));
883 "Return the latest value of the monotonically increasing block_touch value."
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)."
898 PyObject * ret = NULL;
904 PyErr_Format(PyExc_MemoryError,
"%s: Can not create dict for return", __FUNCTION__);
907 for (
const auto &iter: self->pSvf->block_touches()) {
908 PyObject * key = PyLong_FromLong(iter.first);
910 PyErr_Format(PyExc_MemoryError,
"%s: Can not create key", __FUNCTION__);
913 PyObject * val = PyLong_FromLong(iter.second);
915 PyErr_Format(PyExc_MemoryError,
"%s: Can not create value", __FUNCTION__);
918 PyDict_SetItem(ret, key, val);
922 }
catch (
const std::exception &err) {
923 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
926 assert(!PyErr_Occurred());
930 assert(PyErr_Occurred());
932 for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
933 Py_XDECREF(PyList_GET_ITEM(ret, i));
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."
961 PyObject * ret = NULL;
962 size_t cache_size_upper_bound;
963 static const char *kwlist[] = {
"cache_size_upper_bound", NULL};
966 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"K", (
char **) kwlist, &cache_size_upper_bound)) {
970 ret = Py_BuildValue(
"K", self->pSvf->lru_punt(cache_size_upper_bound));
972 PyErr_Format(PyExc_MemoryError,
"%s: Can not create long", __FUNCTION__);
975 }
catch (
const std::exception &err) {
976 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
979 assert(!PyErr_Occurred());
983 assert(PyErr_Occurred());
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."
1000 PyObject * ret = NULL;
1001 double file_mod_time;
1002 static const char *kwlist[] = {
"file_mod_time", NULL};
1004 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"d", (
char **) kwlist, &file_mod_time)) {
1008 if (self->pSvf->file_mod_time_matches(file_mod_time)) {
1012 Py_INCREF(Py_False);
1015 }
catch (
const std::exception &err) {
1016 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
1019 assert(!PyErr_Occurred());
1023 assert(PyErr_Occurred());
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."
1054 PyObject * ret = NULL;
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());
1061 assert(!PyErr_Occurred());
1065 assert(PyErr_Occurred());
1074 "Returns the count of write operations on the Sparse Virtual File."
1079 "Returns the count of read operations on the Sparse Virtual File."
1084 "Returns the count of the number of bytes writen to the Sparse Virtual File."
1089 "Returns the count of the number of bytes read from the Sparse Virtual File."
1094 "Returns the The total count of blocks that have been erased either directly or by punting."
1099 "Returns the The total count of bytes that have been erased either directly or by punting."
1104 "Returns the The total count of blocks that have been erased by punting."
1109 "Returns the The total count of bytes that have been erased by punting."
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."
1125 PyObject * ret = NULL;
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();
1132 std::chrono::time_point_cast<std::chrono::microseconds>(time).time_since_epoch().count() %
1134 const std::tm *p_struct_tm = std::gmtime(&seconds);
1143 }
catch (
const std::exception &err) {
1144 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
1147 assert(!PyErr_Occurred());
1151 assert(PyErr_Occurred());
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."
1171 PyObject * ret = NULL;
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();
1178 std::chrono::time_point_cast<std::chrono::microseconds>(time).time_since_epoch().count() %
1180 const std::tm *p_struct_tm = std::gmtime(&seconds);
1189 }
catch (
const std::exception &err) {
1190 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
1193 assert(!PyErr_Occurred());
1197 assert(PyErr_Occurred());
1205 cp_SparseVirtualFile_config_docstring,
1206 "config(self) -> typing.Dict[str, bool]\n\n"
1207 "Returns the SVF configuration as a dict."
1214 PyObject * ret = Py_BuildValue(
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)
1226 #pragma mark Pickling
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__);
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) {
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__);
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__);
1269 PyTuple_SET_ITEM(blocks_fpos_bytes, index, fpos_bytes);
1273 PyObject * ret = Py_BuildValue(
1280 PICKLE_ID_KEY, PyUnicode_FromKindAndData(PyUnicode_1BYTE_KIND,
1281 self->pSvf->id().c_str(),
1282 self->pSvf->id().size()
1289 Py_DECREF(blocks_fpos_bytes);
1299 if (!PyDict_CheckExact(state)) {
1300 PyErr_Format(PyExc_ValueError,
"%s()#%d: Pickled object is not a dict.", __FUNCTION__, __LINE__);
1308 PyErr_Format(PyExc_KeyError,
"%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__, __LINE__,
PICKLE_VERSION_KEY);
1311 int pickle_version = (int) PyLong_AsLong(temp);
1313 PyErr_Format(PyExc_ValueError,
"%s()#d Pickle version mismatch. Got version %d but expected version %d.",
1320 PyErr_Format(PyExc_KeyError,
"%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__, __LINE__,
PICKLE_ID_KEY);
1323 if (!PyUnicode_Check(
id)) {
1324 PyErr_Format(PyExc_TypeError,
"%s()#%d: \"%s\" is not string.", __FUNCTION__, __LINE__,
PICKLE_ID_KEY);
1329 if (file_mod_time == NULL) {
1331 PyErr_Format(PyExc_KeyError,
"%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__,
1335 if (!PyFloat_Check(file_mod_time)) {
1337 PyErr_Format(PyExc_TypeError,
"%s()#%d: \"%s\" is not a double.", __FUNCTION__,
1341 Py_INCREF(file_mod_time);
1342 PyObject * args = Py_BuildValue(
"OO",
id, file_mod_time);
1344 Py_DECREF(file_mod_time);
1346 PyErr_Format(PyExc_RuntimeError,
"%s()#d Can not create arguments.", __FUNCTION__, __LINE__);
1352 Py_DECREF(file_mod_time);
1354 PyErr_Format(PyExc_RuntimeError,
"%s()#%d: Can not create new SVF object.", __FUNCTION__, __LINE__);
1360 Py_DECREF(file_mod_time);
1361 file_mod_time = NULL;
1366 if (blocks == NULL) {
1367 PyErr_Format(PyExc_KeyError,
"%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__, __LINE__,
PICKLE_BLOCKS_KEY);
1370 if (!PyTuple_Check(blocks)) {
1371 PyErr_Format(PyExc_TypeError,
"%s()#%d: \"%s\" is not a tuple.", __FUNCTION__, __LINE__,
PICKLE_BLOCKS_KEY);
1375 for (Py_ssize_t i = 0; i < PyTuple_Size(blocks); ++i) {
1376 PyObject * fpos_bytes = PyTuple_GetItem(blocks, i);
1377 Py_INCREF(fpos_bytes);
1378 unsigned long long fpos;
1379 PyObject * block_bytes = NULL;
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__,
1383 Py_DECREF(fpos_bytes);
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__,
1390 Py_DECREF(block_bytes);
1391 Py_DECREF(fpos_bytes);
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);
1403 #pragma mark END Pickling
1405 #pragma mark END: SVF functions
1410 {NULL, 0, 0, 0, NULL}
1416 cp_SparseVirtualFile_id_docstring
1425 cp_SparseVirtualFile_has_data_docstring
1430 cp_SparseVirtualFile_write_docstring
1435 cp_SparseVirtualFile_read_docstring
1440 cp_SparseVirtualFile_erase_docstring
1444 cp_SparseVirtualFile_clear_docstring
1449 cp_SparseVirtualFile_need_docstring
1454 cp_SparseVirtualFile_need_many_docstring
1459 cp_SparseVirtualFile_blocks_docstring
1464 cp_SparseVirtualFile_block_touches_docstring
1470 cp_SparseVirtualFile_lru_punt_docstring
1476 cp_SparseVirtualFile_file_mod_time_matches_docstring
1481 cp_SparseVirtualFile_file_mod_time_docstring
1493 cp_SparseVirtualFile_time_write_docstring
1497 cp_SparseVirtualFile_config_docstring
1501 cp_SparseVirtualFile_time_read_docstring
1504 "Return the state for pickling."
1507 "Set the state from a pickled object."
1509 {NULL, NULL, 0, NULL}
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."
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"
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"
1539 "Signature:\n\n``svfsc.cSVF(id: str, mod_time: float = 0.0, overwrite_on_exit: bool = False, compare_for_diff: bool = True)``"
1545 PyVarObject_HEAD_INIT(NULL, 0)
1546 .tp_name =
"svfsc.cSVF",
1550 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1551 .tp_doc = svfs_cSVF_doc,
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)
static void cp_SparseVirtualFile_dealloc(cp_SparseVirtualFile *self)
static const char * PICKLE_VERSION_KEY
static PyObject * cp_SparseVirtualFile_need_many_internal(PyObject *py_seek_reads, const SVFS::SparseVirtualFile *pSvf, unsigned long long greedy_len)
static PyObject * cp_SparseVirtualFile_write(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
static PyTypeObject svfsc_cSVF
static PyObject * cp_SparseVirtualFile_file_mod_time_matches(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
static PyObject * cp_SparseVirtualFile_erase(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
static PyMemberDef cp_SparseVirtualFile_members[]
static PyObject * cp_SparseVirtualFile_time_write(cp_SparseVirtualFile *self)
static PyObject * cp_SparseVirtualFile_new(PyTypeObject *type, PyObject *Py_UNUSED(args), PyObject *Py_UNUSED(kwds))
static PyObject * cp_SparseVirtualFile_id(cp_SparseVirtualFile *self)
static PyObject * cp_SparseVirtualFile_read(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
static const char * PICKLE_ID_KEY
static PyObject * cp_SparseVirtualFile_config(cp_SparseVirtualFile *self)
static PyObject * cp_SparseVirtualFile___setstate__(cp_SparseVirtualFile *self, PyObject *state)
static PyObject * cp_SparseVirtualFile_need_internal(const SVFS::SparseVirtualFile *pSvf, unsigned long long fpos, unsigned long long len, unsigned long long greedy_len)
static PyObject * cp_SparseVirtualFile_time_read(cp_SparseVirtualFile *self)
static int PICKLE_VERSION
static PyObject * cp_SparseVirtualFile_block_touches(cp_SparseVirtualFile *self)
static PyObject * cp_SparseVirtualFile_need_many(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
#define ASSERT_FUNCTION_ENTRY_SVF(member)
static PyObject * cp_SparseVirtualFile_lru_punt(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
static PyObject * cp_SparseVirtualFile_file_mod_time(cp_SparseVirtualFile *self)
static PyObject * cp_SparseVirtualFile_has_data(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
static int cp_SparseVirtualFile_init(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
#define SVFS_SVF_METHOD_SIZE_T_REGISTER(method_name)
static const char * PICKLE_BLOCKS_KEY
static PyObject * cp_SparseVirtualFile___getstate__(cp_SparseVirtualFile *self, PyObject *Py_UNUSED(ignored))
static PyObject * cp_SparseVirtualFile_need(cp_SparseVirtualFile *self, PyObject *args, PyObject *kwargs)
static PyObject * cp_SparseVirtualFile_clear(cp_SparseVirtualFile *self)
static PyObject * cp_SparseVirtualFile_blocks(cp_SparseVirtualFile *self)
static PyMethodDef cp_SparseVirtualFile_methods[]
#define SVFS_SVF_METHOD_SIZE_T_WRAPPER(method_name, docstring)
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
A RAII wrapper around the PyThread_type_lock for the CPython SVF.
AcquireLockSVF(cp_SparseVirtualFile *pSVF)
cp_SparseVirtualFile * _pSVF
Might be thrown during a write operation where the data differs.
Might be thrown during a erase operation where the file position is not at the exact beginning of a b...
Exception specialisation for the SparseVirtualFile.
const std::string & message() const
Might be thrown during a write operation where the data differs.
Implementation of a Sparse Virtual File.
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?
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.
std::vector< t_seek_read > t_seek_reads
Configuration for the Sparse Virtual File.
Python wrapper around a C++ SparseVirtualFile.
PyObject_HEAD SVFS::SparseVirtualFile * pSvf
PyObject * datetime_from_struct_tm(const std::tm *bdt, int usecond)