| 
									
										
										
										
											2023-06-22 22:31:31 +02:00
										 |  |  | #ifndef Py_BUILD_CORE_BUILTIN
 | 
					
						
							|  |  |  | #  define Py_BUILD_CORE_MODULE 1
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  | #include "blob.h"
 | 
					
						
							|  |  |  | #include "util.h"
 | 
					
						
							| 
									
										
										
										
											2023-06-22 22:31:31 +02:00
										 |  |  | #include "pycore_weakref.h"       // _PyWeakref_GET_REF()
 | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define clinic_state() (pysqlite_get_state_by_type(Py_TYPE(self)))
 | 
					
						
							|  |  |  | #include "clinic/blob.c.h"
 | 
					
						
							|  |  |  | #undef clinic_state
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*[clinic input]
 | 
					
						
							|  |  |  | module _sqlite3 | 
					
						
							|  |  |  | class _sqlite3.Blob "pysqlite_Blob *" "clinic_state()->BlobType" | 
					
						
							|  |  |  | [clinic start generated code]*/ | 
					
						
							|  |  |  | /*[clinic end generated code: output=da39a3ee5e6b4b0d input=908d3e16a45f8da7]*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Return 1 if the blob object is usable, 0 if not.
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*[clinic input]
 | 
					
						
							|  |  |  | _sqlite3.Blob.close as blob_close | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Close the blob. | 
					
						
							|  |  |  | [clinic start generated code]*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | blob_close_impl(pysqlite_Blob *self) | 
					
						
							|  |  |  | /*[clinic end generated code: output=848accc20a138d1b input=7bc178a402a40bd8]*/ | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     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); | 
					
						
							| 
									
										
										
										
											2023-06-22 22:31:31 +02:00
										 |  |  |         PyObject *blob = _PyWeakref_GET_REF(weakref); | 
					
						
							|  |  |  |         if (blob == NULL) { | 
					
						
							|  |  |  |             continue; | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-06-22 22:31:31 +02:00
										 |  |  |         close_blob((pysqlite_Blob *)blob); | 
					
						
							|  |  |  |         Py_DECREF(blob); | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | blob_seterror(pysqlite_Blob *self, int rc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(self->connection != NULL); | 
					
						
							|  |  |  |     _pysqlite_seterror(self->connection->state, self->connection->db); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2022-04-30 09:01:37 -06:00
										 |  |  | 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) | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  |     assert(length <= sqlite3_blob_bytes(self->blob)); | 
					
						
							| 
									
										
										
										
											2022-04-30 09:01:37 -06:00
										 |  |  |     assert(offset < sqlite3_blob_bytes(self->blob)); | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  |     PyObject *buffer = PyBytes_FromStringAndSize(NULL, length); | 
					
						
							|  |  |  |     if (buffer == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     char *raw_buffer = PyBytes_AS_STRING(buffer); | 
					
						
							|  |  |  |     int rc; | 
					
						
							|  |  |  |     Py_BEGIN_ALLOW_THREADS | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  |     rc = sqlite3_blob_read(self->blob, raw_buffer, (int)length, (int)offset); | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  |     Py_END_ALLOW_THREADS | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (rc != SQLITE_OK) { | 
					
						
							|  |  |  |         Py_DECREF(buffer); | 
					
						
							|  |  |  |         blob_seterror(self, rc); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return buffer; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*[clinic input]
 | 
					
						
							|  |  |  | _sqlite3.Blob.read as blob_read | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     length: int = -1 | 
					
						
							|  |  |  |         Read length in bytes. | 
					
						
							|  |  |  |     / | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Read data at the current offset position. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | If the end of the blob is reached, the data up to end of file will be returned. | 
					
						
							|  |  |  | When length is not specified, or is negative, Blob.read() will read until the | 
					
						
							|  |  |  | end of the blob. | 
					
						
							|  |  |  | [clinic start generated code]*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | blob_read_impl(pysqlite_Blob *self, int length) | 
					
						
							|  |  |  | /*[clinic end generated code: output=1fc99b2541360dde input=f2e4aa4378837250]*/ | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!check_blob(self)) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Make sure we never read past "EOB". Also read the rest of the blob if a
 | 
					
						
							|  |  |  |      * negative length is specified. */ | 
					
						
							|  |  |  |     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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-30 09:01:37 -06:00
										 |  |  |     assert(length >= 0); | 
					
						
							|  |  |  |     if (length == 0) { | 
					
						
							|  |  |  |         return PyBytes_FromStringAndSize(NULL, 0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PyObject *buffer = read_multiple(self, length, self->offset); | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  |     if (buffer == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     self->offset += length; | 
					
						
							|  |  |  |     return buffer; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  | inner_write(pysqlite_Blob *self, const void *buf, Py_ssize_t len, | 
					
						
							|  |  |  |             Py_ssize_t offset) | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  |     Py_ssize_t blob_len = sqlite3_blob_bytes(self->blob); | 
					
						
							|  |  |  |     Py_ssize_t remaining_len = blob_len - offset; | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  |     if (len > remaining_len) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_ValueError, "data longer than blob length"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  |     assert(offset <= blob_len); | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  |     int rc; | 
					
						
							|  |  |  |     Py_BEGIN_ALLOW_THREADS | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  |     rc = sqlite3_blob_write(self->blob, buf, (int)len, (int)offset); | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  |     Py_END_ALLOW_THREADS | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (rc != SQLITE_OK) { | 
					
						
							|  |  |  |         blob_seterror(self, rc); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*[clinic input]
 | 
					
						
							|  |  |  | _sqlite3.Blob.write as blob_write | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     data: Py_buffer | 
					
						
							|  |  |  |     / | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Write data at the current offset. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This function cannot change the blob length.  Writing beyond the end of the | 
					
						
							|  |  |  | blob will result in an exception being raised. | 
					
						
							|  |  |  | [clinic start generated code]*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | blob_write_impl(pysqlite_Blob *self, Py_buffer *data) | 
					
						
							|  |  |  | /*[clinic end generated code: output=b34cf22601b570b2 input=a84712f24a028e6d]*/ | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*[clinic input]
 | 
					
						
							|  |  |  | _sqlite3.Blob.seek as blob_seek | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     offset: int | 
					
						
							|  |  |  |     origin: int = 0 | 
					
						
							|  |  |  |     / | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Set the current access position to offset. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The origin argument defaults to os.SEEK_SET (absolute blob positioning). | 
					
						
							|  |  |  | Other values for origin are os.SEEK_CUR (seek relative to the current position) | 
					
						
							|  |  |  | and os.SEEK_END (seek relative to the blob's end). | 
					
						
							|  |  |  | [clinic start generated code]*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | blob_seek_impl(pysqlite_Blob *self, int offset, int origin) | 
					
						
							|  |  |  | /*[clinic end generated code: output=854c5a0e208547a5 input=5da9a07e55fe6bb6]*/ | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*[clinic input]
 | 
					
						
							|  |  |  | _sqlite3.Blob.tell as blob_tell | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Return the current access position for the blob. | 
					
						
							|  |  |  | [clinic start generated code]*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | blob_tell_impl(pysqlite_Blob *self) | 
					
						
							|  |  |  | /*[clinic end generated code: output=3d3ba484a90b3a99 input=7e34057aa303612c]*/ | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!check_blob(self)) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return PyLong_FromLong(self->offset); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-16 06:21:12 +02:00
										 |  |  | /*[clinic input]
 | 
					
						
							|  |  |  | _sqlite3.Blob.__enter__ as blob_enter | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Blob context manager enter. | 
					
						
							|  |  |  | [clinic start generated code]*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | blob_enter_impl(pysqlite_Blob *self) | 
					
						
							|  |  |  | /*[clinic end generated code: output=4fd32484b071a6cd input=fe4842c3c582d5a7]*/ | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!check_blob(self)) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return Py_NewRef(self); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*[clinic input]
 | 
					
						
							|  |  |  | _sqlite3.Blob.__exit__ as blob_exit | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     type: object | 
					
						
							|  |  |  |     val: object | 
					
						
							|  |  |  |     tb: object | 
					
						
							|  |  |  |     / | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Blob context manager exit. | 
					
						
							|  |  |  | [clinic start generated code]*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | blob_exit_impl(pysqlite_Blob *self, PyObject *type, PyObject *val, | 
					
						
							|  |  |  |                PyObject *tb) | 
					
						
							|  |  |  | /*[clinic end generated code: output=fc86ceeb2b68c7b2 input=575d9ecea205f35f]*/ | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!check_blob(self)) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     close_blob(self); | 
					
						
							|  |  |  |     Py_RETURN_FALSE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  | 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; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-30 09:01:37 -06:00
										 |  |  |     return read_single(self, i); | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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) { | 
					
						
							| 
									
										
										
										
											2022-04-30 09:01:37 -06:00
										 |  |  |         return read_multiple(self, len, start); | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-30 09:01:37 -06:00
										 |  |  |     PyObject *blob = read_multiple(self, stop - start, start); | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  |     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; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-30 09:01:37 -06:00
										 |  |  |     if (!PyLong_Check(value)) { | 
					
						
							|  |  |  |         PyErr_Format(PyExc_TypeError, | 
					
						
							|  |  |  |                      "'%s' object cannot be interpreted as an integer", | 
					
						
							|  |  |  |                      Py_TYPE(value)->tp_name); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  |     Py_ssize_t i = get_subscript_index(self, item); | 
					
						
							|  |  |  |     if (i < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-30 09:01:37 -06:00
										 |  |  |     long val = PyLong_AsLong(value); | 
					
						
							|  |  |  |     if (val == -1 && PyErr_Occurred()) { | 
					
						
							|  |  |  |         PyErr_Clear(); | 
					
						
							|  |  |  |         val = -1; | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-30 09:01:37 -06:00
										 |  |  |     if (val < 0 || val > 255) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-30 09:01:37 -06:00
										 |  |  |     // Downcast to avoid endianness problems.
 | 
					
						
							|  |  |  |     unsigned char byte = (unsigned char)val; | 
					
						
							|  |  |  |     return inner_write(self, (const void *)&byte, 1, i); | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 { | 
					
						
							| 
									
										
										
										
											2022-04-30 09:01:37 -06:00
										 |  |  |         PyObject *blob_bytes = read_multiple(self, stop - start, start); | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  |         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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-16 06:21:12 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  | static PyMethodDef blob_methods[] = { | 
					
						
							|  |  |  |     BLOB_CLOSE_METHODDEF | 
					
						
							| 
									
										
										
										
											2022-04-16 06:21:12 +02:00
										 |  |  |     BLOB_ENTER_METHODDEF | 
					
						
							|  |  |  |     BLOB_EXIT_METHODDEF | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  |     BLOB_READ_METHODDEF | 
					
						
							|  |  |  |     BLOB_SEEK_METHODDEF | 
					
						
							|  |  |  |     BLOB_TELL_METHODDEF | 
					
						
							|  |  |  |     BLOB_WRITE_METHODDEF | 
					
						
							|  |  |  |     {NULL, NULL} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct PyMemberDef blob_members[] = { | 
					
						
							| 
									
										
										
										
											2023-07-25 15:28:30 +02:00
										 |  |  |     {"__weaklistoffset__", Py_T_PYSSIZET, offsetof(pysqlite_Blob, in_weakreflist), Py_READONLY}, | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  |     {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}, | 
					
						
							| 
									
										
										
										
											2022-04-22 03:45:16 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Mapping protocol
 | 
					
						
							|  |  |  |     {Py_mp_length, blob_length}, | 
					
						
							|  |  |  |     {Py_mp_subscript, blob_subscript}, | 
					
						
							|  |  |  |     {Py_mp_ass_subscript, blob_ass_subscript}, | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  |     {0, NULL}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyType_Spec blob_spec = { | 
					
						
							|  |  |  |     .name = MODULE_NAME ".Blob", | 
					
						
							|  |  |  |     .basicsize = sizeof(pysqlite_Blob), | 
					
						
							|  |  |  |     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | | 
					
						
							| 
									
										
										
										
											2022-04-15 18:25:03 +02:00
										 |  |  |               Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION), | 
					
						
							| 
									
										
										
										
											2022-04-15 02:02:56 +02:00
										 |  |  |     .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; | 
					
						
							|  |  |  | } |