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_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"
555 " If greedy_length is > 0 then, if possible, blocks will be coalesced to reduce the size of the return value."
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()``"
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"
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"
582 unsigned long long fpos,
583 unsigned long long len,
584 unsigned long long greedy_len) {
585 PyObject * ret = NULL;
586 PyObject * list_item = NULL;
589 ret = PyList_New(seek_read.size());
591 PyErr_Format(PyExc_MemoryError,
"%s: Can not create list.", __FUNCTION__);
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);
597 PyErr_Format(PyExc_MemoryError,
"%s: Can not create tuple as list element.", __FUNCTION__);
600 PyList_SET_ITEM(ret, i, list_item);
603 assert(!PyErr_Occurred());
607 assert(PyErr_Occurred());
609 for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
610 Py_XDECREF(PyList_GET_ITEM(ret, i));
631 PyObject * ret = NULL;
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};
638 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"KK|K", (
char **) kwlist, &fpos, &len, &greedy_len)) {
646 }
catch (
const std::exception &err) {
647 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
650 assert(!PyErr_Occurred());
654 assert(PyErr_Occurred());
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"
667 "If greedy_length is > 0 then, if possible, blocks will be coalesced to reduce the size of the return value."
669 "See also :py:meth:`svfsc.cSVF.need`"
679 unsigned long long greedy_len) {
680 PyObject * ret = NULL;
684 if (!PyList_Check(py_seek_reads)) {
685 PyErr_Format(PyExc_TypeError,
"%s: seek_reads is not a list.", __FUNCTION__);
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);
693 if (PyTuple_Size(PyList_GetItem(py_seek_reads, i)) != 2) {
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))
703 for (i = 0; i < PyList_Size(py_seek_reads); ++i) {
704 PyObject * py_fpos_len = PyList_GetItem(py_seek_reads, i);
707 if (!PyArg_ParseTuple(py_fpos_len,
"KK", &fpos, &length)) {
708 PyErr_Format(PyExc_TypeError,
"%s: can not parse list element[%ld].", __FUNCTION__, i);
711 cpp_seek_reads.push_back({fpos, length});
714 cpp_seek_reads = pSvf->
need_many(cpp_seek_reads, greedy_len);
716 ret = PyList_New(cpp_seek_reads.size());
718 PyErr_Format(PyExc_MemoryError,
"%s: Can not create list", __FUNCTION__);
722 for (
const auto &iter: cpp_seek_reads) {
723 PyObject * list_item = Py_BuildValue(
"KK", iter.first, iter.second);
725 PyErr_Format(PyExc_MemoryError,
"%s: Can not create tuple as a list element", __FUNCTION__);
728 PyList_SET_ITEM(ret, i, list_item);
731 assert(!PyErr_Occurred());
735 assert(PyErr_Occurred());
737 for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
738 Py_XDECREF(PyList_GET_ITEM(ret, i));
759 PyObject * ret = NULL;
760 PyObject * py_seek_reads = NULL;
761 unsigned long long greedy_len = 0;
762 static const char *kwlist[] = {
"seek_reads",
"greedy_length", NULL};
765 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"O|K", (
char **) kwlist, &py_seek_reads, &greedy_len)) {
773 }
catch (
const std::exception &err) {
774 PyErr_Format(PyExc_RuntimeError,
"%s#%d: FATAL caught std::exception %s", __FUNCTION__, __LINE__, err.what());
777 assert(!PyErr_Occurred());
781 assert(PyErr_Occurred());
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."
822 PyObject * ret = NULL;
823 PyObject * insert_item = NULL;
828 ret = PyTuple_New(seek_read.size());
830 PyErr_Format(PyExc_MemoryError,
"%s: Can not create tuple for return", __FUNCTION__);
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);
836 PyErr_Format(PyExc_MemoryError,
"%s: Can not create tuple", __FUNCTION__);
839 PyTuple_SET_ITEM(ret, i, insert_item);
842 }
catch (
const std::exception &err) {
843 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
846 assert(!PyErr_Occurred());
850 assert(PyErr_Occurred());
852 for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
853 Py_XDECREF(PyList_GET_ITEM(ret, i));
864 "Return the latest value of the monotonically increasing block_touch value."
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)."
879 PyObject * ret = NULL;
885 PyErr_Format(PyExc_MemoryError,
"%s: Can not create dict for return", __FUNCTION__);
888 for (
const auto &iter: self->pSvf->block_touches()) {
889 PyObject * key = PyLong_FromLong(iter.first);
891 PyErr_Format(PyExc_MemoryError,
"%s: Can not create key", __FUNCTION__);
894 PyObject * val = PyLong_FromLong(iter.second);
896 PyErr_Format(PyExc_MemoryError,
"%s: Can not create value", __FUNCTION__);
899 PyDict_SetItem(ret, key, val);
903 }
catch (
const std::exception &err) {
904 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
907 assert(!PyErr_Occurred());
911 assert(PyErr_Occurred());
913 for (Py_ssize_t i = 0; i < PyList_Size(ret); ++i) {
914 Py_XDECREF(PyList_GET_ITEM(ret, i));
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."
942 PyObject * ret = NULL;
943 size_t cache_size_upper_bound;
944 static const char *kwlist[] = {
"cache_size_upper_bound", NULL};
947 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"K", (
char **) kwlist, &cache_size_upper_bound)) {
951 ret = Py_BuildValue(
"K", self->pSvf->lru_punt(cache_size_upper_bound));
953 PyErr_Format(PyExc_MemoryError,
"%s: Can not create long", __FUNCTION__);
956 }
catch (
const std::exception &err) {
957 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
960 assert(!PyErr_Occurred());
964 assert(PyErr_Occurred());
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."
981 PyObject * ret = NULL;
982 double file_mod_time;
983 static const char *kwlist[] = {
"file_mod_time", NULL};
985 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"d", (
char **) kwlist, &file_mod_time)) {
989 if (self->pSvf->file_mod_time_matches(file_mod_time)) {
996 }
catch (
const std::exception &err) {
997 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
1000 assert(!PyErr_Occurred());
1004 assert(PyErr_Occurred());
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."
1035 PyObject * ret = NULL;
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());
1042 assert(!PyErr_Occurred());
1046 assert(PyErr_Occurred());
1055 "Returns the count of write operations on the Sparse Virtual File."
1060 "Returns the count of read operations on the Sparse Virtual File."
1065 "Returns the count of the number of bytes writen to the Sparse Virtual File."
1070 "Returns the count of the number of bytes read from the Sparse Virtual File."
1075 "Returns the The total count of blocks that have been erased either directly or by punting."
1080 "Returns the The total count of bytes that have been erased either directly or by punting."
1085 "Returns the The total count of blocks that have been erased by punting."
1090 "Returns the The total count of bytes that have been erased by punting."
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."
1106 PyObject * ret = NULL;
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();
1113 std::chrono::time_point_cast<std::chrono::microseconds>(time).time_since_epoch().count() %
1115 const std::tm *p_struct_tm = std::gmtime(&seconds);
1124 }
catch (
const std::exception &err) {
1125 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
1128 assert(!PyErr_Occurred());
1132 assert(PyErr_Occurred());
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."
1152 PyObject * ret = NULL;
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();
1159 std::chrono::time_point_cast<std::chrono::microseconds>(time).time_since_epoch().count() %
1161 const std::tm *p_struct_tm = std::gmtime(&seconds);
1170 }
catch (
const std::exception &err) {
1171 PyErr_Format(PyExc_RuntimeError,
"%s: FATAL caught std::exception %s", __FUNCTION__, err.what());
1174 assert(!PyErr_Occurred());
1178 assert(PyErr_Occurred());
1186 cp_SparseVirtualFile_config_docstring,
1187 "config(self) -> typing.Dict[str, bool]\n\n"
1188 "Returns the SVF configuration as a dict."
1195 PyObject * ret = Py_BuildValue(
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)
1207 #pragma mark Pickling
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__);
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) {
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__);
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__);
1250 PyTuple_SET_ITEM(blocks_fpos_bytes, index, fpos_bytes);
1254 PyObject * ret = Py_BuildValue(
1261 PICKLE_ID_KEY, PyUnicode_FromKindAndData(PyUnicode_1BYTE_KIND,
1262 self->pSvf->id().c_str(),
1263 self->pSvf->id().size()
1270 Py_DECREF(blocks_fpos_bytes);
1280 if (!PyDict_CheckExact(state)) {
1281 PyErr_Format(PyExc_ValueError,
"%s()#%d: Pickled object is not a dict.", __FUNCTION__, __LINE__);
1289 PyErr_Format(PyExc_KeyError,
"%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__, __LINE__,
PICKLE_VERSION_KEY);
1292 int pickle_version = (int) PyLong_AsLong(temp);
1294 PyErr_Format(PyExc_ValueError,
"%s()#d Pickle version mismatch. Got version %d but expected version %d.",
1301 PyErr_Format(PyExc_KeyError,
"%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__, __LINE__,
PICKLE_ID_KEY);
1304 if (!PyUnicode_Check(
id)) {
1305 PyErr_Format(PyExc_TypeError,
"%s()#%d: \"%s\" is not string.", __FUNCTION__, __LINE__,
PICKLE_ID_KEY);
1310 if (file_mod_time == NULL) {
1312 PyErr_Format(PyExc_KeyError,
"%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__,
1316 if (!PyFloat_Check(file_mod_time)) {
1318 PyErr_Format(PyExc_TypeError,
"%s()#%d: \"%s\" is not a double.", __FUNCTION__,
1322 Py_INCREF(file_mod_time);
1323 PyObject * args = Py_BuildValue(
"OO",
id, file_mod_time);
1325 Py_DECREF(file_mod_time);
1327 PyErr_Format(PyExc_RuntimeError,
"%s()#d Can not create arguments.", __FUNCTION__, __LINE__);
1333 Py_DECREF(file_mod_time);
1335 PyErr_Format(PyExc_RuntimeError,
"%s()#%d: Can not create new SVF object.", __FUNCTION__, __LINE__);
1341 Py_DECREF(file_mod_time);
1342 file_mod_time = NULL;
1347 if (blocks == NULL) {
1348 PyErr_Format(PyExc_KeyError,
"%s()#%d: No \"%s\" in pickled dict.", __FUNCTION__, __LINE__,
PICKLE_BLOCKS_KEY);
1351 if (!PyTuple_Check(blocks)) {
1352 PyErr_Format(PyExc_TypeError,
"%s()#%d: \"%s\" is not a tuple.", __FUNCTION__, __LINE__,
PICKLE_BLOCKS_KEY);
1356 for (Py_ssize_t i = 0; i < PyTuple_Size(blocks); ++i) {
1357 PyObject * fpos_bytes = PyTuple_GetItem(blocks, i);
1358 Py_INCREF(fpos_bytes);
1359 unsigned long long fpos;
1360 PyObject * block_bytes = NULL;
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__,
1364 Py_DECREF(fpos_bytes);
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__,
1371 Py_DECREF(block_bytes);
1372 Py_DECREF(fpos_bytes);
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);
1384 #pragma mark END Pickling
1386 #pragma mark END: SVF functions
1391 {NULL, 0, 0, 0, NULL}
1397 cp_SparseVirtualFile_id_docstring
1406 cp_SparseVirtualFile_has_data_docstring
1411 cp_SparseVirtualFile_write_docstring
1416 cp_SparseVirtualFile_read_docstring
1421 cp_SparseVirtualFile_erase_docstring
1426 cp_SparseVirtualFile_need_docstring
1431 cp_SparseVirtualFile_need_many_docstring
1436 cp_SparseVirtualFile_blocks_docstring
1441 cp_SparseVirtualFile_block_touches_docstring
1447 cp_SparseVirtualFile_lru_punt_docstring
1453 cp_SparseVirtualFile_file_mod_time_matches_docstring
1458 cp_SparseVirtualFile_file_mod_time_docstring
1470 cp_SparseVirtualFile_time_write_docstring
1474 cp_SparseVirtualFile_config_docstring
1478 cp_SparseVirtualFile_time_read_docstring
1481 "Return the state for pickling."
1484 "Set the state from a pickled object."
1486 {NULL, NULL, 0, NULL}
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."
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"
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"
1516 "Signature:\n\n``svfsc.cSVF(id: str, mod_time: float = 0.0, overwrite_on_exit: bool = False, compare_for_diff: bool = True)``"
1522 PyVarObject_HEAD_INIT(NULL, 0)
1523 .tp_name =
"svfsc.cSVF",
1527 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1528 .tp_doc = svfs_cSVF_doc,
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)
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_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)