removed better_bencode fork as I found a saner way for "loads2"

This commit is contained in:
Bora M. Alper 2017-06-10 22:04:23 +03:00
parent 643c72ee16
commit ec708db607
7 changed files with 8 additions and 750 deletions

View File

@ -19,8 +19,9 @@ Warning:
"""
import typing
from io import BytesIO
import magneticod.bencoder
import better_bencode
Types = typing.Union[int, bytes, list, "KRPCDict"]
KRPCDict = typing.Dict[bytes, Types]
@ -28,14 +29,14 @@ KRPCDict = typing.Dict[bytes, Types]
def dumps(obj) -> bytes:
try:
return magneticod.bencoder.dumps(obj)
return better_bencode.dumps(obj)
except:
raise BencodeEncodingError()
def loads(bytes_object: bytes) -> Types:
try:
return magneticod.bencoder.loads(bytes_object)
return better_bencode.loads(bytes_object)
except Exception as exc:
raise BencodeDecodingError(exc)
@ -48,8 +49,9 @@ def loads2(bytes_object: bytes) -> typing.Tuple[Types, int]:
object, i = loads2(dump)
print(">>>", dump[i:]) # OUTPUT: >>> b'OH YEAH'
"""
bio = BytesIO(bytes_object)
try:
return magneticod.bencoder.loads2(bytes_object)
return better_bencode.load(bio), bio.tell()
except Exception as exc:
raise BencodeDecodingError(exc)

View File

@ -1,32 +0,0 @@
Copyright (c) 2015 by Krzysztof Kosyl
Some rights reserved.
Redistribution and use in source and binary forms of the software as well
as documentation, with or without modification, are permitted provided
that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

View File

@ -1,10 +0,0 @@
This sub-module is a fork of Krzysztof Kosyl's [better-bencode](https://github.com/kosqx/better-bencode/) for the
specific needs of **magetico**.
The original repository is forked at commit `46bdc09f1b3003b39aa4263e0a052883a5209c2a`.
Key Differenes from *better-bencode*:
* Python 2 support is removed.
* Removed `dump` and `load` functions, as they are not used and most likely will not be maintained. It's better not to
have them than to have two different set of functions with inconsistent, confusing behaviour.

View File

@ -1,6 +0,0 @@
try:
from magneticod.bencoder._fast import dumps, loads, loads2
from magneticod.bencoder._fast import BencodeValueError, BencodeTypeError
except ImportError:
from magneticod.bencoder._pure import dumps, loads, loads2
from magneticod.bencoder._pure import BencodeValueError, BencodeTypeError

View File

