#ifndef Py_BUILD_CORE_BUILTIN
# define Py_BUILD_CORE_MODULE 1
#endif
#include "blob.h"
#include "util.h"
#include "pycore_weakref.h"
#define clinic_state() (pysqlite_get_state_by_type(Py_TYPE(self)))
#include "clinic/blob.c.h"
#undef clinic_state
static void
close_blob(pysqlite_Blob *self)
{
if (self->blob) {
sqlite3_blob *blob = self->blob;
self->blob = NULL;
Py_BEGIN_ALLOW_THREADS
sqlite3_blob_close(blob);
Py_END_ALLOW_THREADS
}
}
static int
blob_traverse(pysqlite_Blob *self, visitproc visit, void *arg)
{
Py_VISIT(Py_TYPE(self));
Py_VISIT(self->connection);
return 0;
}
static int
blob_clear(pysqlite_Blob *self)
{
Py_CLEAR(self->connection);
return 0;
}
static void
blob_dealloc(pysqlite_Blob *self)
{
PyTypeObject *tp = Py_TYPE(self);
PyObject_GC_UnTrack(self);
close_blob(self);
if (self->in_weakreflist != NULL) {
PyObject_ClearWeakRefs((PyObject*)self);
}
tp->tp_clear((PyObject *)self);
tp->tp_free(self);
Py_DECREF(tp);
}
static int
check_blob(pysqlite_Blob *self)
{
if (!pysqlite_check_connection(self->connection) ||
!pysqlite_check_thread(self->connection)) {
return 0;
}
if (self->blob == NULL) {
pysqlite_state *state = self->connection->state;
PyErr_SetString(state->ProgrammingError,
"Cannot operate on a closed blob.");
return 0;
}
return 1;
}
static PyObject *
blob_close_impl(pysqlite_Blob *self)
{
if (!pysqlite_check_connection(self->connection) ||
!pysqlite_check_thread(self->connection))
{
return NULL;
}
close_blob(self);
Py_RETURN_NONE;
};
void
pysqlite_close_all_blobs(pysqlite_Connection *self)
{
for (int i = 0; i < PyList_GET_SIZE(self->blobs); i++) {
PyObject *weakref = PyList_GET_ITEM(self->blobs, i);
PyObject *blob = _PyWeakref_GET_REF(weakref);
if (blob == NULL) {
continue;
}
close_blob((pysqlite_Blob *)blob);
Py_DECREF(blob);
}
}
static void
blob_seterror(pysqlite_Blob *self, int rc)
{
assert(self->connection != NULL);
_pysqlite_seterror(self->connection->state, self->connection->db);
}
static PyObject *
read_single(pysqlite_Blob *self, Py_ssize_t offset)
{
unsigned char buf = 0;
int rc;
Py_BEGIN_ALLOW_THREADS
rc = sqlite3_blob_read(self->blob, (void *)&buf, 1, (int)offset);
Py_END_ALLOW_THREADS
if (rc != SQLITE_OK) {
blob_seterror(self, rc);
return NULL;
}
return PyLong_FromUnsignedLong((unsigned long)buf);
}
static PyObject *
read_multiple(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;
}
char *raw_buffer = PyBytes_AS_STRING(buffer);
int rc;
Py_BEGIN_ALLOW_THREADS
rc = sqlite3_blob_read(self->blob, raw_buffer, (int)length, (int)offset);
Py_END_ALLOW_THREADS
if (rc != SQLITE_OK) {
Py_DECREF(buffer);
blob_seterror(self, rc);
return NULL;
}
return buffer;
}
static PyObject *
blob_read_impl(pysqlite_Blob *self, int length)
{
if (!check_blob(self)) {
return NULL;
}
int blob_len = sqlite3_blob_bytes(self->blob);
int max_read_len = blob_len - self->offset;
if (length < 0 || length > max_read_len) {
length = max_read_len;
}
assert(length >= 0);
if (length == 0) {
return PyBytes_FromStringAndSize(NULL, 0);
}
PyObject *buffer = read_multiple(self, length, self->offset);
if (buffer == NULL) {
return NULL;
}
self->offset += length;
return buffer;
};
static int
inner_write(pysqlite_Blob *self, const void *buf, Py_ssize_t len,
Py_ssize_t 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, (int)offset);
Py_END_ALLOW_THREADS
if (rc != SQLITE_OK) {
blob_seterror(self, rc);
return -1;
}
return 0;
}
static PyObject *
blob_write_impl(pysqlite_Blob *self, Py_buffer *data)
{
if (!check_blob(self)) {
return NULL;
}
int rc = inner_write(self, data->buf, data->len, self->offset);
if (rc < 0) {
return NULL;
}
self->offset += (int)data->len;
Py_RETURN_NONE;
}
static PyObject *
blob_seek_impl(pysqlite_Blob *self, int offset, int origin)
{
if (!check_blob(self)) {
return NULL;
}
int blob_len = sqlite3_blob_bytes(self->blob);
switch (origin) {
case SEEK_SET:
break;
case SEEK_CUR:
if (offset > INT_MAX - self->offset) {
goto overflow;
}
offset += self->offset;
break;
case SEEK_END:
if (offset > INT_MAX - blob_len) {
goto overflow;
}
offset += blob_len;
break;
default:
PyErr_SetString(PyExc_ValueError,
"'origin' should be os.SEEK_SET, os.SEEK_CUR, or "
"os.SEEK_END");
return NULL;
}
if (offset < 0 || offset > blob_len) {
PyErr_SetString(PyExc_ValueError, "offset out of blob range");
return NULL;
}
self->offset = offset;
Py_RETURN_NONE;
overflow:
PyErr_SetString(PyExc_OverflowError, "seek offset results in overflow");
return NULL;
}
static PyObject *
blob_tell_impl(pysqlite_Blob *self)
{
if (!check_blob(self)) {
return NULL;
}
return PyLong_FromLong(self->offset);
}
static PyObject *
blob_enter_impl(pysqlite_Blob *self)
{
if (!check_blob(self)) {
return NULL;
}
return Py_NewRef(self);
}
static PyObject *
blob_exit_impl(pysqlite_Blob *self, PyObject *type, PyObject *val,
PyObject *tb)
{
if (!check_blob(self)) {
return NULL;
}
close_blob(self);
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 read_single(self, 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 read_multiple(self, len, start);
}
PyObject *blob = read_multiple(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;
}
if (!PyLong_Check(value)) {
PyErr_Format(PyExc_TypeError,
"'%s' object cannot be interpreted as an integer",
Py_TYPE(value)->tp_name);
return -1;
}
Py_ssize_t i = get_subscript_index(self, item);
if (i < 0) {
return -1;
}
long val = PyLong_AsLong(value);
if (val == -1 && PyErr_Occurred()) {
PyErr_Clear();
val = -1;
}
if (val < 0 || val > 255) {
PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)");
return -1;
}
unsigned char byte = (unsigned char)val;
return inner_write(self, (const void *)&byte, 1, i);
}
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 = read_multiple(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
BLOB_ENTER_METHODDEF
BLOB_EXIT_METHODDEF
BLOB_READ_METHODDEF
BLOB_SEEK_METHODDEF
BLOB_TELL_METHODDEF
BLOB_WRITE_METHODDEF
{NULL, NULL}
};
static struct PyMemberDef blob_members[] = {
{"__weaklistoffset__", T_PYSSIZET, offsetof(pysqlite_Blob, in_weakreflist), READONLY},
{NULL},
};
static PyType_Slot blob_slots[] = {
{Py_tp_dealloc, blob_dealloc},
{Py_tp_traverse, blob_traverse},
{Py_tp_clear, blob_clear},
{Py_tp_methods, blob_methods},
{Py_tp_members, blob_members},
{Py_mp_length, blob_length},
{Py_mp_subscript, blob_subscript},
{Py_mp_ass_subscript, blob_ass_subscript},
{0, NULL},
};
static PyType_Spec blob_spec = {
.name = MODULE_NAME ".Blob",
.basicsize = sizeof(pysqlite_Blob),
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION),
.slots = blob_slots,
};
int
pysqlite_blob_setup_types(PyObject *mod)
{
PyObject *type = PyType_FromModuleAndSpec(mod, &blob_spec, NULL);
if (type == NULL) {
return -1;
}
pysqlite_state *state = pysqlite_get_state(mod);
state->BlobType = (PyTypeObject *)type;
return 0;
}