from psycopg_c.pq cimport Escaping, _buffer_as_string_and_size
-from psycopg import errors as e
-
@cython.freelist(8)
cdef class CDumper:
return self
@staticmethod
- cdef char *ensure_size(bytearray ba, Py_ssize_t offset, Py_ssize_t size) except NULL:
+ cdef char *ensure_size(
+ bytearray ba, Py_ssize_t offset, Py_ssize_t size
+ ) except NULL:
"""
Grow *ba*, if necessary, to contains at least *size* bytes after *offset*
return 0
-cdef object _format_row_text(object row, Py_ssize_t rowlen, Transformer tx, bytearray out):
+cdef object _format_row_text(
+ object row, Py_ssize_t rowlen, Transformer tx, bytearray out
+):
# offset in 'out' where to write
cdef Py_ssize_t pos = PyByteArray_GET_SIZE(out)
# Improved in:
# https://github.com/linux-sunxi/sunxi-tools/blob/master/include/portable_endian.h
"""
+
// "License": Public Domain
-// I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like.
-// In case there are jurisdictions that don't support putting things in the public domain you can also consider it to
-// be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it
-// an example on how to get the endian conversion functions on different platforms.
+// I, Mathias Panzenböck, place this file hereby into the public domain.
+// Use it at your own risk for whatever you like.
+// In case there are jurisdictions that don't support putting things in the
+// public domain you can also consider it to be "dual licensed" under the BSD,
+// MIT and Apache licenses, if you want to.
+// This code is trivial anyway. Consider it an example on how to get the endian
+// conversion functions on different platforms.
#ifndef PORTABLE_ENDIAN_H__
#define PORTABLE_ENDIAN_H__
#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__)
-# define __WINDOWS__
+# define __WINDOWS__
#endif
#if defined(__linux__) || defined(__CYGWIN__)
-# include <endian.h>
+# include <endian.h>
#elif defined(__APPLE__)
-# include <libkern/OSByteOrder.h>
+# include <libkern/OSByteOrder.h>
-# define htobe16(x) OSSwapHostToBigInt16(x)
-# define htole16(x) OSSwapHostToLittleInt16(x)
-# define be16toh(x) OSSwapBigToHostInt16(x)
-# define le16toh(x) OSSwapLittleToHostInt16(x)
+# define htobe16(x) OSSwapHostToBigInt16(x)
+# define htole16(x) OSSwapHostToLittleInt16(x)
+# define be16toh(x) OSSwapBigToHostInt16(x)
+# define le16toh(x) OSSwapLittleToHostInt16(x)
-# define htobe32(x) OSSwapHostToBigInt32(x)
-# define htole32(x) OSSwapHostToLittleInt32(x)
-# define be32toh(x) OSSwapBigToHostInt32(x)
-# define le32toh(x) OSSwapLittleToHostInt32(x)
+# define htobe32(x) OSSwapHostToBigInt32(x)
+# define htole32(x) OSSwapHostToLittleInt32(x)
+# define be32toh(x) OSSwapBigToHostInt32(x)
+# define le32toh(x) OSSwapLittleToHostInt32(x)
-# define htobe64(x) OSSwapHostToBigInt64(x)
-# define htole64(x) OSSwapHostToLittleInt64(x)
-# define be64toh(x) OSSwapBigToHostInt64(x)
-# define le64toh(x) OSSwapLittleToHostInt64(x)
+# define htobe64(x) OSSwapHostToBigInt64(x)
+# define htole64(x) OSSwapHostToLittleInt64(x)
+# define be64toh(x) OSSwapBigToHostInt64(x)
+# define le64toh(x) OSSwapLittleToHostInt64(x)
-# define __BYTE_ORDER BYTE_ORDER
-# define __BIG_ENDIAN BIG_ENDIAN
-# define __LITTLE_ENDIAN LITTLE_ENDIAN
-# define __PDP_ENDIAN PDP_ENDIAN
+# define __BYTE_ORDER BYTE_ORDER
+# define __BIG_ENDIAN BIG_ENDIAN
+# define __LITTLE_ENDIAN LITTLE_ENDIAN
+# define __PDP_ENDIAN PDP_ENDIAN
-#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
+#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) \\
+ || defined(__DragonFly__)
-# include <sys/endian.h>
+# include <sys/endian.h>
/* For functions still missing, try to substitute 'historic' OpenBSD names */
#ifndef be16toh
-# define be16toh(x) betoh16(x)
+# define be16toh(x) betoh16(x)
#endif
#ifndef le16toh
-# define le16toh(x) letoh16(x)
+# define le16toh(x) letoh16(x)
#endif
#ifndef be32toh
-# define be32toh(x) betoh32(x)
+# define be32toh(x) betoh32(x)
#endif
#ifndef le32toh
-# define le32toh(x) letoh32(x)
+# define le32toh(x) letoh32(x)
#endif
#ifndef be64toh
-# define be64toh(x) betoh64(x)
+# define be64toh(x) betoh64(x)
#endif
#ifndef le64toh
-# define le64toh(x) letoh64(x)
+# define le64toh(x) letoh64(x)
#endif
#elif defined(__WINDOWS__)
-# include <winsock2.h>
+# include <winsock2.h>
# ifndef _MSC_VER
-# include <sys/param.h>
+# include <sys/param.h>
# endif
-# if BYTE_ORDER == LITTLE_ENDIAN
+# if BYTE_ORDER == LITTLE_ENDIAN
-# define htobe16(x) htons(x)
-# define htole16(x) (x)
-# define be16toh(x) ntohs(x)
-# define le16toh(x) (x)
+# define htobe16(x) htons(x)
+# define htole16(x) (x)
+# define be16toh(x) ntohs(x)
+# define le16toh(x) (x)
-# define htobe32(x) htonl(x)
-# define htole32(x) (x)
-# define be32toh(x) ntohl(x)
-# define le32toh(x) (x)
+# define htobe32(x) htonl(x)
+# define htole32(x) (x)
+# define be32toh(x) ntohl(x)
+# define le32toh(x) (x)
-# define htobe64(x) htonll(x)
-# define htole64(x) (x)
-# define be64toh(x) ntohll(x)
-# define le64toh(x) (x)
+# define htobe64(x) htonll(x)
+# define htole64(x) (x)
+# define be64toh(x) ntohll(x)
+# define le64toh(x) (x)
-# elif BYTE_ORDER == BIG_ENDIAN
+# elif BYTE_ORDER == BIG_ENDIAN
- /* that would be xbox 360 */
-# define htobe16(x) (x)
-# define htole16(x) __builtin_bswap16(x)
-# define be16toh(x) (x)
-# define le16toh(x) __builtin_bswap16(x)
+ /* that would be xbox 360 */
+# define htobe16(x) (x)
+# define htole16(x) __builtin_bswap16(x)
+# define be16toh(x) (x)
+# define le16toh(x) __builtin_bswap16(x)
-# define htobe32(x) (x)
-# define htole32(x) __builtin_bswap32(x)
-# define be32toh(x) (x)
-# define le32toh(x) __builtin_bswap32(x)
+# define htobe32(x) (x)
+# define htole32(x) __builtin_bswap32(x)
+# define be32toh(x) (x)
+# define le32toh(x) __builtin_bswap32(x)
-# define htobe64(x) (x)
-# define htole64(x) __builtin_bswap64(x)
-# define be64toh(x) (x)
-# define le64toh(x) __builtin_bswap64(x)
+# define htobe64(x) (x)
+# define htole64(x) __builtin_bswap64(x)
+# define be64toh(x) (x)
+# define le64toh(x) __builtin_bswap64(x)
-# else
+# else
-# error byte order not supported
+# error byte order not supported
-# endif
+# endif
-# define __BYTE_ORDER BYTE_ORDER
-# define __BIG_ENDIAN BIG_ENDIAN
-# define __LITTLE_ENDIAN LITTLE_ENDIAN
-# define __PDP_ENDIAN PDP_ENDIAN
+# define __BYTE_ORDER BYTE_ORDER
+# define __BIG_ENDIAN BIG_ENDIAN
+# define __LITTLE_ENDIAN LITTLE_ENDIAN
+# define __PDP_ENDIAN PDP_ENDIAN
#elif defined(__sun)
-# include <endian.h>
+# include <endian.h>
#else
-# error platform not supported
+# error platform not supported
#endif
cdef int READY_W = Ready.W
cdef int READY_RW = Ready.RW
+
def connect(conninfo: str, *, timeout: float = 0.0) -> PQGenConn[abc.PGconn]:
"""
Generator to create a database connection without blocking.
poll_status = libpq.PQconnectPoll(pgconn_ptr)
logger.debug("connection polled: %s", conn)
- if poll_status == libpq.PGRES_POLLING_READING \
- or poll_status == libpq.PGRES_POLLING_WRITING:
+ if (
+ poll_status == libpq.PGRES_POLLING_READING
+ or poll_status == libpq.PGRES_POLLING_WRITING
+ ):
wait = WAIT_R if poll_status == libpq.PGRES_POLLING_READING else WAIT_W
while True:
ready = yield (libpq.PQsocket(pgconn_ptr), wait)
# Copyright (C) 2020 The Psycopg Team
cimport cython
-from cpython.ref cimport Py_DECREF, Py_INCREF
-from cpython.set cimport PySet_Add, PySet_Contains
+from cpython.ref cimport Py_INCREF
from cpython.dict cimport PyDict_GetItem, PyDict_SetItem
-from cpython.list cimport PyList_CheckExact, PyList_GET_ITEM, PyList_GET_SIZE
-from cpython.list cimport PyList_New, PyList_SET_ITEM
+from cpython.list cimport PyList_GET_ITEM, PyList_GET_SIZE, PyList_New, PyList_SET_ITEM
from cpython.bytes cimport PyBytes_AS_STRING
from cpython.tuple cimport PyTuple_New, PyTuple_SET_ITEM
from cpython.object cimport PyObject, PyObject_CallFunctionObjArgs
-from typing import Any, Iterable, Sequence
+from typing import Sequence
from psycopg import errors as e
from psycopg.pq import Format as PqFormat
-from psycopg.rows import Row, RowMaker
+from psycopg.rows import Row
from psycopg._encodings import conn_encoding
NoneType = type(None)
import logging
from psycopg import errors as e
-from psycopg.pq import Format
logger = logging.getLogger("psycopg")
# importing the ssl module sets up Python's libcrypto callbacks
-import ssl # noqa
+import ssl # no-cython-lint
# disable libcrypto setup in libpq, so it won't stomp on the callbacks
# that have already been set up
pass
ctypedef struct PQconninfoOption:
- char *keyword
- char *envvar
- char *compiled
- char *val
- char *label
- char *dispchar
- int dispsize
+ char *keyword
+ char *envvar
+ char *compiled
+ char *val
+ char *label
+ char *dispchar
+ int dispsize
ctypedef struct PGnotify:
- char *relname
- int be_pid
- char *extra
+ char *relname
+ int be_pid
+ char *extra
ctypedef struct PGcancelConn:
pass
pass
ctypedef struct PGresAttDesc:
- char *name
- Oid tableid
- int columnid
- int format
- Oid typid
- int typlen
- int atttypmod
+ char *name
+ Oid tableid
+ int columnid
+ int format
+ Oid typid
+ int typlen
+ int atttypmod
# enums
PGRES_POLLING_OK
PGRES_POLLING_ACTIVE
-
ctypedef enum PGPing:
PQPING_OK
PQPING_REJECT
size_t *to_length)
unsigned char *PQunescapeBytea(const unsigned char *src, size_t *to_length)
-
# 33.4. Asynchronous Command Processing
int PQsendQuery(PGconn *conn, const char *command) nogil
int PQsendQueryParams(PGconn *conn,
int PQgetCopyData(PGconn *conn, char **buffer, int async) nogil
# 33.10. Control Functions
- void PQtrace(PGconn *conn, FILE *stream);
- void PQsetTraceFlags(PGconn *conn, int flags);
- void PQuntrace(PGconn *conn);
+ void PQtrace(PGconn *conn, FILE *stream)
+ void PQsetTraceFlags(PGconn *conn, int flags)
+ void PQuntrace(PGconn *conn)
# 33.11. Miscellaneous Functions
void PQfreemem(void *ptr) nogil
void PQconninfoFree(PQconninfoOption *connOptions)
char *PQencryptPasswordConn(
- PGconn *conn, const char *passwd, const char *user, const char *algorithm);
- PGresult *PQchangePassword(PGconn *conn, const char *user, const char *passwd);
+ PGconn *conn, const char *passwd, const char *user, const char *algorithm)
+ PGresult *PQchangePassword(PGconn *conn, const char *user, const char *passwd)
PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs)
int PQlibVersion()
with nogil:
pgresult = libpq.PQexec(self._pgconn_ptr, command)
if pgresult is NULL:
- raise e.OperationalError(f"executing query failed: {self.get_error_message()}")
+ raise e.OperationalError(
+ f"executing query failed: {self.get_error_message()}")
return PGresult._from_ptr(pgresult)
with nogil:
rv = libpq.PQsendQuery(self._pgconn_ptr, command)
if not rv:
- raise e.OperationalError(f"sending query failed: {self.get_error_message()}")
+ raise e.OperationalError(
+ f"sending query failed: {self.get_error_message()}")
def exec_params(
self,
<const char *const *>cvalues, clengths, cformats, result_format)
_clear_query_params(ctypes, cvalues, clengths, cformats)
if pgresult is NULL:
- raise e.OperationalError(f"executing query failed: {self.get_error_message()}")
+ raise e.OperationalError(
+ f"executing query failed: {self.get_error_message()}")
return PGresult._from_ptr(pgresult)
def send_query_params(
self._pgconn_ptr, name, command, <int>nparams, atypes)
PyMem_Free(atypes)
if rv is NULL:
- raise e.OperationalError(f"preparing query failed: {self.get_error_message()}")
+ raise e.OperationalError(
+ f"preparing query failed: {self.get_error_message()}")
return PGresult._from_ptr(rv)
def exec_prepared(
def consume_input(self) -> None:
if 1 != libpq.PQconsumeInput(self._pgconn_ptr):
- raise e.OperationalError(f"consuming input failed: {self.get_error_message()}")
+ raise e.OperationalError(
+ f"consuming input failed: {self.get_error_message()}")
def is_busy(self) -> int:
cdef int rv
@nonblocking.setter
def nonblocking(self, int arg) -> None:
if 0 > libpq.PQsetnonblocking(self._pgconn_ptr, arg):
- raise e.OperationalError(f"setting nonblocking failed: {self.get_error_message()}")
+ raise e.OperationalError(
+ f"setting nonblocking failed: {self.get_error_message()}")
cpdef int flush(self) except -1:
if self._pgconn_ptr == NULL:
- raise e.OperationalError(f"flushing failed: the connection is closed")
+ raise e.OperationalError("flushing failed: the connection is closed")
cdef int rv = libpq.PQflush(self._pgconn_ptr)
if rv < 0:
raise e.OperationalError(f"flushing failed: {self.get_error_message()}")
_buffer_as_string_and_size(buffer, &cbuffer, &length)
rv = libpq.PQputCopyData(self._pgconn_ptr, cbuffer, <int>length)
if rv < 0:
- raise e.OperationalError(f"sending copy data failed: {self.get_error_message()}")
+ raise e.OperationalError(
+ f"sending copy data failed: {self.get_error_message()}")
return rv
def put_copy_end(self, error: bytes | None = None) -> int:
cerr = PyBytes_AsString(error)
rv = libpq.PQputCopyEnd(self._pgconn_ptr, cerr)
if rv < 0:
- raise e.OperationalError(f"sending copy end failed: {self.get_error_message()}")
+ raise e.OperationalError(
+ f"sending copy end failed: {self.get_error_message()}")
return rv
def get_copy_data(self, int async_) -> tuple[int, memoryview]:
cdef int nbytes
nbytes = libpq.PQgetCopyData(self._pgconn_ptr, &buffer_ptr, async_)
if nbytes == -2:
- raise e.OperationalError(f"receiving copy data failed: {self.get_error_message()}")
+ raise e.OperationalError(
+ f"receiving copy data failed: {self.get_error_message()}")
if buffer_ptr is not NULL:
data = PyMemoryView_FromObject(
PQBuffer._from_buffer(<unsigned char *>buffer_ptr, nbytes))
_check_supported("PQsendFlushRequest ", 140000)
cdef int rv = libpq.PQsendFlushRequest(self._pgconn_ptr)
if rv == 0:
- raise e.OperationalError(f"flush request failed: {self.get_error_message()}")
+ raise e.OperationalError(
+ f"flush request failed: {self.get_error_message()}")
cdef int _ensure_pgconn(PGconn pgconn) except 0:
from cpython.mem cimport PyMem_Free, PyMem_Realloc
from cpython.ref cimport Py_INCREF
from libc.stdint cimport int32_t, uint32_t
-from libc.string cimport memset, strchr
-from cpython.list cimport PyList_Append, PyList_GET_ITEM, PyList_GET_SIZE
-from cpython.list cimport PyList_GetSlice, PyList_New, PyList_SET_ITEM
+from libc.string cimport strchr
+from cpython.list cimport PyList_Append, PyList_GET_ITEM, PyList_GET_SIZE, PyList_New
+from cpython.list cimport PyList_SET_ITEM
from cpython.object cimport PyObject
-from psycopg_c.pq cimport _buffer_as_string_and_size
from psycopg_c._psycopg cimport endian
from psycopg_c.pq.libpq cimport Oid
oid = oids.DATE_OID
cdef Py_ssize_t cdump(self, obj, bytearray rv, Py_ssize_t offset) except -1:
- cdef Py_ssize_t size;
+ cdef Py_ssize_t size
cdef const char *src
# NOTE: whatever the PostgreSQL DateStyle input format (DMY, MDY, YMD)
format = PQ_TEXT
cdef Py_ssize_t cdump(self, obj, bytearray rv, Py_ssize_t offset) except -1:
- cdef Py_ssize_t size;
+ cdef Py_ssize_t size
cdef const char *src
cdef str s = str(obj)
format = PQ_TEXT
cdef Py_ssize_t cdump(self, obj, bytearray rv, Py_ssize_t offset) except -1:
- cdef Py_ssize_t size;
+ cdef Py_ssize_t size
cdef const char *src
# NOTE: whatever the PostgreSQL DateStyle input format (DMY, MDY, YMD)
cdef int64_t us = cdt.timedelta_microseconds(delta) + 1_000_000 * (
86_400 * <int64_t>cdt.timedelta_days(delta)
- + <int64_t>cdt.timedelta_seconds(delta))
+ + <int64_t>cdt.timedelta_seconds(delta))
cdef uint64_t beus = endian.htobe64(us)
cdef char *buf = CDumper.ensure_size(rv, offset, sizeof(beus))
cdef int64_t us = cdt.timedelta_microseconds(delta) + 1_000_000 * (
86_400 * <int64_t>cdt.timedelta_days(delta)
- + <int64_t>cdt.timedelta_seconds(delta))
+ + <int64_t>cdt.timedelta_seconds(delta))
cdef uint64_t beus = endian.htobe64(us)
cdef char *buf = CDumper.ensure_size(rv, offset, sizeof(beus))
self._style = INTERVALSTYLE_OTHERS
cdef Py_ssize_t cdump(self, obj, bytearray rv, Py_ssize_t offset) except -1:
- cdef Py_ssize_t size;
+ cdef Py_ssize_t size
cdef const char *src
cdef str s
y, m, d = vals[0], vals[1], vals[2]
elif self._order == ORDER_DMY:
d, m, y = vals[0], vals[1], vals[2]
- else: # self._order == ORDER_MDY
+ else: # self._order == ORDER_MDY
m, d, y = vals[0], vals[1], vals[2]
try:
if self._order == ORDER_PGDM:
d = int(seps[0][1 : seps[1] - seps[0]])
m = _month_abbr[seps[1][1 : seps[2] - seps[1]]]
- else: # self._order == ORDER_PGMD
+ else: # self._order == ORDER_PGMD
m = _month_abbr[seps[0][1 : seps[1] - seps[0]]]
d = int(seps[1][1 : seps[2] - seps[1]])
except (KeyError, ValueError) as ex:
y, m, d = vals[0], vals[1], vals[2]
elif self._order == ORDER_DMY:
d, m, y = vals[0], vals[1], vals[2]
- else: # self._order == ORDER_MDY
+ else: # self._order == ORDER_MDY
m, d, y = vals[0], vals[1], vals[2]
# Parse the timezone
dt = cdt.datetime_new(
y, m, d, vals[3], vals[4], vals[5], us, timezone_utc)
dt -= tzoff
- return PyObject_CallFunctionObjArgs(datetime_astimezone,
- <PyObject *>dt, <PyObject *>self._time_zone, NULL)
+ return PyObject_CallFunctionObjArgs(
+ datetime_astimezone, <PyObject *>dt, <PyObject *>self._time_zone, NULL)
except OverflowError as ex:
# If we have created the temporary 'dt' it means that we have a
# datetime close to max, the shift pushed it past max, overflowing.
dt = pg_datetimetz_epoch + delta
else:
dt = pg_datetimetz_epoch - delta
- return PyObject_CallFunctionObjArgs(datetime_astimezone,
- <PyObject *>dt, <PyObject *>self._time_zone, NULL)
+ return PyObject_CallFunctionObjArgs(
+ datetime_astimezone, <PyObject *>dt, <PyObject *>self._time_zone, NULL)
except OverflowError:
# If we were asked about a timestamp which would overflow in UTC,
return -off if sgn == b"-" else off
-cdef object _timezone_from_seconds(int sec, __cache={}):
+cdef object _timezone_from_seconds(int sec, __cache={}): # no-cython-lint
cdef object pysec = sec
cdef PyObject *ptr = PyDict_GetItem(__cache, pysec)
if ptr != NULL:
return PyLong_FromString(buf, NULL, 10)
-
@cython.final
cdef class Int2BinaryLoader(CLoader):
return sizeof(val)
-cdef Py_ssize_t dump_int_to_numeric_binary(obj, bytearray rv, Py_ssize_t offset) except -1:
+cdef Py_ssize_t dump_int_to_numeric_binary(
+ obj, bytearray rv, Py_ssize_t offset
+) except -1:
# Calculate the number of PG digits required to store the number
cdef uint16_t ndigits
ndigits = <uint16_t>((<int>obj.bit_length()) * BIT_PER_PGDIGIT) + 1
cdef const char *pgenc
if self._pgconn is not None:
- pgenc = libpq.PQparameterStatus(self._pgconn._pgconn_ptr, b"client_encoding")
+ pgenc = libpq.PQparameterStatus(
+ self._pgconn._pgconn_ptr, b"client_encoding")
if pgenc == NULL or pgenc == b"UTF8":
self._bytes_encoding = b"utf-8"
self.is_utf8 = 1
cdef Py_ssize_t cdump(self, obj, bytearray rv, Py_ssize_t offset) except -1:
# the server will raise DataError subclass if the string contains 0x00
- cdef Py_ssize_t size;
+ cdef Py_ssize_t size
cdef const char *src
if self.is_utf8:
cdef const char *pgenc
if self._pgconn is not None:
- pgenc = libpq.PQparameterStatus(self._pgconn._pgconn_ptr, b"client_encoding")
+ pgenc = libpq.PQparameterStatus(
+ self._pgconn._pgconn_ptr, b"client_encoding")
if pgenc == NULL or pgenc == b"UTF8":
self._bytes_encoding = b"utf-8"
self.is_utf8 = 1
else:
return data[:length]
+
@cython.final
cdef class TextLoader(_TextLoader):
return len_out
def quote(self, obj) -> Buffer:
- cdef size_t len_out
- cdef unsigned char *out
cdef char *ptr
cdef Py_ssize_t length
cdef const char *scs
# use.
if self._pgconn is not None:
if not self._qplen:
- scs = libpq.PQparameterStatus(self._pgconn._pgconn_ptr,
- b"standard_conforming_strings")
+ scs = libpq.PQparameterStatus(
+ self._pgconn._pgconn_ptr, b"standard_conforming_strings")
if scs and scs[0] == b'o' and scs[1] == b"n": # == "on"
self._qplen = 1
else:
cdef Py_ssize_t cdump(self, obj, bytearray rv, Py_ssize_t offset) except -1:
cdef char *src
- cdef Py_ssize_t size;
+ cdef Py_ssize_t size
_buffer_as_string_and_size(obj, &src, &size)
cdef char *buf = CDumper.ensure_size(rv, offset, size)