1
# Copyright (C) 2007,2009 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""Pyrex implementation for bencode coder/decoder"""
20
cdef extern from "stddef.h":
21
ctypedef unsigned int size_t
23
cdef extern from "Python.h":
24
ctypedef int Py_ssize_t
25
int PyInt_CheckExact(object o)
26
int PyLong_CheckExact(object o)
27
int PyString_CheckExact(object o)
28
int PyTuple_CheckExact(object o)
29
int PyList_CheckExact(object o)
30
int PyDict_CheckExact(object o)
31
int PyBool_Check(object o)
32
object PyString_FromStringAndSize(char *v, Py_ssize_t len)
33
char *PyString_AS_STRING(object o) except NULL
34
Py_ssize_t PyString_GET_SIZE(object o) except -1
35
object PyInt_FromString(char *str, char **pend, int base)
36
int Py_GetRecursionLimit()
37
int Py_EnterRecursiveCall(char *)
38
void Py_LeaveRecursiveCall()
40
cdef extern from "stdlib.h":
41
void free(void *memblock)
42
void *malloc(size_t size)
43
void *realloc(void *memblock, size_t size)
44
long strtol(char *, char **, int)
46
cdef extern from "string.h":
47
void *memcpy(void *dest, void *src, size_t count)
49
cdef extern from "python-compat.h":
50
int snprintf(char* buffer, size_t nsize, char* fmt, ...)
56
cdef readonly char *tail
57
cdef readonly int size
58
cdef readonly int _yield_tuples
61
def __init__(self, s, yield_tuples=0):
62
"""Initialize decoder engine.
63
@param s: Python string.
65
if not PyString_CheckExact(s):
66
raise TypeError("String required")
69
self.tail = PyString_AS_STRING(s)
70
self.size = PyString_GET_SIZE(s)
71
self._yield_tuples = int(yield_tuples)
74
result = self.decode_object()
76
raise ValueError('junk in stream')
79
def decode_object(self):
83
raise ValueError('stream underflow')
85
if Py_EnterRecursiveCall("decode_object"):
86
raise RuntimeError("too deeply nested")
91
return self._decode_int()
92
elif c'0' <= ch <= c'9':
93
return self._decode_string()
96
return self._decode_list()
99
return self._decode_dict()
101
raise ValueError('unknown object type identifier %r' % ch)
103
Py_LeaveRecursiveCall()
105
cdef void _update_tail(self, int n):
106
"""Update tail pointer and resulting size by n characters"""
107
self.size = self.size - n
108
self.tail = &self.tail[n]
110
cdef int _read_digits(self, char stop_char) except -1:
113
while ((self.tail[i] >= c'0' and self.tail[i] <= c'9') or
114
self.tail[i] == c'-') and i < self.size:
117
if self.tail[i] != stop_char:
118
raise ValueError("Stop character %c not found: %c" %
119
(stop_char, self.tail[i]))
120
if (self.tail[0] == c'0' or
121
(self.tail[0] == c'-' and self.tail[1] == c'0')):
125
raise ValueError # leading zeroes are not allowed
128
cdef object _decode_int(self):
130
i = self._read_digits(c'e')
133
ret = PyInt_FromString(self.tail, NULL, 10)
136
self._update_tail(i+1)
139
cdef object _decode_string(self):
141
i = self._read_digits(c':')
142
n = strtol(self.tail, NULL, 10)
143
self._update_tail(i+1)
147
raise ValueError('stream underflow')
149
raise ValueError('string size below zero: %d' % n)
151
result = PyString_FromStringAndSize(self.tail, n)
155
cdef object _decode_list(self):
159
if self.tail[0] == c'e':
161
if self._yield_tuples:
166
result.append(self.decode_object())
168
raise ValueError('malformed list')
170
cdef object _decode_dict(self):
182
# keys should be strings only
183
key = self._decode_string()
185
raise ValueError('dict keys disordered')
188
value = self.decode_object()
191
raise ValueError('malformed dict')
194
def bdecode(object s):
195
"""Decode string x to Python object"""
196
return Decoder(s).decode()
199
def bdecode_as_tuple(object s):
200
"""Decode string x to Python object, using tuples rather than lists."""
201
return Decoder(s, True).decode()
204
class Bencached(object):
205
__slots__ = ['bencoded']
207
def __init__(self, s):
212
INITSIZE = 1024 # initial size for encoder buffer
217
"""Bencode encoder"""
219
cdef readonly char *buffer
220
cdef readonly int maxsize
221
cdef readonly char *tail
222
cdef readonly int size
224
def __init__(self, int maxsize=INITSIZE):
225
"""Initialize encoder engine
226
@param maxsize: initial size of internal char buffer
234
p = <char*>malloc(maxsize)
236
raise MemoryError('Not enough memory to allocate buffer '
239
self.maxsize = maxsize
248
if self.buffer != NULL and self.size != 0:
249
return PyString_FromStringAndSize(self.buffer, self.size)
253
cdef int _ensure_buffer(self, int required) except 0:
254
"""Ensure that tail of CharTail buffer has enough size.
255
If buffer is not big enough then function try to
258
cdef char *new_buffer
261
if self.size + required < self.maxsize:
264
new_size = self.maxsize
265
while new_size < self.size + required:
266
new_size = new_size * 2
267
new_buffer = <char*>realloc(self.buffer, <size_t>new_size)
268
if new_buffer == NULL:
269
raise MemoryError('Cannot realloc buffer for encoder')
271
self.buffer = new_buffer
272
self.maxsize = new_size
273
self.tail = &new_buffer[self.size]
276
cdef void _update_tail(self, int n):
277
"""Update tail pointer and resulting size by n characters"""
278
self.size = self.size + n
279
self.tail = &self.tail[n]
281
cdef int _encode_int(self, int x) except 0:
282
"""Encode int to bencode string iNNNe
283
@param x: value to encode
286
self._ensure_buffer(INT_BUF_SIZE)
287
n = snprintf(self.tail, INT_BUF_SIZE, "i%de", x)
289
raise MemoryError('int %d too big to encode' % x)
293
cdef int _encode_long(self, x) except 0:
294
return self._append_string(''.join(('i', str(x), 'e')))
296
cdef int _append_string(self, s) except 0:
297
self._ensure_buffer(PyString_GET_SIZE(s))
298
memcpy(self.tail, PyString_AS_STRING(s), PyString_GET_SIZE(s))
299
self._update_tail(PyString_GET_SIZE(s))
302
cdef int _encode_string(self, x) except 0:
304
self._ensure_buffer(PyString_GET_SIZE(x) + 32)
305
n = snprintf(self.tail, 32, '%d:', PyString_GET_SIZE(x))
307
raise MemoryError('string %s too big to encode' % x)
308
memcpy(<void *>(self.tail+n), PyString_AS_STRING(x),
309
PyString_GET_SIZE(x))
310
self._update_tail(n+PyString_GET_SIZE(x))
313
cdef int _encode_list(self, x) except 0:
314
self._ensure_buffer(2)
325
cdef int _encode_dict(self, x) except 0:
326
self._ensure_buffer(2)
333
if not PyString_CheckExact(k):
334
raise TypeError('key in dict should be string')
335
self._encode_string(k)
342
def process(self, object x):
343
if Py_EnterRecursiveCall("encode"):
344
raise RuntimeError("too deeply nested")
346
if PyString_CheckExact(x):
347
self._encode_string(x)
348
elif PyInt_CheckExact(x):
350
elif PyLong_CheckExact(x):
352
elif PyList_CheckExact(x) or PyTuple_CheckExact(x):
354
elif PyDict_CheckExact(x):
356
elif PyBool_Check(x):
357
self._encode_int(int(x))
358
elif isinstance(x, Bencached):
359
self._append_string(x.bencoded)
361
raise TypeError('unsupported type %r' % x)
363
Py_LeaveRecursiveCall()
367
"""Encode Python object x to string"""