pax_global_header00006660000000000000000000000064137527641710014527gustar00rootroot0000000000000052 comment=d7e2b7445d808a2ac7aec79562873d0835ae880d qpack-0.0.19/000077500000000000000000000000001375276417100127155ustar00rootroot00000000000000qpack-0.0.19/.gitignore000066400000000000000000000002371375276417100147070ustar00rootroot00000000000000__pycache__/ *.pyc *.pyo *.module-cache* .project .pydevproject .idea .cproject .settings/ test.py *.so *.o build/ dist/ checklist.txt .vscode/ qpack.egg-info/qpack-0.0.19/.travis.yml000066400000000000000000000003141375276417100150240ustar00rootroot00000000000000language: python python: - "2.7" - "3.4" - "3.5" - "3.6" before_install: - pip install pycodestyle - python setup.py install script: - pytest -v - find . -name \*.py -exec pycodestyle {} +qpack-0.0.19/ChangeLog000066400000000000000000000030501375276417100144650ustar00rootroot000000000000002020.11.11, Version 0.0.19 * Use String internal for keys in a map (pr #3). 2019.09.11, Version 0.0.18 * Added `ignore_decode_errors` keyword argument to unpackb(). 2017.08.16, Version 0.0.17 * Fixed C99 compile errors and warnings. 2017.08.14, Version 0.0.16 * raise TypeError when trying to pack an unsupported type. (issue #2, reported by @pumelo) 2017.03.08, Version 0.0.15 (BETA) * Fixed unpacking negative integers using Python2. * Replaced adding double value with a memcpy. 2017.03.08, Version 0.0.14 (BETA) * Fixed checking bytes size. 2017.02.07, Version 0.0.13 (BETA) * Fixed bug in unpacking maps. 2016.10.19, Version 0.0.12 (BETA) * Fixed Python2 bugs in fallback.py * Strict C99 * Fixed minor bugs for compiling on Windows platform 2016.10.18, Version 0.0.11 (BETA) * Removed fallback print line * Removed inttypes.h dependency * No declarations in for-loop 2016.10.17, Version 0.0.10 (BETA) * Removed p3c dependency 2016.10.14, Version 0.0.9 (BETA) * Fixed unicode bug (C-module Python2 only) 2016.10.13, Version 0.0.8 (BETA) * Fixed compile bug (C-module Python2) 2016.10.13, Version 0.0.7 (BETA) * Fixed Latin-1 decoding (C-module Python2) 2016.10.13, Version 0.0.6 (BETA) * Added C module for Python 2 support 2016.10.03, Version 0.0.5 (BETA) * Support for Byte-Array in unpack() method. * Fixed bug in installing this package using pip. * Added C module (Only Python3 support) 2016.10.02, Version 0.0.2 (BETA) * Added support for hooks. * Fixed Python 2 Compatibility bug. qpack-0.0.19/LICENSE.txt000066400000000000000000000021161375276417100145400ustar00rootroot00000000000000Copyright (c) 2016 Jeroen van der Heijden / Transceptor Technology Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.qpack-0.0.19/MANIFEST000066400000000000000000000002001375276417100140360ustar00rootroot00000000000000# file GENERATED by distutils, do NOT edit setup.cfg setup.py ./qpack/_qpack.c qpack/__init__.py qpack/fallback.py test/test.py qpack-0.0.19/README.md000066400000000000000000000015361375276417100142010ustar00rootroot00000000000000QPack ===== QPack is a fast and efficient serialization format like MessagePack. One key difference is flexible map and array support which allows to write directly to a qpack buffer without the need to know the size for the map or array beforehand. Installation ------------ From PyPI (recommend) ``` pip install qpack ``` From source code ``` python setup.py install ``` Pack ---- `qpack.packb(object)` Unpack ---- Unpack serialized data. When decode is left None, each string will be returned as bytes. `qpack.unpackb(qp, decode=None)` Example ------- ```python import qpack # define some test data data = {'name': 'Iris', 'age': 3} # serialize into qpack format qp = qpack.packb(data) # unpack the serialized data unpacked = qpack.unpackb(qp, decode='utf-8') # left see what we've got... print(unpacked) # {'name': 'Iris', 'age': 3} ``` qpack-0.0.19/qpack/000077500000000000000000000000001375276417100140145ustar00rootroot00000000000000qpack-0.0.19/qpack/.gitignore000066400000000000000000000000301375276417100157750ustar00rootroot00000000000000/Debug/ /build/ test.py qpack-0.0.19/qpack/__init__.py000066400000000000000000000006031375276417100161240ustar00rootroot00000000000000'''QPack - (de)serializer :copyright: 2016, Jeroen van der Heijden (Transceptor Technology) :license: MIT ''' try: import qpack._qpack as _qpack packb = _qpack._packb unpackb = _qpack._unpackb except ImportError as ex: from .fallback import packb, unpackb __version_info__ = (0, 0, 19) __version__ = '.'.join(map(str, __version_info__)) __all__ = ['packb', 'unpackb'] qpack-0.0.19/qpack/_qpack.c000066400000000000000000001025011375276417100154150ustar00rootroot00000000000000/* * _qpack.c * * Created on: Oct 2, 2016 * Author: Jeroen van der Heijden */ #include #include #if defined(_WIN32) || defined(_WIN64) /* Copied from stdint.h */ #if (_MSC_VER < 1300) typedef signed char int8_t; typedef signed short int16_t; typedef signed int int32_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else typedef signed __int8 int8_t; typedef signed __int16 int16_t; typedef signed __int32 int32_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; #endif #if PY_MAJOR_VERSION >= 3 #define PY_COMPAT_COMPARE(obj, str) PyUnicode_CompareWithASCIIString(obj, str) #define PY_COMPAT_CHECK PyUnicode_Check #define PY_DECODELATIN1(pt, size, error) PyUnicode_DecodeLatin1(pt, size, error) #define PYLONG_FROMLONGLONG(integer) PyLong_FromLongLong(integer) #else #define PY_COMPAT_COMPARE(obj, str) strcmp(PyString_AsString(obj), str) == 0 #define PY_COMPAT_CHECK PyString_Check #define PY_DECODELATIN1(pt, size, error) PyString_Decode(pt, size, "latin-1", error) #define PYLONG_FROMLONGLONG(integer) PyInt_FromSsize_t((ssize_t) integer) #define PyUnicode_InternInPlace(__straddr) (void) (__straddr) #endif static PyObject PY_ARRAY_CLOSE = {0}; static PyObject PY_MAP_CLOSE = {0}; #define Py_QPackCHECK(obj) (obj == &PY_ARRAY_CLOSE || obj == &PY_MAP_CLOSE) typedef enum { /* * Values with -##- will never be returned while unpacking. For example * a QP_INT8 (1 byte signed integer) will be returned as QP_INT64. */ QP_END, /* at the end while unpacking */ QP_RAW, /* raw string */ /* * Both END and RAW are never actually packed but 0 and 1 are reserved * for positive signed integers. * * Fixed positive integers from 0 till 63 [ 0...63 ] * * Fixed negative integers from -60 till -1 [ 64...123 ] * */ QP_HOOK=124, /* Hook is not used by SiriDB */ QP_DOUBLE_N1=125, /* ## double value -1.0 */ QP_DOUBLE_0, /* ## double value 0.0 */ QP_DOUBLE_1, /* ## double value 1.0 */ /* * Fixed raw strings lengths from 0 till 99 [ 128...227 ] */ QP_RAW8=228, /* ## raw string with length < 1 byte */ QP_RAW16, /* ## raw string with length < 1 byte */ QP_RAW32, /* ## raw string with length < 1 byte */ QP_RAW64, /* ## raw string with length < 1 byte */ QP_INT8, /* ## 1 byte signed integer */ QP_INT16, /* ## 2 byte signed integer */ QP_INT32, /* ## 4 byte signed integer */ QP_INT64, /* 8 bytes signed integer */ QP_DOUBLE, /* 8 bytes double */ QP_ARRAY0, /* empty array */ QP_ARRAY1, /* array with 1 item */ QP_ARRAY2, /* array with 2 items */ QP_ARRAY3, /* array with 3 items */ QP_ARRAY4, /* array with 4 items */ QP_ARRAY5, /* array with 5 items */ QP_MAP0, /* empty map */ QP_MAP1, /* map with 1 item */ QP_MAP2, /* map with 2 items */ QP_MAP3, /* map with 3 items */ QP_MAP4, /* map with 4 items */ QP_MAP5, /* map with 5 items */ QP_TRUE, /* boolean true */ QP_FALSE, /* boolean false */ QP_NULL, /* null (none, nil) */ QP_ARRAY_OPEN, /* open a new array */ QP_MAP_OPEN, /* open a new map */ QP_ARRAY_CLOSE, /* close array */ QP_MAP_CLOSE /* close map */ } qp_types_t; typedef enum { DECODE_NONE, DECODE_UTF8, DECODE_LATIN1 } decode_t; typedef struct { unsigned char * buffer; Py_ssize_t size; Py_ssize_t len; } packer_t; #define DEFAULT_ALLOC_SZ 65536 #define PACKER_RESIZE(LEN) \ if (packer->len + LEN > packer->size) \ { \ unsigned char * tmp; \ packer->size = ((packer->len + LEN) / DEFAULT_ALLOC_SZ + 1) \ * DEFAULT_ALLOC_SZ; \ tmp = (unsigned char *) realloc(packer->buffer, packer->size); \ if (tmp == NULL) \ { \ PyErr_SetString(PyExc_MemoryError, "Memory allocation error"); \ packer->size = packer->len; \ return -1; \ } \ packer->buffer = tmp; \ } #define UNPACK_CHECK_SZ(size) \ if ((*pt) + size > end) \ { \ PyErr_SetString(PyExc_ValueError, "unpackb() is missing data"); \ return NULL; \ } #define UNPACK_RAW(size, __ign_derr) \ UNPACK_CHECK_SZ(size) \ switch(decode) \ { \ case DECODE_NONE: \ obj = PyBytes_FromStringAndSize((const char *) *pt, size); \ break; \ case DECODE_UTF8: \ obj = PyUnicode_DecodeUTF8((const char *) *pt, size, NULL); \ if (__ign_derr && obj == NULL) \ { \ PyErr_Clear(); \ obj = PyBytes_FromStringAndSize((const char *) *pt, size); \ } \ break; \ case DECODE_LATIN1: \ obj = PyUnicode_DecodeLatin1((const char *) *pt, size, NULL); \ if (__ign_derr && obj == NULL) \ { \ PyErr_Clear(); \ obj = PyBytes_FromStringAndSize((const char *) *pt, size); \ } \ break; \ } \ (*pt) += size; \ return obj; #define UNPACK_FIXED_RAW(uintx_t, __ign_derr) \ { \ Py_ssize_t size; \ UNPACK_CHECK_SZ(sizeof(uintx_t)) \ size = (Py_ssize_t) *((uintx_t *) *pt); \ (*pt) += sizeof(uintx_t); \ UNPACK_RAW(size, __ign_derr) \ } #define UNPACK_INT(intx_t) \ { \ long long integer; \ UNPACK_CHECK_SZ(sizeof(intx_t)) \ integer = (long long) *((intx_t *) *pt); \ (*pt) += sizeof(intx_t); \ obj = PYLONG_FROMLONGLONG(integer); \ return obj; \ } #define SET_UNEXPECTED(obj) \ if (Py_QPackCHECK(obj)) \ { \ PyErr_SetString( \ PyExc_ValueError, \ "unpackb() found an unexpected array or map close character"); \ } /* Documentation strings */ static char module_docstring[] = "QPack - Python module in C"; static char packb_docstring[] = "Serialize a Python object to QPack format."; static char unpackb_docstring[] = "De-serialize QPack data to a Python object.\n" "\n" "Keyword arguments:\n" " decode:\n" " Decoding used for de-serializing QPack raw data.\n" " When None, all raw data will be de-serialized to Python bytes.\n" " (Default value: None)\n" " ignore_decode_errors:\n" " If this option is set to False then a `decode` exception will be\n" " raised if a raw value fails to decode.\n" " When set to True, a value which has failed to deocode will be\n" " returned as bytes but other values are still decoded.\n" " (Default value: False)"; /* Available functions */ static PyObject * _qpack_packb( PyObject * self, PyObject * args, PyObject * kwargs); static PyObject * _qpack_unpackb( PyObject * self, PyObject * args, PyObject * kwargs); /* other static methods */ static packer_t * packer_new(void); static void packer_free(packer_t * packer); static int add_raw(packer_t * packer, const unsigned char * buffer, Py_ssize_t size); static int packb(PyObject * obj, packer_t * packer); static PyObject * unpackb( unsigned char ** pt, const unsigned char * const end, decode_t decode, int ignore_decode_errors); /* Module specification */ static PyMethodDef module_methods[] = { { "_packb", (PyCFunction)_qpack_packb, METH_VARARGS | METH_KEYWORDS, packb_docstring }, { "_unpackb", (PyCFunction)_qpack_unpackb, METH_VARARGS | METH_KEYWORDS, unpackb_docstring }, {NULL, NULL, 0, NULL} }; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_qpack", /* m_name */ module_docstring, /* m_doc */ -1, /* m_size */ module_methods, /* m_methods */ NULL, /* m_reload */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ }; /* Initialize the module */ PyMODINIT_FUNC PyInit__qpack(void) { return PyModule_Create(&moduledef); } #else PyMODINIT_FUNC init_qpack(void) { PyObject *m = Py_InitModule3( "_qpack", module_methods, module_docstring); if (m == NULL) return; } #endif static packer_t * packer_new(void) { packer_t * packer = (packer_t *) malloc(sizeof(packer_t)); if (packer != NULL) { packer->size = DEFAULT_ALLOC_SZ; packer->len = 0; packer->buffer = (unsigned char *) malloc(DEFAULT_ALLOC_SZ); if (packer->buffer == NULL) { packer_free(packer); packer = NULL; } } return packer; } static void packer_free(packer_t * packer) { free(packer->buffer); free(packer); } static int add_raw(packer_t * packer, const unsigned char * buffer, Py_ssize_t size) { PACKER_RESIZE(5 + size) if (size < 100) { packer->buffer[packer->len++] = 128 + (char) size; } else if (size < 256) { uint8_t length = (uint8_t) size; packer->buffer[packer->len++] = QP_RAW8; packer->buffer[packer->len++] = length; } else if (size < 65536) { uint16_t length = (uint16_t) size; packer->buffer[packer->len++] = QP_RAW16; memcpy(packer->buffer + packer->len, &length, sizeof(uint16_t)); packer->len += sizeof(uint16_t); } else if (size < 4294967296) { uint32_t length = (uint32_t) size; packer->buffer[packer->len++] = QP_RAW32; memcpy(packer->buffer + packer->len, &length, sizeof(uint32_t)); packer->len += sizeof(uint32_t); } else { uint64_t length = (uint64_t) size; packer->buffer[packer->len++] = QP_RAW64; memcpy(packer->buffer + packer->len, &length, sizeof(uint64_t)); packer->len += sizeof(uint64_t); } memcpy(packer->buffer + packer->len, buffer, size); packer->len += size; return 0; } static int packb(PyObject * obj, packer_t * packer) { if (obj == Py_True) { PACKER_RESIZE(1) packer->buffer[packer->len++] = QP_TRUE; return 0; } if (obj == Py_False) { PACKER_RESIZE(1) packer->buffer[packer->len++] = QP_FALSE; return 0; } if (obj == Py_None) { PACKER_RESIZE(1) packer->buffer[packer->len++] = QP_NULL; return 0; } if (PyList_Check(obj)) { Py_ssize_t size; PACKER_RESIZE(1) size = PyList_GET_SIZE(obj); if (size < 6) { Py_ssize_t i; packer->buffer[packer->len++] = QP_ARRAY0 + (char) size; for (i = 0; i < size; i++) { if (packb(PyList_GET_ITEM(obj, i), packer)) { return -1; /* PyErr is set */ } } } else { Py_ssize_t i; packer->buffer[packer->len++] = QP_ARRAY_OPEN; for (i = 0; i < size; i++) { if (packb(PyList_GET_ITEM(obj, i), packer)) { return -1; /* PyErr is set */ } } PACKER_RESIZE(1) packer->buffer[packer->len++] = QP_ARRAY_CLOSE; } return 0; } if (PyTuple_Check(obj)) { Py_ssize_t size; PACKER_RESIZE(1) size = PyTuple_GET_SIZE(obj); if (size < 6) { Py_ssize_t i; packer->buffer[packer->len++] = QP_ARRAY0 + (char) size; for (i = 0; i < size; i++) { if (packb(PyTuple_GET_ITEM(obj, i), packer)) { return -1; /* PyErr is set */ } } } else { Py_ssize_t i; packer->buffer[packer->len++] = QP_ARRAY_OPEN; for (i = 0; i < size; i++) { if (packb(PyTuple_GET_ITEM(obj, i), packer)) { return -1; /* PyErr is set */ } } PACKER_RESIZE(1) packer->buffer[packer->len++] = QP_ARRAY_CLOSE; } return 0; } if (PyDict_Check(obj)) { PyObject * key; PyObject * value; Py_ssize_t pos = 0; Py_ssize_t size = PyDict_Size(obj); PACKER_RESIZE(1) if (size < 6) { packer->buffer[packer->len++] = QP_MAP0 + (char) size; while (PyDict_Next(obj, &pos, &key, &value)) { if (packb(key, packer) || packb(value, packer)) { return -1; /* PyErr is set */ } } } else { packer->buffer[packer->len++] = QP_MAP_OPEN; while (PyDict_Next(obj, &pos, &key, &value)) { if (packb(key, packer) || packb(value, packer)) { return -1; /* PyErr is set */ } } PACKER_RESIZE(1) packer->buffer[packer->len++] = QP_MAP_CLOSE; } return 0; } #if PY_MAJOR_VERSION >= 3 if (PyLong_Check(obj)) { /* An Overflow Error might be raised */ int64_t i64 = PyLong_AsLongLong(obj); #else if (PyLong_Check(obj) || PyInt_Check(obj)) { /* An Overflow Error might be raised */ int64_t i64 = PyLong_Check(obj) ? (int64_t) PyLong_AsLongLong(obj) : (int64_t) PyInt_AsLong(obj); #endif int8_t i8; int16_t i16; int32_t i32; if ((i8 = (int8_t) i64) == i64) { PACKER_RESIZE(2) if (i8 >= 0 && i8 < 64) { packer->buffer[packer->len++] = i8; } else if (i8 >= -60 && i8 < 0) { packer->buffer[packer->len++] = 63 - i8; } else { packer->buffer[packer->len++] = QP_INT8; packer->buffer[packer->len++] = i8; } return 0; } if ((i16 = (int16_t) i64) == i64) { PACKER_RESIZE(3) packer->buffer[packer->len++] = QP_INT16; memcpy(packer->buffer + packer->len, &i16, sizeof(int16_t)); packer->len += sizeof(int16_t); return 0; } if ((i32 = (int32_t) i64) == i64) { PACKER_RESIZE(5) packer->buffer[packer->len++] = QP_INT32; memcpy(packer->buffer + packer->len, &i32, sizeof(int32_t)); packer->len += sizeof(int32_t); return 0; } PACKER_RESIZE(9) packer->buffer[packer->len++] = QP_INT64; memcpy(packer->buffer + packer->len, &i64, sizeof(int64_t)); packer->len += sizeof(int64_t); return 0; } if (PyFloat_Check(obj)) { double d = PyFloat_AsDouble(obj); if (d == -1.0) { PACKER_RESIZE(1) packer->buffer[packer->len++] = QP_DOUBLE_N1; } else if (d == 0.0) { PACKER_RESIZE(1) packer->buffer[packer->len++] = QP_DOUBLE_0; } else if (d == 1.0) { PACKER_RESIZE(1) packer->buffer[packer->len++] = QP_DOUBLE_1; } else { PACKER_RESIZE(9) packer->buffer[packer->len++] = QP_DOUBLE; memcpy(packer->buffer + packer->len, &d, sizeof(double)); packer->len += sizeof(double); } return 0; } #if PY_MAJOR_VERSION >= 3 if (PyUnicode_Check(obj)) { Py_ssize_t size; unsigned char * raw = (unsigned char *) PyUnicode_AsUTF8AndSize(obj, &size); return (raw == NULL) ? -1 : add_raw(packer, raw, size); } #else if (PyUnicode_Check(obj)) { Py_ssize_t size; unsigned char * raw; int rc; PyObject * tmp = PyUnicode_AsUTF8String(obj); if (tmp == NULL) { return -1; } size = PyString_Size(tmp); raw = PyString_AsString(tmp); if (raw == NULL) { return -1; } rc = add_raw(packer, raw, size); Py_DECREF(tmp); return rc; } if (PyString_Check(obj)) { Py_ssize_t size; unsigned char * raw; return (PyString_AsStringAndSize(obj, &raw, &size) == -1) ? -1 : add_raw(packer, raw, size); } #endif if (PyBytes_Check(obj)) { Py_ssize_t size; unsigned char * buffer; return (PyBytes_AsStringAndSize(obj, (char **) &buffer, &size) == -1) ? -1 : add_raw(packer, buffer, size); } PyErr_SetString( PyExc_TypeError, "packb(), trying to pack an unsupported type"); return -1; } static PyObject * unpackb( unsigned char ** pt, const unsigned char * const end, decode_t decode, int ignore_decode_errors) { PyObject * obj; unsigned char tp; if (*pt >= end) { PyErr_SetString(PyExc_ValueError, "unpackb() is missing data"); return NULL; } tp = **pt; (*pt)++; switch (tp) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: case 58: case 59: case 60: case 61: case 62: case 63: #if PY_MAJOR_VERSION >= 3 obj = PyLong_FromLong((long) tp); #else obj = PyInt_FromLong((long) tp); #endif return obj; case 64: case 65: case 66: case 67: case 68: case 69: case 70: case 71: case 72: case 73: case 74: case 75: case 76: case 77: case 78: case 79: case 80: case 81: case 82: case 83: case 84: case 85: case 86: case 87: case 88: case 89: case 90: case 91: case 92: case 93: case 94: case 95: case 96: case 97: case 98: case 99: case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: case 108: case 109: case 110: case 111: case 112: case 113: case 114: case 115: case 116: case 117: case 118: case 119: case 120: case 121: case 122: case 123: #if PY_MAJOR_VERSION >= 3 obj = PyLong_FromLong((long) 63 - tp); #else obj = PyInt_FromLong((long) 63 - tp); #endif return obj; case 124: /* This is actually reserved for an object hook but * return None as this is not implemented yet */ Py_INCREF(Py_None); return Py_None; case 125: obj = PyFloat_FromDouble(-1.0); return obj; case 126: obj = PyFloat_FromDouble(0.0); return obj; case 127: obj = PyFloat_FromDouble(1.0); return obj; case 128: case 129: case 130: case 131: case 132: case 133: case 134: case 135: case 136: case 137: case 138: case 139: case 140: case 141: case 142: case 143: case 144: case 145: case 146: case 147: case 148: case 149: case 150: case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159: case 160: case 161: case 162: case 163: case 164: case 165: case 166: case 167: case 168: case 169: case 170: case 171: case 172: case 173: case 174: case 175: case 176: case 177: case 178: case 179: case 180: case 181: case 182: case 183: case 184: case 185: case 186: case 187: case 188: case 189: case 190: case 191: case 192: case 193: case 194: case 195: case 196: case 197: case 198: case 199: case 200: case 201: case 202: case 203: case 204: case 205: case 206: case 207: case 208: case 209: case 210: case 211: case 212: case 213: case 214: case 215: case 216: case 217: case 218: case 219: case 220: case 221: case 222: case 223: case 224: case 225: case 226: case 227: { Py_ssize_t size = tp - 128; UNPACK_RAW(size, ignore_decode_errors) } case 228: UNPACK_FIXED_RAW(uint8_t, ignore_decode_errors) case 229: UNPACK_FIXED_RAW(uint16_t, ignore_decode_errors) case 230: UNPACK_FIXED_RAW(uint32_t, ignore_decode_errors) case 231: UNPACK_FIXED_RAW(uint64_t, ignore_decode_errors) case 232: UNPACK_INT(int8_t) case 233: UNPACK_INT(int16_t) case 234: UNPACK_INT(int32_t) case 235: UNPACK_INT(int64_t) case 236: UNPACK_CHECK_SZ(sizeof(double)) { double d; memcpy(&d, *pt, sizeof(double)); obj = PyFloat_FromDouble(d); } (*pt) += sizeof(double); return obj; case 237: case 238: case 239: case 240: case 241: case 242: { PyObject * o; Py_ssize_t size = tp - 237; obj = PyList_New(size); if (obj != NULL) { Py_ssize_t i; for (i = 0; i < size; i++) { o = unpackb(pt, end, decode, ignore_decode_errors); if (o == NULL || Py_QPackCHECK(o)) { SET_UNEXPECTED(o) Py_DECREF(obj); return NULL; } PyList_SET_ITEM(obj, i, o); } } return obj; } case 243: case 244: case 245: case 246: case 247: case 248: { int rc; PyObject * key; PyObject * value; Py_ssize_t size = tp - 243; obj = PyDict_New(); if (obj != NULL) { while (size--) { key = unpackb(pt, end, decode, ignore_decode_errors); if (key == NULL || Py_QPackCHECK(key)) { SET_UNEXPECTED(key) Py_DECREF(obj); return NULL; } else if (PyUnicode_CheckExact(key)) { PyUnicode_InternInPlace(&key); } value = unpackb(pt, end, decode, ignore_decode_errors); if (value == NULL || Py_QPackCHECK(value)) { SET_UNEXPECTED(value) Py_DECREF(key); Py_DECREF(obj); return NULL; } rc = PyDict_SetItem(obj, key, value); Py_DECREF(key); Py_DECREF(value); if (rc == -1) { Py_DECREF(obj); return NULL; } } } return obj; } case 249: Py_INCREF(Py_True); return Py_True; case 250: Py_INCREF(Py_False); return Py_False; case 251: Py_INCREF(Py_None); return Py_None; case 252: { int rc; PyObject * o; obj = PyList_New(0); if (obj != NULL) { while (*pt < end) { o = unpackb(pt, end, decode, ignore_decode_errors); if (o == NULL || o == &PY_MAP_CLOSE) { SET_UNEXPECTED(o) Py_DECREF(obj); return NULL; } else if (o == &PY_ARRAY_CLOSE) { break; } rc = PyList_Append(obj, o); Py_DECREF(o); if (rc == -1) { Py_DECREF(obj); return NULL; } } } return obj; } case 253: { int rc; PyObject * key; PyObject * value = NULL; obj = PyDict_New(); if (obj != NULL) { while (*pt < end) { key = unpackb(pt, end, decode, ignore_decode_errors); if (key == NULL || key == &PY_ARRAY_CLOSE) { SET_UNEXPECTED(key) Py_DECREF(obj); return NULL; } else if (key == &PY_MAP_CLOSE) { break; } else if (PyUnicode_CheckExact(key)) { PyUnicode_InternInPlace(&key); } value = unpackb(pt, end, decode, ignore_decode_errors); if (value == NULL || Py_QPackCHECK(value)) { SET_UNEXPECTED(value) Py_DECREF(key); Py_DECREF(obj); return NULL; } rc = PyDict_SetItem(obj, key, value); Py_DECREF(key); Py_DECREF(value); if (rc == -1) { Py_DECREF(obj); return NULL; } } } return obj; } case 254: return &PY_ARRAY_CLOSE; case 255: return &PY_MAP_CLOSE; } return NULL; } static PyObject * _qpack_packb( PyObject * self, PyObject * args, PyObject * kwargs) { PyObject * packed; PyObject * obj; Py_ssize_t size; packer_t * packer; packer = packer_new(); if (packer == NULL) { PyErr_SetString(PyExc_MemoryError, "Memory allocation error"); return NULL; } size = PyTuple_GET_SIZE(args); if (size != 1) { PyErr_SetString( PyExc_TypeError, "packb() missing 1 required positional argument: 'o'"); packer_free(packer); return NULL; } obj = PyTuple_GET_ITEM(args, 0); packed = (packb(obj, packer)) ? NULL: PyBytes_FromStringAndSize((const char *) packer->buffer, packer->len); packer_free(packer); return packed; } static PyObject * _qpack_unpackb( PyObject * self, PyObject * args, PyObject * kwargs) { PyObject * obj; PyObject * kw_decode; PyObject * kw_ignore_decode_errors; PyObject * o_decode; PyObject * o_ignore_decode_errors; PyObject * unpacked; Py_ssize_t size; decode_t decode = DECODE_NONE; unsigned char * buffer; int ignore_decode_errors = 0; /* false */ size = PyTuple_GET_SIZE(args); if (size != 1) { PyErr_SetString( PyExc_TypeError, "unpackb(), exactly one positional argument is expected"); return NULL; } obj = PyTuple_GET_ITEM(args, 0); if (kwargs) { kw_decode = Py_BuildValue("s", "decode"); kw_ignore_decode_errors = Py_BuildValue("s", "ignore_decode_errors"); o_decode = PyDict_GetItem(kwargs, kw_decode); o_ignore_decode_errors = PyDict_GetItem( kwargs, kw_ignore_decode_errors); Py_DECREF(kw_decode); Py_DECREF(kw_ignore_decode_errors); if (o_decode != NULL) { if (PY_COMPAT_CHECK(o_decode)) { if ( PY_COMPAT_COMPARE(o_decode, "utf-8") || PY_COMPAT_COMPARE(o_decode, "UTF-8") || PY_COMPAT_COMPARE(o_decode, "Utf-8") || PY_COMPAT_COMPARE(o_decode, "utf8") || PY_COMPAT_COMPARE(o_decode, "UTF8") || PY_COMPAT_COMPARE(o_decode, "Utf8")) { decode = DECODE_UTF8; } else if(PY_COMPAT_COMPARE(o_decode, "latin-1") || PY_COMPAT_COMPARE(o_decode, "LATIN-1") || PY_COMPAT_COMPARE(o_decode, "Latin-1") || PY_COMPAT_COMPARE(o_decode, "latin1") || PY_COMPAT_COMPARE(o_decode, "LATIN1") || PY_COMPAT_COMPARE(o_decode, "Latin1")) { decode = DECODE_LATIN1; } else { PyErr_SetString( PyExc_LookupError, "unpackb() unsupported encoding"); return NULL; } } else if (o_decode != Py_None) { PyErr_SetString( PyExc_LookupError, "unpackb() decode is expecting 'None' or a 'str' object"); return NULL; } if (o_ignore_decode_errors != NULL) { ignore_decode_errors = PyObject_IsTrue(o_ignore_decode_errors); } } } if (PyBytes_Check(obj)) { if (PyBytes_AsStringAndSize(obj, (char **) &buffer, &size) == -1) { return NULL; /* PyErr is set */ } } else if (PyByteArray_Check(obj)) { buffer = (unsigned char *) PyByteArray_AS_STRING(obj); size = PyByteArray_GET_SIZE(obj); } else { PyErr_SetString( PyExc_TypeError, "unpackb(), a bytes-like object is required"); return NULL; } unpacked = unpackb(&buffer, buffer + size, decode, ignore_decode_errors); return unpacked; } qpack-0.0.19/qpack/fallback.py000066400000000000000000000214631375276417100161330ustar00rootroot00000000000000'''QPack - (de)serializer :copyright: 2016, Jeroen van der Heijden (Transceptor Technology) ''' import sys import struct # for being Python2 and Python3 compatible if sys.version_info[0] == 3: PYTHON3 = True PY_CONVERT = int INT_TYPES = int STR = str intern = sys.intern def dict_items(d): return d.items() else: PYTHON3 = False PY_CONVERT = ord INT_TYPES = (int, long) STR = (unicode, str) range = xrange def dict_items(d): return d.iteritems() SIZE8_T = struct.Struct(' obj >= 0: container.append(struct.pack("B", obj)) return if 0 > obj >= -60: container.append(struct.pack("B", 63 - obj)) return bit_len = obj.bit_length() if bit_len < 8: container.append(QP_INT8) container.append(INT8_T.pack(obj)) elif bit_len < 16: container.append(QP_INT16) container.append(INT16_T.pack(obj)) elif bit_len < 32: container.append(QP_INT32) container.append(INT32_T.pack(obj)) elif bit_len < 64: container.append(QP_INT64) container.append(INT64_T.pack(obj)) else: raise OverflowError( 'qpack allows up to 64bit signed integers, ' 'got bit length: {}'.format(bit_len)) elif isinstance(obj, float): if obj == 0.0: container.append(QP_DOUBLE_0) elif obj == 1.0: container.append(QP_DOUBLE_1) elif obj == -1.0: container.append(QP_DOUBLE_N1) else: container.append(QP_DOUBLE) container.append(DOUBLE.pack(obj)) elif isinstance(obj, STR): b = obj.encode('utf-8') n = len(b) if n < 100: container.append(struct.pack("B", 128 + n)) elif n < 0x100: container.append(QP_RAW8) container.append(SIZE8_T.pack(n)) elif n < 0x10000: container.append(QP_RAW16) container.append(SIZE16_T.pack(n)) elif n < 0x100000000: container.append(QP_RAW32) container.append(SIZE32_T.pack(n)) elif n < 0x10000000000000000: container.append(QP_RAW64) container.append(SIZE64_T.pack(n)) else: raise ValueError( 'raw string length too large to fit in qpack: {}' .format(n)) container.append(b) elif isinstance(obj, bytes): n = len(obj) if n < 100: container.append(struct.pack("B", 128 + n)) elif n < 0x100: container.append(QP_RAW8) container.append(SIZE8_T.pack(n)) elif n < 0x10000: container.append(QP_RAW16) container.append(SIZE16_T.pack(n)) elif n < 0x100000000: container.append(QP_RAW32) container.append(SIZE32_T.pack(n)) elif n < 0x10000000000000000: container.append(QP_RAW64) container.append(SIZE64_T.pack(n)) else: raise ValueError( 'raw string length too large to fit in qpack: {}' .format(n)) container.append(obj) elif isinstance(obj, (list, tuple)): n = len(obj) if n < 6: container.append(SIZE8_T.pack(START_ARR + n)) for value in obj: _pack(value, container) else: container.append(QP_OPEN_ARRAY) for value in obj: _pack(value, container) container.append(QP_CLOSE_ARRAY) elif isinstance(obj, dict): n = len(obj) if n < 6: container.append(SIZE8_T.pack(START_MAP + n)) for key, value in dict_items(obj): _pack(key, container) _pack(value, container) else: container.append(QP_OPEN_MAP) for key, value in dict_items(obj): _pack(key, container) _pack(value, container) container.append(QP_CLOSE_MAP) else: raise TypeError( 'packing type {} is not supported with qpack'.format(type(obj))) def _decode(qp, pos, end_pos, decode, ignore_decode_errors): raw = qp[pos:end_pos] if decode is None: return raw if ignore_decode_errors: try: raw = raw.decode(decode) finally: return raw return raw.decode(decode) def _unpack(qp, pos, end, decode, ignore_decode_errors): tp = PY_CONVERT(qp[pos]) pos += 1 if tp < 64: return pos, tp if tp < 124: return pos, 63 - tp if tp == QP_HOOK: # This is reserved for an object hook. return pos, 0 if tp < 0x80: return pos, float(tp - 126) if tp < 0xe4: end_pos = pos + (tp - 128) return end_pos, _decode(qp, pos, end_pos, decode, ignore_decode_errors) if tp < 0xe8: qp_type = _RAW_MAP[tp] end_pos = pos + qp_type.size + qp_type.unpack_from(qp, pos)[0] pos += qp_type.size return end_pos, _decode(qp, pos, end_pos, decode, ignore_decode_errors) if tp < 0xed: # double included qp_type = _NUMBER_MAP[tp] return pos + qp_type.size, qp_type.unpack_from(qp, pos)[0] if tp < 0xf3: qp_array = [] for _ in range(tp - 0xed): pos, value = _unpack(qp, pos, end, decode, ignore_decode_errors) qp_array.append(value) return pos, qp_array if tp < 0xf9: qp_map = {} for _ in range(tp - 0xf3): pos, key = _unpack(qp, pos, end, decode, ignore_decode_errors) pos, value = _unpack(qp, pos, end, decode, ignore_decode_errors) if isinstance(key, str): intern(key) qp_map[key] = value return pos, qp_map if tp < 0xfc: return pos, _SIMPLE_MAP[tp] if tp == N_OPEN_ARRAY: qp_array = [] while pos < end and PY_CONVERT(qp[pos]) != N_CLOSE_ARRAY: pos, value = _unpack(qp, pos, end, decode, ignore_decode_errors) qp_array.append(value) return pos + 1, qp_array if tp == N_OPEN_MAP: qp_map = {} while pos < end and PY_CONVERT(qp[pos]) != N_CLOSE_MAP: pos, key = _unpack(qp, pos, end, decode, ignore_decode_errors) pos, value = _unpack(qp, pos, end, decode, ignore_decode_errors) if isinstance(key, str): intern(key) qp_map[key] = value return pos + 1, qp_map raise ValueError('Error in qpack at position {}'.format(pos)) def packb(obj): '''Serialize to QPack. (Pure Python implementation)''' container = [] _pack(obj, container) return b''.join(container) def unpackb(qp, decode=None, ignore_decode_errors=False): '''De-serialize QPack to Python. (Pure Python implementation)''' return _unpack(qp, 0, len(qp), decode, ignore_decode_errors)[1] if __name__ == '__main__': pass qpack-0.0.19/setup.cfg000066400000000000000000000000471375276417100145370ustar00rootroot00000000000000[metadata] description-file = README.mdqpack-0.0.19/setup.py000066400000000000000000000024271375276417100144340ustar00rootroot00000000000000"""setup.py Upload to PyPI: python setup.py sdist twine upload --repository pypitest dist/qpack-X.X.X.tar.gz twine upload --repository pypi dist/qpack-X.X.X.tar.gz """ import setuptools from distutils.core import setup, Extension from qpack import __version__ module = Extension( 'qpack._qpack', define_macros=[], include_dirs=['./qpack'], libraries=[], sources=['./qpack/_qpack.c'], extra_compile_args=["--std=c99", "-pedantic"] ) VERSION = __version__ setup( name='qpack', packages=['qpack'], version=VERSION, description='QPack (de)serializer', author='Jeroen van der Heijden', author_email='jeroen@transceptor.technology', url='https://github.com/transceptor-technology/qpack', ext_modules=[module], download_url='https://github.com/transceptor-technology/' 'qpack/tarball/{}'.format(VERSION), keywords=['serializer', 'deserializer'], classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Other Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Topic :: Software Development' ], ) qpack-0.0.19/test/000077500000000000000000000000001375276417100136745ustar00rootroot00000000000000qpack-0.0.19/test/qpack_test.py000066400000000000000000000100751375276417100164070ustar00rootroot00000000000000# -*- coding: utf-8 -*- import sys import qpack from qpack import fallback import unittest import pickle if sys.version_info[0] == 3: INT_CONVERT = int PYTHON3 = True else: INT_CONVERT = ord PYTHON3 = False class TestQpack(unittest.TestCase): CASES = [ [u" Hi Qpack", [ 140, 239, 163, 159, 32, 72, 105, 32, 81, 112, 97, 99, 107]], [True, [249]], [False, [250]], [None, [251]], [-1, [64]], [-60, [123]], [-61, [232, 195]], [0, [0]], [1, [1]], [4, [4]], [63, [63]], [64, [232, 64]], [-1.0, [125]], [0.0, [126]], [1.0, [127]], [-120, [232, 136]], [-0xfe, [233, 2, 255]], [-0xfedcba, [234, 70, 35, 1, 255]], [-0xfedcba9876, [235, 138, 103, 69, 35, 1, 255, 255, 255]], [120, [232, 120]], [0xfe, [233, 254, 0]], [0xfedcba, [234, 186, 220, 254, 0]], [0xfedcba9876, [235, 118, 152, 186, 220, 254, 0, 0, 0]], [-1.234567, [236, 135, 136, 155, 83, 201, 192, 243, 191]], [123.4567, [236, 83, 5, 163, 146, 58, 221, 94, 64]], [[0.0, 1.1, 2.2], [ 240, 126, 236, 154, 153, 153, 153, 153, 153, 241, 63, 236, 154, 153, 153, 153, 153, 153, 1, 64]], [[10, 20, 30, 40, 50], [242, 10, 20, 30, 40, 50]], [[10, 20, 30, 40, 50, 60], [ 252, 10, 20, 30, 40, 50, 60, 254]], [[0, {"Names": ["Iris", "Sasha"]}], [ 239, 0, 244, 133, 78, 97, 109, 101, 115, 239, 132, 73, 114, 105, 115, 133, 83, 97, 115, 104, 97]]] def _pack(self, packb): for inp, want in self.CASES: out = packb(inp) self.assertEqual([INT_CONVERT(i) for i in out], want) def _unpack(self, unpackb): for inp, want in self.CASES: out = unpackb(qpack.packb(inp), decode='utf8') self.assertEqual(out, inp) def test_packb(self): self.assertEqual( qpack.packb.__doc__, 'Serialize a Python object to QPack format.') self._pack(qpack.packb) def test_fallback_packb(self): self.assertEqual( fallback.packb.__doc__, 'Serialize to QPack. (Pure Python implementation)') self._pack(fallback.packb) def test_unpackb(self): self.assertNotIn( 'Pure Python implementation', qpack.unpackb.__doc__) self._unpack(qpack.unpackb) def test_fallback_unpackb(self): self.assertIn( 'Pure Python implementation', fallback.unpackb.__doc__) self._unpack(fallback.unpackb) def test_packb_unsupported(self): with self.assertRaises(TypeError): fallback.packb({'module': sys}) with self.assertRaises(TypeError): qpack.packb({'module': sys}) def test_decode(self): if not PYTHON3: return bindata = pickle.dumps({}) data = ['normal', bindata] packed = qpack.packb(data) s, b = qpack.unpackb(packed) self.assertEqual(type(s).__name__, 'bytes') self.assertEqual(type(b).__name__, 'bytes') with self.assertRaises(ValueError): s, b = qpack.unpackb(packed, decode='utf8') s, b = qpack.unpackb(packed, decode='utf8', ignore_decode_errors=True) self.assertEqual(type(s).__name__, 'str') self.assertEqual(type(b).__name__, 'bytes') def test_fallback_decode(self): if not PYTHON3: return bindata = pickle.dumps({}) data = ['normal', bindata] packed = fallback.packb(data) s, b = fallback.unpackb(packed) self.assertEqual(type(s).__name__, 'bytes') self.assertEqual(type(b).__name__, 'bytes') with self.assertRaises(ValueError): s, b = fallback.unpackb(packed, decode='utf8') s, b = fallback.unpackb( packed, decode='utf8', ignore_decode_errors=True) self.assertEqual(type(s).__name__, 'str') self.assertEqual(type(b).__name__, 'bytes') if __name__ == '__main__': unittest.main()