{"id":19041545,"url":"https://github.com/xaxys/bubbler","last_synced_at":"2026-03-09T01:06:17.021Z","repository":{"id":209237186,"uuid":"723471961","full_name":"xaxys/bubbler","owner":"xaxys","description":"Bubbler is a proto generator optimized for IoT devices.","archived":false,"fork":false,"pushed_at":"2024-12-29T16:26:32.000Z","size":2307,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-23T21:44:53.501Z","etag":null,"topics":["binary-protocol","bit-manipulation","bubbler","encoder-decoder","iot","protocol"],"latest_commit_sha":null,"homepage":"","language":"Go","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/xaxys.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}},"created_at":"2023-11-25T19:20:46.000Z","updated_at":"2024-12-29T16:26:35.000Z","dependencies_parsed_at":"2024-06-11T21:15:51.695Z","dependency_job_id":"2e2f0656-cd79-4897-9cd2-2355ed94e1d2","html_url":"https://github.com/xaxys/bubbler","commit_stats":null,"previous_names":["xaxys/bubbler"],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xaxys%2Fbubbler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xaxys%2Fbubbler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xaxys%2Fbubbler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xaxys%2Fbubbler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xaxys","download_url":"https://codeload.github.com/xaxys/bubbler/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250522293,"owners_count":21444509,"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":["binary-protocol","bit-manipulation","bubbler","encoder-decoder","iot","protocol"],"created_at":"2024-11-08T22:29:46.934Z","updated_at":"2026-03-09T01:06:11.970Z","avatar_url":"https://github.com/xaxys.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Bubbler-BANNER](.assets/Bubbler-BANNER.png)\n\n# Bubbler\n\nEnglish | [简体中文](README_cn.md)\n\nBubbler is a proto generator optimized for IoT devices. It compiles the `.bb` proto file and generates the output in the specified target language.\n\nBubbler's proto is powerful and can be non-byte-aligned, which is useful for IoT devices with limited resources. Explained below.\n\nAlso, you may need syntax highlighting for `.bb` files, see [bubbler-vscode](https://github.com/xaxys/bubbler-vscode), or install it from [VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=xaxys.Bubbler).\n\nWarning: Bubbler is still in development and is not ready for production use.\n\n## Installation\n\n```sh\ngit clone https://github.com/xaxys/bubbler.git\ncd bubbler\nmake\n```\n\n## Usage\n\n```sh\nbubbler [options] \u003cinput file\u003e\n```\n\n### Options\n\n- `-t \u003ctarget\u003e`: Target language\n- `-o \u003coutput\u003e`: Output Path\n- `-rmpath \u003cpath[,path...]\u003e`: Remove Path Prefix (Remove the path prefix of the output file path when generating files)\n  This option is usually used when generating Go target. For example, if a `.bb` file has `go_package` option set to `github.com/xaxys/bubbler/proto/rpc`, the generated file will be generated in the `output/github.com/xaxys/bubbler/proto/rpc` directory. If you want to remove the path prefix `github.com/xaxys/bubbler/proto/rpc`, you can set this option to `github.com/xaxys/bubbler/proto`. Then the generated file will be generated in the `output/rpc` directory.\n- `-inner`: Generate Inner Class (Nested Struct)\n- `-single`: Generate Single File (Combine all definitions into one file, instead of one generated file per source file)\n- `-minimal`: Generate Minimal Code (Usually without default getter/setter methods)\n- `-decnum`: Force Generate Decimal Format for Constant Value (Translate `0xFF` to `255`, `0b1111` to `15`, etc.)\n- `-memcpy`: Enable memory copy for fields (Duplicate content of `string` and `bytes` fields when decoding, instead of directly referencing the original buffer)\n- `-signext \u003cmethod\u003e`: Sign Extension Method used for Integer Field (Options: `shift`, `arith`)\n\n### Examples\n\n```sh\nbubbler -t c -minimal -o output/ example.bb\nbubbler -t c -single -o gen.hpp example.bb\nbubbler -t py -decnum -signext=arith -o output example.bb\nbubbler -t go -rmpath github.com/xaxys/bubbler/proto -o output example.bb\n```\n\n### Target Languages\n\nRun `bubbler` to see the list of supported target languages.\n\n```text\nTargets:\n  c\n  cpp\n  csharp [cs]\n  commonjs [cjs]\n  go\n  java\n  python [py]\n\n```\n\nWhen selecting the target language, you can use the aliases inside `[]`. For example, `python` can be abbreviated as `py`.\n\n- `dump`: Output the parse tree (intermediate representation) of the `.bb` file.\n\n- `c`: C language, output one `.bb.h` file and one `.bb.c` file for each `.bb` file.\n  - With `-single`: Output one file that includes all definitions for all `.bb` files. The output file name (including the extension) is determined by the `-o` option.\n  - With `-minimal`: No generation of getter/setter methods for fields.\n  - With `-memcpy`: Use `malloc` to heap-allocate memory for `string` and `bytes` fields, and copy the content from the original buffer.\n  - Without `-memcpy`: Pointer reference to the original buffer for `string` and `bytes` fields. Zero-copy and zero-heap-allocate.\n\n- `cpp`: C++ language, output one `.bb.hpp` file and one `.bb.cpp` file for each `.bb` file. The folder structure will not be affected by the `cpp_namespace` option.\n  - With `-single`: Output one file that includes all definitions for all `.bb` files. The output file name (including the extension) is determined by the `-o` option.\n  - With `-minimal`: No generation of getter/setter methods for fields.\n  - With `-memcpy`: Use `std::shared_ptr\u003cuint8_t[]\u003e` to heap-allocate memory for `bytes` fields, and copy the content from the original buffer. `string` fields will always use `std::string` and be copied every time.\n  - Without `-memcpy`: Use `std::shared_ptr\u003cuint8_t[]\u003e` with null deleter to reference the original buffer for `bytes` fields. `string` fields will always use `std::string` and be copied every time.\n\n- `csharp`: C# language, output one `.cs` file for each structure defined in each `.bb` file. The folder structure will not be affected by the `csharp_namespace` option.\n  - With `-single`: Output one file that includes all definitions for all `.bb` files. The output file name (including the extension) is determined by the `-o` option.\n  - With `-memcpy`: Use `byte[]` as the type for `bytes` fields. Encode and decode methods will only be compatible with `byte[]` parameters. Older .NET Framework versions should use this option.\n  - Without `-memcpy`: Use `Memory\u003cbyte\u003e` as the type for `bytes` fields. Encode and decode methods will be compatible with `byte[]`, `Memory\u003cbyte\u003e` and `Span\u003cbyte\u003e` (encode only) parameters. The `System.Memory` package is required for this case.\n\n- `commonjs`: CommonJS module, output one `.bb.js` file for each `.bb` file. (Please note that `BigInt` is used for `int64` and `uint64` fields, which is not supported in some environments.)\n  - With `-single`: Output one file that includes all definitions for all `.bb` files. The output file name (including the extension) is determined by the `-o` option.\n  - Force enabled: `-memcpy`.\n\n- `go`: Go language, output one `.bb.go` file for each `.bb` file. The folder structure will be affected by the `go_package` option. (i.e., `github.com/xaxys/bubbler` will generate in the `github.com/xaxys/bubbler` directory)\n  - With `-single`: Output one file that includes all definitions for all `.bb` files. The output file name (including the extension) is determined by the `-o` option. The package name is determined by the package statement of the input `.bb` file.\n  - With `-memcpy`: Make a copy of the `bytes` field when decoding. The `string` field will always be copied.\n  - Without `-memcpy`: A slice of the original buffer will be assigned to the `bytes` field. The `string` field will always be copied.\n\n- `java`: Java language, output one `.java` file for each structure defined in each `.bb` file. The folder structure will be affected by the `java_package` option. (i.e., `com.example.rovlink` will generate in the `com/example/rovlink` directory)\n  - Force enabled: `-memcpy`.\n\n- `python`: Python language, output one `_bb.py` file for each `.bb` file.\n  - With `-single`: Output one file that includes all definitions for all `.bb` files. The output file name (including the extension) is determined by the `-o` option.\n  - Force enabled: `-memcpy`.\n\n## Protocol Syntax\n\nBubbler uses a concise syntax to define data structures and enumeration types.\n\nSee examples in the [example](example/) directory.\n\n### Package Statements\n\nUse the `package` keyword to define the package name. For example:\n\n```protobuf\npackage com.example.rovlink;\n```\n\nThe package name is used to generate the output file name. For example, if the package name is `com.example.rovlink`, the output file name is `rovlink.xxx` and is placed in the `${Output Path}/com/example/` directory.\n\nOnly one package statement is allowed in a `.bb` file, and it can not be duplicated globally.\n\n### Option Statements\n\nUse the `option` keyword to define options. For example:\n\n```protobuf\noption omit_empty = true;\noption go_package = \"example.com/rovlink\";\noption cpp_namespace = \"com::example::rovlink\";\noption csharp_namespace = \"Example.Rovlink\";\noption java_package = \"com.example.rovlink\";\n```\n\nThe option statement cannot be duplicated in a `.bb` file.\n\nWarning will be reported if an option is unknown.\n\n#### Supported Options\n\n##### `omit_empty`\n\nIf `omit_empty` is set to `true`, the generated code will not generate files without typedefs.\n\n```protobuf\npackage all;\n\noption omit_empty = true;\n\nimport \"rovlink.bb\";\nimport \"control.bb\";\nimport \"excomponent.bb\";\nimport \"excontrol.bb\";\nimport \"exdata.bb\";\nimport \"host.bb\";\nimport \"mode.bb\";\nimport \"sensor.bb\";\n```\n\nIn this example, the `omit_empty` option is set to `true`, and this `.bb` file will not generate an `all.xxx` file.\n\nYou can use this option to generate multiple `.bb` files at once, without writing an external script to do multiple `bubbler` calls.\n\n##### `go_package`\n\nIf `go_package` is set, the generated code will use the specified package name in the generated Go code.\n\n##### `cpp_namespace`\n\nIf `cpp_namespace` is set, the generated code will use the specified namespace in the generated C++ code.\n\n##### `csharp_namespace`\n\nIf `csharp_namespace` is set, the generated code will use the specified namespace in the generated C# code. The folder structure will not be affected.\n\n##### `java_package`\n\nIf `java_package` is set, the generated code will use the specified package name in the generated Java code. The generated folder structure will be based on the package name.\n\n### Import Statements\n\nUse the `import` keyword to import other Bubbler protocol files. For example:\n\n```python\nimport \"control.bb\";\nimport \"a.bb\";\n```\n\n### Enumeration Types\n\nUse the `enum` keyword to define enumeration types. The definition of an enumeration type includes the enumeration name and enumeration values. For example:\n\n```c\nenum FrameType[1] {\n    SENSOR_PRESS = 0x00,\n    SENSOR_HUMID = 0x01,\n    CURRENT_SERVO_A = 0xA0,\n    CURRENT_SERVO_B = 0xA1,\n};\n```\n\nIn this example, `FrameType` is an enumeration type with four enumeration values: `SENSOR_PRESS`, `SENSOR_HUMID`, `CURRENT_SERVO_A`, and `CURRENT_SERVO_B`.\n\nEnumeration values cannot be negative (tentatively), and if the value is not filled in, the default value of the enumeration value is the previous enumeration value plus 1.\n\nThe number in the square brackets after the enumeration type name indicates the width of the enumeration type, for example, `[1]` indicates 1 byte. You can also use the `#` symbol to represent bytes and bits, for example, `#1` represents 1 bit, `#2` represents 2 bits. You can also use them in combination, for example, `1#4` represents 1 byte 4 bits, that is, 12 bits.\n\nRecommended to use **PascalCase** for enumeration type names. But only capitialization of the first letter is mandatory.\n\nRecommended to use **ALLCAP_CASE** for enumeration values. But only capitialization of the first letter is mandatory.\n\n### Data Structures\n\nUse the `struct` keyword to define data structures. The definition of a data structure includes the structure name and a series of fields. For example:\n\n```c\nstruct Frame[20] {\n    FrameType opcode;\n    struct SomeEmbed[1] {\n        bool valid[#1];\n        bool error[#1];\n        uint8 source[#3];\n        uint8 target[#3];\n    };\n    uint8\u003c18\u003e payload;\n};\n```\n\nIn this example, `Frame` is a data structure with three fields: `opcode`, `SomeEmbed`, and `payload`. `opcode` is of type `FrameType`, `SomeEmbed` is an anonymous embedded data structure, and `payload` is of type `uint8`.\n\nPlease note that Bubbler does not have the concept of scope (to accommodate the C language), so the names `Frame` and `SomeEmbed` as data structure names are not allowed to be duplicated globally, even if `SomeEmbed` is an anonymous embedded data structure.\n\nRecommended to use **PascalCase** for data structure names. But only capitialization of the first letter is mandatory.\n\nRecommended to use **snake_case** for field names. But only uncaptialization of the first letter is mandatory.\n\n### Field Types\n\nThe Bubbler protocol supports four types of fields: regular fields, anonymous embedded fields, constant fields, and empty fields.\n\n- Regular fields: Consist of a type name, field name, and field width (optional).\n- Anonymous embedded fields: An anonymous field, which can be a struct definition or a defined struct name, its internal subfields will be promoted and expanded into the parent structure.\n- Constant fields: A field with a fixed value, its value is determined at the time of definition and cannot be modified. The field name is optional. If there is a field name, the corresponding field will be generated. When encoding, the value of the constant field will be ignored. When decoding, the value of the constant field will be checked. If it does not match, an error will be reported.\n- Empty fields: A field without a name and type, only width, used for placeholders.\n\n#### Regular Fields\n\nRegular fields consist of a type name, field name, and field width. For example:\n\n```c\nstruct Frame {\n    RovlinkFrameType opcode;\n};\n```\n\nIn this example, `opcode` is a regular field, its type is `RovlinkFrameType`.\n\nThe field width is optional. If the width is not filled in, the field width is the width of the type.\n\nThe field width can be less than the width of the type, for example:\n\n```c\nstruct Frame[20] {\n    int64 my_int48[6];\n};\n```\n\nIn this example, `my_int48` is a 6-byte field, its type is `int64`, but its width is 6 bytes, so it will only occupy 6 bytes of space when encoding.\n\nHowever, for fields of `struct` type, the field width must be equal to the width of the type\n\n### Anonymous Embedded Fields\n\nAnonymous embedded fields are nameless data structures that can contain multiple subfields. For example:\n\n```c\nstruct Frame {\n    int64 my_int48[6];\n    struct SomeEmbed[1] {\n        bool valid[#1];\n        bool error[#1];\n        uint8 source[#3];\n        uint8 target[#3];\n    };\n};\n```\n\nIn this example, `SomeEmbed` is an anonymous embedded field, it contains four subfields: `valid`, `error`, `source`, and `target`.\n\nThe subfields of the anonymous embedded field will be promoted and expanded into the parent structure. The generated structure is as follows:\n\n```c\nstruct Frame {\n    int64_t my_int48;\n    bool valid;\n    bool error;\n    uint8_t source;\n    uint8_t target;\n};\n```\n\nAnonymous embedded fields can also be a defined data structure, for example:\n\n```c\nstruct AnotherTest {\n    int8\u003c2\u003e arr;\n}\n\nstruct Frame {\n    int64 my_int48[6];\n    AnotherTest;\n    uint8\u003c18\u003e payload;\n};\n```\n\nIn this way, the generated structure is as follows:\n\n```c\nstruct Frame {\n    int64_t my_int48;\n    int8_t arr[2];\n    uint8_t payload;\n};\n```\n\n### Constant Fields\n\nConstant fields are fields with a fixed value, its value is determined at the time of definition and cannot be modified. For example:\n\n```c\nstruct Frame {\n    uint8 FRAME_HEADER = 0xAA;\n};\n```\n\nIn this example, `FRAME_HEADER` is a constant field with a value of `0xAA`.\n\nOr you can use an enum value defined in a previous enum type as a constant value:\n\n```c\nenum FrameType[1] {\n    FRAME_KEEPALIVE = 0x00,\n    FRAME_DATA = 0x01,\n};\n\nstruct Frame {\n    FrameType opcode = FRAME_DATA;\n    bytes data;\n};\n```\n\nThe value of the constant field will be ignored during encoding and checked during decoding. If it does not match, an error will be reported.\n\n### Empty Fields\n\nEmpty fields are fields without a name and type, they only have a width. Empty fields are often used for padding or aligning data structures. For example:\n\n```c\nstruct Frame {\n    void [#2];\n};\n```\n\nIn this example, `void [#2]` is an empty field that occupies 2 bits of space.\n\n### Field Options\n\nField options are used to specify additional attributes of a field. For example, you can use the `order` option to specify the byte order of an array:\n\n```c\nstruct AnotherTest {\n    int8\u003c2\u003e arr [order = \"big\"];\n}\n```\n\nIn this example, the byte order of the `arr` field is set to big-endian.\n\n\u003e Note: The setting of endianness is also effective for floating-point types. However, currently, floating-point values are always interpreted in little-endian order, with the most significant bit storing the sign bit, followed by the exponent bits, and finally the fraction bits.\n\n### Custom getter/setter\n\nYou can define custom getter and setter methods for a field to perform specific operations when reading or writing field values. For example:\n\n```c\nstruct SensorTemperatureData {\n    uint16 temperature[2] {\n        get temperature_display(float64): value / 10 - 40;\n        set temperature_display(float64): value == 0 ? 0 : (value + 40) * 10;\n        set another_custom_setter(uint8): value == 0 ? 0 : (value + 40) * 10;\n    };\n}\n```\n\nIn this example, the `temperature` field has a custom getter method and two custom setter methods.\n\nThe custom getter named `temperature_display` returns a`float64` type and calculates the result based on `value / 10 - 40`. Here,`value` is filled with the field value and is of type `uint16`.\n\nThe custom setter named `temperature_display` accepts a `float64` type parameter and calculates the result based on `value == 0 ? 0 : (value + 40) * 10` to set the field value. Here, `value` is filled with the parameter value and is of type `float64`.\n\nThe custom setter named `another_custom_setter` accepts a `uint8` type parameter and calculates the result based on `value == 0 ? 0 : (value + 40) * 10` to set the field value. Here, `value` is filled with the parameter value and is of type `uint8`.\n\nPlease note that the custom getter and setter method names cannot be the same as any field names, and getter and setter methods with the same name must return and accept the same type.\n\nRecommended to use **snake_case** for getter/setter names. But only uncaptialization of the first letter is mandatory.\n\n## Contributing\n\nContributions to Bubbler are welcome.\n\n## License\n\nMIT License\n\n## Related Repositories\n\n- [CoralReefPlayer](https://github.com/DawningW/CoralReefPlayer) - CoralReefPlayer, a low-latency streaming media player.\n- [OpenFinNAV](https://github.com/redlightASl/OpenFinNAV) - FinNAV, a flight control firmware library for underwater robots (ROV/AUV).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxaxys%2Fbubbler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxaxys%2Fbubbler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxaxys%2Fbubbler/lists"}