{"id":44091912,"url":"https://github.com/zeriyoshi/libpino","last_synced_at":"2026-03-07T17:10:20.294Z","repository":{"id":330976114,"uuid":"1124769302","full_name":"zeriyoshi/libpino","owner":"zeriyoshi","description":"lightweight serialization library, written in C99.","archived":false,"fork":false,"pushed_at":"2026-02-26T04:45:21.000Z","size":65,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-26T08:34:26.898Z","etag":null,"topics":["c99","serialization"],"latest_commit_sha":null,"homepage":"https://zeriyoshi.github.io/libpino/","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/zeriyoshi.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-29T15:28:51.000Z","updated_at":"2026-02-26T04:45:23.000Z","dependencies_parsed_at":"2026-01-01T22:07:29.859Z","dependency_job_id":null,"html_url":"https://github.com/zeriyoshi/libpino","commit_stats":null,"previous_names":["zeriyoshi/libpino"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/zeriyoshi/libpino","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeriyoshi%2Flibpino","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeriyoshi%2Flibpino/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeriyoshi%2Flibpino/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeriyoshi%2Flibpino/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zeriyoshi","download_url":"https://codeload.github.com/zeriyoshi/libpino/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeriyoshi%2Flibpino/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30222581,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T17:00:40.062Z","status":"ssl_error","status_checked_at":"2026-03-07T17:00:39.026Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["c99","serialization"],"created_at":"2026-02-08T11:12:04.285Z","updated_at":"2026-03-07T17:10:20.287Z","avatar_url":"https://github.com/zeriyoshi.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# libpino\n\n[![CI](https://img.shields.io/badge/build-passing-brightgreen.svg)](https://github.com/zeriyoshi/libpino)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n**Packageing Notation - PINO**\n\n[🇯🇵 日本語版 README はこちら](README_ja.md)\n\nlibpino is a serialization library written in C99. It provides a handler-based architecture for packing and unpacking structured data, with helpers for byte-order conversion and optional SIMD-backed memory operations.\n\n## Features\n\n- **Pure C99 Implementation** - No external runtime dependencies\n- **Handler-Based Architecture** - Extensible design with custom type handlers\n- **Byte-Order Helpers** - Utilities for converting between little-endian, big-endian, and native byte order\n- **Optional SIMD Paths** - AVX2 (amd64), NEON (arm64), and WASM SIMD128 implementations are available where supported\n- **Custom Memory Managers** - Handler-local allocation is routed through the library memory manager\n- **WebAssembly Support** - Can be compiled to WASM using Emscripten\n- **Test Coverage** - Includes unit tests, sanitizer builds, and Valgrind support\n\n## Quick Start\n\n### Building\n\n```bash\n# Clone the repository\ngit clone https://github.com/zeriyoshi/libpino.git\ncd libpino\n\n# Build with CMake\ncmake -B build -DCMAKE_BUILD_TYPE=Release\ncmake --build build\n```\n\n### Build Options\n\n| Option | Default | Description |\n|--------|---------|-------------|\n| `PINO_USE_SIMD` | `ON` | Enable SIMD optimizations |\n| `PINO_USE_TESTS` | `OFF` | Build test suite |\n| `PINO_USE_VALGRIND` | `OFF` | Enable Valgrind memory checking |\n| `PINO_USE_COVERAGE` | `OFF` | Enable code coverage |\n| `PINO_USE_ASAN` | `OFF` | Enable AddressSanitizer |\n| `PINO_USE_MSAN` | `OFF` | Enable MemorySanitizer |\n| `PINO_USE_UBSAN` | `OFF` | Enable UndefinedBehaviorSanitizer |\n\n### Running Tests\n\n```bash\ncmake -B build -DCMAKE_BUILD_TYPE=Debug -DPINO_USE_TESTS=ON\ncmake --build build\nctest --test-dir build --output-on-failure\n```\n\n## Usage Example\n\n```c\n#include \u003cpino.h\u003e\n#include \u003cpino/handler.h\u003e\n#include \u003cstdio.h\u003e\n#include \u003cstring.h\u003e\n\n// Define a custom handler for your data structure\nPH_BEGIN(mydt);\n\nPH_DEF_STATIC_FIELDS_STRUCT(mydt) {\n    uint32_t data_size;\n}\nPH_DEF_STATIC_FIELDS_STRUCT_END;\n\nPH_DEF_STRUCT(mydt) {\n    uint8_t *buffer;\n}\nPH_DEF_STRUCT_END;\n\nPH_DEFUN_SERIALIZE_SIZE(mydt) {\n    uint32_t size;\n    PH_THIS_STATIC_GET(mydt, data_size, \u0026size);\n    return (size_t)size;\n}\n\nPH_DEFUN_SERIALIZE(mydt) {\n    uint32_t size;\n    PH_THIS_STATIC_GET(mydt, data_size, \u0026size);\n    PH_SERIALIZE_DATA(mydt, buffer, (size_t)size);\n    return true;\n}\n\nPH_DEFUN_UNSERIALIZE(mydt) {\n    uint32_t size;\n    PH_THIS_STATIC_GET(mydt, data_size, \u0026size);\n    PH_UNSERIALIZE_DATA(mydt, buffer, (size_t)size);\n    return true;\n}\n\nPH_DEFUN_PACK(mydt) {\n    uint32_t size = (uint32_t)PH_ARG_SIZE;\n    PH_THIS_STATIC_SET(mydt, data_size, \u0026size);\n    PH_PACK_DATA(mydt, buffer, PH_ARG_SIZE);\n    return true;\n}\n\nPH_DEFUN_UNPACK_SIZE(mydt) {\n    uint32_t size;\n    PH_THIS_STATIC_GET(mydt, data_size, \u0026size);\n    return (size_t)size;\n}\n\nPH_DEFUN_UNPACK(mydt) {\n    uint32_t size;\n    PH_THIS_STATIC_GET(mydt, data_size, \u0026size);\n    PH_UNPACK_DATA(mydt, buffer, (size_t)size);\n    return true;\n}\n\nPH_DEFUN_CREATE(mydt) {\n    PH_CREATE_THIS(mydt);\n    \n    PH_THIS(mydt)-\u003ebuffer = (uint8_t *)PH_CALLOC(mydt, 1, PH_ARG_SIZE);\n    if (!PH_THIS(mydt)-\u003ebuffer) {\n        PH_DESTROY_THIS(mydt);\n        return NULL;\n    }\n    \n    uint32_t size = (uint32_t)PH_ARG_SIZE;\n    PH_THIS_STATIC_SET(mydt, data_size, \u0026size);\n    \n    return PH_THIS(mydt);\n}\n\nPH_DEFUN_DESTROY(mydt) {\n    if (PH_THIS(mydt)-\u003ebuffer) {\n        PH_FREE(mydt, PH_THIS(mydt)-\u003ebuffer);\n    }\n    PH_DESTROY_THIS(mydt);\n}\n\nPH_END(mydt);\n\nint main(void) {\n    // Initialize PINO\n    if (!pino_init()) {\n        fprintf(stderr, \"Failed to initialize PINO\\n\");\n        return 1;\n    }\n    \n    // Register handler\n    if (!PH_REG(mydt)) {\n        fprintf(stderr, \"Failed to register handler\\n\");\n        pino_free();\n        return 1;\n    }\n    \n    // Prepare data\n    const char *message = \"Hello, PINO!\";\n    size_t message_len = strlen(message) + 1;\n    \n    // Pack data\n    pino_t *pino = pino_pack(\"mydt\", message, message_len);\n    if (!pino) {\n        fprintf(stderr, \"Failed to pack data\\n\");\n        PH_UNREG(mydt);\n        pino_free();\n        return 1;\n    }\n    \n    // Serialize\n    size_t serialized_size = pino_serialize_size(pino);\n    uint8_t *serialized = (uint8_t *)malloc(serialized_size);\n    if (!pino_serialize(pino, serialized)) {\n        fprintf(stderr, \"Failed to serialize\\n\");\n        free(serialized);\n        pino_destroy(pino);\n        PH_UNREG(mydt);\n        pino_free();\n        return 1;\n    }\n    \n    pino_destroy(pino);\n    \n    // Unserialize\n    pino_t *restored = pino_unserialize(serialized, serialized_size);\n    free(serialized);\n    \n    if (!restored) {\n        fprintf(stderr, \"Failed to unserialize\\n\");\n        PH_UNREG(mydt);\n        pino_free();\n        return 1;\n    }\n    \n    // Unpack\n    size_t unpacked_size = pino_unpack_size(restored);\n    char *unpacked = (char *)malloc(unpacked_size);\n    if (!pino_unpack(restored, unpacked)) {\n        fprintf(stderr, \"Failed to unpack\\n\");\n        free(unpacked);\n        pino_destroy(restored);\n        PH_UNREG(mydt);\n        pino_free();\n        return 1;\n    }\n    \n    printf(\"Unpacked message: %s\\n\", unpacked);\n    \n    // Cleanup\n    free(unpacked);\n    pino_destroy(restored);\n    PH_UNREG(mydt);\n    pino_free();\n    \n    return 0;\n}\n```\n\n## API Reference\n\n### Core Types\n\n```c\ntypedef struct _pino_t pino_t;              // Main PINO object\ntypedef struct _pino_handler_t pino_handler_t;  // Handler definition\ntypedef char pino_magic_t[4];               // 4-byte magic identifier\ntypedef char pino_magic_safe_t[5];          // Null-terminated magic\ntypedef uint64_t pino_static_fields_size_t; // Static fields size type\ntypedef uint32_t pino_buildtime_t;          // Build timestamp type\n```\n\n### Main Functions\n\n#### `pino_init`\n\n```c\nbool pino_init(void);\n```\n\nInitializes the PINO library. Must be called before any other PINO functions.\n\n**Returns:** `true` on success, `false` on failure.\n\n#### `pino_free`\n\n```c\nvoid pino_free(void);\n```\n\nReleases global handler-registration state owned by the PINO library. Existing PINO objects remain valid until `pino_destroy()` is called for each object.\n\n#### `pino_pack`\n\n```c\npino_t *pino_pack(pino_magic_safe_t magic, const void *src, size_t size);\n```\n\nPacks raw data into a PINO object using the specified handler.\n\n**Parameters:**\n- `magic` - 4-character handler identifier (null-terminated)\n- `src` - Pointer to source data\n- `size` - Size of source data in bytes\n\n**Returns:** New PINO object, or `NULL` on failure.\n\n#### `pino_unpack`\n\n```c\nbool pino_unpack(const pino_t *pino, void *dest);\n```\n\nUnpacks data from a PINO object into a buffer.\n\n**Parameters:**\n- `pino` - PINO object to unpack\n- `dest` - Destination buffer (must be at least `pino_unpack_size()` bytes)\n\n**Returns:** `true` on success, `false` on failure.\n\n#### `pino_unpack_size`\n\n```c\nsize_t pino_unpack_size(const pino_t *pino);\n```\n\nGets the size of unpacked data.\n\n**Parameters:**\n- `pino` - PINO object\n\n**Returns:** Size in bytes, or 0 on error.\n\n#### `pino_serialize`\n\n```c\nbool pino_serialize(const pino_t *pino, void *dest);\n```\n\nSerializes a PINO object to a byte buffer for storage or transmission.\n\n**Parameters:**\n- `pino` - PINO object to serialize\n- `dest` - Destination buffer (must be at least `pino_serialize_size()` bytes)\n\n**Returns:** `true` on success, `false` on failure.\n\n#### `pino_serialize_size`\n\n```c\nsize_t pino_serialize_size(const pino_t *pino);\n```\n\nGets the size needed for serialization.\n\n**Parameters:**\n- `pino` - PINO object\n\n**Returns:** Size in bytes, or 0 on error.\n\n#### `pino_unserialize`\n\n```c\npino_t *pino_unserialize(const void *src, size_t size);\n```\n\nUnserializes a byte buffer back into a PINO object.\n\n**Parameters:**\n- `src` - Serialized data buffer\n- `size` - Size of serialized data\n\n**Returns:** New PINO object, or `NULL` on failure.\n\n#### `pino_destroy`\n\n```c\nvoid pino_destroy(pino_t *pino);\n```\n\nDestroys a PINO object and frees all associated resources.\n\n**Parameters:**\n- `pino` - PINO object to destroy\n\nLifecycle note: `pino_free()` only releases global registration state. Every `pino_t` still has to be released with `pino_destroy()`.\n\nRepresentation note: `pino_pack()` and `pino_unpack()` operate on native in-memory layout. `pino_serialize()` and `pino_unserialize()` are the wire-format boundary and are responsible for little-endian conversion.\n\n### Handler API\n\n#### `pino_handler_register`\n\n```c\nbool pino_handler_register(pino_magic_safe_t magic, pino_handler_t *handler);\n```\n\nRegisters a custom handler with a magic identifier.\n\n**Parameters:**\n- `magic` - 4-character identifier (null-terminated)\n- `handler` - Pointer to handler structure\n\n**Returns:** `true` on success, `false` on failure.\n\n#### `pino_handler_unregister`\n\n```c\nbool pino_handler_unregister(pino_magic_safe_t magic);\n```\n\nUnregisters a handler by magic identifier.\n\n**Parameters:**\n- `magic` - Handler identifier\n\n**Returns:** `true` on success, `false` on failure.\n\n### Endianness API\n\n```c\n// Copy with endianness conversion\nvoid *pino_endianness_memcpy_le2native(void *dest, const void *src, size_t size, size_t elem_size);\nvoid *pino_endianness_memcpy_be2native(void *dest, const void *src, size_t size, size_t elem_size);\nvoid *pino_endianness_memcpy_native2le(void *dest, const void *src, size_t size, size_t elem_size);\nvoid *pino_endianness_memcpy_native2be(void *dest, const void *src, size_t size, size_t elem_size);\n\n// Move with endianness conversion\nvoid *pino_endianness_memmove_le2native(void *dest, const void *src, size_t size, size_t elem_size);\nvoid *pino_endianness_memmove_be2native(void *dest, const void *src, size_t size, size_t elem_size);\nvoid *pino_endianness_memmove_native2le(void *dest, const void *src, size_t size, size_t elem_size);\nvoid *pino_endianness_memmove_native2be(void *dest, const void *src, size_t size, size_t elem_size);\n\n// Compare with endianness conversion\nint pino_endianness_memcmp_le2native(const void *s1, const void *s2, size_t size, size_t elem_size);\nint pino_endianness_memcmp_be2native(const void *s1, const void *s2, size_t size, size_t elem_size);\nint pino_endianness_memcmp_native2le(const void *s1, const void *s2, size_t size, size_t elem_size);\nint pino_endianness_memcmp_native2be(const void *s1, const void *s2, size_t size, size_t elem_size);\n```\n\n`elem_size` is the size of each converted element. For scalars, pass `sizeof(value_type)`. For arrays, pass `sizeof(array[0])`.\n\n### Utility Functions\n\n```c\n// Get library version as integer\nuint32_t pino_version_id(void);\n\n// Get library build timestamp\npino_buildtime_t pino_buildtime(void);\n```\n\n### Handler Macros\n\nPINO provides a comprehensive set of macros for defining custom handlers:\n\n#### Structure Definition\n\n```c\nPH_BEGIN(name)                          // Begin handler definition\nPH_DEF_STRUCT(name) { ... }             // Define handler instance structure\nPH_DEF_STRUCT_END\nPH_DEF_STATIC_FIELDS_STRUCT(name) { ... } // Define static fields structure\nPH_DEF_STATIC_FIELDS_STRUCT_END\nPH_END(name)                            // End handler definition\n```\n\n#### Function Definition\n\n```c\nPH_DEFUN_SERIALIZE_SIZE(name)           // Define serialize size function\nPH_DEFUN_SERIALIZE(name)                // Define serialize function\nPH_DEFUN_UNSERIALIZE(name)              // Define unserialize function\nPH_DEFUN_PACK(name)                     // Define pack function\nPH_DEFUN_UNPACK_SIZE(name)              // Define unpack size function\nPH_DEFUN_UNPACK(name)                   // Define unpack function\nPH_DEFUN_CREATE(name)                   // Define create function\nPH_DEFUN_DESTROY(name)                  // Define destroy function\n```\n\n#### Data Access\n\n```c\nPH_THIS(name)                           // Access instance structure\nPH_THIS_STATIC(name)                    // Access static fields structure\nPH_THIS_STATIC_GET(name, param, dest)   // Get static field value\nPH_THIS_STATIC_SET(name, param, src)    // Set static field value\n```\n\n#### Memory Management\n\n```c\nPH_CREATE_THIS(name)                    // Allocate instance structure\nPH_DESTROY_THIS(name)                   // Free instance structure\nPH_MALLOC(name, size)                   // Allocate memory\nPH_CALLOC(name, count, size)            // Allocate zero-initialized memory\nPH_FREE(name, ptr)                      // Free memory\n```\n\n#### Data Operations\n\n```c\nPH_SERIALIZE_DATA(name, src, size)      // Serialize data field\nPH_UNSERIALIZE_DATA(name, dest, size)   // Unserialize data field\nPH_PACK_DATA(name, param, size)         // Pack data into field\nPH_UNPACK_DATA(name, param, size)       // Unpack data from field\n```\n\n#### Registration\n\n```c\nPH_REG(name)                            // Register handler\nPH_UNREG(name)                          // Unregister handler\n```\n\n#### Endianness Conversion\n\n```c\nPH_MEMCPY_N2L(dst, src, size)           // Native to little-endian\nPH_MEMCPY_N2B(dst, src, size)           // Native to big-endian\nPH_MEMCPY_L2N(dst, src, size)           // Little-endian to native\nPH_MEMCPY_B2N(dst, src, size)           // Big-endian to native\n```\n\n## Platform Support\n\n| Platform | SIMD | Status |\n|----------|------|--------|\n| Linux x86_64 | AVX2 | ✅ Fully supported |\n| Linux ARM64 | NEON | ✅ Fully supported |\n| Linux i386 | None | ✅ Supported (scalar) |\n| Linux s390x | None | ✅ Supported (scalar) |\n| macOS x86_64 | AVX2 | ✅ Fully supported |\n| macOS ARM64 | NEON | ✅ Fully supported |\n| Windows x86_64 | AVX2 | ✅ Fully supported |\n| WebAssembly | SIMD128 | ✅ Fully supported |\n\n## Project Structure\n\n```\nlibpino/\n├── include/\n│   ├── pino.h               # Main public header\n│   └── pino/\n│       ├── endianness.h     # Endianness conversion API\n│       └── handler.h        # Handler definition API\n├── src/\n│   ├── pino.c               # Main API implementation\n│   ├── handler.c            # Handler registry\n│   ├── memory.c             # Memory manager\n│   ├── endianness.c         # Endianness conversion\n│   └── internal/\n│       ├── common.h         # Internal types and macros\n│       └── simd.h           # SIMD abstractions\n├── tests/                   # Test suite using Unity\n│   ├── test_basic.c         # Basic functionality tests\n│   ├── test_endianness.c    # Endianness tests\n│   ├── test_invalid.c       # Error handling tests\n│   ├── handler_spl1.h       # Sample handler implementation\n│   └── util.h               # Test utilities\n├── cmake/                   # CMake modules\n│   ├── buildtime.cmake      # Build timestamp generation\n│   ├── emscripten.cmake     # WebAssembly configuration\n│   └── test.cmake           # Test configuration\n└── third_party/             # Dependencies (Unity, Emscripten SDK)\n```\n\n## Use Cases\n\nlibpino is intended for scenarios where you need:\n\n- **Cross-platform binary data exchange** - A fixed wire format can be combined with the byte-order helpers\n- **Custom serialization formats** - Handler-based architecture allows defining domain-specific formats\n- **Memory-copy-heavy workloads** - SIMD-backed paths are available for supported targets\n- **Embedded systems** - Pure C99 with no external runtime dependencies\n- **WebAssembly applications** - Emscripten builds are supported\n\n## License\n\nMIT License - see [LICENSE](LICENSE) for details.\n\n## Author\n\n**Go Kudo** ([@zeriyoshi](https://github.com/zeriyoshi)) - [zeriyoshi@gmail.com](mailto:zeriyoshi@gmail.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzeriyoshi%2Flibpino","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzeriyoshi%2Flibpino","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzeriyoshi%2Flibpino/lists"}