20
20
ctypedef unsigned size_t
22
22
cdef extern from "Python.h":
23
ctypedef struct PyObject:
23
25
int PyList_Append(object lst, object item) except -1
25
char *PyString_AsString(object p)
27
char *PyString_AsString(object p) except NULL
26
28
object PyString_FromStringAndSize(char *, Py_ssize_t)
27
29
int PyString_CheckExact(object s)
30
int PyString_CheckExact_ptr "PyString_CheckExact" (PyObject *)
28
31
Py_ssize_t PyString_Size(object p)
32
Py_ssize_t PyString_GET_SIZE_ptr "PyString_GET_SIZE" (PyObject *)
33
char * PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *)
34
int PyString_AsStringAndSize_ptr(PyObject *, char **buf, Py_ssize_t *len)
29
35
int PyTuple_CheckExact(object t)
36
Py_ssize_t PyTuple_GET_SIZE(object t)
37
PyObject *PyTuple_GET_ITEM_ptr_object "PyTuple_GET_ITEM" (object tpl, int index)
31
39
cdef extern from "string.h":
32
40
void *memcpy(void *dest, void *src, size_t n)
125
133
last - self._start)))
126
134
raise AssertionError(failure_string)
127
135
# capture the key string
136
# TODO: Consider using PyIntern_FromString, the only caveat is that
137
# it assumes a NULL-terminated string, so we have to check if
138
# temp_ptr[0] == c'\0' or some other char.
128
139
key_element = safe_string_from_size(self._start,
129
140
temp_ptr - self._start)
130
141
# advance our pointer
258
269
cdef Py_ssize_t flat_len
259
270
cdef Py_ssize_t key_len
271
cdef Py_ssize_t node_len
260
273
cdef char * value
261
274
cdef Py_ssize_t value_len
263
cdef Py_ssize_t ref_len
276
cdef Py_ssize_t refs_len
264
277
cdef Py_ssize_t next_len
265
278
cdef int first_ref_list
266
279
cdef int first_reference
281
cdef PyObject *ref_bit
282
cdef Py_ssize_t ref_bit_len
284
if not PyTuple_CheckExact(node):
285
raise TypeError('We expected a tuple() for node not: %s'
287
node_len = PyTuple_GET_SIZE(node)
290
raise ValueError('With ref_lists, we expected 4 entries not: %s'
293
raise ValueError('Without ref_lists, we need at least 3 entries not: %s'
269
295
# I don't expect that we can do faster than string.join()
270
string_key = '\0'.join(node[1])
296
string_key = '\0'.join(<object>PyTuple_GET_ITEM_ptr_object(node, 1))
272
298
# TODO: instead of using string joins, precompute the final string length,
273
299
# and then malloc a single string and copy everything in.
290
316
# If there are no nodes, we don't need to do any work
291
317
# Otherwise we will need (len - 1) '\t' characters to separate
292
318
# the reference lists
293
ref_len = ref_len + (next_len - 1)
319
refs_len = refs_len + (next_len - 1)
294
320
for ref_list in ref_lists:
295
321
next_len = len(ref_list)
297
323
# We will need (len - 1) '\r' characters to separate the
299
ref_len = ref_len + (next_len - 1)
325
refs_len = refs_len + (next_len - 1)
300
326
for reference in ref_list:
301
next_len = len(reference)
327
if not PyTuple_CheckExact(reference):
329
'We expect references to be tuples not: %s'
331
next_len = PyTuple_GET_SIZE(reference)
303
333
# We will need (len - 1) '\x00' characters to
304
334
# separate the reference key
305
ref_len = ref_len + (next_len - 1)
306
for ref in reference:
307
ref_len = ref_len + PyString_Size(ref)
335
refs_len = refs_len + (next_len - 1)
336
for i from 0 <= i < next_len:
337
ref_bit = PyTuple_GET_ITEM_ptr_object(reference, i)
338
if not PyString_CheckExact(<object>ref_bit):
339
raise TypeError('We expect reference bits'
340
' to be strings not: %s'
341
% type(<object>ref_bit))
342
refs_len = refs_len + PyString_GET_SIZE_ptr(ref_bit)
309
344
# So we have the (key NULL refs NULL value LF)
310
345
key_len = PyString_Size(string_key)
311
value = PyString_AsString(node[2])
312
value_len = PyString_Size(node[2])
313
flat_len = (key_len + 1 + ref_len + 1 + value_len + 1)
346
val = PyTuple_GET_ITEM_ptr_object(node, 2)
347
if not PyString_CheckExact(<object>val):
348
raise TypeError('Expected a plain str for value not: %s'
350
value = PyString_AS_STRING_ptr(val)
351
value_len = PyString_GET_SIZE_ptr(val)
352
flat_len = (key_len + 1 + refs_len + 1 + value_len + 1)
314
353
line = PyString_FromStringAndSize(NULL, flat_len)
315
354
# Get a pointer to the new buffer
316
355
out = PyString_AsString(line)
333
372
first_reference = 0
335
for bit in reference:
373
next_len = PyTuple_GET_SIZE(reference)
374
for i from 0 <= i < next_len:
340
next_len = PyString_Size(bit)
341
memcpy(out, PyString_AsString(bit), next_len)
378
ref_bit = PyTuple_GET_ITEM_ptr_object(reference, i)
379
ref_bit_len = PyString_GET_SIZE_ptr(ref_bit)
380
memcpy(out, PyString_AS_STRING_ptr(ref_bit), ref_bit_len)
381
out = out + ref_bit_len
345
384
memcpy(out, value, value_len)