{
  "id": "peterhinch/micropython-msgpack",
  "name": "micropython-msgpack",
  "prettyName": "micropython-msgpack",
  "description": "MessagePack serialisation library optimised for MicroPython",
  "version": "",
  "author": "peterhinch",
  "license": "MIT",
  "tags": [],
  "status": "VALID",
  "official": false,
  "source": "https://github.com/peterhinch/micropython-msgpack",
  "updatedAt": "2026-07-10T07:00:50.244Z",
  "page": "https://checkmim.com/packages/peterhinch+micropython-msgpack",
  "createdAt": "2025-05-27T04:29:03.157Z",
  "submittedBy": "oliverr",
  "install": {
    "mpremote": "mpremote mip install github:peterhinch/micropython-msgpack",
    "mip": "import mip; mip.install(\"github:peterhinch/micropython-msgpack\")"
  },
  "readme": "# MicroPython MessagePack\n\nAn efficient drop-in binary alternative to JSON.\n\n# 0. Contents\n\n1. [Introduction](./README.md#1-introduction)  \n 1.1 [Supported Types](./README.md#11-supported-types) List of supported data types.  \n 1.2 [Library update](./README.md#12-library-update) Changes in release 0.2.0.  \n &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.2.1 [Extended built-in classes](./README.md#121-extended-built-in-classes)  \n &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.2.2 [Asynchronous unpacking](./README.md#122-asynchronous-unpacking) Use with streaming data.  \n 1.3 [Comprssion performance](./README.md#13-compression-performance)  \n2. [The MicroPython implementation](./README.md#2-the-micropython-implementation)  \n 2.1 [Inter-operation](./README.md#21-inter-operation) Standard compliance: data exchange with other systems.  \n3. [Installation](./README.md#3-installation)  \n 3.1 [Quick install](./README.md#31-quick-install)  \n 3.2 [Files](./README.md#32-files) List of installed files.  \n 3.3 [Manual install](./README.md#33-manual-install) Doing it the hard way.  \n4. [API](./README.md#4-api) User interface.  \n 4.1 [Load options](./README.md#41-load-options) Specialist options.  \n 4.2 [Dump options](./README.md#42-dump-options)  \n5. [Extension modules](./README.md#5-extension-modules) Using extension modules to process additional built-in types.  \n6. [Extending umsgpack](./README.md#6-extending-umsgpack) Writing extensions.  \n 6.1 [Serialisable user classes](./README.md#61-serialisable-user-classes)  \n 6.2 [Adding built-in types](./README.md#62-adding-built-in-types)  \n7. [Asynchronous use](./README.md#7-asynchronous-use) Use with streaming I/O.  \n 7.1 [Serialisation](./README.md#71-serialisation)  \n 7.2 [De-serialisation](./README.md#72-de-serialisation)  \n &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7.2.1 [The Observer object](./README.md#721-the-observer-object) Optionally observe the data stream.  \n8. [Exceptions](./README.md#8-exceptions)  \n9. [Test suite](./README.md#9-test-suite)  \n10. [Changes for MicroPython](./README.md#10-changes-for-micropython) Brief summary of changes for microcontroller use.  \n11. [Notes on extension classes](./README.md#11-notes-on-extension-classes) Detailed \"how it works\" notes.  \n12. [Measurement of RAM usage](./README.md#12-measurement-of-ram-usage)  \n13. [Acknowledgements](./README.md#13-acknowledgements)  \n14. [References](./README.md#14-References)  \n15. [Licence](./README.md#15-licence)\n\n# 1. Introduction\n\n[MessagePack](http://msgpack.org/) is a serialization protocol similar to JSON.\nWhere JSON produces human readable strings, MessagePack produces binary `bytes`\ndata. The protocol achieves a substantial reduction in data volume. Its\ncombination of ease of use, extensibility, and packing performance makes it\nworth considering in any application where these capabilities are of use.\n\nThis MicroPython implementation has usage identical to that of the `json`\nlibrary:\n```python\nimport umsgpack\nobj = [1, 2, 3.14159]\ns = umsgpack.dumps(obj)  # s is a bytes object\nprint(umsgpack.loads(s))  # Outcome [1, 2, 3.14159]\n```\nMessagePack efficiently serialises a range of Python built-in types, a range\nthat can readily be extended to support further Python types or instances of\nuser defined classes. A key feature of the protocol is that, like JSON, it is\nself describing: the receiver gets all the information needed to decode the\nstream from the stream itself. This example assumes a file whose contents were\nencoded with MessagePack.\n```python\nimport umsgpack\nwith open('data', 'rb') as f:\n    z = umsgpack.load(f)\nprint(z)  # z might be a wide range of Python objects\n```\nThe protocol is self describing even when extended. Extended usage is simply a\nmatter of importing the relevant library:\n```python\nimport umsgpack, umsgpack.mpk_complex  # Add complex support\ns = umsgpack.dumps([1, \"abc\", 1+9j])\nprint(umsgpack.loads(s))  # Outcome [1, 'abc', (1+9j)]\n```\nThis document focuses on usage with MicroPython and skips most of the detail on\nhow MessagePack works. The\n[source document](https://github.com/vsergeev/u-msgpack-python) provides a lot\nof useful information for those wishing to understand the protocol. Also see\n[the MessagePack spec](https://github.com/msgpack/msgpack/blob/master/spec.md).\n\n## 1.1 Supported types\n\nThe following types are natively supported.\n * `int` In range `-2**63` to `2**64 - 1`.\n * `True`, `False` and `None`.\n * `str` Unicode string.\n * `bytes` Binary data.\n * `float` IEEE-754 single or double precision controlled by a dump option.\n * `tuple` By default tuples are de-serialised as lists, but this can be\n overridden by a load option. An extension module provides proper support\n whereby `list` and `tuple` objects unpack unchanged.\n * `list` Termed \"array\" in MessagePack documentation.\n * `dict` Termed \"map\" in MessagePack docs.\n\nThe `list` and `dict` types may contain any supported type including `list` and\n`dict` instances (ad nauseam).\n\nExtended support currently adds the following options:\n * `tuple` Provides an explicit distinction between `tuple` and `list` types:\n if it is encoded as a `tuple` it will be decoded as one.\n * `complex`\n * `set`\n * `bytearray` Provides an explicit distinction between `bytes` and `byterray`.\n\nThis document details how to support further built-in types and/or user-defined\nclasses.\n\n## 1.2 Library Update\n\nA substantial update has been undertaken to V0.2.0. For users of \"advanced\"\nfeatures (extension and asynchronous use) this is a breaking change. Objectives\nwere to save RAM by removing unnecessary features and simplifying code. PEP5\ncompliance has been improved. To eliminate duplication the `as_load` module has\nbeen abandoned in favour of `as_loader`.\n\n### 1.2.1 Extended built-in classes\n\nThe ext handler approach (formerly an option) has been discarded. Extension is\nexclusively via an enhanced `ext_serializable` decorator. The `mpext` function\nhas been discarded. This enables each extension class to be handled by a separate\nPython file, improving modularity, reducing RAM use, and easing the addition of\nnew classes. In consequence the old `umsgpack_ext.py` is replaced by individual\nfiles such as `umsgpack.mpk_set.py`. Other improvements minimise object creation\nwhen packing extended built-ins.\n\n### 1.2.2 Asynchronous unpacking\n\nUnpacking is now via an asynchronous iterator. The `observer` object can now be\nany callable (a callback function/method or a class).\n\n## 1.3 Compression performance\n\nThe degree of compression compared to UJSON depends on the object being\nserialised. The following gives an example:\n```python\nimport umsgpack\nimport ujson\nobj = [1, True, False, 0xffffffff, {u\"foo\": b\"\\x80\\x01\\x02\", u\"bar\": [1,2,3, {u\"a\": [1,2,3,{}]}]}, -1, 2.12345]\nlj = len(ujson.dumps(obj))\nlm = len(umsgpack.dumps(obj, force_float_precision = \"single\"))\nprint(lj, lm, lj/lm)\n```\nOutcome: `lj` == 106 bytes, `lm` == 41 bytes corresponding to a transmission\nspeedup factor of 2.6. The `force_float_precision` option ensures the same\nresult on 32 bit and 64 bit platforms.\n\nIf large quantities of text are to be transmitted, a greater gain could be\nachieved by compressing the text with a `gzip` style compressor and serialising\nthe resultant `bytes` object with MessagePack.\n\n#### [Contents](./README.md#0-contents)  \n\n# 2. The MicroPython implementation\n\nThis implementation is based on the following well proven MessagePack repo\n[u-msgpack-python](https://github.com/vsergeev/u-msgpack-python). This runs\nunder Python 2 and Python 3. With trivial adaptations it will run under\n[MicroPython](https://micropython.org/) but at a high cost in RAM consumption.\nThis version was adapted from that codebase and optimised to minimise RAM usage\nwhen run on microcontrollers. When using frozen bytecode or romfs RAM use can be\nas low as 2192 bytes for `dumps` and 1008 bytes for `loads` - see\n[section 12](./README.md#12-measurement-of-ram-usage).\n\nThis version is a subset of the original. Support was removed for features\nthought problematic for microcontroller use. The principal example is that of\ntimestamps. MicroPython does not support the `datetime` module. There are also\nissues with platforms differing in their time handling, notably the epoch. On a\nmicrocontroller it is simple to send the integer result from `time.time()` or\neven `time.time_ns()`. Python 2 support is removed. The code will run under\nPython 3.\n\nSupported types are fully compliant with a subset of the latest\n[MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md).\nIn particular, it supports the new binary, UTF-8 string and application-defined\next types. As stated above, timestamps are unsupported.\n\nThis MicroPython version uses various techniques to minimise RAM use including\n\"lazy imports\": a module is only imported on first usage. For example an\napplication that only de-serialises data using synchronous code will not import\ncode to dump data or that to support asynchronous programming.\n\nThere is now an [implementation in C](https://github.com/Tangerino/micropython-msgpackc)\nby Carlos Tangerino which has the same API while being much faster and using\nless RAM. Firmware must be compiled to include the code.\n\n# 2.1 Inter-operation\n\nThe un-extended module conforms to the MessagePack specification. Consequently\nmessages may be exchanged between systems using different MessagePack modules\nincluding applications written in non-Python languages. If extension modules are\nto be used in such applications, a matching extension will be required by the\nforeign language party.\n\nThe MessagePack specification does not distinguish between mutable and\nimmutable types. Consequently the non-extended module will be unable to\ndistinguish `tuple` and `list` items, also `bytes` and `bytearray`. The\nextension modules address this.\n\n#### [Contents](./README.md#0-contents)  \n\n# 3. Installation\n\n## 3.1 Quick install\n\nThe library may be installed using the official `mpremote`:\n```bash\n$ mpremote mip install \"github:peterhinch/micropython-msgpack\"\n```\n\n## 3.2 Files\n\nThe following files are installed by `mpremote`:  \n1. `umsgpack/__init__.py` Necessary library support.  \n2. `umsgpack/mp_dump.py` Supports `dump` and `dumps` commands.  \n3. `umsgpack/mp_load.py` Supports `load` and `loads` commands.  \n4. `umsgpack/as_loader.py` Supports `ALoader` asynchronous loader class.  \n5. `umsgpack/mpk_bytearray.py` Extends support to `bytearray`.  \n6. `umsgpack/mpk_complex.py` Extends support to `complex`.  \n7. `umsgpack/mpk_set.py` Extends support to `set`.  \n8. `umsgpack/mpk_tuple.py` Extends support to `tuple`.  \n9. `asyntest.py` Demo of asynchronous use of MessagePack.  \n10. `user_class.py` Demo of a user defined class that is serialisable by MessagePack.  \n\nIn a minimal installation only items 1-3 are required.\n\nAdditional files:\n1. `asyntest_py3_serial` Demo of running the library on a PC.\n2. `test_umsgpack.py` The full test suite.\n\nSee [section 9](./README.md#9-test-suite) for details of the test suite.\n\n## 3.3 Manual install\n\nClone the repo by moving to a directory on your PC and issuing\n```bash\n$ git clone https://github.com/peterhinch/micropython-msgpack\n```\nCopy the directory `umsgpack` and its contents to your target hardware.\n\nThe following optional files may also be copied to the target:\n * `user_class.py` A serialisable user class.\n * `asyntest.py` Demo of asynchronous use. See [section 7](./README.md#7-asynchronous-use).\n\nThe file `test_umsgpack.py` is the test suite which runs on a PC. See\n[section 9](./README.md#9-test-suite).\n\n#### [Contents](./README.md#0-contents)  \n\n# 4. API\n\nIn applications using `ujson`, MessagePack provides a drop-in replacement with\nthe same API. The human readable (but large) strings are replaced by compact\nbinary `bytes` objects.\n\nThe API supports the following methods:\n 1. `dumps(obj, **options)` Pack an arbitrary Python object. Returns a `bytes`\n instance.\n 2. `dump(obj, fp, **options)` Pack a Python object to a stream or file.\n 3. `loads(s, **options)` Deserialise a `bytes` object. Returns a Python\n object.\n 4. `load(fp, **options)` Unpack data from a stream or file.\n 5. `ALoader(fp, **options)` Asynchronous unpack of data from a `StreamReader`.\n See [section 7](./README.md#7-asynchronous-use).\n\nThe options are discussed below. Most are rather specialised - the principal use\ncase being exchanging data with non-Python targets.\n\n## 4.1 Load options\n\n`load`, `loads` and `ALoader` support the following options as keyword args:  \n 1. `allow_invalid_utf8` (bool): unpack invalid strings into bytes (default\n `False` which causes an exception to be raised).\n 2. `use_ordered_dict` (bool): unpack dicts into `OrderedDict`, instead of\n `dict`. (default `False`).\n 3. `use_tuple` (bool): unpacks arrays into tuples, instead of lists (default\n `False`). The `mpk_tuple` module (if used) makes this redundant.\n 4. `observer` (ALoader only): a `callable` (a function or an object with a\n `__call()__` method) which is called with the results of each `readexactly(n)`\n call. This could be used, for example, to calculate a CRC value on the received\n message data.\n\nWork is [in progress](https://github.com/micropython/micropython/issues/6170) to\nmake `dict` instances ordered by default, so option 2 may become pointless.\n\n## 4.2 Dump options\n\n`dump` and `dumps` support the following option as a keyword arg:  \n\n 1. `force_float_precision` (str): `\"single\"` to force packing floats as\n IEEE-754 single-precision floats, `\"double\"` to force packing floats as\n IEEE-754 double-precision floats. By default the precision of the target's\n firmware is detected and used.\n\nAt risk of stating the obvious, on a 32-bit platform there is little point in\npacking to 64-bit precision as the platform's underlying precision is only 32\nbits. However packing as single on a 64-bit platform saves a lot of space. The\nsupplied `mpk_complex.py` honours the `force_float_precision` arg.\n\n#### [Contents](./README.md#0-contents)  \n\n# 5. Extension modules\n\nThese extend MessagePack to handle further built-in data types. Each module\nsupports one data type; importing a module enables that support. Currently the\nfollowing types are supported:\n* `set`\n* `complex`\n* `tuple` Natively `tuple` instances unpack as `list` objects. This module enables\n`tuple` and `list` data types to be preserved.\n* `bytearray` By default a `bytearray` decodes as a `bytes` object. This module\nremedies this.\n* `OrderedDict` By default an `OrderedDict` decodes as a `dict`. This module\npreserves it as a separate type.\n\nThe following enables support for `byterray`, `set`, `complex` and `tuple` and\nillustrates saving and restoring a `set`.\n```py\nimport umsgpack\nimport umsgpack.mpk_bytearray, umsgpack.mpk_set, umsgpack.mpk_complex, umsgpack.mpk_tuple\numsgpack.dumps({1,2,3})\n# Outcome: b'\\xd6Q\\x93\\x03\\x01\\x02'\numsgpack.loads(b'\\xd6Q\\x93\\x03\\x01\\x02')\n# Outcome {1, 2, 3}\n```\nThe following examples may be pasted at the REPL:\n```python\nimport umsgpack\nimport umsgpack.mpk_complex\nwith open('data', 'wb') as f:\n   umsgpack.dump(1 + 4j, f)\n```\nReading back:\n```python\nimport umsgpack\nimport umsgpack.mpk_complex\nwith open('data', 'rb') as f:\n    z = umsgpack.load(f)\nprint(z)\n# Outcome (1+4j)\n```\n#### [Contents](./README.md#0-contents)  \n\n# 6 Extending umsgpack\n\nThis is done via the `ext_serializable` decorator which is used in two ways: to\ncreate serialisable user classes and to support additional built-in types. Each\nextension type is assigned an `ext_type` value which, to the application code,\nmust be unique. Please treat values `0x50` to `0x5F` as reserved for built-in\ntypes supported by this library (current and future).\n\n## 6.1 Serialisable user classes\n\nThis example of a serialisable user class may be found in `user_class.py`. It\nprovides a `Point3d` class representing a point in 3D space stored as three\n`float` values.\n```python\nimport umsgpack\nimport struct\n\n@umsgpack.ext_serializable(0x10)\nclass Point3d:\n    def __init__(self, x, y, z):\n        self.v = (float(x), float(y), float(z))\n\n    def __str__(self):\n        return \"Point3d({:5.2f} {:5.2f} {:5.2f})\".format(*self.v)\n\n    def packb(self):\n        return struct.pack(\">fff\", *self.v)\n\n    @staticmethod\n    def unpackb(data, options):\n        return Point3d(*struct.unpack(\">fff\", data))\n```\nThe single arg to `ext_serializable` is the extension type: this is an arbitrary\nbyte value which must be unique to the application. The `packb` and `unpackb`\nmethods specify how the object is to be packed by `struct`. The `packb` method\nmust return a `bytes` object and `unpackb` returns a new instance of the class.\n\nIt may be run as follows (paste at the REPL):\n```python\nimport umsgpack\nfrom user_class import Point3d\np = Point3d(1.0, 2.1, 3)\ns = umsgpack.dumps(p)\nprint(umsgpack.loads(s))  # Outcome Point3d( 1.00  2.10  3.00)\n```\n\n## 6.2 Adding built-in types\n\nThe following is a simplified version of the contents of `mpk_complex.py` (the\nactual module includes code to define the float precision):\n```python\nimport umsgpack\nimport struct\n\n@umsgpack.ext_serializable(0x49, complex)\nclass Complex:\n    @staticmethod\n    def packb(s, options):  # s is a complex instance\n        return struct.pack(\">ff\", s.real, s.imag)\n\n    @staticmethod\n    def unpackb(data, options):\n        return complex(*struct.unpack(\">ff\", data))\n```\nThe decorator takes two args, the extension type and the type to be handled.\nA class defined with the two-arg decorator must provide the following static\nmethods:\n\n * `packb` Takes two args, an instance of the type to be serialised and a\n `dict` of pack options. Returns a `bytes` instance containing the serialised\n object. The method can optionally access `.options`.\n * `unpackb` This accepts a `bytes` instance of packed data and a `dict` of\n unpack options. Returns a new instance of the unpacked data type.\n\nThe options comprise the keyword args supplied to `dump(s)` and `load(s)`\nrespectively.\n\nTypically packing and unpacking is done using the `struct` module, but in some\nsimple cases it may be done by `umsgpack` itself. For example `mpk_set.py`:\n```py\n@umsgpack.ext_serializable(0x51, set)\nclass Set:\n    @staticmethod\n    def packb(s, options):  # Pack as a list\n        return umsgpack.dumps(list(s))\n\n    @staticmethod\n    def unpackb(data, options):\n        return set(umsgpack.loads(data))  # Cast to set\n```\nContributions of new built-in serialisers are welcome.\n\n#### [Contents](./README.md#0-contents)  \n\n# 7. Asynchronous use\n\n## 7.1 Serialisation\n\nSerialisation presents no problem in asynchronous code. The following example\nserialises the data using the normal synchronous `dumps` method then sends it\nasynchronously:\n```python\nasync def sender():\n    swriter = asyncio.StreamWriter(uart, {})\n    obj = [1, 2, 3.14159]\n    while True:\n        s = umsgpack.dumps(obj)  # Synchronous serialisation\n        swriter.write(s)\n        await swriter.drain()  # Asynchonous transmission\n        await asyncio.sleep(5)\n        obj[0] += 1\n```\n\n## 7.2 De-serialisation\n\nThis is potentially difficult. In the case of ASCII protocols like JSON and\nPickle it is possible to append a `b'\\n'` delimiter to each message, then use\n`StreamReader.readline()` to perform an asynchronous read of an entire message.\nThis works because the messages themselves cannot contain that character.\nMessagePack is a binary protocol so the data may include all possible byte\nvalues. Consequently a unique delimiter is unavailable.\n\nMessagePack messages are binary sequences whose length is unknown to the\nreceiver. Further, in many cases a substantial amount of the message must be\nread before the total length can be deduced. The `ALoader` class is\ninstantiated with a `StreamReader`: incoming data is unpacked and objects\nretrieved using an asynchronous iterator:\n```python\nasync def receiver():\n    uart_aloader = umsgpack.ALoader(asyncio.StreamReader(uart))\n    async for item in uart_aloader:\n        print('Received', item)\n```\n\nThe demo `asyntest.py` runs on a Pyboard with pins X1 and X2 linked. See code\ncomments for connections with other platforms. The code includes notes regarding\nRAM overhead.\n\n### 7.2.1 The Observer object\n\nThis enables the incoming data stream to be monitored. An observer can be any\ncallable - typically an instance of a user class with an `__call__` method. This\nis called whenever data is read from the stream: it receives a `bytes` instance\nwith the latest data or `b\"\"` when a decode is complete. The following (from\n`asyntest.py`) illustrates an example which displays a buffer full of data for\neach decode.\n```py\nclass StreamObserver:\n    def __init__(self, size=100):\n        self.buf = bytearray(size)\n        self.n = 0\n\n    def __call__(self, data: bytes) -> None:\n        if l := len(data):\n            self.buf[self.n : self.n + l] = data\n            self.n += l\n        else:  # End of data\n            print(f\"{self.buf[:self.n]}\")\n            self.n = 0\n\nasync def receiver():\n    uart_aloader = umsgpack.ALoader(asyncio.StreamReader(uart), observer=StreamObserver())\n    async for res in uart_aloader:\n        print(\"Received:\", res)\n```\n#### [Contents](./README.md#0-contents)  \n\n# 8. Exceptions\n\nThese are defined in `umsgpack/__init__.py`.\n\nThe `dump` and `dumps` methods can throw the following:\n```python\n# Packing error\nclass UnsupportedTypeException(PackException):\n    \"Object type not supported for packing.\"\n```\nThe `load` and `loads` methods can throw the following. In practice these are\nonly likely to occur if data has been corrupted, for example if transmitted via\nan unreliable medium:\n```python\nclass InsufficientDataException(UnpackException):\n    \"Insufficient data to unpack the serialized object.\"\n\nclass InvalidStringException(UnpackException):\n    \"Invalid UTF-8 string encountered during unpacking.\"\n\nclass ReservedCodeException(UnpackException):\n    \"Reserved code encountered during unpacking.\"\n\nclass UnhashableKeyException(UnpackException):\n    \"\"\"\n    Unhashable key encountered during map unpacking.\n    The serialized map cannot be deserialized into a Python dictionary.\n    \"\"\"\n\nclass DuplicateKeyException(UnpackException):\n    \"Duplicate key encountered during map unpacking.\"\n```\n#### [Contents](./README.md#0-contents)  \n\n# 9. Test suite\n\nThis is mainly of interest to those wanting to modify the code.\n\nThe repo includes the test suite `test_umsgpack.py` which must be run under\nPython 3 in a directory containing the `umsgpack` tree. It will not run under\nMicroPython because it tests large data types: the test suite causes memory\nerrors when run under even the Unix build of MicroPython. To run the test suite\nissue:\n```bash\n$ python3 test_umsgpack.py\n```\n#### [Contents](./README.md#0-contents)  \n\n# 10. Changes for MicroPython\n\nCode in this repo is based on\n[this implementation](https://github.com/vsergeev/u-msgpack-python), whose code\nis of high quality. It required minimal changes to run under MicroPython.\n\nHosted on a microcontroller its RAM consumption was high; most changes were to\nreduce this. Further, the nature of the protocol results in an issue when using\nasynchronous code to de-serialise data which arrives slowly or sporadically.\nThis is handled by adding an asynchronous de-serialiser.\n\nThese can be summarised as follows:  \nPython2 code removed.  \nCompatibility mode removed.  \nTimestamps removed.  \nConverted to Python package with lazy import to save RAM.\nReplace function despatch table with code.   \nProvide asyncio StreamReader support.\nExported functions now match ujson: dump, dumps, load, loads (only).  \nMany functions refactored to save bytes.  \n`InvalidString` class removed because it is a subclass of a native type.  \nMethod of detecting platform's float size changed (MicroPython does not support\nthe original method).  \n\n#### [Contents](./README.md#0-contents)  \n\n# 11. Notes on extension classes\n\nThese notes are for those wishing to understand how this works, perhaps to add\nsupport for further types. Consider this code which adds support for complex\nnumbers:\n```py\nimport umsgpack\nimport umsgpack.mpk_complex\n```\nThe `mpk_complex` module contains this code\n```py\n@umsgpack.ext_serializable(0x50, complex)\nclass Complex:\n    @staticmethod\n    def packb(obj, options):\n      # Code omitted\n    @staticmethod\n    def unpackb(data, options):\n      # code omitted\n```\nClasses decorated with `ext_serializable` are known as \"packers\" and must have\nstatic methods `packb` and `unpackb`.\n\nFor builtins the `ext_serializable` decorator takes two args, the `ext_type`\ninteger value which represents the record type, and the class to be encoded. The\n`ext_type` value should be a byte unique to this class. The decorator\n(in `__init__.py`) runs when the module is imported. Because there are two args\nit populates a global `builtins` dictionary. The key is the target class\n(`complex`) and the value is a 2-tuple `(0x50, Complex)` i.e. the unique\n`ext_type` byte and the packer class.\n\nIn the case of user classes the decorator receives only one arg being the\n`ext_type` value. A global dictionary `custom` is populated: the key is the\nuser class and the value is the `ext_type`. (No instance of the user class is\ncreated). The user class must have a `packb` method and an `unpackb` static\nmethod.\n\nOn packing the `builtins` and `custom` dictionaries are used to locate the\nappropriate packer with its `packb` method. Packers are never instantiated.\n\nIn both type of extensions the decorator populates a global `packers`\ndictionary: the key is the `ext_type` and the value is the packer class or user\nclass. This class is used on unpacking to run the `unpackb` static method.\n\nThis mechanism implies that the names of a packer class and the module\ncontaining it are arbitrary.\n\nThe `packb` method of a packer or use class converts the data, returning a\n`bytes` instance. It receives the object to be packed and a dict of `.options`,\nbeing the keyword args passed to `dump(s)`.\n\nThe `unpackb` static method accepts a `bytes` instance as created by `packb` and\nan `options` dict containing any options passed to `load(s)`. It returns an\ninstance of the supported class.\n\nTypically `packb` and `unpackb` use the `struct` module, but in simple cases\nthey can convert between the supported data type and one natively supported,\nand use `umsgpack` itself. See `mpk_set.py` which converts a `set` to a `list`\nand _vice versa_.\n\nCurrently supported extension types:\n| ext_type  | Type        | Support File     |\n|:----------|:------------|:-----------------|\n| 0x50      | complex     | mpk_complex.py   |\n| 0x51      | set         | mpk_set.py       |\n| 0x52      | tuple       | mpk_tuple.py     |\n| 0x53      | bytearray   | mpk_bytearray.py |\n| 0x54      | OrderedDict | mpk_odict.py     |\n| 0x55-0x5F | Reserved    | future use       |              \n\n#### [Contents](./README.md#0-contents)  \n\n# 12. Measurement of RAM usage\n\nThis test used an RP2040 with precompiled code in romfs. In each case the code\nbelow was placed in `main.py`, the board re-booted, and the result observed.\n\nPack:\n```py\nimport gc, umsgpack\ngc.collect()\na = gc.mem_free()\numsgpack.dumps(1)\ngc.collect()\nb = gc.mem_free()\nprint(a-b)\n```\nUnpack:\n```py\nimport gc, umsgpack\ngc.collect()\na = gc.mem_free()\numsgpack.loads(b'\\x01')\ngc.collect()\nb = gc.mem_free()\nprint(a-b)\n```\nResults were pack: 2192 bytes, unpack 1008 bytes.\n\n#### [Contents](./README.md#0-contents)  \n\n# 13. Acknowledgements\n\nThis project was inspired by\n[this forum thread](https://forum.micropython.org/viewtopic.php?f=15&t=10827)\nwhere user WZab performed an initial port of the source library. See also\n[this GitHub issue](https://github.com/micropython/micropython/issues/4241).\n\n# 14. References\n\nMessagePack main site:  \n[MessagePack](http://msgpack.org/)  \nMessagePack spec:  \n[the MessagePack spec](https://github.com/msgpack/msgpack/blob/master/spec.md).  \nCode on which this repo is based:  \n[u-msgpack-python](https://github.com/vsergeev/u-msgpack-python).\n\n# 16. Licence\n\nmicropython-msgpack is MIT licenced. See the included `LICENSE` file for more\ndetails.\n"
}