diff options
| author | Scott Shawcroft <scott@adafruit.com> | 2021-01-11 15:22:39 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-01-11 15:22:39 -0800 |
| commit | 91245297e827494b6d4f51991ecbdc26acfed36d (patch) | |
| tree | 7669bacfde989aebccd2d44461785affc532a388 | |
| parent | 1fb3d2448c8cb9734fe7f2a739cc7ed27fe4ca2f (diff) | |
| parent | 13efbf24e58b2be940cad44e4f0816d6daa67202 (diff) | |
Merge pull request #3659 from iot49/msgpack
module msgpack
27 files changed, 952 insertions, 0 deletions
diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index f319b82f8..3ca08b2a9 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -276,6 +276,10 @@ msgstr "" msgid "3-arg pow() not supported" msgstr "" +#: shared-module/msgpack/__init__.c +msgid "64 bit types" +msgstr "" + #: ports/atmel-samd/common-hal/countio/Counter.c #: ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c msgid "A hardware interrupt channel is already in use" @@ -2426,6 +2430,10 @@ msgstr "" msgid "circle can only be registered in one parent" msgstr "" +#: shared-bindings/msgpack/ExtType.c +msgid "code outside range 0~127" +msgstr "" + #: shared-bindings/displayio/Palette.c msgid "color buffer must be 3 bytes (RGB) or 4 bytes (RGB + pad byte)" msgstr "" @@ -2510,6 +2518,10 @@ msgstr "" msgid "default 'except' must be last" msgstr "" +#: shared-bindings/msgpack/__init__.c +msgid "default is not a function" +msgstr "" + #: shared-bindings/audiobusio/PDMIn.c msgid "" "destination buffer must be a bytearray or array of type 'B' for bit_depth = 8" @@ -2609,6 +2621,10 @@ msgstr "" msgid "expecting key:value for dict" msgstr "" +#: shared-bindings/msgpack/__init__.c +msgid "ext_hook is not a function" +msgstr "" + #: py/argcheck.c msgid "extra keyword arguments given" msgstr "" @@ -3141,6 +3157,10 @@ msgstr "" msgid "no binding for nonlocal found" msgstr "" +#: shared-module/msgpack/__init__.c +msgid "no default packer" +msgstr "" + #: py/builtinimport.c msgid "no module named '%q'" msgstr "" @@ -3474,6 +3494,10 @@ msgstr "" msgid "shape must be a tuple" msgstr "" +#: shared-module/msgpack/__init__.c +msgid "short read" +msgstr "" + #: py/objstr.c msgid "sign not allowed in string format specifier" msgstr "" diff --git a/ports/atmel-samd/boards/bdmicro_vina_d21/mpconfigboard.mk b/ports/atmel-samd/boards/bdmicro_vina_d21/mpconfigboard.mk index a0d9a779f..cff5a32d1 100644 --- a/ports/atmel-samd/boards/bdmicro_vina_d21/mpconfigboard.mk +++ b/ports/atmel-samd/boards/bdmicro_vina_d21/mpconfigboard.mk @@ -14,6 +14,7 @@ LONGINT_IMPL = MPZ CIRCUITPY_BITBANGIO = 0 CIRCUITPY_COUNTIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_VECTORIO = 0 CIRCUITPY_BUSDEVICE = 0 diff --git a/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk b/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk index 13ec9e861..505f5c145 100644 --- a/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk @@ -15,6 +15,7 @@ LONGINT_IMPL = MPZ CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 +CIRCUITPY_MSGPACK = 0 MICROPY_PY_ASYNC_AWAIT = 0 SUPEROPT_GC = 0 diff --git a/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk b/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk index 7aa45eb39..c3be33134 100644 --- a/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk +++ b/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk @@ -16,6 +16,7 @@ CIRCUITPY_BITBANGIO = 0 CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_PIXELBUF = 1 CIRCUITPY_ROTARYIO = 0 CIRCUITPY_RTC = 0 diff --git a/ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.mk b/ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.mk index 3a43093a9..51e9b05af 100644 --- a/ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.mk +++ b/ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.mk @@ -16,6 +16,7 @@ CIRCUITPY_BITBANGIO = 0 CIRCUITPY_COUNTIO = 0 CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_PIXELBUF = 0 CIRCUITPY_ROTARYIO = 0 CIRCUITPY_RTC = 0 diff --git a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk index 2fe085567..d1a6fc7ae 100644 --- a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk @@ -15,6 +15,7 @@ CIRCUITPY_BITBANGIO = 0 CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_COUNTIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_VECTORIO = 0 CIRCUITPY_BUSDEVICE = 0 diff --git a/ports/atmel-samd/boards/feather_m0_express_crickit/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_express_crickit/mpconfigboard.mk index 5624144e8..f06163b84 100644 --- a/ports/atmel-samd/boards/feather_m0_express_crickit/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_express_crickit/mpconfigboard.mk @@ -18,6 +18,7 @@ CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 CIRCUITPY_GAMEPAD = 0 CFLAGS_INLINE_LIMIT = 50 +CIRCUITPY_MSGPACK = 0 # Include these Python libraries in firmware. FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice diff --git a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk index c2d692f9b..3078544fc 100644 --- a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk @@ -15,6 +15,7 @@ CIRCUITPY_BITBANGIO = 0 CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_COUNTIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 +CIRCUITPY_MSGPACK = 0 # supersized, not ultra-supersized CIRCUITPY_VECTORIO = 0 CIRCUITPY_BUSDEVICE = 0 diff --git a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk index c35854758..03f633e6d 100644 --- a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk @@ -14,6 +14,7 @@ LONGINT_IMPL = MPZ CIRCUITPY_BITBANGIO = 0 CIRCUITPY_COUNTIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_VECTORIO = 0 CIRCUITPY_BUSDEVICE = 0 diff --git a/ports/atmel-samd/boards/qtpy_m0_haxpress/mpconfigboard.mk b/ports/atmel-samd/boards/qtpy_m0_haxpress/mpconfigboard.mk index a63f14274..8773c5771 100644 --- a/ports/atmel-samd/boards/qtpy_m0_haxpress/mpconfigboard.mk +++ b/ports/atmel-samd/boards/qtpy_m0_haxpress/mpconfigboard.mk @@ -17,6 +17,7 @@ CIRCUITPY_BITBANGIO = 0 CIRCUITPY_COUNTIO = 0 CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 +CIRCUITPY_MSGPACK = 0 SUPEROPT_GC = 0 diff --git a/ports/atmel-samd/boards/snekboard/mpconfigboard.mk b/ports/atmel-samd/boards/snekboard/mpconfigboard.mk index 67e5b2312..889490450 100644 --- a/ports/atmel-samd/boards/snekboard/mpconfigboard.mk +++ b/ports/atmel-samd/boards/snekboard/mpconfigboard.mk @@ -14,6 +14,7 @@ LONGINT_IMPL = MPZ CIRCUITPY_BITBANGIO = 0 CIRCUITPY_GAMEPAD = 0 CIRCUITPY_I2CPERIPHERAL = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_VECTORIO = 0 CIRCUITPY_BUSDEVICE = 0 diff --git a/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk b/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk index 590c4795f..5b7f93328 100755 --- a/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk +++ b/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk @@ -15,6 +15,7 @@ CIRCUITPY_BITBANGIO = 0 CIRCUITPY_COUNTIO = 0 CIRCUITPY_GAMEPAD = 0 CIRCUITPY_I2CPERIPHERAL = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_VECTORIO = 0 CIRCUITPY_BUSDEVICE = 0 diff --git a/ports/atmel-samd/boards/stackrduino_m0_pro/mpconfigboard.mk b/ports/atmel-samd/boards/stackrduino_m0_pro/mpconfigboard.mk index 704d26514..734be2d14 100644 --- a/ports/atmel-samd/boards/stackrduino_m0_pro/mpconfigboard.mk +++ b/ports/atmel-samd/boards/stackrduino_m0_pro/mpconfigboard.mk @@ -16,6 +16,7 @@ CIRCUITPY_BITBANGIO = 0 CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_COUNTIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_VECTORIO = 0 CIRCUITPY_BUSDEVICE = 0 diff --git a/ports/cxd56/boards/spresense/mpconfigboard.mk b/ports/cxd56/boards/spresense/mpconfigboard.mk index 7b8ac6ff6..80c6e4b69 100644 --- a/ports/cxd56/boards/spresense/mpconfigboard.mk +++ b/ports/cxd56/boards/spresense/mpconfigboard.mk @@ -4,3 +4,5 @@ USB_PRODUCT = "Spresense" USB_MANUFACTURER = "Sony" INTERNAL_FLASH_FILESYSTEM = 1 + +CIRCUITPY_MSGPACK = 0 diff --git a/ports/nrf/boards/pca10100/mpconfigboard.mk b/ports/nrf/boards/pca10100/mpconfigboard.mk index 86ba65454..76d15e608 100644 --- a/ports/nrf/boards/pca10100/mpconfigboard.mk +++ b/ports/nrf/boards/pca10100/mpconfigboard.mk @@ -15,6 +15,7 @@ CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_FRAMEBUFFERIO = 0 CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_NEOPIXEL_WRITE = 0 CIRCUITPY_NVM = 0 CIRCUITPY_PIXELBUF = 0 diff --git a/ports/nrf/boards/simmel/mpconfigboard.mk b/ports/nrf/boards/simmel/mpconfigboard.mk index dd6b1fbfd..4eb6c98c7 100644 --- a/ports/nrf/boards/simmel/mpconfigboard.mk +++ b/ports/nrf/boards/simmel/mpconfigboard.mk @@ -16,6 +16,7 @@ CIRCUITPY_AUDIOMP3 = 0 CIRCUITPY_BUSIO = 1 CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_FRAMEBUFFERIO = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_GAMEPAD = 0 CIRCUITPY_NEOPIXEL_WRITE = 0 CIRCUITPY_NVM = 0 diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index d6d192a95..ca8d548f3 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -292,6 +292,9 @@ endif ifeq ($(CIRCUITPY_PEW),1) SRC_PATTERNS += _pew/% endif +ifeq ($(CIRCUITPY_MSGPACK),1) +SRC_PATTERNS += msgpack/% +endif # All possible sources are listed here, and are filtered by SRC_PATTERNS in SRC_COMMON_HAL SRC_COMMON_HAL_ALL = \ @@ -412,6 +415,8 @@ $(filter $(SRC_PATTERNS), \ math/__init__.c \ microcontroller/ResetReason.c \ microcontroller/RunMode.c \ + msgpack/__init__.c \ + msgpack/ExtType.c \ supervisor/RunReason.c \ ) @@ -481,6 +486,7 @@ SRC_SHARED_MODULE_ALL = \ memorymonitor/AllocationAlarm.c \ memorymonitor/AllocationSize.c \ network/__init__.c \ + msgpack/__init__.c \ os/__init__.c \ random/__init__.c \ rgbmatrix/RGBMatrix.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 08c92a77e..a3411b7ad 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -774,6 +774,13 @@ extern const struct _mp_obj_module_t wifi_module; #define WIFI_MODULE #endif +#if CIRCUITPY_MSGPACK +extern const struct _mp_obj_module_t msgpack_module; +#define MSGPACK_MODULE { MP_ROM_QSTR(MP_QSTR_msgpack), MP_ROM_PTR(&msgpack_module) }, +#else +#define MSGPACK_MODULE +#endif + // Define certain native modules with weak links so they can be replaced with Python // implementations. This list may grow over time. #define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \ @@ -830,6 +837,7 @@ extern const struct _mp_obj_module_t wifi_module; _EVE_MODULE \ MEMORYMONITOR_MODULE \ MICROCONTROLLER_MODULE \ + MSGPACK_MODULE \ NEOPIXEL_WRITE_MODULE \ NETWORK_MODULE \ SOCKET_MODULE \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index e588c41e0..a01da2956 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -308,3 +308,6 @@ CFLAGS += -DCIRCUITPY_WIFI=$(CIRCUITPY_WIFI) # Enabled micropython.native decorator (experimental) CIRCUITPY_ENABLE_MPY_NATIVE ?= 0 CFLAGS += -DCIRCUITPY_ENABLE_MPY_NATIVE=$(CIRCUITPY_ENABLE_MPY_NATIVE) + +CIRCUITPY_MSGPACK ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_MSGPACK=$(CIRCUITPY_MSGPACK) diff --git a/shared-bindings/msgpack/ExtType.c b/shared-bindings/msgpack/ExtType.c new file mode 100644 index 000000000..e9ddd32bc --- /dev/null +++ b/shared-bindings/msgpack/ExtType.c @@ -0,0 +1,126 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Bernhard Boser + * + * 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. + */ + +#include "py/runtime.h" +#include "py/smallint.h" +#include "py/objproperty.h" +#include "shared-bindings/msgpack/ExtType.h" + +//| class ExtType: +//| """ExtType represents ext type in msgpack.""" +//| def __init__(self, code: int, data: bytes) -> None: +//| """Constructor +//| :param int code: type code in range 0~127. +//| :param bytes data: representation.""" +//| +STATIC mp_obj_t mod_msgpack_exttype_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + mod_msgpack_extype_obj_t *self = m_new_obj(mod_msgpack_extype_obj_t); + self->base.type = &mod_msgpack_exttype_type; + enum { ARG_code, ARG_data }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_code, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_data, MP_ARG_OBJ | MP_ARG_REQUIRED }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + int code = args[ARG_code].u_int; + if (code < 0 || code > 127) { + mp_raise_AttributeError(translate("code outside range 0~127")); + } + self->code = code; + + mp_obj_t data = args[ARG_data].u_obj; + self->data = data; + return MP_OBJ_FROM_PTR(self); +} + + +//| code: int +//| """The type code, in range 0~127.""" +//| ... +//| +STATIC mp_obj_t mod_msgpack_exttype_get_code(mp_obj_t self_in) { + mod_msgpack_extype_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(self->code); +} +MP_DEFINE_CONST_FUN_OBJ_1(mod_msgpack_exttype_get_code_obj, mod_msgpack_exttype_get_code); + +STATIC mp_obj_t mod_msgpack_exttype_set_code(mp_obj_t self_in, mp_obj_t code_in) { + mod_msgpack_extype_obj_t *self = MP_OBJ_TO_PTR(self_in); + int code = mp_obj_get_int(code_in); + if (code < 0 || code > 127) { + mp_raise_AttributeError(translate("code outside range 0~127")); + } + self->code = code; + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(mod_msgpack_exttype_set_code_obj, mod_msgpack_exttype_set_code); + +const mp_obj_property_t mod_msgpack_exttype_code_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&mod_msgpack_exttype_get_code_obj, + (mp_obj_t)&mod_msgpack_exttype_set_code_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| data: bytes +//| """Data.""" +//| ... +//| +STATIC mp_obj_t mod_msgpack_exttype_get_data(mp_obj_t self_in) { + mod_msgpack_extype_obj_t *self = MP_OBJ_TO_PTR(self_in); + return self->data; +} +MP_DEFINE_CONST_FUN_OBJ_1(mod_msgpack_exttype_get_data_obj, mod_msgpack_exttype_get_data); + +STATIC mp_obj_t mod_msgpack_exttype_set_data(mp_obj_t self_in, mp_obj_t data_in) { + mod_msgpack_extype_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->data = data_in; + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(mod_msgpack_exttype_set_data_obj, mod_msgpack_exttype_set_data); + +const mp_obj_property_t mod_msgpack_exttype_data_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&mod_msgpack_exttype_get_data_obj, + (mp_obj_t)&mod_msgpack_exttype_set_data_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +STATIC mp_rom_map_elem_t mod_msgpack_exttype_locals_dict_table[] = { + // Properties + { MP_ROM_QSTR(MP_QSTR_code), MP_ROM_PTR(&mod_msgpack_exttype_code_obj) }, + { MP_ROM_QSTR(MP_QSTR_data), MP_ROM_PTR(&mod_msgpack_exttype_data_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(mod_msgpack_exttype_locals_dict, mod_msgpack_exttype_locals_dict_table); + +const mp_obj_type_t mod_msgpack_exttype_type = { + { &mp_type_type }, + .name = MP_QSTR_ExtType, + .make_new = mod_msgpack_exttype_make_new, + .locals_dict = (mp_obj_dict_t*)&mod_msgpack_exttype_locals_dict, +}; diff --git a/shared-bindings/msgpack/ExtType.h b/shared-bindings/msgpack/ExtType.h new file mode 100644 index 000000000..64173b221 --- /dev/null +++ b/shared-bindings/msgpack/ExtType.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Bernhard Boser + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_MSGPACK_EXTTYPE___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_MSGPACK_EXTTYPE___INIT___H + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + int32_t code; + mp_obj_t data; +} mod_msgpack_extype_obj_t; + +extern const mp_obj_type_t mod_msgpack_exttype_type; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_MSGPACK_EXTTYPE___INIT___H diff --git a/shared-bindings/msgpack/__init__.c b/shared-bindings/msgpack/__init__.c new file mode 100644 index 000000000..f5c47bb21 --- /dev/null +++ b/shared-bindings/msgpack/__init__.c @@ -0,0 +1,162 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Bernhard Boser + * + * 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. + */ + +#include <stdio.h> +#include "py/obj.h" +#include "py/runtime.h" +#include "shared-bindings/msgpack/__init__.h" +#include "shared-module/msgpack/__init__.h" +#include "shared-bindings/msgpack/ExtType.h" + +#define MP_OBJ_IS_METH(o) (MP_OBJ_IS_OBJ(o) && (((mp_obj_base_t*)MP_OBJ_TO_PTR(o))->type->name == MP_QSTR_bound_method)) + +//| """Pack object in msgpack format +//| +//| The msgpack format is similar to json, except that the encoded data is binary. +//| See https://msgpack.org for details. The module implements a subset of the cpython +//| module msgpack-python. +//| +//| Not implemented: 64-bit int, uint, float. +//| +//| Example 1:: +//| +//| import msgpack +//| from io import BytesIO +//| +//| b = BytesIO() +//| msgpack.pack({'list': [True, False, None, 1, 3.14], 'str': 'blah'}, b) +//| b.seek(0) +//| print(msgpack.unpack(b)) +//| +//| Example 2: handling objects:: +//| +//| from msgpack import pack, unpack, ExtType +//| from io import BytesIO +//| +//| class MyClass: +//| def __init__(self, val): +//| self.value = val +//| def __str__(self): +//| return str(self.value) +//| +//| data = MyClass(b'my_value') +//| +//| def encoder(obj): +//| if isinstance(obj, MyClass): +//| return ExtType(1, obj.value) +//| return f"no encoder for {obj}" +//| +//| def decoder(code, data): +//| if code == 1: +//| return MyClass(data) +//| return f"no decoder for type {code}" +//| +//| buffer = BytesIO() +//| pack(data, buffer, default=encoder) +//| buffer.seek(0) +//| decoded = unpack(buffer, ext_hook=decoder) +//| print(f"{data} -> {buffer.getvalue()} -> {decoded}") +//| +//| """ +//| + +//| def pack(obj: object, buffer: WriteableBuffer, *, default: Union[Callable[[object], None], None] = None) -> None: +//| """Ouput object to buffer in msgpack format. +//| +//| :param object obj: Object to convert to msgpack format. +//| :param ~_typing.WriteableBuffer buffer: buffer to write into +//| :param Optional[~_typing.Callable[[object], None]] default: +//| function called for python objects that do not have +//| a representation in msgpack format. +//| """ +//| ... +//| +STATIC mp_obj_t mod_msgpack_pack(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_obj, ARG_buffer, ARG_default }; + STATIC const mp_arg_t allowed_args[] = { + { MP_QSTR_obj, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_default, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_obj = mp_const_none } }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t handler = args[ARG_default].u_obj; + if (handler != mp_const_none && !MP_OBJ_IS_FUN(handler) && !MP_OBJ_IS_METH(handler)) { + mp_raise_ValueError(translate("default is not a function")); + } + + common_hal_msgpack_pack(args[ARG_obj].u_obj, args[ARG_buffer].u_obj, handler); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mod_msgpack_pack_obj, 1, mod_msgpack_pack); + + +//| def unpack(buffer: ReadableBuffer, *, ext_hook: Union[Callable[[int, bytes], object], None] = None, use_list: bool=True) -> object: +//| """Unpack and return one object from buffer. +//| +//| :param ~_typing.ReadableBuffer buffer: buffer to read from +//| :param Optional[~_typing.Callable[[int, bytes], object]] ext_hook: function called for objects in +//| msgpack ext format. +//| :param Optional[bool] use_list: return array as list or tuple (use_list=False). +//| +//| :return object: object read from buffer. +//| """ +//| ... +//| +STATIC mp_obj_t mod_msgpack_unpack(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buffer, ARG_ext_hook, ARG_use_list }; + STATIC const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, }, + { MP_QSTR_ext_hook, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_obj = mp_const_none } }, + { MP_QSTR_use_list, MP_ARG_KW_ONLY | MP_ARG_BOOL, { .u_bool = true } }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t hook = args[ARG_ext_hook].u_obj; + if (hook != mp_const_none && !MP_OBJ_IS_FUN(hook) && !MP_OBJ_IS_METH(hook)) { + mp_raise_ValueError(translate("ext_hook is not a function")); + } + + return common_hal_msgpack_unpack(args[ARG_buffer].u_obj, hook, args[ARG_use_list].u_bool); +} +MP_DEFINE_CONST_FUN_OBJ_KW(mod_msgpack_unpack_obj, 1, mod_msgpack_unpack); + + +STATIC const mp_rom_map_elem_t msgpack_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_msgpack) }, + { MP_ROM_QSTR(MP_QSTR_ExtType), MP_ROM_PTR(&mod_msgpack_exttype_type) }, + { MP_ROM_QSTR(MP_QSTR_pack), MP_ROM_PTR(&mod_msgpack_pack_obj) }, + { MP_ROM_QSTR(MP_QSTR_unpack), MP_ROM_PTR(&mod_msgpack_unpack_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(msgpack_module_globals, msgpack_module_globals_table); + +const mp_obj_module_t msgpack_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&msgpack_module_globals, +}; diff --git a/shared-bindings/msgpack/__init__.h b/shared-bindings/msgpack/__init__.h new file mode 100644 index 000000000..a02ead0bd --- /dev/null +++ b/shared-bindings/msgpack/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_MSGPACK___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_MSGPACK___INIT___H + +#include "py/obj.h" + +// nothing for now + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_MSGPACK___INIT___H diff --git a/shared-module/msgpack/__init__.c b/shared-module/msgpack/__init__.c new file mode 100644 index 000000000..39178d46e --- /dev/null +++ b/shared-module/msgpack/__init__.c @@ -0,0 +1,464 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Bernhard Boser + * + * 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. + */ + +#include <stdio.h> +#include <inttypes.h> + +#include "py/obj.h" +#include "py/binary.h" +#include "py/objarray.h" +#include "py/objlist.h" +#include "py/objstringio.h" +#include "py/parsenum.h" +#include "py/runtime.h" +#include "py/stream.h" + +#include "supervisor/shared/translate.h" +#include "shared-bindings/msgpack/ExtType.h" + +//////////////////////////////////////////////////////////////// +// stream management + +typedef struct _msgpack_stream_t { + mp_obj_t stream_obj; + mp_uint_t (*read)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode); + mp_uint_t (*write)(mp_obj_t obj, const void *buf, mp_uint_t size, int *errcode); + int errcode; +} msgpack_stream_t; + +STATIC msgpack_stream_t get_stream(mp_obj_t stream_obj, int flags) { + const mp_stream_p_t *stream_p = mp_get_stream_raise(stream_obj, flags); + msgpack_stream_t s = {stream_obj, stream_p->read, stream_p->write, 0}; + return s; +} + +//////////////////////////////////////////////////////////////// +// readers + +STATIC void read(msgpack_stream_t *s, void *buf, mp_uint_t size) { + if (size == 0) return; + mp_uint_t ret = s->read(s->stream_obj, buf, size, &s->errcode); + if (s->errcode != 0) { + mp_raise_OSError(s->errcode); + } + if (ret == 0) { + mp_raise_msg(&mp_type_EOFError, NULL); + } + if (ret < size) { + mp_raise_ValueError(translate("short read")); + } +} + +STATIC uint8_t read1(msgpack_stream_t *s) { + uint8_t res = 0; + read(s, &res, 1); + return res; +} + +STATIC uint16_t read2(msgpack_stream_t *s) { + uint16_t res = 0; + read(s, &res, 2); + int n = 1; + if (*(char *)&n == 1) res = __builtin_bswap16(res); + return res; +} + +STATIC uint32_t read4(msgpack_stream_t *s) { + uint32_t res = 0; + read(s, &res, 4); + int n = 1; + if (*(char *)&n == 1) res = __builtin_bswap32(res); + return res; +} + +STATIC size_t read_size(msgpack_stream_t *s, uint8_t len_index) { + size_t res = 0; + switch (len_index) { + case 0: res = (size_t)read1(s); break; + case 1: res = (size_t)read2(s); break; + case 2: res = (size_t)read4(s); break; + } + return res; +} + +//////////////////////////////////////////////////////////////// +// writers + +STATIC void write(msgpack_stream_t *s, const void *buf, mp_uint_t size) { + mp_uint_t ret = s->write(s->stream_obj, buf, size, &s->errcode); + if (s->errcode != 0) { + mp_raise_OSError(s->errcode); + } + if (ret == 0) { + mp_raise_msg(&mp_type_EOFError, NULL); + } +} + +STATIC void write1(msgpack_stream_t *s, uint8_t obj) { + write(s, &obj, 1); +} + +STATIC void write2(msgpack_stream_t *s, uint16_t obj) { + int n = 1; + if (*(char *)&n == 1) obj = __builtin_bswap16(obj); + write(s, &obj, 2); +} + +STATIC void write4(msgpack_stream_t *s, uint32_t obj) { + int n = 1; + if (*(char *)&n == 1) obj = __builtin_bswap32(obj); + write(s, &obj, 4); +} + +// compute and write msgpack size code (array structures) +STATIC void write_size(msgpack_stream_t *s, uint8_t code, size_t size) { + if ((uint8_t)size == size) { + write1(s, code); + write1(s, size); + } else if ((uint16_t)size == size) { + write1(s, code+1); + write2(s, size); + } else { + write1(s, code+2); + write4(s, size); + } +} + +//////////////////////////////////////////////////////////////// +// packers + +// This is a helper function to iterate through a dictionary. The state of +// the iteration is held in *cur and should be initialised with zero for the +// first call. Will return NULL when no more elements are available. +STATIC mp_map_elem_t *dict_iter_next(mp_obj_dict_t *dict, size_t *cur) { + size_t max = dict->map.alloc; + mp_map_t *map = &dict->map; + + for (size_t i = *cur; i < max; i++) { + if (MP_MAP_SLOT_IS_FILLED(map, i)) { + *cur = i + 1; + return &(map->table[i]); + } + } + + return NULL; +} + +STATIC void pack_int(msgpack_stream_t *s, int32_t x) { + if (x > -32 && x < 128) { + write1(s, x); + } else if ((int8_t)x == x) { + write1(s, 0xd0); + write1(s, x); + } else if ((int16_t)x == x) { + write1(s, 0xd1); + write2(s, x); + } else { + write1(s, 0xd2); + write4(s, x); + } +} + +STATIC void pack_bin(msgpack_stream_t *s, const uint8_t* data, size_t len) { + write_size(s, 0xc4, len); + if (len > 0) write(s, data, len); +} + +STATIC void pack_ext(msgpack_stream_t *s, int8_t code, const uint8_t* data, size_t len) { + if (len == 1) { + write1(s, 0xd4); + } else if (len == 2) { + write1(s, 0xd5); + } else if (len == 4) { + write1(s, 0xd6); + } else if (len == 8) { + write1(s, 0xd7); + } else if (len == 16) { + write1(s, 0xd8); + } else { + write_size(s, 0xc7, len); + } + write1(s, code); // type byte + if (len > 0) write(s, data, len); +} + +STATIC void pack_str(msgpack_stream_t *s, const char* str, size_t len) { + if (len < 32) { + write1(s, 0b10100000 | (uint8_t)len); + } else { + write_size(s, 0xd9, len); + } + if (len > 0) write(s, str, len); +} + +STATIC void pack_array(msgpack_stream_t *s, size_t len) { + // only writes the header, manually write the objects after calling pack_array! + if (len < 16) { + write1(s, 0b10010000 | (uint8_t)len); + } else if (len < 0x10000) { + write1(s, 0xdc); + write2(s, len); + } else { + write1(s, 0xdd); + write4(s, len); + } +} + +STATIC void pack_dict(msgpack_stream_t *s, size_t len) { + // only writes the header, manually write the objects after calling pack_array! + if (len < 16) { + write1(s, 0b10000000 | (uint8_t)len); + } else if (len < 0x10000) { + write1(s, 0xde); + write2(s, len); + } else { + write1(s, 0xdf); + write4(s, len); + } +} + +STATIC void pack(mp_obj_t obj, msgpack_stream_t *s, mp_obj_t default_handler) { + if (MP_OBJ_IS_SMALL_INT(obj)) { + // int + int32_t x = MP_OBJ_SMALL_INT_VALUE(obj); + pack_int(s, x); + } else if (MP_OBJ_IS_STR(obj)) { + // string + size_t len; + const char *data = mp_obj_str_get_data(obj, &len); + pack_str(s, data, len); + } else if (MP_OBJ_IS_TYPE(obj, &mod_msgpack_exttype_type)) { + mod_msgpack_extype_obj_t *ext = MP_OBJ_TO_PTR(obj); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(ext->data, &bufinfo, MP_BUFFER_READ); + pack_ext(s, ext->code, bufinfo.buf, bufinfo.len); + } else if (MP_OBJ_IS_TYPE(obj, &mp_type_bytes)) { + // bytes + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(obj, &bufinfo, MP_BUFFER_READ); + pack_bin(s, bufinfo.buf, bufinfo.len); + } else if (MP_OBJ_IS_TYPE(obj, &mp_type_tuple)) { + // tuple + mp_obj_tuple_t *self = MP_OBJ_TO_PTR(obj); + pack_array(s, self->len); + for (size_t i=0; i<self->len; i++) { + pack(self->items[i], s, default_handler); + } + } else if (MP_OBJ_IS_TYPE(obj, &mp_type_list)) { + // list (layout differs from tuple) + mp_obj_list_t *self = MP_OBJ_TO_PTR(obj); + pack_array(s, self->len); + for (size_t i=0; i<self->len; i++) { + pack(self->items[i], s, default_handler); + } + } else if (MP_OBJ_IS_TYPE(obj, &mp_type_dict)) { + // dict + mp_obj_dict_t *self = MP_OBJ_TO_PTR(obj); + pack_dict(s, self->map.used); + size_t cur = 0; + mp_map_elem_t *next = NULL; + while ((next = dict_iter_next(self, &cur)) != NULL) { + pack(next->key, s, default_handler); + pack(next->value, s, default_handler); + } + } else if (mp_obj_is_float(obj)) { + union Float { mp_float_t f; uint32_t u; }; + union Float data; + data.f = mp_obj_float_get(obj); + write1(s, 0xca); + write4(s, data.u); + } else if (obj == mp_const_none) { + write1(s, 0xc0); + } else if (obj == mp_const_false) { + write1(s, 0xc2); + } else if (obj == mp_const_true) { + write1(s, 0xc3); + } else { + if (default_handler != mp_const_none) { + // set default_handler to mp_const_none to avoid infinite recursion + // this also precludes some valid outputs + pack(mp_call_function_1(default_handler, obj), s, mp_const_none); + } else { + mp_raise_ValueError(translate("no default packer")); + } + } +} + +//////////////////////////////////////////////////////////////// +// unpacker + +STATIC mp_obj_t unpack(msgpack_stream_t *s, mp_obj_t ext_hook, bool use_list); + +STATIC mp_obj_t unpack_array_elements(msgpack_stream_t *s, size_t size, mp_obj_t ext_hook, bool use_list) { + if (use_list) { + mp_obj_list_t *t = MP_OBJ_TO_PTR(mp_obj_new_list(size, NULL)); + for (size_t i=0; i<size; i++) { + t->items[i] = unpack(s, ext_hook, use_list); + } + return MP_OBJ_FROM_PTR(t); + } else { + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(size, NULL)); + for (size_t i=0; i<size; i++) { + t->items[i] = unpack(s, ext_hook, use_list); + } + return MP_OBJ_FROM_PTR(t); + } +} + +STATIC mp_obj_t unpack_bytes(msgpack_stream_t *s, size_t size) { + vstr_t vstr; + vstr_init_len(&vstr, size); + byte *p = (byte*)vstr.buf; + read(s, p, size); + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} + +STATIC mp_obj_t unpack_ext(msgpack_stream_t *s, size_t size, mp_obj_t ext_hook) { + int8_t code = read1(s); + mp_obj_t data = unpack_bytes(s, size); + if (ext_hook != mp_const_none) { + return mp_call_function_2(ext_hook, MP_OBJ_NEW_SMALL_INT(code), data); + } else { + mod_msgpack_extype_obj_t *o = m_new_obj(mod_msgpack_extype_obj_t); + o->base.type = &mod_msgpack_exttype_type; + o->code = code; + o->data = data; + return MP_OBJ_FROM_PTR(o); + } +} + +STATIC mp_obj_t unpack(msgpack_stream_t *s, mp_obj_t ext_hook, bool use_list) { + uint8_t code = read1(s); + if (((code & 0b10000000) == 0) || ((code & 0b11100000) == 0b11100000)) { + // int + return MP_OBJ_NEW_SMALL_INT((int8_t)code); + } + if ((code & 0b11100000) == 0b10100000) { + // str + size_t len = code & 0b11111; + // allocate on stack; len < 32 + char str[len]; + read(s, &str, len); + return mp_obj_new_str(str, len); + } + if ((code & 0b11110000) == 0b10010000) { + // array (list / tuple) + return unpack_array_elements(s, code & 0b1111, ext_hook, use_list); + } + if ((code & 0b11110000) == 0b10000000) { + // map (dict) + size_t len = code & 0b1111; + mp_obj_dict_t *d = MP_OBJ_TO_PTR(mp_obj_new_dict(len)); + for (size_t i=0; i<len; i++) { + mp_obj_dict_store(d, unpack(s, ext_hook, use_list), unpack(s, ext_hook, use_list)); + } + return MP_OBJ_FROM_PTR(d); + } + switch (code) { + case 0xc0: return mp_const_none; + case 0xc2: return mp_const_false; + case 0xc3: return mp_const_true; + case 0xc4: + case 0xc5: + case 0xc6: { + // bin 8, 16, 32 + return unpack_bytes(s, read_size(s, code-0xc4)); + } + case 0xcc: // uint8 + case 0xd0: // int8 + return MP_OBJ_NEW_SMALL_INT((int8_t)read1(s)); + case 0xcd: // uint16 + case 0xd1: // int16 + return MP_OBJ_NEW_SMALL_INT((int16_t)read2(s)); + case 0xce: // uint32 + case 0xd2: // int32 + return MP_OBJ_NEW_SMALL_INT((int32_t)read4(s)); + case 0xca: { + union Float { mp_float_t f; uint32_t u; }; + union Float data; + data.u = read4(s); + return mp_obj_new_float(data.f); + } + case 0xd9: + case 0xda: + case 0xdb: { + // str 8, 16, 32 + size_t size = read_size(s, code-0xd9); + vstr_t vstr; + vstr_init_len(&vstr, size); + byte *p = (byte*)vstr.buf; + read(s, p, size); + return mp_obj_new_str_from_vstr(&mp_type_str, &vstr); + } + case 0xde: + case 0xdf: { + // map 16 & 32 + size_t len = read_size(s, code - 0xde + 1); + mp_obj_dict_t *d = MP_OBJ_TO_PTR(mp_obj_new_dict(len)); + for (size_t i=0; i<len; i++) { + mp_obj_dict_store(d, unpack(s, ext_hook, use_list), unpack(s, ext_hook, use_list)); + } + return MP_OBJ_FROM_PTR(d); + } + case 0xdc: + case 0xdd: { + // array 16 & 32 + size_t size = read_size(s, code - 0xdc + 1); + return unpack_array_elements(s, size, ext_hook, use_list); + } + case 0xd4: // fixenxt 1 + return unpack_ext(s, 1, ext_hook); + case 0xd5: // fixenxt 2 + return unpack_ext(s, 2, ext_hook); + case 0xd6: // fixenxt 4 + return unpack_ext(s, 4, ext_hook); + case 0xd7: // fixenxt 8 + return unpack_ext(s, 8, ext_hook); + case 0xd8: // fixenxt 16 + return unpack_ext(s, 16, ext_hook); + case 0xc7: // ext 8 + case 0xc8: // ext 16 + case 0xc9: + // ext 8, 16, 32 + return unpack_ext(s, read_size(s, code-0xc7), ext_hook); + case 0xc1: // never used + case 0xcb: // float 64 + case 0xcf: // uint 64 + case 0xd3: // int 64 + default: + mp_raise_NotImplementedError(translate("64 bit types")); + } +} + +void common_hal_msgpack_pack(mp_obj_t obj, mp_obj_t stream_obj, mp_obj_t default_handler) { + msgpack_stream_t stream = get_stream(stream_obj, MP_STREAM_OP_WRITE); + pack(obj, &stream, default_handler); +} + +mp_obj_t common_hal_msgpack_unpack(mp_obj_t stream_obj, mp_obj_t ext_hook, bool use_list) { + msgpack_stream_t stream = get_stream(stream_obj, MP_STREAM_OP_WRITE); + return unpack(&stream, ext_hook, use_list); +} diff --git a/shared-module/msgpack/__init__.h b/shared-module/msgpack/__init__.h new file mode 100644 index 000000000..88b4809f9 --- /dev/null +++ b/shared-module/msgpack/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * 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. + */ +#ifndef MICROPY_INCLUDED_SHARED_MODULE_MSGPACK___INIT___H +#define MICROPY_INCLUDED_SHARED_MODULE_MSGPACK___INIT___H + +#include "py/stream.h" + +void common_hal_msgpack_pack(mp_obj_t obj, mp_obj_t stream_obj, mp_obj_t default_handler); +mp_obj_t common_hal_msgpack_unpack(mp_obj_t stream_obj, mp_obj_t ext_hook, bool use_list); + +#endif diff --git a/tests/extmod/umsgpack_pack.py b/tests/extmod/umsgpack_pack.py new file mode 100644 index 000000000..d9f0005e1 --- /dev/null +++ b/tests/extmod/umsgpack_pack.py @@ -0,0 +1,30 @@ +try: + from uio import BytesIO + import umsgpack as msgpack +except: + try: + from io import BytesIO + import msgpack + except ImportError: + print("SKIP") + raise SystemExit + +b = BytesIO() +msgpack.pack(False, s) +print(b.getvalue()) + +b = BytesIO() +msgpack.pack({"a": (-1, 0, 2, [3, None], 128)}, b) +print(b.getvalue()) + +# pack to a small-int not allowed +try: + msgpack.pack(123, 1) +except (AttributeError, OSError): # CPython and uPy have different errors + print('Exception') + +# pack to an object not allowed +try: + msgpack.pack(123, {}) +except (AttributeError, OSError): # CPython and uPy have different errors + print('Exception') diff --git a/tests/extmod/umsgpack_pack.py.ext b/tests/extmod/umsgpack_pack.py.ext new file mode 100644 index 000000000..2f966be06 --- /dev/null +++ b/tests/extmod/umsgpack_pack.py.ext @@ -0,0 +1,5 @@ +b'\xc2' +b'\x82\xa1a\x96\xff\x00\x02\x92\x03\xc0\xd1\x00\x80\xc4\x07abcdefg\xa1b\xd4\x05x' +Exception ExtType +Exception to int +Exception to object |
