summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorErlend Egeberg Aasland <erlend.aasland@innova.no>2022-04-22 01:45:16 (GMT)
committerGitHub <noreply@github.com>2022-04-22 01:45:16 (GMT)
commit29afb7d2efed6ee48a67dafdc1a1f34dd60153cf (patch)
treec1a3cd0033011f3286c1ec97fb7cdc82cdf3250e /Modules
parent1317b70f89606bd14597116b7ab68a968ea6c017 (diff)
downloadcpython-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.c207
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},
};