{"id":15645470,"url":"https://github.com/theacodes/structy","last_synced_at":"2025-04-16T03:49:31.194Z","repository":{"id":46111718,"uuid":"328262866","full_name":"theacodes/structy","owner":"theacodes","description":"Structy is an irresponsibly dumb and simple struct serialization/deserialization library for C, Python, and vanilla JavaScript.","archived":false,"fork":false,"pushed_at":"2021-11-13T20:04:13.000Z","size":101,"stargazers_count":63,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-29T04:51:12.647Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/theacodes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-01-09T23:03:17.000Z","updated_at":"2024-11-06T20:10:12.000Z","dependencies_parsed_at":"2022-08-30T19:22:04.631Z","dependency_job_id":null,"html_url":"https://github.com/theacodes/structy","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theacodes%2Fstructy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theacodes%2Fstructy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theacodes%2Fstructy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theacodes%2Fstructy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/theacodes","download_url":"https://codeload.github.com/theacodes/structy/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248564702,"owners_count":21125408,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-03T12:07:54.005Z","updated_at":"2025-04-16T03:49:31.176Z","avatar_url":"https://github.com/theacodes.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Structy\n\nStructy is an irresponsibly dumb and simple serialization/deserialization library for C, Python, and vanilla JavaScript. You can think of it like protobuf, thrift, flatbuffers, etc. but imagine that instead of a team of engineers maintaining it, it's instead written by [a single moron](https://thea.codes).\n\nStructy was created to exchange data between C-based firmware on embedded devices and Python- and JavaScript-based programming, test, and calibration scripts running on a big-girl computer. As such, its C implementation is designed specifically for embedded devices: it doesn't do any dynamic allocation and it doesn't have any fancy code for optimizations.\n\nStructy's design goals:\n\n\u003e Be small, be simple, be useful\n\nExplicit non-goals:\n\n\u003e Be fast, be clever\n\nIf you want something far more thought out and comprehensive I'd suggest checking out things like [Kaitai Struct](https://kaitai.io/).\n\n## Using structy\n\nLike protobuf and other complicated data exchange libraries, Structy uses a *schema*. Structy's schemas use Python's syntax (so it can be lazy and re-use Python's parser):\n\n```python\nclass UserSettings:\n    brightness : uint8 = 127\n    dark_mode: bool = False\n    user_id : uint32\n```\n\nWith this nonsense you can run Structy's generator to generate C, JavaScript, and Python code for this \"struct\".\n\n```bash\n$ python3 -m pip install structy\n$ structy user_settings.schema --c generated\n```\n\nFor C, it just generates a normal struct. You can include it and use it just as you would any other struct:\n\n```c\n#include \"generated/user_settings.h\"\n\nstruct UserSettings settings = {\n    .brightness = 127,\n    .dark_mode = true,\n    .user_id = 6,\n};\n```\n\nIt also creates functions for initialization of default values (`UserSettings_init`), serialization (`UserSettings_pack`), and deserialization (`UserSettings_unpack`).\n\n## C implementation\n\nStructy's C implementation is written specifically with microcontrollers in mind. Notably:\n\n- The runtime is *tiny*, coming in at right at ~250 lines of code and compiles to less than 1kB of thumb code.\n- **Never** uses the heap\n- Uses very very little stack space\n- Compiles cleanly with `-Werror -Wall -Wextra -Wpedantic -std=c17`\n- It includes optional support for [`libfixmath`](https://github.com/PetteriAimonen/libfixmath)'s `fix16_t`.\n\n\n### Including Structy's runtime\nYou must include the files in `runtimes/c` in your project to use Structy-generated C code.\n\n\n### Using generated code\n\nStructy generates the struct definition and four functions for use with the struct:\n\n### Initialization\n\n```c\nvoid StructName_init(struct StructName* inst);\n```\n\nInitializes an instance of the struct with the default values specified in the struct's schema.\n\n### Pack\n\n```c\nstruct StructyResult StructName_pack(const struct StructName* inst, uint8_t* buf);\n```\n\nPacks an instance of the struct into the given buffer. The buffer must have at least `STRUCTNAME_PACKED_SIZE` bytes. Example:\n\n```c\nuint8_t output_buffer[STRUCTNAME_PACKED_SIZE];\nStructName_pack(\u0026instance, output_buffer);\n```\n\n### Unpack\n\n```c\nstruct StructyResult StructName_unpack(struct StructName* inst, const uint8_t* buf);\n```\n\nUnpacks the buffer into the given instance. The buffer must have at least `STRUCTNAME_PACKED_SIZE` bytes.\n\n### Print\n\n```c\nvoid StructName_print(const struct StructName* inst);\n```\n\nPrints out the struct in a nicely formatted way, for example:\n\n```\nstruct UserSettings @ 0x0B00B135\n- brightness: 127\n- dark_mode: 1\n- user_id: 6\n```\n\nSince the runtime is designed for embedded systems, Structy doesn't hardcode `printf` here. You can override the print function used by creating a `structy_config.h` file and setting the `STRUCTY_PRINTF` macro:\n\n```c\n#include \"super_cool_printf.h\"\n\n#define STRUCTY_PRINTF(...) super_cool_printf(__VA_ARGS__)\n```\n\nBy default, Structy will try to use `\u003cstdio.h\u003e`'s `printf` if you're on a big girl computer. If you're on a 32-bit ARM system, Structy will check and see if you have [mpland's embedded-friendly printf](https://github.com/mpaland/printf) and use that if you have it. Otherwise, it'll disable printing.\n\n## Python implementation\n\nStructy's Python implementation is written to be simple, not fast or \"powerful\" or whatever. Some notes:\n\n- The runtime depends on Python 3.7+\n- The runtime has complete type annotations.\n- The runtime supports converting floats between libfixmath's fix16 during packing \u0026 unpacking.\n- It's built on top of Python's excellent [struct](https://docs.python.org/3/library/struct.html) module.\n\n### Including Structy's runtime\n\nThe runtime can be installed by installing the `structy` package:\n\n```bash\n$ python3 -m pip install structy\n```\n\n\n### Using generated code\n\nStructy generates the struct as a [dataclass](https://docs.python.org/3/library/dataclasses.html) in its own module.\n\n\n### Initialization\n\nSince it's a dataclass, an empty constructor gives each field the default value specified in the `.structy` definition file, but you can pass keyword arguments to override them:\n\n```py\n# Create an instance with default values for all fields.\ninst = StructName()\n\n# Or, override fields with a custom value while creating an instance.\ninst = StructName(field_name=something)\n```\n\n### Pack\n\n```python\nresult: bytes = inst.pack()\n```\n\nPacks an instance and returns a `bytes` object with the data. `len(result)` will be `StructName.PACKED_SIZE`.\n\n### Unpack\n\n```python\ninst = StructName.unpack(data)\n```\n\nUnpacks the `bytes`-like buffer into a new instance. The buffer must be at least `StructName.PACKED_SIZE` long.\n\n### Print\n\nStructy does define a nice `__str__` method:\n\n```plaintext\nUserSettings:\n· brightness:   127\n· dark_mode:    False\n· user_id:      42\n```\n\n\n## JavaScript implementation\n\nStructy's JavaScript implementation, like the others, is intended to be simple. Here's some notes on it:\n\n- It's implemented as a [JavaScript module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules).\n- It's meant to be used in a web browser. It also works with [Deno](https://deno.land/), but I only use Deno for testing.\n- It's implemented using [Javascript classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) and uses *public static members* which means [Safari will probably be a dick about it](https://bugs.webkit.org/show_bug.cgi?id=194095) until like Safari 14.1 or 15, who the heck knows?\n\n\n### Including Structy's runtime\n\nThe runtime is a single file located at [runtimes/js/structy.js](runtimes/js/structy.js). There's no Node/NPM package because the last time I used NPM my nose suddenly starting bleeding and I passed out and woke up with several mysterious lesions, and the last time I used Node.js I managed to somehow unleash a 10,000 year-old demon who's currently causing minor chaos by removing stop signs in low-traffic rural areas.\n\nSo just copy it into your project next to wherever you're going to place the generated code, like we did when the web was young. If you're lazy (and you probably are), just run this:\n\n```bash\n$ wget https://raw.githubusercontent.com/theacodes/structy/runtimes/js/structy.js\n$ wget https://raw.githubusercontent.com/theacodes/structy/third_party/struct.js/struct.mjs\n```\n\nThose commands also copy in the one dependency: the excellent and tiny [struct.js](https://github.com/lyngklip/structjs) module- yes, I know it's confusing to have `structy.js` and `struct.js`, but I'm sure you'll get used to it. `struct.js` implements Python [struct](https://docs.python.org/3/library/struct.html) module in JavaScript which is great because I can write less code.\n\n### Using generated code\n\nStructy generates the struct as a nice class in its own module. The module's only export is the class, so you can import it like this:\n\n```html\n\u003cscript type=\"module\"\u003e\nimport StructName from \"./structname.js\";\n\u003c/script\u003e\n```\n\n\n### Initialization\n\nIt's a JavaScript class so just use `new` to make an instance. It uses a [named parameter pattern](https://exploringjs.com/impatient-js/ch_callables.html#named-parameters) so you can set the value of individual fields during construction without needing to specify all of them:\n\n```js\n// Default values.\ninst = new StructName();\n// Override a default value by passing in an object literal\ninst = new StructName({field_name: something});\n```\n\n\n### Pack\n\n```python\nresult = inst.pack();\n```\n\nPacks an instance and returns a [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) object with the data. `result.length()` will be `StructName.packed_size`.\n\n### Unpack\n\n```python\ninst = StructName.unpack(data);\n```\n\nUnpacks the `Uint8Array` or `ArrayBuffer` into a new instance. The buffer must be at least `StructName.packed_size` long.\n\n\n\n## FAQ\n\n\u003e Does Structy support arrays/lists?\n\nNo, Struct structs must be a deterministic length when encoded.\n\n\u003e How about bitfields?\n\nNo yet, but it could. I'm just too lazy.\n\n\u003e Little-endian? Mixed-endianess?\n\nNo. Supporting anything other than big-endian would complicate the C runtime and I don't want to do that. Idk, maybe I could be talked into accepting a PR for it.\n\n\u003e Custom types?\n\nNo, but it's possible in the future. There's a little bit of this thought out because Structy supports Q16.16 fixed-point values.\n\n\u003e Nested structs?\n\nNo, but it could be added. I just haven't needed it yet.\n\n\u003e Why does structy use `snake_case` for properties and methods even in C and JS where it's common to use `lowerCamelCase`?\n\nMostly because I want data access to be identical in all languages, but also because I deeply, deeply, hate `lowerCamelCase`. I find it hard to read.\n\n## Support / issues / etc\n\nUnlike some of my other open-source projects, this project was made with one user in mind: me. I made it open source in case someone else finds it useful but I am not in the business of supporting this project. If you run into issues or need help feel free to submit an issue on GitHub, however, please know that I do not feel any obligation to support this project.\n\n## License\n\nThe Structy generator and runtime components are all published under the MIT license. See [LICENSE](LICENSE) for the full text.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheacodes%2Fstructy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftheacodes%2Fstructy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheacodes%2Fstructy/lists"}