/Users/erlendaasland/src/cpython.git/Modules/_sqlite/blob.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "blob.h" |
2 | | #include "util.h" |
3 | | |
4 | | #define clinic_state() (pysqlite_get_state_by_type(Py_TYPE(self))) |
5 | | #include "clinic/blob.c.h" |
6 | | #undef clinic_state |
7 | | |
8 | | /*[clinic input] |
9 | | module _sqlite3 |
10 | | class _sqlite3.Blob "pysqlite_Blob *" "clinic_state()->BlobType" |
11 | | [clinic start generated code]*/ |
12 | | /*[clinic end generated code: output=da39a3ee5e6b4b0d input=908d3e16a45f8da7]*/ |
13 | | |
14 | | static void |
15 | | close_blob(pysqlite_Blob *self) |
16 | 123 | { |
17 | 123 | if (self->blob) { |
18 | 42 | sqlite3_blob *blob = self->blob; |
19 | 42 | self->blob = NULL; |
20 | | |
21 | 42 | Py_BEGIN_ALLOW_THREADS |
22 | 42 | sqlite3_blob_close(blob); |
23 | 42 | Py_END_ALLOW_THREADS |
24 | 42 | } |
25 | 123 | } |
26 | | |
27 | | static int |
28 | | blob_traverse(pysqlite_Blob *self, visitproc visit, void *arg) |
29 | 42 | { |
30 | 42 | Py_VISIT(Py_TYPE(self)); |
31 | 42 | Py_VISIT(self->connection); |
32 | 42 | return 0; |
33 | 42 | } |
34 | | |
35 | | static int |
36 | | blob_clear(pysqlite_Blob *self) |
37 | 42 | { |
38 | 42 | Py_CLEAR(self->connection); |
39 | 42 | return 0; |
40 | 42 | } |
41 | | |
42 | | static void |
43 | | blob_dealloc(pysqlite_Blob *self) |
44 | 42 | { |
45 | 42 | PyTypeObject *tp = Py_TYPE(self); |
46 | 42 | PyObject_GC_UnTrack(self); |
47 | | |
48 | 42 | close_blob(self); |
49 | | |
50 | 42 | if (self->in_weakreflist != NULL) { |
51 | 42 | PyObject_ClearWeakRefs((PyObject*)self); |
52 | 42 | } |
53 | 42 | tp->tp_clear((PyObject *)self); |
54 | 42 | tp->tp_free(self); |
55 | 42 | Py_DECREF(tp); |
56 | 42 | } |
57 | | |
58 | | // Return 1 if the blob object is usable, 0 if not. |
59 | | static int |
60 | | check_blob(pysqlite_Blob *self) |
61 | 103 | { |
62 | 103 | if (!pysqlite_check_connection(self->connection) || |
63 | 103 | !pysqlite_check_thread(self->connection)) { |
64 | 1 | return 0; |
65 | 1 | } |
66 | 102 | if (self->blob == NULL) { |
67 | 11 | pysqlite_state *state = self->connection->state; |
68 | 11 | PyErr_SetString(state->ProgrammingError, |
69 | 11 | "Cannot operate on a closed blob."); |
70 | 11 | return 0; |
71 | 11 | } |
72 | 91 | return 1; |
73 | 102 | } |
74 | | |
75 | | |
76 | | /*[clinic input] |
77 | | _sqlite3.Blob.close as blob_close |
78 | | |
79 | | Close the blob. |
80 | | [clinic start generated code]*/ |
81 | | |
82 | | static PyObject * |
83 | | blob_close_impl(pysqlite_Blob *self) |
84 | | /*[clinic end generated code: output=848accc20a138d1b input=7bc178a402a40bd8]*/ |
85 | 39 | { |
86 | 39 | if (!pysqlite_check_connection(self->connection) || |
87 | 39 | !pysqlite_check_thread(self->connection)) |
88 | 0 | { |
89 | 0 | return NULL; |
90 | 0 | } |
91 | 39 | close_blob(self); |
92 | 39 | Py_RETURN_NONE; |
93 | 39 | }; |
94 | | |
95 | | void |
96 | | pysqlite_close_all_blobs(pysqlite_Connection *self) |
97 | 388 | { |
98 | 431 | for (int i = 0; i < PyList_GET_SIZE(self->blobs); i++) { |
99 | 43 | PyObject *weakref = PyList_GET_ITEM(self->blobs, i); |
100 | 0 | PyObject *blob = PyWeakref_GetObject(weakref); |
101 | 43 | if (!Py_IsNone(blob)) { |
102 | 40 | close_blob((pysqlite_Blob *)blob); |
103 | 40 | } |
104 | 43 | } |
105 | 388 | } |
106 | | |
107 | | static void |
108 | | blob_seterror(pysqlite_Blob *self, int rc) |
109 | 4 | { |
110 | 4 | assert(self->connection != NULL); |
111 | | #if SQLITE_VERSION_NUMBER < 3008008 |
112 | | // SQLite pre 3.8.8 does not set this blob error on the connection |
113 | | if (rc == SQLITE_ABORT) { |
114 | | PyErr_SetString(self->connection->OperationalError, |
115 | | "Cannot operate on an expired blob handle"); |
116 | | return; |
117 | | } |
118 | | #endif |
119 | 0 | _pysqlite_seterror(self->connection->state, self->connection->db); |
120 | 4 | } |
121 | | |
122 | | static PyObject * |
123 | | read_single(pysqlite_Blob *self, Py_ssize_t offset) |
124 | 7 | { |
125 | 7 | unsigned char buf = 0; |
126 | 7 | int rc; |
127 | 7 | Py_BEGIN_ALLOW_THREADS |
128 | 7 | rc = sqlite3_blob_read(self->blob, (void *)&buf, 1, (int)offset); |
129 | 7 | Py_END_ALLOW_THREADS |
130 | | |
131 | 7 | if (rc != SQLITE_OK) { |
132 | 1 | blob_seterror(self, rc); |
133 | 1 | return NULL; |
134 | 1 | } |
135 | 6 | return PyLong_FromUnsignedLong((unsigned long)buf); |
136 | 7 | } |
137 | | |
138 | | static PyObject * |
139 | | read_multiple(pysqlite_Blob *self, Py_ssize_t length, Py_ssize_t offset) |
140 | 15 | { |
141 | 15 | assert(length <= sqlite3_blob_bytes(self->blob)); |
142 | 0 | assert(offset < sqlite3_blob_bytes(self->blob)); |
143 | | |
144 | 0 | PyObject *buffer = PyBytes_FromStringAndSize(NULL, length); |
145 | 15 | if (buffer == NULL) { |
146 | 0 | return NULL; |
147 | 0 | } |
148 | | |
149 | 15 | char *raw_buffer = PyBytes_AS_STRING(buffer); |
150 | 15 | int rc; |
151 | 15 | Py_BEGIN_ALLOW_THREADS |
152 | 15 | rc = sqlite3_blob_read(self->blob, raw_buffer, (int)length, (int)offset); |
153 | 15 | Py_END_ALLOW_THREADS |
154 | | |
155 | 15 | if (rc != SQLITE_OK) { |
156 | 1 | Py_DECREF(buffer); |
157 | 1 | blob_seterror(self, rc); |
158 | 1 | return NULL; |
159 | 1 | } |
160 | 14 | return buffer; |
161 | 15 | } |
162 | | |
163 | | |
164 | | /*[clinic input] |
165 | | _sqlite3.Blob.read as blob_read |
166 | | |
167 | | length: int = -1 |
168 | | Read length in bytes. |
169 | | / |
170 | | |
171 | | Read data at the current offset position. |
172 | | |
173 | | If the end of the blob is reached, the data up to end of file will be returned. |
174 | | When length is not specified, or is negative, Blob.read() will read until the |
175 | | end of the blob. |
176 | | [clinic start generated code]*/ |
177 | | |
178 | | static PyObject * |
179 | | blob_read_impl(pysqlite_Blob *self, int length) |
180 | | /*[clinic end generated code: output=1fc99b2541360dde input=f2e4aa4378837250]*/ |
181 | 10 | { |
182 | 10 | if (!check_blob(self)) { |
183 | 3 | return NULL; |
184 | 3 | } |
185 | | |
186 | | /* Make sure we never read past "EOB". Also read the rest of the blob if a |
187 | | * negative length is specified. */ |
188 | 7 | int blob_len = sqlite3_blob_bytes(self->blob); |
189 | 7 | int max_read_len = blob_len - self->offset; |
190 | 7 | if (length < 0 || length > max_read_len) { |
191 | 5 | length = max_read_len; |
192 | 5 | } |
193 | | |
194 | 7 | assert(length >= 0); |
195 | 7 | if (length == 0) { |
196 | 1 | return PyBytes_FromStringAndSize(NULL, 0); |
197 | 1 | } |
198 | | |
199 | 6 | PyObject *buffer = read_multiple(self, length, self->offset); |
200 | 6 | if (buffer == NULL) { |
201 | 1 | return NULL; |
202 | 1 | } |
203 | 5 | self->offset += length; |
204 | 5 | return buffer; |
205 | 6 | }; |
206 | | |
207 | | static int |
208 | | inner_write(pysqlite_Blob *self, const void *buf, Py_ssize_t len, |
209 | | Py_ssize_t offset) |
210 | 19 | { |
211 | 19 | Py_ssize_t blob_len = sqlite3_blob_bytes(self->blob); |
212 | 19 | Py_ssize_t remaining_len = blob_len - offset; |
213 | 19 | if (len > remaining_len) { |
214 | 2 | PyErr_SetString(PyExc_ValueError, "data longer than blob length"); |
215 | 2 | return -1; |
216 | 2 | } |
217 | | |
218 | 17 | assert(offset <= blob_len); |
219 | 0 | int rc; |
220 | 17 | Py_BEGIN_ALLOW_THREADS |
221 | 17 | rc = sqlite3_blob_write(self->blob, buf, (int)len, (int)offset); |
222 | 17 | Py_END_ALLOW_THREADS |
223 | | |
224 | 17 | if (rc != SQLITE_OK) { |
225 | 2 | blob_seterror(self, rc); |
226 | 2 | return -1; |
227 | 2 | } |
228 | 15 | return 0; |
229 | 17 | } |
230 | | |
231 | | |
232 | | /*[clinic input] |
233 | | _sqlite3.Blob.write as blob_write |
234 | | |
235 | | data: Py_buffer |
236 | | / |
237 | | |
238 | | Write data at the current offset. |
239 | | |
240 | | This function cannot change the blob length. Writing beyond the end of the |
241 | | blob will result in an exception being raised. |
242 | | [clinic start generated code]*/ |
243 | | |
244 | | static PyObject * |
245 | | blob_write_impl(pysqlite_Blob *self, Py_buffer *data) |
246 | | /*[clinic end generated code: output=b34cf22601b570b2 input=a84712f24a028e6d]*/ |
247 | 11 | { |
248 | 11 | if (!check_blob(self)) { |
249 | 1 | return NULL; |
250 | 1 | } |
251 | | |
252 | 10 | int rc = inner_write(self, data->buf, data->len, self->offset); |
253 | 10 | if (rc < 0) { |
254 | 4 | return NULL; |
255 | 4 | } |
256 | 6 | self->offset += (int)data->len; |
257 | 6 | Py_RETURN_NONE; |
258 | 10 | } |
259 | | |
260 | | |
261 | | /*[clinic input] |
262 | | _sqlite3.Blob.seek as blob_seek |
263 | | |
264 | | offset: int |
265 | | origin: int = 0 |
266 | | / |
267 | | |
268 | | Set the current access position to offset. |
269 | | |
270 | | The origin argument defaults to os.SEEK_SET (absolute blob positioning). |
271 | | Other values for origin are os.SEEK_CUR (seek relative to the current position) |
272 | | and os.SEEK_END (seek relative to the blob's end). |
273 | | [clinic start generated code]*/ |
274 | | |
275 | | static PyObject * |
276 | | blob_seek_impl(pysqlite_Blob *self, int offset, int origin) |
277 | | /*[clinic end generated code: output=854c5a0e208547a5 input=5da9a07e55fe6bb6]*/ |
278 | 17 | { |
279 | 17 | if (!check_blob(self)) { |
280 | 1 | return NULL; |
281 | 1 | } |
282 | | |
283 | 16 | int blob_len = sqlite3_blob_bytes(self->blob); |
284 | 16 | switch (origin) { |
285 | 9 | case SEEK_SET: |
286 | 9 | break; |
287 | 2 | case SEEK_CUR: |
288 | 2 | if (offset > INT_MAX - self->offset) { |
289 | 1 | goto overflow; |
290 | 1 | } |
291 | 1 | offset += self->offset; |
292 | 1 | break; |
293 | 3 | case SEEK_END: |
294 | 3 | if (offset > INT_MAX - blob_len) { |
295 | 1 | goto overflow; |
296 | 1 | } |
297 | 2 | offset += blob_len; |
298 | 2 | break; |
299 | 2 | default: |
300 | 2 | PyErr_SetString(PyExc_ValueError, |
301 | 2 | "'origin' should be os.SEEK_SET, os.SEEK_CUR, or " |
302 | 2 | "os.SEEK_END"); |
303 | 2 | return NULL; |
304 | 16 | } |
305 | | |
306 | 12 | if (offset < 0 || offset > blob_len) { |
307 | 2 | PyErr_SetString(PyExc_ValueError, "offset out of blob range"); |
308 | 2 | return NULL; |
309 | 2 | } |
310 | | |
311 | 10 | self->offset = offset; |
312 | 10 | Py_RETURN_NONE; |
313 | | |
314 | 2 | overflow: |
315 | 2 | PyErr_SetString(PyExc_OverflowError, "seek offset results in overflow"); |
316 | 2 | return NULL; |
317 | 12 | } |
318 | | |
319 | | |
320 | | /*[clinic input] |
321 | | _sqlite3.Blob.tell as blob_tell |
322 | | |
323 | | Return the current access position for the blob. |
324 | | [clinic start generated code]*/ |
325 | | |
326 | | static PyObject * |
327 | | blob_tell_impl(pysqlite_Blob *self) |
328 | | /*[clinic end generated code: output=3d3ba484a90b3a99 input=7e34057aa303612c]*/ |
329 | 7 | { |
330 | 7 | if (!check_blob(self)) { |
331 | 1 | return NULL; |
332 | 1 | } |
333 | 6 | return PyLong_FromLong(self->offset); |
334 | 7 | } |
335 | | |
336 | | |
337 | | /*[clinic input] |
338 | | _sqlite3.Blob.__enter__ as blob_enter |
339 | | |
340 | | Blob context manager enter. |
341 | | [clinic start generated code]*/ |
342 | | |
343 | | static PyObject * |
344 | | blob_enter_impl(pysqlite_Blob *self) |
345 | | /*[clinic end generated code: output=4fd32484b071a6cd input=fe4842c3c582d5a7]*/ |
346 | 3 | { |
347 | 3 | if (!check_blob(self)) { |
348 | 1 | return NULL; |
349 | 1 | } |
350 | 2 | return Py_NewRef(self); |
351 | 3 | } |
352 | | |
353 | | |
354 | | /*[clinic input] |
355 | | _sqlite3.Blob.__exit__ as blob_exit |
356 | | |
357 | | type: object |
358 | | val: object |
359 | | tb: object |
360 | | / |
361 | | |
362 | | Blob context manager exit. |
363 | | [clinic start generated code]*/ |
364 | | |
365 | | static PyObject * |
366 | | blob_exit_impl(pysqlite_Blob *self, PyObject *type, PyObject *val, |
367 | | PyObject *tb) |
368 | | /*[clinic end generated code: output=fc86ceeb2b68c7b2 input=575d9ecea205f35f]*/ |
369 | 3 | { |
370 | 3 | if (!check_blob(self)) { |
371 | 1 | return NULL; |
372 | 1 | } |
373 | 2 | close_blob(self); |
374 | 2 | Py_RETURN_FALSE; |
375 | 3 | } |
376 | | |
377 | | static Py_ssize_t |
378 | | blob_length(pysqlite_Blob *self) |
379 | 4 | { |
380 | 4 | if (!check_blob(self)) { |
381 | 1 | return -1; |
382 | 1 | } |
383 | 3 | return sqlite3_blob_bytes(self->blob); |
384 | 4 | }; |
385 | | |
386 | | static Py_ssize_t |
387 | | get_subscript_index(pysqlite_Blob *self, PyObject *item) |
388 | 19 | { |
389 | 19 | Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); |
390 | 19 | if (i == -1 && PyErr_Occurred()) { |
391 | 1 | return -1; |
392 | 1 | } |
393 | 18 | int blob_len = sqlite3_blob_bytes(self->blob); |
394 | 18 | if (i < 0) { |
395 | 5 | i += blob_len; |
396 | 5 | } |
397 | 18 | if (i < 0 || i >= blob_len) { |
398 | 4 | PyErr_SetString(PyExc_IndexError, "Blob index out of range"); |
399 | 4 | return -1; |
400 | 4 | } |
401 | 14 | return i; |
402 | 18 | } |
403 | | |
404 | | static PyObject * |
405 | | subscript_index(pysqlite_Blob *self, PyObject *item) |
406 | 11 | { |
407 | 11 | Py_ssize_t i = get_subscript_index(self, item); |
408 | 11 | if (i < 0) { |
409 | 4 | return NULL; |
410 | 4 | } |
411 | 7 | return read_single(self, i); |
412 | 11 | } |
413 | | |
414 | | static int |
415 | | get_slice_info(pysqlite_Blob *self, PyObject *item, Py_ssize_t *start, |
416 | | Py_ssize_t *stop, Py_ssize_t *step, Py_ssize_t *slicelen) |
417 | 19 | { |
418 | 19 | if (PySlice_Unpack(item, start, stop, step) < 0) { |
419 | 2 | return -1; |
420 | 2 | } |
421 | 17 | int len = sqlite3_blob_bytes(self->blob); |
422 | 17 | *slicelen = PySlice_AdjustIndices(len, start, stop, *step); |
423 | 17 | return 0; |
424 | 19 | } |
425 | | |
426 | | static PyObject * |
427 | | subscript_slice(pysqlite_Blob *self, PyObject *item) |
428 | 9 | { |
429 | 9 | Py_ssize_t start, stop, step, len; |
430 | 9 | if (get_slice_info(self, item, &start, &stop, &step, &len) < 0) { |
431 | 1 | return NULL; |
432 | 1 | } |
433 | | |
434 | 8 | if (step == 1) { |
435 | 7 | return read_multiple(self, len, start); |
436 | 7 | } |
437 | 1 | PyObject *blob = read_multiple(self, stop - start, start); |
438 | 1 | if (blob == NULL) { |
439 | 0 | return NULL; |
440 | 0 | } |
441 | 1 | PyObject *result = PyBytes_FromStringAndSize(NULL, len); |
442 | 1 | if (result != NULL) { |
443 | 1 | char *blob_buf = PyBytes_AS_STRING(blob); |
444 | 1 | char *res_buf = PyBytes_AS_STRING(result); |
445 | 6 | for (Py_ssize_t i = 0, j = 0; i < len; i++, j += step) { |
446 | 5 | res_buf[i] = blob_buf[j]; |
447 | 5 | } |
448 | 1 | Py_DECREF(blob); |
449 | 1 | } |
450 | 1 | return result; |
451 | 1 | } |
452 | | |
453 | | static PyObject * |
454 | | blob_subscript(pysqlite_Blob *self, PyObject *item) |
455 | 23 | { |
456 | 23 | if (!check_blob(self)) { |
457 | 2 | return NULL; |
458 | 2 | } |
459 | | |
460 | 21 | if (PyIndex_Check(item)) { |
461 | 11 | return subscript_index(self, item); |
462 | 11 | } |
463 | 10 | if (PySlice_Check(item)) { |
464 | 9 | return subscript_slice(self, item); |
465 | 9 | } |
466 | | |
467 | 1 | PyErr_SetString(PyExc_TypeError, "Blob indices must be integers"); |
468 | 1 | return NULL; |
469 | 10 | } |
470 | | |
471 | | static int |
472 | | ass_subscript_index(pysqlite_Blob *self, PyObject *item, PyObject *value) |
473 | 12 | { |
474 | 12 | if (value == NULL) { |
475 | 1 | PyErr_SetString(PyExc_TypeError, |
476 | 1 | "Blob doesn't support item deletion"); |
477 | 1 | return -1; |
478 | 1 | } |
479 | 11 | if (!PyLong_Check(value)) { |
480 | 3 | PyErr_Format(PyExc_TypeError, |
481 | 3 | "'%s' object cannot be interpreted as an integer", |
482 | 3 | Py_TYPE(value)->tp_name); |
483 | 3 | return -1; |
484 | 3 | } |
485 | 8 | Py_ssize_t i = get_subscript_index(self, item); |
486 | 8 | if (i < 0) { |
487 | 1 | return -1; |
488 | 1 | } |
489 | | |
490 | 7 | long val = PyLong_AsLong(value); |
491 | 7 | if (val == -1 && PyErr_Occurred()) { |
492 | 1 | PyErr_Clear(); |
493 | 1 | val = -1; |
494 | 1 | } |
495 | 7 | if (val < 0 || val > 255) { |
496 | 3 | PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)"); |
497 | 3 | return -1; |
498 | 3 | } |
499 | | // Downcast to avoid endianness problems. |
500 | 4 | unsigned char byte = (unsigned char)val; |
501 | 4 | return inner_write(self, (const void *)&byte, 1, i); |
502 | 7 | } |
503 | | |
504 | | static int |
505 | | ass_subscript_slice(pysqlite_Blob *self, PyObject *item, PyObject *value) |
506 | 11 | { |
507 | 11 | if (value == NULL) { |
508 | 1 | PyErr_SetString(PyExc_TypeError, |
509 | 1 | "Blob doesn't support slice deletion"); |
510 | 1 | return -1; |
511 | 1 | } |
512 | | |
513 | 10 | Py_ssize_t start, stop, step, len; |
514 | 10 | if (get_slice_info(self, item, &start, &stop, &step, &len) < 0) { |
515 | 1 | return -1; |
516 | 1 | } |
517 | | |
518 | 9 | if (len == 0) { |
519 | 1 | return 0; |
520 | 1 | } |
521 | | |
522 | 8 | Py_buffer vbuf; |
523 | 8 | if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0) { |
524 | 1 | return -1; |
525 | 1 | } |
526 | | |
527 | 7 | int rc = -1; |
528 | 7 | if (vbuf.len != len) { |
529 | 2 | PyErr_SetString(PyExc_IndexError, |
530 | 2 | "Blob slice assignment is wrong size"); |
531 | 2 | } |
532 | 5 | else if (step == 1) { |
533 | 4 | rc = inner_write(self, vbuf.buf, len, start); |
534 | 4 | } |
535 | 1 | else { |
536 | 1 | PyObject *blob_bytes = read_multiple(self, stop - start, start); |
537 | 1 | if (blob_bytes != NULL) { |
538 | 1 | char *blob_buf = PyBytes_AS_STRING(blob_bytes); |
539 | 6 | for (Py_ssize_t i = 0, j = 0; i < len; i++, j += step) { |
540 | 5 | blob_buf[j] = ((char *)vbuf.buf)[i]; |
541 | 5 | } |
542 | 1 | rc = inner_write(self, blob_buf, stop - start, start); |
543 | 1 | Py_DECREF(blob_bytes); |
544 | 1 | } |
545 | 1 | } |
546 | 7 | PyBuffer_Release(&vbuf); |
547 | 7 | return rc; |
548 | 8 | } |
549 | | |
550 | | static int |
551 | | blob_ass_subscript(pysqlite_Blob *self, PyObject *item, PyObject *value) |
552 | 25 | { |
553 | 25 | if (!check_blob(self)) { |
554 | 1 | return -1; |
555 | 1 | } |
556 | | |
557 | 24 | if (PyIndex_Check(item)) { |
558 | 12 | return ass_subscript_index(self, item, value); |
559 | 12 | } |
560 | 12 | if (PySlice_Check(item)) { |
561 | 11 | return ass_subscript_slice(self, item, value); |
562 | 11 | } |
563 | | |
564 | 1 | PyErr_SetString(PyExc_TypeError, "Blob indices must be integers"); |
565 | 1 | return -1; |
566 | 12 | } |
567 | | |
568 | | |
569 | | static PyMethodDef blob_methods[] = { |
570 | | BLOB_CLOSE_METHODDEF |
571 | | BLOB_ENTER_METHODDEF |
572 | | BLOB_EXIT_METHODDEF |
573 | | BLOB_READ_METHODDEF |
574 | | BLOB_SEEK_METHODDEF |
575 | | BLOB_TELL_METHODDEF |
576 | | BLOB_WRITE_METHODDEF |
577 | | {NULL, NULL} |
578 | | }; |
579 | | |
580 | | static struct PyMemberDef blob_members[] = { |
581 | | {"__weaklistoffset__", T_PYSSIZET, offsetof(pysqlite_Blob, in_weakreflist), READONLY}, |
582 | | {NULL}, |
583 | | }; |
584 | | |
585 | | static PyType_Slot blob_slots[] = { |
586 | | {Py_tp_dealloc, blob_dealloc}, |
587 | | {Py_tp_traverse, blob_traverse}, |
588 | | {Py_tp_clear, blob_clear}, |
589 | | {Py_tp_methods, blob_methods}, |
590 | | {Py_tp_members, blob_members}, |
591 | | |
592 | | // Mapping protocol |
593 | | {Py_mp_length, blob_length}, |
594 | | {Py_mp_subscript, blob_subscript}, |
595 | | {Py_mp_ass_subscript, blob_ass_subscript}, |
596 | | {0, NULL}, |
597 | | }; |
598 | | |
599 | | static PyType_Spec blob_spec = { |
600 | | .name = MODULE_NAME ".Blob", |
601 | | .basicsize = sizeof(pysqlite_Blob), |
602 | | .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | |
603 | | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION), |
604 | | .slots = blob_slots, |
605 | | }; |
606 | | |
607 | | int |
608 | | pysqlite_blob_setup_types(PyObject *mod) |
609 | 1 | { |
610 | 1 | PyObject *type = PyType_FromModuleAndSpec(mod, &blob_spec, NULL); |
611 | 1 | if (type == NULL) { |
612 | 0 | return -1; |
613 | 0 | } |
614 | 1 | pysqlite_state *state = pysqlite_get_state(mod); |
615 | 1 | state->BlobType = (PyTypeObject *)type; |
616 | 1 | return 0; |
617 | 1 | } |