@ -1,545 +0,0 @@
#include <Python.h>
#define PY_BUILD_VALUE_BYTES "y#"
#define PyString_FromStringAndSize PyBytes_FromStringAndSize
#define PyString_AsStringAndSize PyBytes_AsStringAndSize
#define PyString_Size PyBytes_Size
#define PyInt_CheckExact(obj) 0
struct benc_state {
unsigned int cast : 1;
int size;
int offset;
char* buffer;
PyObject* file;
PyObject** references_stack;
int references_size;
int references_top;
};
PyObject* BencodeValueError;
PyObject* BencodeTypeError;
static void benc_state_init(struct benc_state* bs) {
bs->size = 256;
bs->offset = 0;
bs->buffer = malloc(bs->size);
bs->file = NULL;
bs->references_size = 8;
bs->references_top = 0;
bs->references_stack = malloc(sizeof(PyObject*) * bs->references_size);
}
static void benc_state_free(struct benc_state* bs) {
if (bs->buffer != NULL) {
free(bs->buffer);
}
if (bs->references_stack != NULL) {
free(bs->references_stack);
}
}
static void benc_state_flush(struct benc_state* bs) {
if (bs->offset > 0) {
PyObject_CallMethod(bs->file, "write", PY_BUILD_VALUE_BYTES, bs->buffer, bs->offset);
bs->offset = 0;
}
}
static void benc_state_write_char(struct benc_state* bs, char c) {
if (bs->file == NULL) {
if ((bs->offset + 1) >= bs->size) {
bs->buffer = realloc(bs->buffer, bs->size * 2);
}
bs->buffer[bs->offset++] = c;
} else {
if ((bs->offset + 1) >= bs->size) {
PyObject_CallMethod(bs->file, "write", PY_BUILD_VALUE_BYTES, bs->buffer, bs->offset);
bs->offset = 0;
}
bs->buffer[bs->offset++] = c;
}
}
static void benc_state_write_buffer(struct benc_state* bs, char* buff, int size) {
if (bs->file == NULL) {
int new_size;
for (new_size = bs->size; new_size <= (bs->offset + size); new_size *= 2);
if (new_size > bs->size) {
bs->buffer = realloc(bs->buffer, new_size);
bs->size = new_size;
}
memcpy(bs->buffer + bs->offset, buff, size);
bs->offset += size;
} else {
if (bs->offset + size >= bs->size) {
PyObject_CallMethod(bs->file, "write", PY_BUILD_VALUE_BYTES, bs->buffer, bs->offset);
bs->offset = 0;
}
if (size >= bs->size) {
PyObject_CallMethod(bs->file, "write", PY_BUILD_VALUE_BYTES, buff, size);
} else {
memcpy(bs->buffer + bs->offset, buff, size);
bs->offset += size;
}
}
}
static void benc_state_write_format(struct benc_state* bs, const int limit, const void *format, ...) {
char buffer[limit + 1]; // moze by malloca()?
va_list ap;
va_start(ap, format);
int size = vsnprintf(buffer, limit, format, ap);
va_end(ap);
return benc_state_write_buffer(bs, buffer, (size < limit) ? size : (limit - 1));
}
static int benc_state_read_char(struct benc_state* bs) {
if (bs->file == NULL) {
if (bs->offset < bs->size) {
return bs->buffer[bs->offset++];
} else {
return -1;
}
} else {
char *buffer;
int result;
Py_ssize_t length;
PyObject *data = PyObject_CallMethod(bs->file, "read", "i", 1);
if (-1 == PyString_AsStringAndSize(data, &buffer, &length)) {
return -1;
}
if (length == 1) {
result = buffer[0];
} else {
result = -1;
}
Py_DECREF(data);
return result;
}
}
static PyObject *benc_state_read_pystring(struct benc_state* bs, int size) {
if (bs->file == NULL) {
if (bs->offset + size <= bs->size) {
PyObject *result = PyString_FromStringAndSize(bs->buffer + bs->offset, size);
bs->offset += size;
return result;
} else {
PyErr_Format(
BencodeValueError,
"unexpected end of data"
);
return NULL;
}
} else {
PyObject *result = PyObject_CallMethod(bs->file, "read", "i", size);
if (PyString_Size(result) == size) {
return result;
} else {
Py_DECREF(result);
PyErr_Format(
BencodeValueError,
"unexpected end of data"
);
return NULL;
}
}
}
static void benc_state_references_push(struct benc_state* bs, PyObject *obj) {
if ((bs->references_top + 1) == bs->references_size) {
bs->references_size *= 2;
bs->references_stack = realloc(
bs->references_stack,
sizeof(PyObject*) * bs->references_size
);
}
bs->references_stack[bs->references_top++] = obj;
}
static void benc_state_references_pop(struct benc_state* bs) {
bs->references_top--;
}
static int benc_state_references_contains(struct benc_state* bs, PyObject *obj) {
int i;
for (i = 0; i < bs->references_top; i++) {
if (bs->references_stack[i] == obj) {
return 1;
}
}
return 0;
}
static int do_dump(struct benc_state *bs, PyObject* obj);
static int do_dump(struct benc_state *bs, PyObject* obj) {
int i = 0, n = 0;
if (benc_state_references_contains(bs, obj)) {
PyErr_Format(
BencodeValueError,
"circular reference detected"
);
return 0;
}
if (PyBytes_CheckExact(obj)) {
char *buff = PyBytes_AS_STRING(obj);
int size = PyBytes_GET_SIZE(obj);
benc_state_write_format(bs, 12, "%d:", size);
benc_state_write_buffer(bs, buff, size);
} else if (PyInt_CheckExact(obj) || PyLong_CheckExact(obj)) {
long x = PyLong_AsLong(obj);
benc_state_write_format(bs, 23, "i%lde", x);
} else if (bs->cast && PyBool_Check(obj)) {
long x = PyLong_AsLong(obj);
benc_state_write_format(bs, 4, "i%lde", x);
} else if (PyList_CheckExact(obj) || (bs->cast && PyList_Check(obj))) {
n = PyList_GET_SIZE(obj);
benc_state_references_push(bs, obj);
benc_state_write_char(bs, 'l');
for (i = 0; i < n; i++) {
do_dump(bs, PyList_GET_ITEM(obj, i));
}
benc_state_write_char(bs, 'e');
benc_state_references_pop(bs);
} else if (bs->cast && PyTuple_Check(obj)) {
n = PyTuple_GET_SIZE(obj);
benc_state_references_push(bs, obj);
benc_state_write_char(bs, 'l');
for (i = 0; i < n; i++) {
do_dump(bs, PyTuple_GET_ITEM(obj, i));
}
benc_state_write_char(bs, 'e');
benc_state_references_pop(bs);
} else if (PyDict_CheckExact(obj)) {
Py_ssize_t index = 0;
PyObject *keys, *key, *value;
keys = PyDict_Keys(obj);
PyList_Sort(keys);
benc_state_references_push(bs, obj);
benc_state_write_char(bs, 'd');
for (index = 0; index < PyList_Size(keys); index++) {
key = PyList_GetItem(keys, index);
value = PyDict_GetItem(obj, key);
do_dump(bs, key);
do_dump(bs, value);
}
benc_state_write_char(bs, 'e');
benc_state_references_pop(bs);
Py_DECREF(keys);
} else {
PyErr_Format(
BencodeTypeError,
"type %s is not Bencode serializable",
Py_TYPE(obj)->tp_name
);
}
return 0;
}
static PyObject* dumps(PyObject* self, PyObject* args, PyObject* kwargs) {
static char *kwlist[] = {"obj", "cast", NULL};
PyObject* obj;
PyObject* result;
int cast = 0;
struct benc_state bs;
benc_state_init(&bs);
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "O|i", kwlist,
&obj, &cast
))
{
return NULL;
}
bs.cast = !!cast;
do_dump(&bs, obj);
if (PyErr_Occurred()) {
benc_state_free(&bs);
return NULL;
} else {
result = Py_BuildValue(PY_BUILD_VALUE_BYTES, bs.buffer, bs.offset);
benc_state_free(&bs);
return result;
}
}
static PyObject *do_load(struct benc_state *bs) {
PyObject *retval = NULL;
int first = benc_state_read_char(bs);
switch (first) {
case 'i': {
int sign = 1;
int read_cnt = 0;
long long value = 0;
int current = benc_state_read_char(bs);
if (current == '-') {
sign = -1;
current = benc_state_read_char(bs);
}
while (('0' <= current) && (current <= '9')) {
value = value * 10 + (current - '0');
current = benc_state_read_char(bs);
read_cnt++;
}
if ('e' == current) {
if (read_cnt > 0) {
value *= sign;
retval = PyLong_FromLongLong(value);
} else {
PyErr_Format(
BencodeValueError,
"unexpected end of data"
);
retval = NULL;
}
} else if (-1 == current) {
PyErr_Format(
BencodeValueError,
"unexpected end of data"
);
retval = NULL;
} else {
PyErr_Format(
BencodeValueError,
"unexpected byte 0x%.2x",
current
);
retval = NULL;
}
} break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
int size = first - '0';
char current = benc_state_read_char(bs);
while (('0' <= current) && (current <= '9')) {
size = size * 10 + (current - '0');
current = benc_state_read_char(bs);
}
if (':' == current) {
retval = benc_state_read_pystring(bs, size);
} else if (-1 == current) {
PyErr_Format(
BencodeValueError,
"unexpected end of data"
);
retval = NULL;
} else {
PyErr_Format(
BencodeValueError,
"unexpected byte 0x%.2x",
current
);
retval = NULL;
}
} break;
case 'e':
Py_INCREF(PyExc_StopIteration);
retval = PyExc_StopIteration;
break;
case 'l': {
PyObject *v = PyList_New(0);
PyObject *item;
while (1) {
item = do_load(bs);
if (item == PyExc_StopIteration) {
Py_DECREF(PyExc_StopIteration);
break;
}
if (item == NULL) {
if (!PyErr_Occurred()) {
PyErr_SetString(
BencodeTypeError,
"unexpected error in list"
);
}
Py_DECREF(v);
v = NULL;
break;
}
PyList_Append(v, item);
Py_DECREF(item);
}
retval = v;
} break;
case 'd': {
PyObject *v = PyDict_New();
while (1) {
PyObject *key, *val;
key = val = NULL;
key = do_load(bs);
if (key == PyExc_StopIteration) {
Py_DECREF(PyExc_StopIteration);
break;
}
if (key == NULL) {
if (!PyErr_Occurred()) {
PyErr_SetString(BencodeTypeError, "unexpected error in dict");
}
break;
}
val = do_load(bs);
if (val != NULL) {
PyDict_SetItem(v, key, val);
} else {
if (!PyErr_Occurred()) {
PyErr_SetString(BencodeTypeError, "unexpected error in dict");
}
break;
}
Py_DECREF(key);
Py_XDECREF(val);
}
if (PyErr_Occurred()) {
Py_DECREF(v);
v = NULL;
}
retval = v;
} break;
case -1: {
PyErr_Format(
BencodeValueError,
"unexpected end of data"
);
retval = NULL;
} break;
default:
PyErr_Format(
BencodeValueError,
"unexpected byte 0x%.2x",
first
);
retval = NULL;
break;
}
return retval;
}
static PyObject* loads(PyObject* self, PyObject* args) {
struct benc_state bs;
memset(&bs, 0, sizeof(struct benc_state));
if (!PyArg_ParseTuple(args, PY_BUILD_VALUE_BYTES, &(bs.buffer), &(bs.size)))
return NULL;
PyObject* obj = do_load(&bs);
return obj;
}
static PyObject* loads2(PyObject* self, PyObject* args) {
/* TODO:
*
* PyLong_FromLong and PyTuple_Pack might return NULL. How to handle these errors?
*/
struct benc_state bs;
memset(&bs, 0, sizeof(struct benc_state));
if (!PyArg_ParseTuple(args, PY_BUILD_VALUE_BYTES, &(bs.buffer), &(bs.size)))
return NULL;
PyObject* obj = do_load(&bs);
PyObject* offset = PyLong_FromLong((long) bs.offset);
return PyTuple_Pack(2, obj, offset);
}
static PyObject *add_errors(PyObject *module) {
BencodeValueError = PyErr_NewException(
"bencoder._fast.BencodeValueError", PyExc_ValueError, NULL
);
Py_INCREF(BencodeValueError);
PyModule_AddObject(module, "BencodeValueError", BencodeValueError);
BencodeTypeError = PyErr_NewException(
"bencoder._fast.BencodeTypeError", PyExc_TypeError, NULL
);
Py_INCREF(BencodeTypeError);
PyModule_AddObject(module, "BencodeTypeError", BencodeTypeError);
return module;
}
static PyMethodDef bencoder_fastMethods[] = {
{"loads", loads, METH_VARARGS, "Deserialize ``s`` to a Python object."},
{"loads2", loads2, METH_VARARGS, "Deserialize ``s`` to a Python object and return end index."},
{"dumps", dumps, METH_VARARGS|METH_KEYWORDS, "Serialize ``obj`` to a Bencode formatted ``str``."},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef bencoder_fast_module = {
PyModuleDef_HEAD_INIT,
"bencoder._fast",
NULL,
-1,
bencoder_fastMethods,
NULL,
NULL,
NULL,
NULL
};
PyMODINIT_FUNC
PyInit__fast(void) {
PyObject *module = PyModule_Create(&bencoder_fast_module);
return add_errors(module);
}

View File

@ -1,152 +0,0 @@
"""
Pure Python implementation of Bencode serialization format.
To be used when fast C Extension cannot be compiled.
"""
from io import BytesIO as StringIO
INTEGER_TYPES = (int,)
BINARY_TYPES = (bytes, )
int_to_binary = lambda val: bytes(str(val), 'ascii')
class BencodeValueError(ValueError):
pass
class BencodeTypeError(TypeError):
pass
def _dump_implementation(obj, write, path, cast):
""" dump()/dumps() implementation """
t = type(obj)
if id(obj) in path:
raise BencodeValueError('circular reference detected')
if t in INTEGER_TYPES:
write(b'i')
write(int_to_binary(obj))
write(b'e')
elif t in BINARY_TYPES:
write(int_to_binary(len(obj)))
write(b':')
write(obj)
elif t is list or (cast and issubclass(t, (list, tuple))):
write(b'l')
for item in obj:
_dump_implementation(item, write, path + [id(obj)], cast)
write(b'e')
elif t is dict:
write(b'd')
data = sorted(obj.items())
for key, val in data:
_dump_implementation(key, write, path + [id(obj)], cast)
_dump_implementation(val, write, path + [id(obj)], cast)
write(b'e')
elif cast and t is bool:
write(b'i')
write(int_to_binary(int(obj)))
write(b'e')
else:
raise BencodeTypeError(
'type %s is not Bencode serializable' % type(obj).__name__
)
def dumps(obj, cast=False):
"""Serialize ``obj`` to a Bencode formatted ``str``."""
fp = []
_dump_implementation(obj, fp.append, [], cast)
return b''.join(fp)
def _read_until(delimiter, read):
""" Read char by char until ``delimiter`` occurs. """
result = b''
ch = read(1)
if not ch:
raise BencodeValueError('unexpected end of data')
while ch != delimiter:
result += ch
ch = read(1)
if not ch:
raise BencodeValueError('unexpected end of data')
return result
def _load_implementation(read):
""" load()/loads() implementation """
first = read(1)
if first == b'e':
return StopIteration
elif first == b'i':
value = b''
ch = read(1)
while (b'0' <= ch <= b'9') or (ch == b'-'):
value += ch
ch = read(1)
if ch == b'' or (ch == b'e' and value in (b'', b'-')):
raise BencodeValueError('unexpected end of data')
if ch != b'e':
raise BencodeValueError('unexpected byte 0x%.2x' % ord(ch))
return int(value)
elif b'0' <= first <= b'9':
size = 0
while b'0' <= first <= b'9':
size = size * 10 + (ord(first) - ord('0'))
first = read(1)
if first == b'':
raise BencodeValueError('unexpected end of data')
if first != b':':
raise BencodeValueError('unexpected byte 0x%.2x' % ord(first))
data = read(size)
if len(data) != size:
raise BencodeValueError('unexpected end of data')
return data
elif first == b'l':
result = []
while True:
val = _load_implementation(read)
if val is StopIteration:
return result
result.append(val)
elif first == b'd':
result = {}
while True:
this = read(1)
if this == b'e':
return result
elif this == b'':
raise BencodeValueError('unexpected end of data')
elif not this.isdigit():
raise BencodeValueError('unexpected byte 0x%.2x' % ord(this))
size = int(this + _read_until(b':', read))
key = read(size)
val = _load_implementation(read)
result[key] = val
elif first == b'':
raise BencodeValueError('unexpected end of data')
else:
raise BencodeValueError('unexpected byte 0x%.2x' % ord(first))
def loads(data):
"""Deserialize ``s`` to a Python object."""
fp = StringIO(data)
return _load_implementation(fp.read)
def loads2(data):
"""Deserialize ``s`` to a Python object."""
fp = StringIO(data)
return _load_implementation(fp.read), fp.tell()

View File

@ -11,7 +11,8 @@ def run_setup():
install_requirements = [
"appdirs >= 1.4.3",
"bencoder.pyx >= 1.1.3",
"humanfriendly"
"humanfriendly",
"better_bencode >= 0.2.1"
]
if sys.platform in ["linux", "darwin"]: