diff --git a/magneticod/magneticod/bencode.py b/magneticod/magneticod/bencode.py index 4eb2d0c..ebe992c 100755 --- a/magneticod/magneticod/bencode.py +++ b/magneticod/magneticod/bencode.py @@ -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) diff --git a/magneticod/magneticod/bencoder/LICENSE b/magneticod/magneticod/bencoder/LICENSE deleted file mode 100644 index 70a2b4e..0000000 --- a/magneticod/magneticod/bencoder/LICENSE +++ /dev/null @@ -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. diff --git a/magneticod/magneticod/bencoder/README.rst b/magneticod/magneticod/bencoder/README.rst deleted file mode 100644 index 52f3fc3..0000000 --- a/magneticod/magneticod/bencoder/README.rst +++ /dev/null @@ -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. diff --git a/magneticod/magneticod/bencoder/__init__.py b/magneticod/magneticod/bencoder/__init__.py deleted file mode 100644 index 6a753fd..0000000 --- a/magneticod/magneticod/bencoder/__init__.py +++ /dev/null @@ -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 diff --git a/magneticod/magneticod/bencoder/_fast.c b/magneticod/magneticod/bencoder/_fast.c deleted file mode 100644 index d2aeb01..0000000 --- a/magneticod/magneticod/bencoder/_fast.c +++ /dev/null @@ -1,545 +0,0 @@ -#include - - -#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); - -} diff --git a/magneticod/magneticod/bencoder/_pure.py b/magneticod/magneticod/bencoder/_pure.py deleted file mode 100644 index 9ee204d..0000000 --- a/magneticod/magneticod/bencoder/_pure.py +++ /dev/null @@ -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() diff --git a/magneticod/setup.py b/magneticod/setup.py index 0f186e5..d9e9507 100644 --- a/magneticod/setup.py +++ b/magneticod/setup.py @@ -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"]: