munud


Namemunud JSON
Version 0.3.1 PyPI version JSON
download
home_pageNone
SummaryBuilding sub-byte payloads, and generating according C code
upload_time2024-09-27 16:20:31
maintainerNone
docs_urlNone
authorUlysse Moreau
requires_python<4.0,>=3.9
licenseMIT
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Munud

Building payloads with sub-byte unaligned values, and generating according C code.

# Installation

The easiest way to install this library is using pip:

```bash
pip install munud
```

# Motivation

This module is built around the concept of "Unaligned Bytes", which means a sequence
of n bits forming an int which might not be aligned on the bytes themselves.

Take as an example the following bytes : 

```py
some_bytes = b"\xfa\x04\xde"
```

We will assume that the first 3 bits corresponds to value A, the next 17 bits to value B, and
the next 4 bits to value C. This will correspond to the following representation in memory:

```txt
 +--------+--------+--------+
 | Byte 0 | Byte 1 | Byte 2 |
 +---+----+--------+---+----+
 | A |        B        | C  |
 +---+-----------------+----+

```

Thus, a traditionnal python way of retrieving those three values would be as such:

```python
A = some_bytes[0] >> 5
B = (some_bytes[0] & 0x1F) << 12 | some_bytes[1] << 4 | some_bytes[2] >> 4
C = some_bytes[2] & 0x0F
```

And a traditional way of shoving those values in a byte array would be as follows:

```python
some_bytes = [0, 0, 0]

some_bytes[0] |= A << 5

some_bytes[0] |= (B >> 12) & 0x1F
some_bytes[1] = (B >> 4) & 0xFF
some_bytes[2] |= (B << 4) & 0xFF

some_bytes[2] |= C
```

This module provides two main functionalities:

- Provide a python API to do those read/write operations dynamically
- Generate a C code to perform those operations efficiently, given a fixed format

# Usage

## Programmation API

With munud, the same can be achieved using the following:

```python

from munud import UnalignedBytes

ub_A = UnalignedBytes(offset=0, bit_size=3)
ub_B = UnalignedBytes(3, 17)
ub_C = UnalignedBytes(20, 4)

# Writing
some_bytes = [0, 0, 0]

ub_A.shove(byte_buffer=some_bytes, value=3)
ub_B.shove(some_bytes, 1234)
ub_C.shove(some_bytes, 5)

# > b"\x60\x4d\x25"

# Reading
A = ub_A.extract(some_bytes)
B = ub_B.extract(some_bytes)
C = ub_C.extract(some_bytes)

```

### Using a yaml file : 

Using the following `test.yml` file:

```yml
- name: A
  size: 3
  type: uint8_t
- name: B
  size: 17
  type: uint16_t
- name: C
  size: 4
  type: uint8_t
```

Use the following to obtain a python dict representing the extracted bytes :


```python
bytes_to_read = b"\x60\x4d\x25"

from_yaml("test.yml", bytes_to_read)
# > {'A': 3, 'B': 1234, 'C': 5}

```

## C generation with the cli

Using the following `test.yml` file:

```yml
- name: A
  size: 3
  type: uint8_t
- name: B
  size: 17
  type: uint16_t
- name: C
  size: 4
  type: uint8_t
```

To show a table summarizing the payload, use :

```bash
munud -f test.yml show
```

```txt
                    Payload
┏━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
┃     Type ┃ Name ┃ Size (bits) ┃    Byte Span ┃
┡━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
│  uint8_t │    A │ 3           │ [0 (+0) - 1] │
│ uint16_t │    B │ 17          │ [0 (+3) - 3] │
│  uint8_t │    C │ 4           │ [2 (+4) - 3] │
└──────────┴──────┴─────────────┴──────────────┘
Total payload size: 24 bits
```

To decode an hex payload, use:

```bash
munud -f test.yml decode -p AAAAAAAA
```

```
          Payload
┏━━━━━━━━━━┳━━━━━━┳━━━━━━━┓
┃     Type ┃ Name ┃ Value ┃
┡━━━━━━━━━━╇━━━━━━╇━━━━━━━┩
│  uint8_t │    A │     5 │
│ uint16_t │    B │ 43690 │
│  uint8_t │    C │    10 │
└──────────┴──────┴───────┘
Total payload size: 24 bits
```

To generate c getters and setters, use:

```bash
munud -f test.yml cgen 
```

This will generate the following c code:

```c
#include "assert.h"
#include "stdint.h"
        

inline static uint8_t get_A(uint8_t *payload)
{
    /*
     * This function retrives the value A (3 bits).
     * The value is found in byte 0.
     * The value starts at bit 0.
     */

    return payload[0] >> 5;
}

inline static void set_A(uint8_t *payload, uint8_t value)
{
    /*
     * This function writes the value A (3 bits).
     * The value will be written in byte 0.
     * The value starts at bit 0.
     */

    payload[0] |= value << 5;
}


inline static uint16_t get_B(uint8_t *payload)
{
    /*
     * This function retrives the value B (17 bits).
     * The value spreads between bytes 0 and 2.
     * The value starts at bit 3.
     */

    return (payload[0] & 0x1F) << 12 | payload[1] << 4 | payload[2] >> 4;
}

inline static void set_B(uint8_t *payload, uint16_t value)
{
    /*
     * This function writes the value B (17 bits).
     * The value will spread between bytes 0 and 2.
     * The value starts at bit 3.
     */

    payload[0] |= (value >> 12) & 0x1F;
    payload[1] = (value >> 4) & 0xFF;
    payload[2] |= (value << 4) & 0xFF;
}


inline static uint8_t get_C(uint8_t *payload)
{
    /*
     * This function retrives the value C (4 bits).
     * The value is found in byte 2.
     * The value starts at bit 4.
     */

    return payload[2] & 0x0F;
}

inline static void set_C(uint8_t *payload, uint8_t value)
{
    /*
     * This function writes the value C (4 bits).
     * The value will be written in byte 20.
     * The value starts at bit 4.
     */

    payload[2] |= value;
}


struct Payload
{

    uint16_t B;
    uint8_t A;
    uint8_t C;
};



inline static void decode(struct Payload *payload, uint8_t *packed)
{
    /*
     * 
     */

    payload->A = get_A(packed);
    payload->B = get_B(packed);
    payload->C = get_C(packed);
}

inline static void encode(struct Payload *payload, uint8_t *packed)
{
    /*
     * 
     */

    set_A(packed, payload->A);
    set_B(packed, payload->B);
    set_C(packed, payload->C);
}
```


Many options are available for customisation :

```bash

$ munud cgen --help

Usage: munud.cmd cgen [OPTIONS]

Options:
  -f, --fmt TEXT          A file describing the wanted format.
  -o, --output TEXT       The output .h file.
  --crlf                  Use \r\n (crlf) as newline instead of \n (crlf).
  --use-assert            Generate assert check before writing to payload.
  --get-only              Generate only getters.
  --set-only              Generate only setters.
  --without-struct        Do not generate a struct containing the payload.
  --packed-struct         Add the gcc __attribute__((packed)) attribute to the
                          struct.
  -n, --struct-name TEXT  Payload struct name.
  --without-safety-mask   Removes the 0xff mask when writing ints on bytes.
  --payload-type TEXT     The type of the payload, usually uint8_t*.
  --cpp                   Generate cpp-style namespace.
  --namespace TEXT        The name of an optional namespace. If cpp is not
                          enabled, add it as a prefix.
  --help                  Show this message and exit.
```

# Development

This library uses poetry as a development tool.

You can start development by running :

```bash
poetry install
```

# Testing

You can test this library using :

```bash
poetry run pytest
```

# Tox

You can test multiple python versions using tox :

```bash
poetry run tox
```


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "munud",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.9",
    "maintainer_email": null,
    "keywords": null,
    "author": "Ulysse Moreau",
    "author_email": "u.moreau@hexa-h.com",
    "download_url": "https://files.pythonhosted.org/packages/a6/45/7ad5d4dda71ab4866f91fce7ff541799a68d281ba83e7ed723e5c82f8baa/munud-0.3.1.tar.gz",
    "platform": null,
    "description": "# Munud\n\nBuilding payloads with sub-byte unaligned values, and generating according C code.\n\n# Installation\n\nThe easiest way to install this library is using pip:\n\n```bash\npip install munud\n```\n\n# Motivation\n\nThis module is built around the concept of \"Unaligned Bytes\", which means a sequence\nof n bits forming an int which might not be aligned on the bytes themselves.\n\nTake as an example the following bytes : \n\n```py\nsome_bytes = b\"\\xfa\\x04\\xde\"\n```\n\nWe will assume that the first 3 bits corresponds to value A, the next 17 bits to value B, and\nthe next 4 bits to value C. This will correspond to the following representation in memory:\n\n```txt\n +--------+--------+--------+\n | Byte 0 | Byte 1 | Byte 2 |\n +---+----+--------+---+----+\n | A |        B        | C  |\n +---+-----------------+----+\n\n```\n\nThus, a traditionnal python way of retrieving those three values would be as such:\n\n```python\nA = some_bytes[0] >> 5\nB = (some_bytes[0] & 0x1F) << 12 | some_bytes[1] << 4 | some_bytes[2] >> 4\nC = some_bytes[2] & 0x0F\n```\n\nAnd a traditional way of shoving those values in a byte array would be as follows:\n\n```python\nsome_bytes = [0, 0, 0]\n\nsome_bytes[0] |= A << 5\n\nsome_bytes[0] |= (B >> 12) & 0x1F\nsome_bytes[1] = (B >> 4) & 0xFF\nsome_bytes[2] |= (B << 4) & 0xFF\n\nsome_bytes[2] |= C\n```\n\nThis module provides two main functionalities:\n\n- Provide a python API to do those read/write operations dynamically\n- Generate a C code to perform those operations efficiently, given a fixed format\n\n# Usage\n\n## Programmation API\n\nWith munud, the same can be achieved using the following:\n\n```python\n\nfrom munud import UnalignedBytes\n\nub_A = UnalignedBytes(offset=0, bit_size=3)\nub_B = UnalignedBytes(3, 17)\nub_C = UnalignedBytes(20, 4)\n\n# Writing\nsome_bytes = [0, 0, 0]\n\nub_A.shove(byte_buffer=some_bytes, value=3)\nub_B.shove(some_bytes, 1234)\nub_C.shove(some_bytes, 5)\n\n# > b\"\\x60\\x4d\\x25\"\n\n# Reading\nA = ub_A.extract(some_bytes)\nB = ub_B.extract(some_bytes)\nC = ub_C.extract(some_bytes)\n\n```\n\n### Using a yaml file : \n\nUsing the following `test.yml` file:\n\n```yml\n- name: A\n  size: 3\n  type: uint8_t\n- name: B\n  size: 17\n  type: uint16_t\n- name: C\n  size: 4\n  type: uint8_t\n```\n\nUse the following to obtain a python dict representing the extracted bytes :\n\n\n```python\nbytes_to_read = b\"\\x60\\x4d\\x25\"\n\nfrom_yaml(\"test.yml\", bytes_to_read)\n# > {'A': 3, 'B': 1234, 'C': 5}\n\n```\n\n## C generation with the cli\n\nUsing the following `test.yml` file:\n\n```yml\n- name: A\n  size: 3\n  type: uint8_t\n- name: B\n  size: 17\n  type: uint16_t\n- name: C\n  size: 4\n  type: uint8_t\n```\n\nTo show a table summarizing the payload, use :\n\n```bash\nmunud -f test.yml show\n```\n\n```txt\n                    Payload\n\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503     Type \u2503 Name \u2503 Size (bits) \u2503    Byte Span \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502  uint8_t \u2502    A \u2502 3           \u2502 [0 (+0) - 1] \u2502\n\u2502 uint16_t \u2502    B \u2502 17          \u2502 [0 (+3) - 3] \u2502\n\u2502  uint8_t \u2502    C \u2502 4           \u2502 [2 (+4) - 3] \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\nTotal payload size: 24 bits\n```\n\nTo decode an hex payload, use:\n\n```bash\nmunud -f test.yml decode -p AAAAAAAA\n```\n\n```\n          Payload\n\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503     Type \u2503 Name \u2503 Value \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502  uint8_t \u2502    A \u2502     5 \u2502\n\u2502 uint16_t \u2502    B \u2502 43690 \u2502\n\u2502  uint8_t \u2502    C \u2502    10 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\nTotal payload size: 24 bits\n```\n\nTo generate c getters and setters, use:\n\n```bash\nmunud -f test.yml cgen \n```\n\nThis will generate the following c code:\n\n```c\n#include \"assert.h\"\n#include \"stdint.h\"\n        \n\ninline static uint8_t get_A(uint8_t *payload)\n{\n    /*\n     * This function retrives the value A (3 bits).\n     * The value is found in byte 0.\n     * The value starts at bit 0.\n     */\n\n    return payload[0] >> 5;\n}\n\ninline static void set_A(uint8_t *payload, uint8_t value)\n{\n    /*\n     * This function writes the value A (3 bits).\n     * The value will be written in byte 0.\n     * The value starts at bit 0.\n     */\n\n    payload[0] |= value << 5;\n}\n\n\ninline static uint16_t get_B(uint8_t *payload)\n{\n    /*\n     * This function retrives the value B (17 bits).\n     * The value spreads between bytes 0 and 2.\n     * The value starts at bit 3.\n     */\n\n    return (payload[0] & 0x1F) << 12 | payload[1] << 4 | payload[2] >> 4;\n}\n\ninline static void set_B(uint8_t *payload, uint16_t value)\n{\n    /*\n     * This function writes the value B (17 bits).\n     * The value will spread between bytes 0 and 2.\n     * The value starts at bit 3.\n     */\n\n    payload[0] |= (value >> 12) & 0x1F;\n    payload[1] = (value >> 4) & 0xFF;\n    payload[2] |= (value << 4) & 0xFF;\n}\n\n\ninline static uint8_t get_C(uint8_t *payload)\n{\n    /*\n     * This function retrives the value C (4 bits).\n     * The value is found in byte 2.\n     * The value starts at bit 4.\n     */\n\n    return payload[2] & 0x0F;\n}\n\ninline static void set_C(uint8_t *payload, uint8_t value)\n{\n    /*\n     * This function writes the value C (4 bits).\n     * The value will be written in byte 20.\n     * The value starts at bit 4.\n     */\n\n    payload[2] |= value;\n}\n\n\nstruct Payload\n{\n\n    uint16_t B;\n    uint8_t A;\n    uint8_t C;\n};\n\n\n\ninline static void decode(struct Payload *payload, uint8_t *packed)\n{\n    /*\n     * \n     */\n\n    payload->A = get_A(packed);\n    payload->B = get_B(packed);\n    payload->C = get_C(packed);\n}\n\ninline static void encode(struct Payload *payload, uint8_t *packed)\n{\n    /*\n     * \n     */\n\n    set_A(packed, payload->A);\n    set_B(packed, payload->B);\n    set_C(packed, payload->C);\n}\n```\n\n\nMany options are available for customisation :\n\n```bash\n\n$ munud cgen --help\n\nUsage: munud.cmd cgen [OPTIONS]\n\nOptions:\n  -f, --fmt TEXT          A file describing the wanted format.\n  -o, --output TEXT       The output .h file.\n  --crlf                  Use \\r\\n (crlf) as newline instead of \\n (crlf).\n  --use-assert            Generate assert check before writing to payload.\n  --get-only              Generate only getters.\n  --set-only              Generate only setters.\n  --without-struct        Do not generate a struct containing the payload.\n  --packed-struct         Add the gcc __attribute__((packed)) attribute to the\n                          struct.\n  -n, --struct-name TEXT  Payload struct name.\n  --without-safety-mask   Removes the 0xff mask when writing ints on bytes.\n  --payload-type TEXT     The type of the payload, usually uint8_t*.\n  --cpp                   Generate cpp-style namespace.\n  --namespace TEXT        The name of an optional namespace. If cpp is not\n                          enabled, add it as a prefix.\n  --help                  Show this message and exit.\n```\n\n# Development\n\nThis library uses poetry as a development tool.\n\nYou can start development by running :\n\n```bash\npoetry install\n```\n\n# Testing\n\nYou can test this library using :\n\n```bash\npoetry run pytest\n```\n\n# Tox\n\nYou can test multiple python versions using tox :\n\n```bash\npoetry run tox\n```\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Building sub-byte payloads, and generating according C code",
    "version": "0.3.1",
    "project_urls": null,
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6e61c1cb243a5f15bf7df7e9861cbf99cf804d909a60aaadf355c40c359ea319",
                "md5": "004c5d0ead88a3b7d49a9fb71b110ab6",
                "sha256": "f1f211532e1780fa2340a483292cc29cb4aca361e5bad76db02f6cbc537df5e5"
            },
            "downloads": -1,
            "filename": "munud-0.3.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "004c5d0ead88a3b7d49a9fb71b110ab6",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.9",
            "size": 14539,
            "upload_time": "2024-09-27T16:20:29",
            "upload_time_iso_8601": "2024-09-27T16:20:29.844517Z",
            "url": "https://files.pythonhosted.org/packages/6e/61/c1cb243a5f15bf7df7e9861cbf99cf804d909a60aaadf355c40c359ea319/munud-0.3.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a6457ad5d4dda71ab4866f91fce7ff541799a68d281ba83e7ed723e5c82f8baa",
                "md5": "ba6e3812b2253e5744c6e579442e49d1",
                "sha256": "d4f1f59529e31abda1838998cbe528d5ec900e6f594ac819081c78b4f9a59e11"
            },
            "downloads": -1,
            "filename": "munud-0.3.1.tar.gz",
            "has_sig": false,
            "md5_digest": "ba6e3812b2253e5744c6e579442e49d1",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.9",
            "size": 14370,
            "upload_time": "2024-09-27T16:20:31",
            "upload_time_iso_8601": "2024-09-27T16:20:31.266559Z",
            "url": "https://files.pythonhosted.org/packages/a6/45/7ad5d4dda71ab4866f91fce7ff541799a68d281ba83e7ed723e5c82f8baa/munud-0.3.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-09-27 16:20:31",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "munud"
}
        
Elapsed time: 0.34645s