diff options
author | Erlend Egeberg Aasland <erlend.aasland@innova.no> | 2022-04-22 01:45:16 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-22 01:45:16 (GMT) |
commit | 29afb7d2efed6ee48a67dafdc1a1f34dd60153cf (patch) | |
tree | c1a3cd0033011f3286c1ec97fb7cdc82cdf3250e /Modules | |
parent | 1317b70f89606bd14597116b7ab68a968ea6c017 (diff) | |
download | cpython-29afb7d2efed6ee48a67dafdc1a1f34dd60153cf.zip cpython-29afb7d2efed6ee48a67dafdc1a1f34dd60153cf.tar.gz cpython-29afb7d2efed6ee48a67dafdc1a1f34dd60153cf.tar.bz2 |
gh-69093: Add indexing and slicing support to sqlite3.Blob (#91599)
Authored-by: Aviv Palivoda <palaviv@gmail.com>
Co-authored-by: Erlend E. Aasland <erlend.aasland@innova.no>
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_sqlite/blob.c | 207 |
1 files changed, 202 insertions, 5 deletions
diff --git a/Modules/_sqlite/blob.c b/Modules/_sqlite/blob.c index 3f76630..0c57ff8 100644 --- a/Modules/_sqlite/blob.c +++ b/Modules/_sqlite/blob.c @@ -120,8 +120,11 @@ blob_seterror(pysqlite_Blob *self, int rc) } static PyObject * -inner_read(pysqlite_Blob *self, int length, int offset) +inner_read(pysqlite_Blob *self, Py_ssize_t length, Py_ssize_t offset) { + assert(length <= sqlite3_blob_bytes(self->blob)); + assert(offset <= sqlite3_blob_bytes(self->blob)); + PyObject *buffer = PyBytes_FromStringAndSize(NULL, length); if (buffer == NULL) { return NULL; @@ -130,7 +133,7 @@ inner_read(pysqlite_Blob *self, int length, int offset) char *raw_buffer = PyBytes_AS_STRING(buffer); int rc; Py_BEGIN_ALLOW_THREADS - rc = sqlite3_blob_read(self->blob, raw_buffer, length, offset); + rc = sqlite3_blob_read(self->blob, raw_buffer, (int)length, (int)offset); Py_END_ALLOW_THREADS if (rc != SQLITE_OK) { @@ -181,17 +184,20 @@ blob_read_impl(pysqlite_Blob *self, int length) }; static int -inner_write(pysqlite_Blob *self, const void *buf, Py_ssize_t len, int offset) +inner_write(pysqlite_Blob *self, const void *buf, Py_ssize_t len, + Py_ssize_t offset) { - int remaining_len = sqlite3_blob_bytes(self->blob) - self->offset; + Py_ssize_t blob_len = sqlite3_blob_bytes(self->blob); + Py_ssize_t remaining_len = blob_len - offset; if (len > remaining_len) { PyErr_SetString(PyExc_ValueError, "data longer than blob length"); return -1; } + assert(offset <= blob_len); int rc; Py_BEGIN_ALLOW_THREADS - rc = sqlite3_blob_write(self->blob, buf, (int)len, offset); + rc = sqlite3_blob_write(self->blob, buf, (int)len, (int)offset); Py_END_ALLOW_THREADS if (rc != SQLITE_OK) { @@ -347,6 +353,192 @@ blob_exit_impl(pysqlite_Blob *self, PyObject *type, PyObject *val, Py_RETURN_FALSE; } +static Py_ssize_t +blob_length(pysqlite_Blob *self) +{ + if (!check_blob(self)) { + return -1; + } + return sqlite3_blob_bytes(self->blob); +}; + +static Py_ssize_t +get_subscript_index(pysqlite_Blob *self, PyObject *item) +{ + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) { + return -1; + } + int blob_len = sqlite3_blob_bytes(self->blob); + if (i < 0) { + i += blob_len; + } + if (i < 0 || i >= blob_len) { + PyErr_SetString(PyExc_IndexError, "Blob index out of range"); + return -1; + } + return i; +} + +static PyObject * +subscript_index(pysqlite_Blob *self, PyObject *item) +{ + Py_ssize_t i = get_subscript_index(self, item); + if (i < 0) { + return NULL; + } + return inner_read(self, 1, i); +} + +static int +get_slice_info(pysqlite_Blob *self, PyObject *item, Py_ssize_t *start, + Py_ssize_t *stop, Py_ssize_t *step, Py_ssize_t *slicelen) +{ + if (PySlice_Unpack(item, start, stop, step) < 0) { + return -1; + } + int len = sqlite3_blob_bytes(self->blob); + *slicelen = PySlice_AdjustIndices(len, start, stop, *step); + return 0; +} + +static PyObject * +subscript_slice(pysqlite_Blob *self, PyObject *item) +{ + Py_ssize_t start, stop, step, len; + if (get_slice_info(self, item, &start, &stop, &step, &len) < 0) { + return NULL; + } + + if (step == 1) { + return inner_read(self, len, start); + } + PyObject *blob = inner_read(self, stop - start, start); + if (blob == NULL) { + return NULL; + } + PyObject *result = PyBytes_FromStringAndSize(NULL, len); + if (result != NULL) { + char *blob_buf = PyBytes_AS_STRING(blob); + char *res_buf = PyBytes_AS_STRING(result); + for (Py_ssize_t i = 0, j = 0; i < len; i++, j += step) { + res_buf[i] = blob_buf[j]; + } + Py_DECREF(blob); + } + return result; +} + +static PyObject * +blob_subscript(pysqlite_Blob *self, PyObject *item) +{ + if (!check_blob(self)) { + return NULL; + } + + if (PyIndex_Check(item)) { + return subscript_index(self, item); + } + if (PySlice_Check(item)) { + return subscript_slice(self, item); + } + + PyErr_SetString(PyExc_TypeError, "Blob indices must be integers"); + return NULL; +} + +static int +ass_subscript_index(pysqlite_Blob *self, PyObject *item, PyObject *value) +{ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "Blob doesn't support item deletion"); + return -1; + } + Py_ssize_t i = get_subscript_index(self, item); + if (i < 0) { + return -1; + } + + Py_buffer vbuf; + if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0) { + return -1; + } + int rc = -1; + if (vbuf.len != 1) { + PyErr_SetString(PyExc_ValueError, "Blob assignment must be a single byte"); + } + else { + rc = inner_write(self, (const char *)vbuf.buf, 1, i); + } + PyBuffer_Release(&vbuf); + return rc; +} + +static int +ass_subscript_slice(pysqlite_Blob *self, PyObject *item, PyObject *value) +{ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "Blob doesn't support slice deletion"); + return -1; + } + + Py_ssize_t start, stop, step, len; + if (get_slice_info(self, item, &start, &stop, &step, &len) < 0) { + return -1; + } + + if (len == 0) { + return 0; + } + + Py_buffer vbuf; + if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0) { + return -1; + } + + int rc = -1; + if (vbuf.len != len) { + PyErr_SetString(PyExc_IndexError, + "Blob slice assignment is wrong size"); + } + else if (step == 1) { + rc = inner_write(self, vbuf.buf, len, start); + } + else { + PyObject *blob_bytes = inner_read(self, stop - start, start); + if (blob_bytes != NULL) { + char *blob_buf = PyBytes_AS_STRING(blob_bytes); + for (Py_ssize_t i = 0, j = 0; i < len; i++, j += step) { + blob_buf[j] = ((char *)vbuf.buf)[i]; + } + rc = inner_write(self, blob_buf, stop - start, start); + Py_DECREF(blob_bytes); + } + } + PyBuffer_Release(&vbuf); + return rc; +} + +static int +blob_ass_subscript(pysqlite_Blob *self, PyObject *item, PyObject *value) +{ + if (!check_blob(self)) { + return -1; + } + + if (PyIndex_Check(item)) { + return ass_subscript_index(self, item, value); + } + if (PySlice_Check(item)) { + return ass_subscript_slice(self, item, value); + } + + PyErr_SetString(PyExc_TypeError, "Blob indices must be integers"); + return -1; +} + static PyMethodDef blob_methods[] = { BLOB_CLOSE_METHODDEF @@ -370,6 +562,11 @@ static PyType_Slot blob_slots[] = { {Py_tp_clear, blob_clear}, {Py_tp_methods, blob_methods}, {Py_tp_members, blob_members}, + + // Mapping protocol + {Py_mp_length, blob_length}, + {Py_mp_subscript, blob_subscript}, + {Py_mp_ass_subscript, blob_ass_subscript}, {0, NULL}, }; |