diff --git a/changes.rst b/changes.rst index 8fa62584..02948446 100644 --- a/changes.rst +++ b/changes.rst @@ -1,5 +1,23 @@ .. currentmodule:: bitproto +Version 1.3.0 +------------- + +.. _version-1.3.0: + +- Feature: Big-endian host support. bitproto's wire format is little-endian on + every platform; the C library (standard mode) and the C optimization-mode + generated code now also produce correct output when compiled for a big-endian + host. Python and Go were already endian-neutral and unaffected. +- Feature: New compiler option ``--endian`` (``both`` | ``little`` | ``big``) + controlling the host byte order targeted by optimization-mode C/C++ code. + The default ``both`` emits both code paths guarded by ``#ifdef BP_BIG_ENDIAN`` + and auto-detects the host (overridable by defining ``BP_BIG_ENDIAN``); + ``little`` keeps only the original, smaller and faster byte-pointer code (no + big-endian support); ``big`` emits only the portable path. +- The little-endian generated code is unchanged from previous releases, so there + is no performance regression on the common little-endian target. + Version 1.2.2 ------------- diff --git a/compiler/bitproto/__init__.py b/compiler/bitproto/__init__.py index 663ffd0c..b6b61e5d 100644 --- a/compiler/bitproto/__init__.py +++ b/compiler/bitproto/__init__.py @@ -8,5 +8,5 @@ """ -__version__ = "1.2.2" +__version__ = "1.3.0" __description__ = "bit level data interchange format." diff --git a/compiler/bitproto/_main.py b/compiler/bitproto/_main.py index 793a0425..617f60d1 100644 --- a/compiler/bitproto/_main.py +++ b/compiler/bitproto/_main.py @@ -84,6 +84,21 @@ def build_arg_parser() -> argparse.ArgumentParser: "this works only if optimization mode enabled." ), ) + args_parser.add_argument( + "--endian", + dest="endian", + type=str, + choices=["both", "little", "big"], + default="both", + help=( + "host byte order to target for optimization-mode C/C++ code. " + "'both' (default) emits both paths guarded by #ifdef BP_BIG_ENDIAN " + "and auto-detects the host; 'little' emits only the fast " + "little-endian path (smaller output, no big-endian support); " + "'big' emits only the portable big-endian path. " + "Only affects optimization mode." + ), + ) args_parser.add_argument( "-v", "--version", @@ -115,6 +130,7 @@ def run_bitproto() -> None: check=args.check, enable_optimize=args.enable_optimize, filter_messages=filter_messages, + endian=args.endian, ) @@ -126,6 +142,7 @@ def main( check: bool = False, enable_optimize: bool = False, filter_messages: Optional[List[str]] = None, + endian: str = "both", ) -> None: # Parse try: @@ -161,6 +178,7 @@ def main( outdir=outdir, optimization_mode=enable_optimize, optimization_mode_filter_messages=filter_messages, + optimization_mode_endian=endian, ) except RendererError as error: fatal(error.colored()) diff --git a/compiler/bitproto/renderer/__init__.py b/compiler/bitproto/renderer/__init__.py index 76a4abf4..48a5a963 100644 --- a/compiler/bitproto/renderer/__init__.py +++ b/compiler/bitproto/renderer/__init__.py @@ -18,6 +18,7 @@ def render( outdir: Optional[str] = None, optimization_mode: bool = False, optimization_mode_filter_messages: Optional[List[str]] = None, + optimization_mode_endian: str = "both", ) -> List[str]: """Render given `proto` to directory `outdir`. Returns the filepath list generated. @@ -33,6 +34,7 @@ def render( outdir=outdir, optimization_mode=optimization_mode, optimization_mode_filter_messages=optimization_mode_filter_messages, + optimization_mode_endian=optimization_mode_endian, ) outs.append(renderer.render()) return outs diff --git a/compiler/bitproto/renderer/block.py b/compiler/bitproto/renderer/block.py index 202a1527..ae41be40 100644 --- a/compiler/bitproto/renderer/block.py +++ b/compiler/bitproto/renderer/block.py @@ -59,6 +59,9 @@ class BlockRenderContext(Generic[F]): formatter: F bound: Proto optimization_mode_filter_messages: Optional[List[str]] = None + # One of "both" / "little" / "big": which host-endianness path(s) the + # optimization-mode C/C++ codegen should emit. See bitproto._main. + optimization_mode_endian: str = "both" class Block(Generic[F]): diff --git a/compiler/bitproto/renderer/impls/c/formatter.py b/compiler/bitproto/renderer/impls/c/formatter.py index 7a1ed258..c2f9bfdb 100644 --- a/compiler/bitproto/renderer/impls/c/formatter.py +++ b/compiler/bitproto/renderer/impls/c/formatter.py @@ -315,34 +315,133 @@ def format_bp_array_json_formatter_name_from_alias(self, t: Array, d: Alias) -> def format_op_mode_endecoder_message_var(self) -> str: return "(*m)" + def _format_unsigned_chain_type(self, t: Type) -> str: + """Returns the unsigned C integer type for the field type t. + Used to generate endian-neutral bit-shift code instead of byte-pointer + indexing, which assumes little-endian host byte order. + """ + if isinstance(t, Alias): + return self._format_unsigned_chain_type(t.type) + if isinstance(t, Enum): + return self._format_unsigned_chain_type(t.type) + if isinstance(t, Bool): + return "uint8_t" + if isinstance(t, Byte): + return "unsigned char" + if isinstance(t, Uint): + return self.format_uint_type(t) + if isinstance(t, Int): + n = self.get_nbits_of_integer(t) + return f"uint{n}_t" + return "unsigned" + + # When True, the op-mode item formatters emit endian-neutral, value-based + # bit-shift code (correct on big-endian hosts). When False (default), they + # emit the faster byte-pointer indexing code, which assumes little-endian + # host byte order. The C renderer emits both variants guarded by + # #ifdef BP_BIG_ENDIAN so little-endian builds keep the fast path. + _op_mode_big_endian: bool = False + + def format_op_mode_message_endian( + self, message: Message, is_encode: bool, big_endian: bool + ) -> List[str]: + """Generate the op-mode encode/decode statements for a message, choosing + the little-endian (fast) or big-endian (portable) item variant. + """ + self._op_mode_big_endian = big_endian + try: + if is_encode: + return self.format_op_mode_encode_message(message) + return self.format_op_mode_decode_message(message) + finally: + self._op_mode_big_endian = False + @override(Formatter) def format_op_mode_encoder_item( self, chain: str, t: Type, si: int, fi: int, shift: int, mask: int, r: int ) -> str: - """Implements format_op_mode_encoder_item for C. + if self._op_mode_big_endian: + return self._format_op_mode_encoder_item_be( + chain, t, si, fi, shift, mask, r + ) + return self._format_op_mode_encoder_item_le(chain, t, si, fi, shift, mask, r) + + def _format_op_mode_encoder_item_le( + self, chain: str, t: Type, si: int, fi: int, shift: int, mask: int, r: int + ) -> str: + """Little-endian (fast) encoder item, using byte-pointer indexing. Generated C statement like: s[0] = (((unsigned char *)&((*m).color))[0] >> 3) & 7; - """ assign = "=" if r == 0 else "|=" shift_s = self.format_op_mode_smart_shift(shift) return f"s[{si}] {assign} (((unsigned char *)&({chain}))[{fi}] {shift_s}) & {mask};" + def _format_op_mode_encoder_item_be( + self, chain: str, t: Type, si: int, fi: int, shift: int, mask: int, r: int + ) -> str: + """Big-endian (portable) encoder item, using explicit bit shifts instead + of byte-pointer indexing so the output is correct on big-endian hosts. + fi*8 scales the byte index into a bit offset; the per-byte shift is then + folded in so a single right- or left-shift covers both. + Generated C statement like: + + s[0] = ((uint8_t)((*m).color) >> 3) & 7; + """ + assign = "=" if r == 0 else "|=" + unsigned_type = self._format_unsigned_chain_type(t) + total_shift = fi * 8 + shift + total_shift_s = self.format_op_mode_smart_shift(total_shift) + return f"s[{si}] {assign} (({unsigned_type})({chain}){total_shift_s}) & {mask};" + @override(Formatter) def format_op_mode_decoder_item( self, chain: str, t: Type, si: int, fi: int, shift: int, mask: int, r: int ) -> str: - """Implements format_op_mode_decoder_item for C. + if self._op_mode_big_endian: + return self._format_op_mode_decoder_item_be( + chain, t, si, fi, shift, mask, r + ) + return self._format_op_mode_decoder_item_le(chain, t, si, fi, shift, mask, r) + + def _format_op_mode_decoder_item_le( + self, chain: str, t: Type, si: int, fi: int, shift: int, mask: int, r: int + ) -> str: + """Little-endian (fast) decoder item, using byte-pointer indexing. + Uses = on the first write to a byte (r == 0) so no memset is required. Generated C statement like: ((unsigned char *)&((*m).color))[0] = (s[0] << 3) & 7; - """ assign = "=" if r == 0 else "|=" shift_s = self.format_op_mode_smart_shift(shift) return f"((unsigned char *)&({chain}))[{fi}] {assign} (s[{si}] {shift_s}) & {mask};" + def _format_op_mode_decoder_item_be( + self, chain: str, t: Type, si: int, fi: int, shift: int, mask: int, r: int + ) -> str: + """Big-endian (portable) decoder item. + Generated C statement like: + + (*m).color |= (uint8_t)(((unsigned)(s[0]) << 3) & 7); + + Always uses |= (never =) so each byte contributes its bits without + clobbering already-written bytes; the big-endian Decode branch starts + with memset(m, 0, sizeof(*m)) to guarantee a zero baseline. + fi*8 positions the extracted bits at the correct byte of the field. + """ + chain_type = self.format_type(t) + shift_s = self.format_op_mode_smart_shift(shift) + fi_shift = fi * 8 + byte_val = f"(((unsigned)(s[{si}]){shift_s}) & {mask})" + if fi_shift > 0: + unsigned_type = self._format_unsigned_chain_type(t) + return ( + f"{chain} |= ({chain_type})(({unsigned_type}){byte_val} << {fi_shift});" + ) + return f"{chain} |= ({chain_type}){byte_val};" + @override(Formatter) def post_format_op_mode_endecode_single_type( self, t: Type, chain: str, is_encode: bool diff --git a/compiler/bitproto/renderer/impls/c/renderer_c.py b/compiler/bitproto/renderer/impls/c/renderer_c.py index b94f7c2b..c6b4fb30 100644 --- a/compiler/bitproto/renderer/impls/c/renderer_c.py +++ b/compiler/bitproto/renderer/impls/c/renderer_c.py @@ -407,29 +407,93 @@ class BlockIncludeOpMode(Block[F]): def render(self) -> None: header_filename = self.formatter.format_out_filename(self.bound, extension=".h") self.push(f'#include "{header_filename}"') + endian = self._get_ctx_or_raise().optimization_mode_endian + if endian == "both": + # Auto-detect a big-endian host so the default just works without + # the user defining BP_BIG_ENDIAN; still overridable via -D. + self.push("#if !defined(BP_BIG_ENDIAN) && defined(__BYTE_ORDER__) && \\") + self.push(" (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)") + self.push("#define BP_BIG_ENDIAN 1") + self.push("#endif") + # is only needed for memset in the big-endian path. + self.push("#ifdef BP_BIG_ENDIAN") + self.push("#include ") + self.push("#endif") + elif endian == "big": + self.push("#include ") class BlockMessageEncoderOpMode(BlockMessageEncoderBase): @override(Block) def render(self) -> None: + endian = self._get_ctx_or_raise().optimization_mode_endian self.push(f"{self.function_signature} {{") - l = self.formatter.format_op_mode_encode_message(self.d) - for line in l: - self.push(line, indent=4) + self._push_body(endian) self.push("return 0;", indent=4) self.push("}") + def _push_body(self, endian: str) -> None: + def le() -> None: + for line in self.formatter.format_op_mode_message_endian( + self.d, is_encode=True, big_endian=False + ): + self.push(line, indent=4) + + def be() -> None: + for line in self.formatter.format_op_mode_message_endian( + self.d, is_encode=True, big_endian=True + ): + self.push(line, indent=4) + + if endian == "little": + le() + elif endian == "big": + be() + else: # both: fast little-endian path, portable big-endian behind #else. + self.push("#ifndef BP_BIG_ENDIAN") + le() + self.push("#else") + be() + self.push("#endif") + class BlockMessageDecoderOpMode(BlockMessageDecoderBase): @override(Block) def render(self) -> None: + endian = self._get_ctx_or_raise().optimization_mode_endian self.push(f"{self.function_signature} {{") - l = self.formatter.format_op_mode_decode_message(self.d) - for line in l: - self.push(line, indent=4) + self._push_body(endian) self.push("return 0;", indent=4) self.push("}") + def _push_body(self, endian: str) -> None: + # The little-endian path uses = on the first byte write, so it needs no + # zeroing. The big-endian path always uses |=, so it must start from a + # zeroed message (memset). + def le() -> None: + for line in self.formatter.format_op_mode_message_endian( + self.d, is_encode=False, big_endian=False + ): + self.push(line, indent=4) + + def be() -> None: + self.push("memset(m, 0, sizeof(*m));", indent=4) + for line in self.formatter.format_op_mode_message_endian( + self.d, is_encode=False, big_endian=True + ): + self.push(line, indent=4) + + if endian == "little": + le() + elif endian == "big": + be() + else: # both + self.push("#ifndef BP_BIG_ENDIAN") + le() + self.push("#else") + be() + self.push("#endif") + class BlockMessageFunctionsOpMode(BlockBindMessage[F], BlockComposition[F]): @override(BlockComposition) diff --git a/compiler/bitproto/renderer/renderer.py b/compiler/bitproto/renderer/renderer.py index 8859588c..d4213fe3 100644 --- a/compiler/bitproto/renderer/renderer.py +++ b/compiler/bitproto/renderer/renderer.py @@ -31,12 +31,14 @@ def __init__( outdir: Optional[str] = None, optimization_mode: bool = False, optimization_mode_filter_messages: Optional[List[str]] = None, + optimization_mode_endian: str = "both", ) -> None: self.proto = proto self.outdir = outdir or self.get_outdir_default(proto) self.optimization_mode = optimization_mode self.check_proto_for_optimization_mode() self.optimization_mode_filter_messages = optimization_mode_filter_messages + self.optimization_mode_endian = optimization_mode_endian self.out_filename = self.get_out_filename() self.out_filepath = os.path.join(self.outdir, self.out_filename) @@ -66,6 +68,7 @@ def render_string(self) -> str: formatter=formatter, bound=self.proto, optimization_mode_filter_messages=self.optimization_mode_filter_messages, + optimization_mode_endian=self.optimization_mode_endian, ) block._render_with_ctx(ctx) return block._collect() diff --git a/docs/compiler.rst b/docs/compiler.rst index 73b8e1d5..d43c9498 100644 --- a/docs/compiler.rst +++ b/docs/compiler.rst @@ -73,3 +73,11 @@ bitproto file doesn't meet the :ref:`style-guide`, to disable the linter: .. sourcecode:: bash $ bitproto c proto.bitproto -q + +In :ref:`optimization mode ` (``-O``), the ``--endian`` option +controls the host byte order targeted by the generated C/C++ code, one of ``both`` (default), +``little`` or ``big``. See :ref:`endianness`. + +.. sourcecode:: bash + + $ bitproto c proto.bitproto -O --endian=little diff --git a/docs/endianness.rst b/docs/endianness.rst new file mode 100644 index 00000000..b35bea89 --- /dev/null +++ b/docs/endianness.rst @@ -0,0 +1,106 @@ +.. currentmodule:: bitproto + +.. _endianness: + +Endianness +========== + +bitproto's wire format is **little-endian on every platform**: the lowest byte +index on the wire holds the least-significant bits of a field. This is a fixed +property of the encoding, independent of the machine that runs the encoder or +decoder — two peers with different byte orders always interoperate. + +.. _endianness-languages: + +Language support +---------------- + +Whether the *host* CPU is big-endian only ever mattered for C, because only C +reads an integer field's raw memory bytes. Python and Go operate on integer +*values* with shifts and masks, which is byte-order independent by construction. + +.. list-table:: + :header-rows: 1 + :widths: 22 40 22 + + * - Target + - How bits are moved + - Big-endian host + * - Python + - value math on Python ints + - always correct + * - Go + - value shifts on typed fields + - always correct + * - C (standard mode) + - ``lib/c`` runtime + - correct + * - C (optimization mode) + - generated bit-copy statements + - correct + +So nothing special is required for Go or Python. The remainder of this page is +about C. + +.. _endianness-c-standard-mode: + +C standard mode +--------------- + +The C library (``lib/c/bitproto.c``) detects a big-endian host automatically via +``__BYTE_ORDER__`` and selects an endian-neutral code path. You may also force it +by defining ``BP_BIG_ENDIAN`` when compiling the library, for toolchains that do +not define ``__BYTE_ORDER__``: + +.. sourcecode:: bash + + $ cc -DBP_BIG_ENDIAN -c bitproto.c + +On a little-endian host the library is byte-for-byte unchanged, so there is no +performance impact on the common target. + +.. _endianness-c-optimization-mode: + +C optimization mode +------------------- + +:ref:`Optimization mode ` generates plain bit-copy +statements that read and write integer fields through their in-memory bytes. To +stay correct on both little-endian and big-endian hosts, by default bitproto +emits two equivalent code paths guarded by a preprocessor macro: + +.. sourcecode:: c + + #ifndef BP_BIG_ENDIAN + // fast byte-pointer path (little-endian hosts) + #else + // portable bit-shift path (big-endian hosts) + #endif + +A big-endian host is auto-detected via ``__BYTE_ORDER__``; you may also force the +big-endian path by defining ``BP_BIG_ENDIAN`` when compiling the generated code. +The little-endian path is identical to earlier bitproto releases, so there is no +performance change on little-endian targets — only the generated source is a +little larger. + +.. _endianness-option: + +The ``--endian`` option +----------------------- + +If you know your target's byte order you can drop the unused path and shrink the +generated source with the ``--endian`` option (optimization mode only): + +.. sourcecode:: bash + + # default: both paths, host auto-detected + $ bitproto c example.bitproto -O + + # only the fast little-endian path (smaller output, no big-endian support) + $ bitproto c example.bitproto -O --endian=little + + # only the portable big-endian path + $ bitproto c example.bitproto -O --endian=big + +``--endian`` only affects optimization-mode C/C++ code. It has no effect on the +wire format, and none on Go or Python. diff --git a/docs/faq.rst b/docs/faq.rst index 71b066c5..e46dfcce 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -8,10 +8,11 @@ This document answers some of the often asked questions about bitproto. Does bitproto depend on endianness ? '''''''''''''''''''''''''''''''''''' -Yes, bitproto is not endianness independent, it currently supports only little-endian systems. -Endianness is always an issue for data serialization. But most of cpu processors, (including x86, -armv7, armv8 and most arm cortex-m mcus) are little-endian, or little-endian as default. -There's no plan for now to support big-endian systems or endianness independent serialization. +Yes, bitproto's wire format is little-endian on every platform. The C library +(standard mode) and the C optimization-mode generated code now both produce +correct output on big-endian hosts. Python and Go are endian-neutral and were +always unaffected. +See :ref:`endianness` for details. What's the advantage of this over a bit field ? '''''''''''''''''''''''''''''''''''''''''''''''' diff --git a/docs/index.rst b/docs/index.rst index a01a065b..df610abe 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -178,6 +178,7 @@ Content list compiler language performance + endianness faq changelog license diff --git a/docs/locales/zh/LC_MESSAGES/changelog.po b/docs/locales/zh/LC_MESSAGES/changelog.po index d2ffaa82..6b5fd028 100644 --- a/docs/locales/zh/LC_MESSAGES/changelog.po +++ b/docs/locales/zh/LC_MESSAGES/changelog.po @@ -8,128 +8,203 @@ msgid "" msgstr "" "Project-Id-Version: bitproto 0.4.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-03-22 16:32+0800\n" +"POT-Creation-Date: 2026-06-19 01:28-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.17.0\n" +"Generated-By: Babel 2.18.0\n" -#: ../../changelog.rst:2 bd0c49799ad1472b9ebc5a93eae789ae +#: ../../changelog.rst:2 76d5d3603c404178ae5b7c007ad11007 msgid "Changelog" msgstr "版本历史" -#: ../../../changes.rst:4 4ca3aa397b074fb7a87296b215f52ecb +#: ../../../changes.rst:4 21ceda6cb0ef42a98dcbc98ccfb9241d +#, fuzzy +msgid "Version 1.3.0" +msgstr "Version 1.1.2" + +#: ../../../changes.rst:8 67b139acd1c14750b76eb78d59628835 +msgid "" +"Feature: Big-endian host support. bitproto's wire format is little-endian" +" on every platform; the C library (standard mode) and the C optimization-" +"mode generated code now also produce correct output when compiled for a " +"big-endian host. Python and Go were already endian-neutral and " +"unaffected." +msgstr "" + +#: ../../../changes.rst:12 9eff75eba59548dda5b36bd7771b0973 +msgid "" +"Feature: New compiler option ``--endian`` (``both`` | ``little`` | " +"``big``) controlling the host byte order targeted by optimization-mode " +"C/C++ code. The default ``both`` emits both code paths guarded by " +"``#ifdef BP_BIG_ENDIAN`` and auto-detects the host (overridable by " +"defining ``BP_BIG_ENDIAN``); ``little`` keeps only the original, smaller " +"and faster byte-pointer code (no big-endian support); ``big`` emits only " +"the portable path." +msgstr "" + +#: ../../../changes.rst:18 abc52528f16c4b7c81153ab7c2240866 +msgid "" +"The little-endian generated code is unchanged from previous releases, so " +"there is no performance regression on the common little-endian target." +msgstr "" + +#: ../../../changes.rst:22 22529ec400cf407abacf99ac626d435b +#, fuzzy +msgid "Version 1.2.2" +msgstr "Version 1.1.2" + +#: ../../../changes.rst:26 ../../../changes.rst:35 +#: 95eba896850f414e89ebb84b70d40c3c e25d49c311624576abd66476d2d553d6 +msgid "Warning: May break some existing projects's generated names:" +msgstr "" + +#: ../../../changes.rst:28 fe2017ebb9f447f8bddfeaeb8e987c0a +msgid "Improve `snake_case` function. #74, #75" +msgstr "" + +#: ../../../changes.rst:31 ba8f42bbecf644b29baad003f4b766df +#, fuzzy +msgid "Version 1.2.1" +msgstr "Version 1.1.2" + +#: ../../../changes.rst:37 b0189e46434948b8b61eed17372426b6 +msgid "Bugfix: `pascal_case` formatter. ISSUE #68, PR #69." +msgstr "" + +#: ../../../changes.rst:38 5ecfae5bd6fb4920b9c61479c9b71be6 +msgid "" +"Bugfix: Fixed naming style of generated code such as IntegerConstant " +"(style lookup supports inheritance). ISSUE #70 PR #70" +msgstr "" + +#: ../../../changes.rst:42 600da5f679ad4b60907b5a12d9dc661a +#, fuzzy +msgid "Version 1.2.0" +msgstr "Version 1.1.2" + +#: ../../../changes.rst:46 80ff49dbfa30413fa75ceaf145ec6a3b +msgid "Feature: Add a simple language-server, tested on neovim and vscode." +msgstr "" + +#: ../../../changes.rst:47 c16cae5e4b5a413f890924aae18b10f1 +msgid "" +"Editor: Upgrade vscode extenions to support the ``bitproto-language-" +"server``." +msgstr "" + +#: ../../../changes.rst:50 d760bd22453742f9a43c8144660042af msgid "Version 1.1.2" msgstr "Version 1.1.2" -#: ../../../changes.rst:8 427ec549afc64dd3ba6e4efb7ba67165 +#: ../../../changes.rst:54 26ac073c24044181a22c1d41b56d25df msgid "Feature: Allow using constants as option values. ISSUE #61, PR #63" msgstr "" -#: ../../../changes.rst:11 4ca3aa397b074fb7a87296b215f52ecb +#: ../../../changes.rst:57 b0046c32c229421392f8d3c2badbb7d7 msgid "Version 1.1.1" msgstr "" -#: ../../../changes.rst:15 61227587c4e443038089921f7bf8584f +#: ../../../changes.rst:61 12f263a55eda445a9b3c92bf86a4c3de msgid "" "Fix bug: enum importing other bitproto's field name generation bug. #53 " "#52" msgstr "" -#: ../../../changes.rst:16 66206e28775a4993a98af4b796bd3097 +#: ../../../changes.rst:62 52d66b4337644fd18beae488ad3255e2 msgid "" "Fix bug: import statements of bitprotos should be placed ahead of other " "declarations. #53" msgstr "" -#: ../../../changes.rst:19 9c595ec0cf7f4e21b9cab4878149574c +#: ../../../changes.rst:65 22cb18322ba44cbca1d0d06797f7b798 msgid "Version 1.1.0" msgstr "" -#: ../../../changes.rst:23 668377b7aead4b6e9dfe5650f07f550d +#: ../../../changes.rst:69 c46626dc5a5d4c2ba9a836831a7821ae msgid "" "Performance improvements for C bitprotolib, 40~60us improvement per call " "on stm32. PR #48." msgstr "" -#: ../../../changes.rst:24 351457b1434647ff926e98fe7fb00c5a +#: ../../../changes.rst:70 4ddf229304cb447085af86c7cb1500d0 msgid "" "Fix Python nested message ``__post_init___`` function code generation. PR" " #48, commit 73f4b01." msgstr "" -#: ../../../changes.rst:27 24745d925dd0496c8a1dd647e7b7225f +#: ../../../changes.rst:73 3f89d51d88b843489a296cead4dfea86 msgid "Version 1.0.1" msgstr "" -#: ../../../changes.rst:31 c9cede67a4354f85a113fe9802d7c3f1 +#: ../../../changes.rst:77 dc8ee87e18ef422f9906a8d85f1e93df msgid "Add support for Python 3.11" msgstr "" -#: ../../../changes.rst:34 33d884cb4ef54a7e80d5c425391b372c +#: ../../../changes.rst:80 256f5b5a0e7e41299e71e29f5be6aa8b msgid "Version 1.0.0" msgstr "" -#: ../../../changes.rst:38 3592669cb8e847c49cea8a365c2c3a5f +#: ../../../changes.rst:84 1b006c1612a549fb8b1c2054fea6294d msgid "First fully release version" msgstr "第一次发布 1.0 版本" -#: ../../../changes.rst:43 b3a828ec2f7a44a4b6401d53455fffc6 +#: ../../../changes.rst:89 123c8b04025f4014a2a6ea3601292901 msgid "Version 0.4.6" msgstr "" -#: ../../../changes.rst:45 4f633212411841cd8396ddd38a9f24cc +#: ../../../changes.rst:91 32d4097cc1bb42a284d013b0d6bf0f2c msgid "Support signed integers with arbitrary bits, e.g. int24 PR#45." msgstr "支持任意比特数目的有符号整数类型 例如 int24, 见 PR#45" -#: ../../../changes.rst:50 90f0ae1491a84c41a2b9c5dc0e66f845 +#: ../../../changes.rst:96 cd1d51ee9e3c4b708cd9a9fd31ffd497 msgid "Version 0.4.5" msgstr "" -#: ../../../changes.rst:52 5390534c2c094a6b881c30f62d565d17 +#: ../../../changes.rst:98 dc88283855f843a495bab4f3c9ad415d msgid "" "Use Python IntEnum for enum generation (respecting backward " "compatibility) PR#41." msgstr "" -#: ../../../changes.rst:57 3bfcca062ff44f87aea90bf7a6f339b1 +#: ../../../changes.rst:103 ebed9fb7d14142d591662da7185c23d2 msgid "Version 0.4.4" msgstr "" -#: ../../../changes.rst:59 c5a2117f96e4413da42ee13729394184 +#: ../../../changes.rst:105 f3413287976c47e2b9de95e528c6a72e msgid "Minor fix compiler setup.py path issue." msgstr "" -#: ../../../changes.rst:64 86561ffe65894180a12c77dd57d8172f +#: ../../../changes.rst:110 3746a5e757ac41b582f2d6d9f91f5379 msgid "Version 0.4.2" msgstr "" -#: ../../../changes.rst:66 bc317a756c0f44999c0c22be675a7deb +#: ../../../changes.rst:112 2cc45826f9304b42b0e5899dfdb17f23 msgid "Allow using ``type`` as message field name, fixes issue #39." msgstr "" -#: ../../../changes.rst:71 a00793c65d2c427f9b81c1ccab8ad424 +#: ../../../changes.rst:117 26969591f7454f34be89e5998982923a msgid "Version 0.4.0" msgstr "" -#: ../../../changes.rst:73 b0917dbfc0524e74ba171e75741e97e2 +#: ../../../changes.rst:119 eb895e1c963342f489630f6dc2fd642b msgid "" "Add support for ``message`` and ``enum`` extensiblity for protocol " "forward compatibility." msgstr "" -#: ../../../changes.rst:74 95abb35c9fcd4d449a32519fd31ac1aa +#: ../../../changes.rst:120 a557134b8d8148a9ae61364d595e6d61 msgid "Cut down the code size of generated language-specific files." msgstr "" -#: ../../../changes.rst:75 ccf790e19105464b9c222eb7d5916f8d +#: ../../../changes.rst:121 bc8f1b17164e4cd58c6b434cb68f6099 msgid "Refactor the bitproto compiler." msgstr "" -#: ../../../changes.rst:76 6f93b6bbf73748d4b801a715f0cba57b +#: ../../../changes.rst:122 147604be81434646a2a96ffdfc87e87f msgid "" "Refactor the bitproto serialization mechanism, using language-specific " "libraries instead of pure compiler-generated files." diff --git a/docs/locales/zh/LC_MESSAGES/compiler.po b/docs/locales/zh/LC_MESSAGES/compiler.po index 97aa4806..40ef008d 100644 --- a/docs/locales/zh/LC_MESSAGES/compiler.po +++ b/docs/locales/zh/LC_MESSAGES/compiler.po @@ -8,30 +8,30 @@ msgid "" msgstr "" "Project-Id-Version: bitproto 0.4.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-07-31 14:53+0800\n" +"POT-Creation-Date: 2026-06-19 01:28-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.1\n" +"Generated-By: Babel 2.18.0\n" -#: ../../compiler.rst:4 18430fedb15b4d3293f76aaa45209f7c +#: ../../compiler.rst:4 dfe0c3e2f4cf4292871daedec03293ed msgid "Compiler" msgstr "编译器" -#: ../../compiler.rst:6 9e286ee7758449789059175f86faa807 +#: ../../compiler.rst:6 70412f560d1543d0a3725c91c2a942fc msgid "" "The bitproto compiler generates language-specific code, which provides " "the encode and decode api functions." msgstr "bitproto 的编译器用来解析 bitproto 的源文件,然后生成目标语言的代码。" -#: ../../compiler.rst:12 b938ad80b944441c8c87b486f9e0f4ea +#: ../../compiler.rst:12 37f426ccf0a94e0d86d616b8c511178c msgid "Installing the compiler" msgstr "安装编译器" -#: ../../compiler.rst:14 a3bc78797a7a4ff482d2e84b40903627 +#: ../../compiler.rst:14 8f9a12c179554f7a85622adf82a24380 msgid "" "The bitproto compiler is written in Python, and requires `Python3.7+ " "`_ to work, it's best to be installed " @@ -41,13 +41,13 @@ msgstr "" "`_ ,最佳的安装方式是通过 `pip `_ :" -#: ../../compiler.rst:21 5bedfeb9579741369085ec9e5205f84b +#: ../../compiler.rst:21 ae4efc4a52f94d0598b69f87ab7ccc93 msgid "" "This will install a command named ``bitproto`` to your system, you can " "check it's version after the installation:" msgstr "上面的命令会安装一个叫做 ``bitproto`` 的命令行工具,你可以在安装后查看下它的版本信息:" -#: ../../compiler.rst:28 7a5f262f8bcb411da41e661aaea34714 +#: ../../compiler.rst:28 90fccfc4c43f4aa6a2672d0ebd75fd0b msgid "" "If you're new to Python, or wish to skip a Python installation, you can " "download the compiler from `this download link " @@ -59,41 +59,41 @@ msgstr "" "`_ 提供了预先打包好的单文件可执行程序,包含 Mac " "OS, Windows 和 Linux 的三个版本,这些打包好的程序无需安装 Python3.7+ 就可以使用。" -#: ../../compiler.rst:34 1d3a8dd1b86245558740171ef5223154 +#: ../../compiler.rst:34 68f15954315043ab8f58d49813223de5 msgid "Upgrade the compiler" msgstr "升级编译器" -#: ../../compiler.rst:36 1faec5148a96460abe43432787bee0bd +#: ../../compiler.rst:36 8ef673f4fe414df8ab6cc66220727d06 msgid "To upgrade bitproto via ``pip``:" msgstr "要通过 ``pip`` 升级 bitproto 的编译器" -#: ../../compiler.rst:43 a7454cdbca2746a2b91ec35990ed5d90 +#: ../../compiler.rst:43 689bf9bde5a94622926433c2da9f1b43 msgid "Command line usage" msgstr "命令行使用" -#: ../../compiler.rst:45 1517ffb46cff4411a5dc33ad9aff1462 +#: ../../compiler.rst:45 112cccf657cb4495aaca24e00f87091d msgid "Generates code for given language:" msgstr "给目标语言生成代码:" -#: ../../compiler.rst:53 9d3996501f7a4d8fa7ec689ca842829a +#: ../../compiler.rst:53 67e394b9ac56457b859a31ca40ea0978 msgid "" "It generates language-specific codes to current directory by default, to " "specify a output directory:" msgstr "默认地,它会把代码生成到当前目录,如果想指定一个生成目录:" -#: ../../compiler.rst:60 13643cb35666433086796cf4b0e145c9 +#: ../../compiler.rst:60 c13449dc7c5844c9bf68bab7f1b7c74d msgid "" "Validates bitproto source file syntax, exits with a non-zero code if any " "syntax wrongs:" msgstr "仅仅检查 bitproto 的语法是否正确,如果语法错误,编译器会以一个非零的码退出:" -#: ../../compiler.rst:66 4114f3fa739b4df6a18a1e2b04d2000b +#: ../../compiler.rst:66 14daa2fac2cc4bc2840fbb09014185cd msgid "" "The compiler won't generate files but only run a protocol syntax checking" " if `-c` option is given." msgstr "如果 ``-c`` 选项传入,编译器只做语法检查,而不会进行代码生成。" -#: ../../compiler.rst:70 18227812bc6941dda8cd7ac54766f9e5 +#: ../../compiler.rst:70 c85aa668d3c74e90af43a0877f99bede msgid "" "By default, the compiler runs a simple protocol linter, which gives " "warnings if the given bitproto file doesn't meet the :ref:`style-guide`, " @@ -102,3 +102,11 @@ msgstr "" "默认地,编译器会执行一个简单的协议风格检查器,以检查是否符合 bitproto 推荐的 :ref:`语法风格 `。如果需要禁用它:" +#: ../../compiler.rst:77 411be5b081374106be71713dca345373 +msgid "" +"In :ref:`optimization mode ` (``-O``), the" +" ``--endian`` option controls the host byte order targeted by the " +"generated C/C++ code, one of ``both`` (default), ``little`` or ``big``. " +"See :ref:`endianness`." +msgstr "" + diff --git a/docs/locales/zh/LC_MESSAGES/endianness.po b/docs/locales/zh/LC_MESSAGES/endianness.po new file mode 100644 index 00000000..f845a0fd --- /dev/null +++ b/docs/locales/zh/LC_MESSAGES/endianness.po @@ -0,0 +1,163 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2021, Chao Wang +# This file is distributed under the same license as the bitproto package. +# FIRST AUTHOR , 2026. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: bitproto 0.4.4\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-06-19 01:28-0700\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: zh\n" +"Language-Team: zh \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.18.0\n" + +#: ../../endianness.rst:6 0b040be315ce42209b21a9e8b15b6036 +msgid "Endianness" +msgstr "" + +#: ../../endianness.rst:8 ccaaaf10bdfa4f648bcb9d02270e5878 +msgid "" +"bitproto's wire format is **little-endian on every platform**: the lowest" +" byte index on the wire holds the least-significant bits of a field. This" +" is a fixed property of the encoding, independent of the machine that " +"runs the encoder or decoder — two peers with different byte orders always" +" interoperate." +msgstr "" + +#: ../../endianness.rst:16 102ab05c4eb54687b25d8137490bbd8d +msgid "Language support" +msgstr "" + +#: ../../endianness.rst:18 4949128459f0422bb13dae054d386f55 +msgid "" +"Whether the *host* CPU is big-endian only ever mattered for C, because " +"only C reads an integer field's raw memory bytes. Python and Go operate " +"on integer *values* with shifts and masks, which is byte-order " +"independent by construction." +msgstr "" + +#: ../../endianness.rst:26 199c9c0766e04113b484d2840ecfba51 +msgid "Target" +msgstr "" + +#: ../../endianness.rst:27 688b7dde52824f65b24529a68090d35f +msgid "How bits are moved" +msgstr "" + +#: ../../endianness.rst:28 f2ba25db7dbe4c54bc6e9cb8564a02a5 +msgid "Big-endian host" +msgstr "" + +#: ../../endianness.rst:29 7185def3bcd24250bb7adcf4c8c05176 +msgid "Python" +msgstr "" + +#: ../../endianness.rst:30 0e787b8470584e63ab81d90da3c15128 +msgid "value math on Python ints" +msgstr "" + +#: ../../endianness.rst:31 ../../endianness.rst:34 +#: 41384d1bc15f45ada45248e4fdd697d3 d28241f219114b51b5d2002609b864d0 +msgid "always correct" +msgstr "" + +#: ../../endianness.rst:32 69d0a06ec23d462f9e142333c75d80d1 +msgid "Go" +msgstr "" + +#: ../../endianness.rst:33 cb9b9a23775744cc89f3f5ab9c07b6dc +msgid "value shifts on typed fields" +msgstr "" + +#: ../../endianness.rst:35 a4d294b5cc7f44cb8263527b2e7fb893 +msgid "C (standard mode)" +msgstr "" + +#: ../../endianness.rst:36 4fd18130c01a4c6f827652c3904fc3ec +msgid "``lib/c`` runtime" +msgstr "" + +#: ../../endianness.rst:37 ../../endianness.rst:40 +#: d7b60cd145a54177ac0e4d328767a09b e982e973ea5740e7887c8c0b6e4121ed +msgid "correct" +msgstr "" + +#: ../../endianness.rst:38 76b701d8127a4e508b5cb4004301b984 +msgid "C (optimization mode)" +msgstr "" + +#: ../../endianness.rst:39 ab408111deb7443b927252ec5053ffe0 +msgid "generated bit-copy statements" +msgstr "" + +#: ../../endianness.rst:42 267c254a223341d890878a779748f20d +msgid "" +"So nothing special is required for Go or Python. The remainder of this " +"page is about C." +msgstr "" + +#: ../../endianness.rst:48 0fda01f52aba404ca7e7841635e1b526 +msgid "C standard mode" +msgstr "" + +#: ../../endianness.rst:50 b789baff2f8842d7b046e63d83fe28fc +msgid "" +"The C library (``lib/c/bitproto.c``) detects a big-endian host " +"automatically via ``__BYTE_ORDER__`` and selects an endian-neutral code " +"path. You may also force it by defining ``BP_BIG_ENDIAN`` when compiling " +"the library, for toolchains that do not define ``__BYTE_ORDER__``:" +msgstr "" + +#: ../../endianness.rst:59 dfe5ca7c24b74b21a037d8a627cb31f4 +msgid "" +"On a little-endian host the library is byte-for-byte unchanged, so there " +"is no performance impact on the common target." +msgstr "" + +#: ../../endianness.rst:65 e88b8e2df1064aa88e0681f1e15a5577 +msgid "C optimization mode" +msgstr "" + +#: ../../endianness.rst:67 668c05ce7e5b4cd299438125249d1301 +msgid "" +":ref:`Optimization mode ` generates plain " +"bit-copy statements that read and write integer fields through their in-" +"memory bytes. To stay correct on both little-endian and big-endian hosts," +" by default bitproto emits two equivalent code paths guarded by a " +"preprocessor macro:" +msgstr "" + +#: ../../endianness.rst:80 c3e4f7e8383b4362af0360e008b10f49 +msgid "" +"A big-endian host is auto-detected via ``__BYTE_ORDER__``; you may also " +"force the big-endian path by defining ``BP_BIG_ENDIAN`` when compiling " +"the generated code. The little-endian path is identical to earlier " +"bitproto releases, so there is no performance change on little-endian " +"targets — only the generated source is a little larger." +msgstr "" + +#: ../../endianness.rst:89 8d62efc6537d4a4f9cb8e5f7d54d1f6d +msgid "The ``--endian`` option" +msgstr "" + +#: ../../endianness.rst:91 e84c122aadef45e887a7e8d2f84f37b7 +msgid "" +"If you know your target's byte order you can drop the unused path and " +"shrink the generated source with the ``--endian`` option (optimization " +"mode only):" +msgstr "" + +#: ../../endianness.rst:105 dbc23d079cd24ab0ac8cb9144b9fe748 +msgid "" +"``--endian`` only affects optimization-mode C/C++ code. It has no effect " +"on the wire format, and none on Go or Python." +msgstr "" + diff --git a/docs/locales/zh/LC_MESSAGES/faq.po b/docs/locales/zh/LC_MESSAGES/faq.po index 9c0f9de7..6cc851c8 100644 --- a/docs/locales/zh/LC_MESSAGES/faq.po +++ b/docs/locales/zh/LC_MESSAGES/faq.po @@ -31,15 +31,13 @@ msgstr "bitproto 是否和大小端有关?" #: ../../faq.rst:11 696fa0eba0ac4b64b351f8260ec95798 msgid "" -"Yes, bitproto is not endianness independent, it currently supports only " -"little-endian systems. Endianness is always an issue for data " -"serialization. But most of cpu processors, (including x86, armv7, armv8 " -"and most arm cortex-m mcus) are little-endian, or little-endian as " -"default. There's no plan for now to support big-endian systems or " -"endianness independent serialization." -msgstr "是的,bitproto 是大小端相关的,目前它只支持小端系统。" -"在数据序列化方面,大小端一直是一个问题。但是对于大部分的 CPU 处理器 (包括 x86, armv7, armv8 和大多数的 arm cortex-m mcu) " -"都是小端的,或者默认设置为小端的。目前还没有计划来支持大端系统,也没有计划做大小端无关的序列化支持。" +"Yes, bitproto's wire format is little-endian on every platform. The C library " +"(standard mode) and the C optimization-mode generated code now both produce " +"correct output on big-endian hosts. Python and Go are endian-neutral and were " +"always unaffected. See :ref:`endianness` for details." +msgstr "" +"是的,bitproto 的编码格式在所有平台上都是小端的。C 库(标准模式)和 C 优化模式生成的代码现在都支持大端主机。" +"Python 和 Go 是大小端无关的,一直不受影响。详见 :ref:`endianness`。 #: ../../faq.rst:17 41b0f1c69ab7427ca3aa6dc6fc386d32 msgid "What's the advantage of this over a bit field ?" diff --git a/docs/locales/zh/LC_MESSAGES/performance.po b/docs/locales/zh/LC_MESSAGES/performance.po index 004cc8e1..d422c38b 100644 --- a/docs/locales/zh/LC_MESSAGES/performance.po +++ b/docs/locales/zh/LC_MESSAGES/performance.po @@ -8,56 +8,56 @@ msgid "" msgstr "" "Project-Id-Version: bitproto 0.4.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-05-22 21:23+0800\n" +"POT-Creation-Date: 2026-06-19 01:28-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.12.1\n" +"Generated-By: Babel 2.18.0\n" -#: ../../performance.rst:4 bb38bc42f11b4de68c861ba21eb53e25 +#: ../../performance.rst:4 01b5592725e64db1988c757e7b610f49 msgid "Performance" msgstr "性能表现" -#: ../../performance.rst:6 565279d6ab2b45ae926a491b590b3719 +#: ../../performance.rst:6 87f40610002a4b89b45bd0fb395af38a msgid "" "This document will introduce the performance of bitproto encoding and " "decoding, along with the optimization mechanism." msgstr "本文将介绍 bitproto 的编解码性能表现和优化机制。" -#: ../../performance.rst:12 cff5d0014bdc49eeb3c552d882755d81 +#: ../../performance.rst:12 c677b3e5b7c649039c0c8c375c0075b2 msgid "Performance Benchmark" msgstr "性能压测" -#: ../../performance.rst:14 f6c964417bc940a0931709156b859931 +#: ../../performance.rst:14 1c2bb54fedb449589277b0fad55f639f msgid "Benchmark of bitproto encoding/decoding shows that it runs very fast." msgstr "对 bitproto 的编解码的性能压测结果显示,它跑的非常快。" -#: ../../performance.rst:17 7fdb50154c7142a3b3e6661e4f57425d +#: ../../performance.rst:17 7c91cb2a73bd46d49bc18f8d15926ae7 msgid "Unix OS" msgstr "Unix 系统" -#: ../../performance.rst:19 9ff4fdd3009e4d279e1ad4c1dd3d2d97 +#: ../../performance.rst:19 17aed890f3bb48888f2e289f438f3bb1 msgid "" "On unix like systems (mac, ubuntu etc.), a single encoding/decoding call " "costs for a ``100`` bytes message:" msgstr "在类 unix 系统 (mac, ubuntu etc.) 上,测试 ``100`` 字节大小的消息, 一次编码调用或者解码调用占用:" -#: ../../performance.rst:21 c4a0f56b87034684a5509ce772c7ec60 +#: ../../performance.rst:21 a0a08859bbb44868b7fba11785c4464f msgid "``< 2μs`` in C" msgstr "C 语言中 ``< 2μs``" -#: ../../performance.rst:22 38d3b1edebcc438795978ff0cadd2a4f +#: ../../performance.rst:22 7db0ac8ab8d34712a9fde8c40f51fd5f msgid "``< 10μs`` in Go" msgstr "Go 语言中 ``<10μs``" -#: ../../performance.rst:23 189a5f9077ed4d3e83354a61d500b1fe +#: ../../performance.rst:23 f6a8cfad50774f79bacc6cf74207b72b msgid "``< 1ms`` in Python" msgstr "Python 语言中 ``< 1ms``" -#: ../../performance.rst:25 d167f0f132bb4392982fe39152fbb9b2 +#: ../../performance.rst:25 57fc01af8edd4198985e8f868a1f90c2 msgid "" "You can checkout `the detail benchmark results for unix on github " "`_." @@ -65,11 +65,11 @@ msgstr "" "你可以在 `github 上查看 unix 系统上详细的压测结果 " "`_ 。" -#: ../../performance.rst:28 0e6650dc602a44448a74e4ddb2cedf29 +#: ../../performance.rst:28 de83eff9dd3443b68299d9516b7c5670 msgid "Embedded" msgstr "嵌入式" -#: ../../performance.rst:30 6cbb565ad7294e18b25be803e7ed86b1 +#: ../../performance.rst:30 ba887e2a7c094e0194ef3aa42cafdada msgid "" "I have tested the benchmark on a `stm32 board " "` " "来降低到 ``15 μs`` 左右。" -#: ../../performance.rst:34 cc212dc5e38441c8ac7fe153e792ebcd +#: ../../performance.rst:34 0637f1f2ae4f4a168baaae7b91a9ceb7 msgid "" "You can checkout `the detail benchmark results for stm32 on github " "`_." @@ -94,11 +94,11 @@ msgstr "" "你可以在 `github 上查看 stm32 上详细的压测结果 " "`_ 。" -#: ../../performance.rst:39 e78ee0dc97794378acd00c87abbe6682 +#: ../../performance.rst:39 e660e34d8e9342ef8b27001d2c055cd1 msgid "The Optimization Mode" msgstr "优化模式" -#: ../../performance.rst:41 f0201738176e47cdb69f4a0ec881cbfe +#: ../../performance.rst:41 c48cdc62087649d8bd405664c251a6cb msgid "" "For most cases, the performance may meet the requirements. But if you are" " not satisfied with this, there's still a way to go, the called " @@ -108,11 +108,11 @@ msgstr "" "对于大多数场景,bitproto 的性能应该可以满足需求。但是如果你对此不满意,仍然有一种方法可以尝试,即 bitproto 中所谓的 " "\"优化模式\" 。通过给 bitproto 的编译器添加一个 选项 ``-O`` , 可以开启优化模式:" -#: ../../performance.rst:49 643aa5e274ee443397b22c797406a9fa +#: ../../performance.rst:49 bf24d3c1564a458f9f09032bcab6e8ed msgid "By this way, bitproto will generate code for you in optimization mode." msgstr "这样,bitproto 会生成优化模式下的代码。" -#: ../../performance.rst:51 cd88013761cf41a28c8877259dc8da4c +#: ../../performance.rst:51 93026da4ebc241e6b4c1b4572e17ca3d msgid "" "The mechanism behind optimization mode is to generate plain " "encoding/decoding code statements directly at code-generation time. We " @@ -125,7 +125,7 @@ msgstr "" "中所有的类型都是定长的,因此在代码生成阶段是完全可以知道如何对数据进行编解码的。在代码生成阶段,bitproto " "会遍历消息的所有字段然后生成比特拷贝的语句。" -#: ../../performance.rst:58 4ae6c204f15b4893bb8757b3bdc0e882 +#: ../../performance.rst:58 85afbe4dee614bd8baae2504a88dd627 msgid "" "The optimization mode doesn't work for :ref:`extensible messages " "`. Because extensible messages decoding " @@ -134,13 +134,13 @@ msgstr "" "优化模式不支持和 :ref:`扩展性消息 ` " "一起工作。因为扩展性消息的解码需要依赖动态的计算。" -#: ../../performance.rst:61 1f1afa007ef0498a930972b21f2b901e +#: ../../performance.rst:61 e353a89aae2d4e1d95024c275b92ed90 msgid "" "For an instance in C, the generated code in optimization mode looks like " "this:" msgstr "以 C 语言举例,优化模式下生成的代码是这个样子的:" -#: ../../performance.rst:77 e314913063c34550b7ecf2c56c93ad46 +#: ../../performance.rst:77 8086f145aaf84b6494f47e66990c1281 msgid "" "See the generated code example above, there's no loops, no if-else, all " "statements are plain bit operations. In this way, bitproto's optimization" @@ -149,7 +149,14 @@ msgstr "" "上面的生成代码中,没有循环语句、没有 if-else 语句,所有的语句都是直白的比特操作。通过这种方式,bitproto " "的优化模式可以给我编解码效率上最大化的提升。" -#: ../../performance.rst:80 6ad9e983aa0f41bbba9c637af969d063 +#: ../../performance.rst:82 c26913a6d61f4c32873f275bba4424e7 +msgid "" +"The byte-pointer code shown above is the little-endian path. By default " +"the compiler also emits an equivalent big-endian path behind ``#ifdef " +"BP_BIG_ENDIAN``, see :ref:`endianness`." +msgstr "" + +#: ../../performance.rst:86 89772fb5b96c4c2789fb6bbc80d21ad7 msgid "" "It's fine of course to use optimization mode on one end and non-" "optimization mode (the standard mode) on another end in message " @@ -160,7 +167,7 @@ msgstr "" "对于通信双方中,一方使用无优化模式 " "(标准模式),一方使用优化模式,这种情况也是没有问题的。优化模式只改变了编码函数和解码函数的编写方式,并没有改变数据的交换格式。" -#: ../../performance.rst:84 74e8b4a84f7c4682adfffbbc964ba0bf +#: ../../performance.rst:90 456e8bcb9a6a4d14b7a0eafbff4fe34c msgid "" "In fact, using the optimization mode is also a trade-off sometimes. In " "this mode, we have to drop the benefits of :ref:`extensibility ` " ",这样其实对协议的兼容性设计是不友好的。优化模式是为了性能敏感的场景设计的,比如低功耗的嵌入式板,计算密集的微控制器。在以下场景下,推荐使用优化模式:" -#: ../../performance.rst:89 c6816344404e4252b7e3a22beaa1671d +#: ../../performance.rst:95 afb539f8d85540a8977725ff335d0db8 msgid "" "Performance-sensitive scenarios, where ``100μs`` means totally different " "with ``10μs``." msgstr "性能敏感的场景,``100μs`` 和 ``10μs`` 完全不是一回事。" -#: ../../performance.rst:90 6e43e0071f2446cdb2e9b84f507785ea +#: ../../performance.rst:96 4367d689658c4391aace95f4e2580c43 msgid "" "The firmwares of communication ends are always upgraded together, thus " "the forward-compatibility is not so important." msgstr "通信相关方的固件升级总是一起进行的,这样向前兼容性就不那么重要了。" -#: ../../performance.rst:91 57d569b743564fdab21876c78629b899 +#: ../../performance.rst:97 001eca7f9c70478a92b07ae9aa19ffde msgid "Firmware updates are not frequent, even only once for a long time." msgstr "固件的升级并不会频繁,甚至很长时间一次。" -#: ../../performance.rst:93 a29e2ec75184440fa7d3cab1ec1dd27b +#: ../../performance.rst:99 2f80bc38de7b446f81cdd16ca500892c #, python-format msgid "" "Specially, for the scenario that firmware-upgrading of communication ends" @@ -204,13 +211,13 @@ msgstr "" "`_ " ",我还是推荐坚持使用标准模式,而不是优化模式。" -#: ../../performance.rst:97 61b90c2ee4304b5a832c4810dbea7163 +#: ../../performance.rst:103 6c3a9c79bce24323bc49a2926ab9baac msgid "" "The optimization mode is currently supported for language C and Go, (not " "yet Python)." msgstr "目前,bitproto 的优化模式只支持 C 语言和 Go 语言,目前还不支持 Python 。" -#: ../../performance.rst:99 c07f490b42d0466cad9a5b3684d73d99 +#: ../../performance.rst:105 d375ba69d7454c9399e0dbfff01e27ae msgid "" "Another benefit of optimization mode is that the bitproto libraries are " "no longer required to be dropped in. The bitproto compiler in " @@ -222,25 +229,25 @@ msgstr "" "优化模式所带来的另一个好处是不必在使用支持库了。因为 bitproto 的编译器在优化模式下已经生成了最终的编解码语句,因此 bitproto " "的支持库就不需要了。这些支持库最初的设计是为了支持标准模式下的协议扩展性能力。" -#: ../../performance.rst:105 51209f0777834e9c92df2572aa7f8c40 +#: ../../performance.rst:111 b0f9de702c6d4f0a8285240e7a941336 msgid "Smaller Code Size" msgstr "更小的代码量" -#: ../../performance.rst:107 7cbc443c96834a3ea35fe93314fb6dc2 +#: ../../performance.rst:113 f33e6882fbfb4dea8cda8511f151f1aa msgid "" "Embedded firmware may be limited in program size. Bitproto provides " "another compiler option ``-F`` to filter messages to generate in " "optimization mode:" msgstr "嵌入式固件可能受限于编译后程序占用空间的大小。 bitproto 提供了一个编译器选项 ``-F`` 来筛选需要生成编解码函数的消息名字列表:" -#: ../../performance.rst:114 62712955bc93474ebb591251f4d9a035 +#: ../../performance.rst:120 c8095326ca474bbe95501b3e12ed1f90 msgid "" "The command above tells bitproto only to generate encoder and decoder " "functions for message ``Packet``, other messages's encoder and decoder " "functions will be skpped without generating." msgstr "上面的命令中, bitproto 会只生成消息 ``Packet`` 的编码函数和解码函数,其他消息的编码函数和解码函数则会跳过,不再生成。" -#: ../../performance.rst:117 639483829f28416592a1c785b179356f +#: ../../performance.rst:123 34f607a80a7a49f2b64a6ef0ee760369 msgid "" "The ``-F`` trick is useful because in most scenarios we just exchange a " "single \"top-level\" bitproto message in communication. This option can " @@ -249,9 +256,23 @@ msgstr "" "``-F`` " "选项之所以是有用处的,是因为在很多情况下,我们在通信过程中只交换一个\"顶级的\"消息。这个选项也可以用来筛选多个消息名字,使用逗号分隔即可:" -#: ../../performance.rst:124 3eb9587ae1c746f595698d7066707b7d +#: ../../performance.rst:130 a2e03e784e4e49babef44ece6ad5626d msgid "" "Finally to note that, the ``-F`` option can be only used together with " "option ``-O``." msgstr "最后需要说明的是,``-F`` 选项只可以用在优化模式下,也就是所只能和 ``-O`` 选项搭配使用。" +#: ../../performance.rst:133 105db1b073424fbb820c4f08c787110a +msgid "Host Byte Order (Endianness)" +msgstr "" + +#: ../../performance.rst:135 6bcd1597efd74fc38a8af2bcedb82954 +msgid "" +"The optimization-mode C code generates plain bit-copy statements that " +"access integer fields through their in-memory bytes, so by default " +"bitproto emits both a little-endian and a big-endian code path (guarded " +"by ``#ifdef BP_BIG_ENDIAN``). The little-endian path is unchanged from " +"earlier releases. See :ref:`endianness` for details and the ``--endian`` " +"option." +msgstr "" + diff --git a/docs/performance.rst b/docs/performance.rst index 59ee8926..c05bdb98 100644 --- a/docs/performance.rst +++ b/docs/performance.rst @@ -77,6 +77,12 @@ For an instance in C, the generated code in optimization mode looks like this: See the generated code example above, there's no loops, no if-else, all statements are plain bit operations. In this way, bitproto's optimization mode gives us a maximum performance improvement on encoding/decoding. +.. note:: + + The byte-pointer code shown above is the little-endian path. By default the + compiler also emits an equivalent big-endian path behind ``#ifdef BP_BIG_ENDIAN``, + see :ref:`endianness`. + It's fine of course to use optimization mode on one end and non-optimization mode (the standard mode) on another end in message communication. The optimization mode only changes the way how to execute the encoder and decoder, without changing the format of the message encoding. @@ -122,3 +128,12 @@ in communication. This option can also be used with multiple message names: $ bitproto example.bitproto -O -F "PacketA,PacketB" Finally to note that, the ``-F`` option can be only used together with option ``-O``. + +Host Byte Order (Endianness) +'''''''''''''''''''''''''''' + +The optimization-mode C code generates plain bit-copy statements that access +integer fields through their in-memory bytes, so by default bitproto emits both +a little-endian and a big-endian code path (guarded by ``#ifdef BP_BIG_ENDIAN``). +The little-endian path is unchanged from earlier releases. See :ref:`endianness` +for details and the ``--endian`` option. diff --git a/example/C-optimization-mode/Makefile b/example/C-optimization-mode/Makefile index daa86fe8..6f70bc81 100644 --- a/example/C-optimization-mode/Makefile +++ b/example/C-optimization-mode/Makefile @@ -9,6 +9,6 @@ run: build ./example clean: - rm example_bp.h example_bp.c example + rm -f example_bp.h example_bp.c example .PHONY: build run clean diff --git a/example/C-optimization-mode/example_bp.c b/example/C-optimization-mode/example_bp.c index 4cf17e0d..721c8ee8 100644 --- a/example/C-optimization-mode/example_bp.c +++ b/example/C-optimization-mode/example_bp.c @@ -1,8 +1,16 @@ // Code generated by bitproto. DO NOT EDIT. #include "example_bp.h" +#if !defined(BP_BIG_ENDIAN) && defined(__BYTE_ORDER__) && \ + (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#define BP_BIG_ENDIAN 1 +#endif +#ifdef BP_BIG_ENDIAN +#include +#endif int EncodeDrone(struct Drone *m, unsigned char *s) { +#ifndef BP_BIG_ENDIAN s[0] = (((unsigned char *)&((*m).status))[0] ) & 7; s[0] |= (((unsigned char *)&((*m).position.latitude))[0] << 3) & 248; s[1] = (((unsigned char *)&((*m).position.latitude))[0] >> 5) & 7; @@ -145,10 +153,155 @@ int EncodeDrone(struct Drone *m, unsigned char *s) { s[65] = (((unsigned char *)&((*m).pressure_sensor.pressures[1]))[1] >> 4) & 15; s[65] |= (((unsigned char *)&((*m).pressure_sensor.pressures[1]))[2] << 4) & 240; s[66] = (((unsigned char *)&((*m).pressure_sensor.pressures[1]))[2] >> 4) & 15; +#else + s[0] = ((uint8_t)((*m).status)) & 7; + s[0] |= ((uint32_t)((*m).position.latitude)<< 3) & 248; + s[1] = ((uint32_t)((*m).position.latitude)>> 5) & 7; + s[1] |= ((uint32_t)((*m).position.latitude)>> 5) & 248; + s[2] = ((uint32_t)((*m).position.latitude)>> 13) & 7; + s[2] |= ((uint32_t)((*m).position.latitude)>> 13) & 248; + s[3] = ((uint32_t)((*m).position.latitude)>> 21) & 7; + s[3] |= ((uint32_t)((*m).position.latitude)>> 21) & 248; + s[4] = ((uint32_t)((*m).position.latitude)>> 29) & 7; + s[4] |= ((uint32_t)((*m).position.longitude)<< 3) & 248; + s[5] = ((uint32_t)((*m).position.longitude)>> 5) & 7; + s[5] |= ((uint32_t)((*m).position.longitude)>> 5) & 248; + s[6] = ((uint32_t)((*m).position.longitude)>> 13) & 7; + s[6] |= ((uint32_t)((*m).position.longitude)>> 13) & 248; + s[7] = ((uint32_t)((*m).position.longitude)>> 21) & 7; + s[7] |= ((uint32_t)((*m).position.longitude)>> 21) & 248; + s[8] = ((uint32_t)((*m).position.longitude)>> 29) & 7; + s[8] |= ((uint32_t)((*m).position.altitude)<< 3) & 248; + s[9] = ((uint32_t)((*m).position.altitude)>> 5) & 7; + s[9] |= ((uint32_t)((*m).position.altitude)>> 5) & 248; + s[10] = ((uint32_t)((*m).position.altitude)>> 13) & 7; + s[10] |= ((uint32_t)((*m).position.altitude)>> 13) & 248; + s[11] = ((uint32_t)((*m).position.altitude)>> 21) & 7; + s[11] |= ((uint32_t)((*m).position.altitude)>> 21) & 248; + s[12] = ((uint32_t)((*m).position.altitude)>> 29) & 7; + s[12] |= ((uint32_t)((*m).flight.pose.yaw)<< 3) & 248; + s[13] = ((uint32_t)((*m).flight.pose.yaw)>> 5) & 7; + s[13] |= ((uint32_t)((*m).flight.pose.yaw)>> 5) & 248; + s[14] = ((uint32_t)((*m).flight.pose.yaw)>> 13) & 7; + s[14] |= ((uint32_t)((*m).flight.pose.yaw)>> 13) & 248; + s[15] = ((uint32_t)((*m).flight.pose.yaw)>> 21) & 7; + s[15] |= ((uint32_t)((*m).flight.pose.yaw)>> 21) & 248; + s[16] = ((uint32_t)((*m).flight.pose.yaw)>> 29) & 7; + s[16] |= ((uint32_t)((*m).flight.pose.pitch)<< 3) & 248; + s[17] = ((uint32_t)((*m).flight.pose.pitch)>> 5) & 7; + s[17] |= ((uint32_t)((*m).flight.pose.pitch)>> 5) & 248; + s[18] = ((uint32_t)((*m).flight.pose.pitch)>> 13) & 7; + s[18] |= ((uint32_t)((*m).flight.pose.pitch)>> 13) & 248; + s[19] = ((uint32_t)((*m).flight.pose.pitch)>> 21) & 7; + s[19] |= ((uint32_t)((*m).flight.pose.pitch)>> 21) & 248; + s[20] = ((uint32_t)((*m).flight.pose.pitch)>> 29) & 7; + s[20] |= ((uint32_t)((*m).flight.pose.roll)<< 3) & 248; + s[21] = ((uint32_t)((*m).flight.pose.roll)>> 5) & 7; + s[21] |= ((uint32_t)((*m).flight.pose.roll)>> 5) & 248; + s[22] = ((uint32_t)((*m).flight.pose.roll)>> 13) & 7; + s[22] |= ((uint32_t)((*m).flight.pose.roll)>> 13) & 248; + s[23] = ((uint32_t)((*m).flight.pose.roll)>> 21) & 7; + s[23] |= ((uint32_t)((*m).flight.pose.roll)>> 21) & 248; + s[24] = ((uint32_t)((*m).flight.pose.roll)>> 29) & 7; + s[24] |= ((uint32_t)((*m).flight.velocity[0])<< 3) & 248; + s[25] = ((uint32_t)((*m).flight.velocity[0])>> 5) & 7; + s[25] |= ((uint32_t)((*m).flight.velocity[0])>> 5) & 248; + s[26] = ((uint32_t)((*m).flight.velocity[0])>> 13) & 7; + s[26] |= ((uint32_t)((*m).flight.velocity[0])>> 13) & 248; + s[27] = ((uint32_t)((*m).flight.velocity[0])>> 21) & 7; + s[27] |= ((uint32_t)((*m).flight.velocity[0])>> 21) & 248; + s[28] = ((uint32_t)((*m).flight.velocity[0])>> 29) & 7; + s[28] |= ((uint32_t)((*m).flight.velocity[1])<< 3) & 248; + s[29] = ((uint32_t)((*m).flight.velocity[1])>> 5) & 7; + s[29] |= ((uint32_t)((*m).flight.velocity[1])>> 5) & 248; + s[30] = ((uint32_t)((*m).flight.velocity[1])>> 13) & 7; + s[30] |= ((uint32_t)((*m).flight.velocity[1])>> 13) & 248; + s[31] = ((uint32_t)((*m).flight.velocity[1])>> 21) & 7; + s[31] |= ((uint32_t)((*m).flight.velocity[1])>> 21) & 248; + s[32] = ((uint32_t)((*m).flight.velocity[1])>> 29) & 7; + s[32] |= ((uint32_t)((*m).flight.velocity[2])<< 3) & 248; + s[33] = ((uint32_t)((*m).flight.velocity[2])>> 5) & 7; + s[33] |= ((uint32_t)((*m).flight.velocity[2])>> 5) & 248; + s[34] = ((uint32_t)((*m).flight.velocity[2])>> 13) & 7; + s[34] |= ((uint32_t)((*m).flight.velocity[2])>> 13) & 248; + s[35] = ((uint32_t)((*m).flight.velocity[2])>> 21) & 7; + s[35] |= ((uint32_t)((*m).flight.velocity[2])>> 21) & 248; + s[36] = ((uint32_t)((*m).flight.velocity[2])>> 29) & 7; + s[36] |= ((uint32_t)((*m).flight.acceleration[0])<< 3) & 248; + s[37] = ((uint32_t)((*m).flight.acceleration[0])>> 5) & 7; + s[37] |= ((uint32_t)((*m).flight.acceleration[0])>> 5) & 248; + s[38] = ((uint32_t)((*m).flight.acceleration[0])>> 13) & 7; + s[38] |= ((uint32_t)((*m).flight.acceleration[0])>> 13) & 248; + s[39] = ((uint32_t)((*m).flight.acceleration[0])>> 21) & 7; + s[39] |= ((uint32_t)((*m).flight.acceleration[0])>> 21) & 248; + s[40] = ((uint32_t)((*m).flight.acceleration[0])>> 29) & 7; + s[40] |= ((uint32_t)((*m).flight.acceleration[1])<< 3) & 248; + s[41] = ((uint32_t)((*m).flight.acceleration[1])>> 5) & 7; + s[41] |= ((uint32_t)((*m).flight.acceleration[1])>> 5) & 248; + s[42] = ((uint32_t)((*m).flight.acceleration[1])>> 13) & 7; + s[42] |= ((uint32_t)((*m).flight.acceleration[1])>> 13) & 248; + s[43] = ((uint32_t)((*m).flight.acceleration[1])>> 21) & 7; + s[43] |= ((uint32_t)((*m).flight.acceleration[1])>> 21) & 248; + s[44] = ((uint32_t)((*m).flight.acceleration[1])>> 29) & 7; + s[44] |= ((uint32_t)((*m).flight.acceleration[2])<< 3) & 248; + s[45] = ((uint32_t)((*m).flight.acceleration[2])>> 5) & 7; + s[45] |= ((uint32_t)((*m).flight.acceleration[2])>> 5) & 248; + s[46] = ((uint32_t)((*m).flight.acceleration[2])>> 13) & 7; + s[46] |= ((uint32_t)((*m).flight.acceleration[2])>> 13) & 248; + s[47] = ((uint32_t)((*m).flight.acceleration[2])>> 21) & 7; + s[47] |= ((uint32_t)((*m).flight.acceleration[2])>> 21) & 248; + s[48] = ((uint32_t)((*m).flight.acceleration[2])>> 29) & 7; + s[48] |= ((uint8_t)((*m).propellers[0].id)<< 3) & 248; + s[49] = ((uint8_t)((*m).propellers[0].id)>> 5) & 7; + s[49] |= ((uint8_t)((*m).propellers[0].status)<< 3) & 24; + s[49] |= ((uint8_t)((*m).propellers[0].direction)<< 5) & 96; + s[49] |= ((uint8_t)((*m).propellers[1].id)<< 7) & 128; + s[50] = ((uint8_t)((*m).propellers[1].id)>> 1) & 127; + s[50] |= ((uint8_t)((*m).propellers[1].status)<< 7) & 128; + s[51] = ((uint8_t)((*m).propellers[1].status)>> 1) & 1; + s[51] |= ((uint8_t)((*m).propellers[1].direction)<< 1) & 6; + s[51] |= ((uint8_t)((*m).propellers[2].id)<< 3) & 248; + s[52] = ((uint8_t)((*m).propellers[2].id)>> 5) & 7; + s[52] |= ((uint8_t)((*m).propellers[2].status)<< 3) & 24; + s[52] |= ((uint8_t)((*m).propellers[2].direction)<< 5) & 96; + s[52] |= ((uint8_t)((*m).propellers[3].id)<< 7) & 128; + s[53] = ((uint8_t)((*m).propellers[3].id)>> 1) & 127; + s[53] |= ((uint8_t)((*m).propellers[3].status)<< 7) & 128; + s[54] = ((uint8_t)((*m).propellers[3].status)>> 1) & 1; + s[54] |= ((uint8_t)((*m).propellers[3].direction)<< 1) & 6; + s[54] |= ((uint8_t)((*m).power.battery)<< 3) & 248; + s[55] = ((uint8_t)((*m).power.battery)>> 5) & 7; + s[55] |= ((uint8_t)((*m).power.status)<< 3) & 24; + s[55] |= ((uint8_t)((*m).power.is_charging)<< 5) & 32; + s[55] |= ((uint8_t)((*m).network.signal)<< 6) & 192; + s[56] = ((uint8_t)((*m).network.signal)>> 2) & 3; + s[56] |= ((uint32_t)((*m).network.heartbeat_at)<< 2) & 252; + s[57] = ((uint32_t)((*m).network.heartbeat_at)>> 6) & 3; + s[57] |= ((uint32_t)((*m).network.heartbeat_at)>> 6) & 252; + s[58] = ((uint32_t)((*m).network.heartbeat_at)>> 14) & 3; + s[58] |= ((uint32_t)((*m).network.heartbeat_at)>> 14) & 252; + s[59] = ((uint32_t)((*m).network.heartbeat_at)>> 22) & 3; + s[59] |= ((uint32_t)((*m).network.heartbeat_at)>> 22) & 252; + s[60] = ((uint32_t)((*m).network.heartbeat_at)>> 30) & 3; + s[60] |= ((uint8_t)((*m).landing_gear.status)<< 2) & 12; + s[60] |= ((uint32_t)((*m).pressure_sensor.pressures[0])<< 4) & 240; + s[61] = ((uint32_t)((*m).pressure_sensor.pressures[0])>> 4) & 15; + s[61] |= ((uint32_t)((*m).pressure_sensor.pressures[0])>> 4) & 240; + s[62] = ((uint32_t)((*m).pressure_sensor.pressures[0])>> 12) & 15; + s[62] |= ((uint32_t)((*m).pressure_sensor.pressures[0])>> 12) & 240; + s[63] = ((uint32_t)((*m).pressure_sensor.pressures[0])>> 20) & 15; + s[63] |= ((uint32_t)((*m).pressure_sensor.pressures[1])<< 4) & 240; + s[64] = ((uint32_t)((*m).pressure_sensor.pressures[1])>> 4) & 15; + s[64] |= ((uint32_t)((*m).pressure_sensor.pressures[1])>> 4) & 240; + s[65] = ((uint32_t)((*m).pressure_sensor.pressures[1])>> 12) & 15; + s[65] |= ((uint32_t)((*m).pressure_sensor.pressures[1])>> 12) & 240; + s[66] = ((uint32_t)((*m).pressure_sensor.pressures[1])>> 20) & 15; +#endif return 0; } int DecodeDrone(struct Drone *m, unsigned char *s) { +#ifndef BP_BIG_ENDIAN ((unsigned char *)&((*m).status))[0] = (s[0] ) & 7; ((unsigned char *)&((*m).position.latitude))[0] = (s[0] >> 3) & 31; ((unsigned char *)&((*m).position.latitude))[0] |= (s[1] << 5) & 224; @@ -293,5 +446,152 @@ int DecodeDrone(struct Drone *m, unsigned char *s) { ((unsigned char *)&((*m).pressure_sensor.pressures[1]))[2] = (s[65] >> 4) & 15; ((unsigned char *)&((*m).pressure_sensor.pressures[1]))[2] |= (s[66] << 4) & 240; if (((*m).pressure_sensor.pressures[1] >> 23) & 1) (*m).pressure_sensor.pressures[1] |= -16777216; +#else + memset(m, 0, sizeof(*m)); + (*m).status |= (DroneStatus)(((unsigned)(s[0])) & 7); + (*m).position.latitude |= (uint32_t)(((unsigned)(s[0])>> 3) & 31); + (*m).position.latitude |= (uint32_t)(((unsigned)(s[1])<< 5) & 224); + (*m).position.latitude |= (uint32_t)((uint32_t)(((unsigned)(s[1])>> 3) & 31) << 8); + (*m).position.latitude |= (uint32_t)((uint32_t)(((unsigned)(s[2])<< 5) & 224) << 8); + (*m).position.latitude |= (uint32_t)((uint32_t)(((unsigned)(s[2])>> 3) & 31) << 16); + (*m).position.latitude |= (uint32_t)((uint32_t)(((unsigned)(s[3])<< 5) & 224) << 16); + (*m).position.latitude |= (uint32_t)((uint32_t)(((unsigned)(s[3])>> 3) & 31) << 24); + (*m).position.latitude |= (uint32_t)((uint32_t)(((unsigned)(s[4])<< 5) & 224) << 24); + (*m).position.longitude |= (uint32_t)(((unsigned)(s[4])>> 3) & 31); + (*m).position.longitude |= (uint32_t)(((unsigned)(s[5])<< 5) & 224); + (*m).position.longitude |= (uint32_t)((uint32_t)(((unsigned)(s[5])>> 3) & 31) << 8); + (*m).position.longitude |= (uint32_t)((uint32_t)(((unsigned)(s[6])<< 5) & 224) << 8); + (*m).position.longitude |= (uint32_t)((uint32_t)(((unsigned)(s[6])>> 3) & 31) << 16); + (*m).position.longitude |= (uint32_t)((uint32_t)(((unsigned)(s[7])<< 5) & 224) << 16); + (*m).position.longitude |= (uint32_t)((uint32_t)(((unsigned)(s[7])>> 3) & 31) << 24); + (*m).position.longitude |= (uint32_t)((uint32_t)(((unsigned)(s[8])<< 5) & 224) << 24); + (*m).position.altitude |= (uint32_t)(((unsigned)(s[8])>> 3) & 31); + (*m).position.altitude |= (uint32_t)(((unsigned)(s[9])<< 5) & 224); + (*m).position.altitude |= (uint32_t)((uint32_t)(((unsigned)(s[9])>> 3) & 31) << 8); + (*m).position.altitude |= (uint32_t)((uint32_t)(((unsigned)(s[10])<< 5) & 224) << 8); + (*m).position.altitude |= (uint32_t)((uint32_t)(((unsigned)(s[10])>> 3) & 31) << 16); + (*m).position.altitude |= (uint32_t)((uint32_t)(((unsigned)(s[11])<< 5) & 224) << 16); + (*m).position.altitude |= (uint32_t)((uint32_t)(((unsigned)(s[11])>> 3) & 31) << 24); + (*m).position.altitude |= (uint32_t)((uint32_t)(((unsigned)(s[12])<< 5) & 224) << 24); + (*m).flight.pose.yaw |= (int32_t)(((unsigned)(s[12])>> 3) & 31); + (*m).flight.pose.yaw |= (int32_t)(((unsigned)(s[13])<< 5) & 224); + (*m).flight.pose.yaw |= (int32_t)((uint32_t)(((unsigned)(s[13])>> 3) & 31) << 8); + (*m).flight.pose.yaw |= (int32_t)((uint32_t)(((unsigned)(s[14])<< 5) & 224) << 8); + (*m).flight.pose.yaw |= (int32_t)((uint32_t)(((unsigned)(s[14])>> 3) & 31) << 16); + (*m).flight.pose.yaw |= (int32_t)((uint32_t)(((unsigned)(s[15])<< 5) & 224) << 16); + (*m).flight.pose.yaw |= (int32_t)((uint32_t)(((unsigned)(s[15])>> 3) & 31) << 24); + (*m).flight.pose.yaw |= (int32_t)((uint32_t)(((unsigned)(s[16])<< 5) & 224) << 24); + (*m).flight.pose.pitch |= (int32_t)(((unsigned)(s[16])>> 3) & 31); + (*m).flight.pose.pitch |= (int32_t)(((unsigned)(s[17])<< 5) & 224); + (*m).flight.pose.pitch |= (int32_t)((uint32_t)(((unsigned)(s[17])>> 3) & 31) << 8); + (*m).flight.pose.pitch |= (int32_t)((uint32_t)(((unsigned)(s[18])<< 5) & 224) << 8); + (*m).flight.pose.pitch |= (int32_t)((uint32_t)(((unsigned)(s[18])>> 3) & 31) << 16); + (*m).flight.pose.pitch |= (int32_t)((uint32_t)(((unsigned)(s[19])<< 5) & 224) << 16); + (*m).flight.pose.pitch |= (int32_t)((uint32_t)(((unsigned)(s[19])>> 3) & 31) << 24); + (*m).flight.pose.pitch |= (int32_t)((uint32_t)(((unsigned)(s[20])<< 5) & 224) << 24); + (*m).flight.pose.roll |= (int32_t)(((unsigned)(s[20])>> 3) & 31); + (*m).flight.pose.roll |= (int32_t)(((unsigned)(s[21])<< 5) & 224); + (*m).flight.pose.roll |= (int32_t)((uint32_t)(((unsigned)(s[21])>> 3) & 31) << 8); + (*m).flight.pose.roll |= (int32_t)((uint32_t)(((unsigned)(s[22])<< 5) & 224) << 8); + (*m).flight.pose.roll |= (int32_t)((uint32_t)(((unsigned)(s[22])>> 3) & 31) << 16); + (*m).flight.pose.roll |= (int32_t)((uint32_t)(((unsigned)(s[23])<< 5) & 224) << 16); + (*m).flight.pose.roll |= (int32_t)((uint32_t)(((unsigned)(s[23])>> 3) & 31) << 24); + (*m).flight.pose.roll |= (int32_t)((uint32_t)(((unsigned)(s[24])<< 5) & 224) << 24); + (*m).flight.velocity[0] |= (int32_t)(((unsigned)(s[24])>> 3) & 31); + (*m).flight.velocity[0] |= (int32_t)(((unsigned)(s[25])<< 5) & 224); + (*m).flight.velocity[0] |= (int32_t)((uint32_t)(((unsigned)(s[25])>> 3) & 31) << 8); + (*m).flight.velocity[0] |= (int32_t)((uint32_t)(((unsigned)(s[26])<< 5) & 224) << 8); + (*m).flight.velocity[0] |= (int32_t)((uint32_t)(((unsigned)(s[26])>> 3) & 31) << 16); + (*m).flight.velocity[0] |= (int32_t)((uint32_t)(((unsigned)(s[27])<< 5) & 224) << 16); + (*m).flight.velocity[0] |= (int32_t)((uint32_t)(((unsigned)(s[27])>> 3) & 31) << 24); + (*m).flight.velocity[0] |= (int32_t)((uint32_t)(((unsigned)(s[28])<< 5) & 224) << 24); + (*m).flight.velocity[1] |= (int32_t)(((unsigned)(s[28])>> 3) & 31); + (*m).flight.velocity[1] |= (int32_t)(((unsigned)(s[29])<< 5) & 224); + (*m).flight.velocity[1] |= (int32_t)((uint32_t)(((unsigned)(s[29])>> 3) & 31) << 8); + (*m).flight.velocity[1] |= (int32_t)((uint32_t)(((unsigned)(s[30])<< 5) & 224) << 8); + (*m).flight.velocity[1] |= (int32_t)((uint32_t)(((unsigned)(s[30])>> 3) & 31) << 16); + (*m).flight.velocity[1] |= (int32_t)((uint32_t)(((unsigned)(s[31])<< 5) & 224) << 16); + (*m).flight.velocity[1] |= (int32_t)((uint32_t)(((unsigned)(s[31])>> 3) & 31) << 24); + (*m).flight.velocity[1] |= (int32_t)((uint32_t)(((unsigned)(s[32])<< 5) & 224) << 24); + (*m).flight.velocity[2] |= (int32_t)(((unsigned)(s[32])>> 3) & 31); + (*m).flight.velocity[2] |= (int32_t)(((unsigned)(s[33])<< 5) & 224); + (*m).flight.velocity[2] |= (int32_t)((uint32_t)(((unsigned)(s[33])>> 3) & 31) << 8); + (*m).flight.velocity[2] |= (int32_t)((uint32_t)(((unsigned)(s[34])<< 5) & 224) << 8); + (*m).flight.velocity[2] |= (int32_t)((uint32_t)(((unsigned)(s[34])>> 3) & 31) << 16); + (*m).flight.velocity[2] |= (int32_t)((uint32_t)(((unsigned)(s[35])<< 5) & 224) << 16); + (*m).flight.velocity[2] |= (int32_t)((uint32_t)(((unsigned)(s[35])>> 3) & 31) << 24); + (*m).flight.velocity[2] |= (int32_t)((uint32_t)(((unsigned)(s[36])<< 5) & 224) << 24); + (*m).flight.acceleration[0] |= (int32_t)(((unsigned)(s[36])>> 3) & 31); + (*m).flight.acceleration[0] |= (int32_t)(((unsigned)(s[37])<< 5) & 224); + (*m).flight.acceleration[0] |= (int32_t)((uint32_t)(((unsigned)(s[37])>> 3) & 31) << 8); + (*m).flight.acceleration[0] |= (int32_t)((uint32_t)(((unsigned)(s[38])<< 5) & 224) << 8); + (*m).flight.acceleration[0] |= (int32_t)((uint32_t)(((unsigned)(s[38])>> 3) & 31) << 16); + (*m).flight.acceleration[0] |= (int32_t)((uint32_t)(((unsigned)(s[39])<< 5) & 224) << 16); + (*m).flight.acceleration[0] |= (int32_t)((uint32_t)(((unsigned)(s[39])>> 3) & 31) << 24); + (*m).flight.acceleration[0] |= (int32_t)((uint32_t)(((unsigned)(s[40])<< 5) & 224) << 24); + (*m).flight.acceleration[1] |= (int32_t)(((unsigned)(s[40])>> 3) & 31); + (*m).flight.acceleration[1] |= (int32_t)(((unsigned)(s[41])<< 5) & 224); + (*m).flight.acceleration[1] |= (int32_t)((uint32_t)(((unsigned)(s[41])>> 3) & 31) << 8); + (*m).flight.acceleration[1] |= (int32_t)((uint32_t)(((unsigned)(s[42])<< 5) & 224) << 8); + (*m).flight.acceleration[1] |= (int32_t)((uint32_t)(((unsigned)(s[42])>> 3) & 31) << 16); + (*m).flight.acceleration[1] |= (int32_t)((uint32_t)(((unsigned)(s[43])<< 5) & 224) << 16); + (*m).flight.acceleration[1] |= (int32_t)((uint32_t)(((unsigned)(s[43])>> 3) & 31) << 24); + (*m).flight.acceleration[1] |= (int32_t)((uint32_t)(((unsigned)(s[44])<< 5) & 224) << 24); + (*m).flight.acceleration[2] |= (int32_t)(((unsigned)(s[44])>> 3) & 31); + (*m).flight.acceleration[2] |= (int32_t)(((unsigned)(s[45])<< 5) & 224); + (*m).flight.acceleration[2] |= (int32_t)((uint32_t)(((unsigned)(s[45])>> 3) & 31) << 8); + (*m).flight.acceleration[2] |= (int32_t)((uint32_t)(((unsigned)(s[46])<< 5) & 224) << 8); + (*m).flight.acceleration[2] |= (int32_t)((uint32_t)(((unsigned)(s[46])>> 3) & 31) << 16); + (*m).flight.acceleration[2] |= (int32_t)((uint32_t)(((unsigned)(s[47])<< 5) & 224) << 16); + (*m).flight.acceleration[2] |= (int32_t)((uint32_t)(((unsigned)(s[47])>> 3) & 31) << 24); + (*m).flight.acceleration[2] |= (int32_t)((uint32_t)(((unsigned)(s[48])<< 5) & 224) << 24); + (*m).propellers[0].id |= (uint8_t)(((unsigned)(s[48])>> 3) & 31); + (*m).propellers[0].id |= (uint8_t)(((unsigned)(s[49])<< 5) & 224); + (*m).propellers[0].status |= (PropellerStatus)(((unsigned)(s[49])>> 3) & 3); + (*m).propellers[0].direction |= (RotatingDirection)(((unsigned)(s[49])>> 5) & 3); + (*m).propellers[1].id |= (uint8_t)(((unsigned)(s[49])>> 7) & 1); + (*m).propellers[1].id |= (uint8_t)(((unsigned)(s[50])<< 1) & 254); + (*m).propellers[1].status |= (PropellerStatus)(((unsigned)(s[50])>> 7) & 1); + (*m).propellers[1].status |= (PropellerStatus)(((unsigned)(s[51])<< 1) & 2); + (*m).propellers[1].direction |= (RotatingDirection)(((unsigned)(s[51])>> 1) & 3); + (*m).propellers[2].id |= (uint8_t)(((unsigned)(s[51])>> 3) & 31); + (*m).propellers[2].id |= (uint8_t)(((unsigned)(s[52])<< 5) & 224); + (*m).propellers[2].status |= (PropellerStatus)(((unsigned)(s[52])>> 3) & 3); + (*m).propellers[2].direction |= (RotatingDirection)(((unsigned)(s[52])>> 5) & 3); + (*m).propellers[3].id |= (uint8_t)(((unsigned)(s[52])>> 7) & 1); + (*m).propellers[3].id |= (uint8_t)(((unsigned)(s[53])<< 1) & 254); + (*m).propellers[3].status |= (PropellerStatus)(((unsigned)(s[53])>> 7) & 1); + (*m).propellers[3].status |= (PropellerStatus)(((unsigned)(s[54])<< 1) & 2); + (*m).propellers[3].direction |= (RotatingDirection)(((unsigned)(s[54])>> 1) & 3); + (*m).power.battery |= (uint8_t)(((unsigned)(s[54])>> 3) & 31); + (*m).power.battery |= (uint8_t)(((unsigned)(s[55])<< 5) & 224); + (*m).power.status |= (PowerStatus)(((unsigned)(s[55])>> 3) & 3); + (*m).power.is_charging |= (bool)(((unsigned)(s[55])>> 5) & 1); + (*m).network.signal |= (uint8_t)(((unsigned)(s[55])>> 6) & 3); + (*m).network.signal |= (uint8_t)(((unsigned)(s[56])<< 2) & 12); + (*m).network.heartbeat_at |= (Timestamp)(((unsigned)(s[56])>> 2) & 63); + (*m).network.heartbeat_at |= (Timestamp)(((unsigned)(s[57])<< 6) & 192); + (*m).network.heartbeat_at |= (Timestamp)((uint32_t)(((unsigned)(s[57])>> 2) & 63) << 8); + (*m).network.heartbeat_at |= (Timestamp)((uint32_t)(((unsigned)(s[58])<< 6) & 192) << 8); + (*m).network.heartbeat_at |= (Timestamp)((uint32_t)(((unsigned)(s[58])>> 2) & 63) << 16); + (*m).network.heartbeat_at |= (Timestamp)((uint32_t)(((unsigned)(s[59])<< 6) & 192) << 16); + (*m).network.heartbeat_at |= (Timestamp)((uint32_t)(((unsigned)(s[59])>> 2) & 63) << 24); + (*m).network.heartbeat_at |= (Timestamp)((uint32_t)(((unsigned)(s[60])<< 6) & 192) << 24); + (*m).landing_gear.status |= (LandingGearStatus)(((unsigned)(s[60])>> 2) & 3); + (*m).pressure_sensor.pressures[0] |= (int32_t)(((unsigned)(s[60])>> 4) & 15); + (*m).pressure_sensor.pressures[0] |= (int32_t)(((unsigned)(s[61])<< 4) & 240); + (*m).pressure_sensor.pressures[0] |= (int32_t)((uint32_t)(((unsigned)(s[61])>> 4) & 15) << 8); + (*m).pressure_sensor.pressures[0] |= (int32_t)((uint32_t)(((unsigned)(s[62])<< 4) & 240) << 8); + (*m).pressure_sensor.pressures[0] |= (int32_t)((uint32_t)(((unsigned)(s[62])>> 4) & 15) << 16); + (*m).pressure_sensor.pressures[0] |= (int32_t)((uint32_t)(((unsigned)(s[63])<< 4) & 240) << 16); + if (((*m).pressure_sensor.pressures[0] >> 23) & 1) (*m).pressure_sensor.pressures[0] |= -16777216; + (*m).pressure_sensor.pressures[1] |= (int32_t)(((unsigned)(s[63])>> 4) & 15); + (*m).pressure_sensor.pressures[1] |= (int32_t)(((unsigned)(s[64])<< 4) & 240); + (*m).pressure_sensor.pressures[1] |= (int32_t)((uint32_t)(((unsigned)(s[64])>> 4) & 15) << 8); + (*m).pressure_sensor.pressures[1] |= (int32_t)((uint32_t)(((unsigned)(s[65])<< 4) & 240) << 8); + (*m).pressure_sensor.pressures[1] |= (int32_t)((uint32_t)(((unsigned)(s[65])>> 4) & 15) << 16); + (*m).pressure_sensor.pressures[1] |= (int32_t)((uint32_t)(((unsigned)(s[66])<< 4) & 240) << 16); + if (((*m).pressure_sensor.pressures[1] >> 23) & 1) (*m).pressure_sensor.pressures[1] |= -16777216; +#endif return 0; } \ No newline at end of file diff --git a/example/C/Makefile b/example/C/Makefile index daa86fe8..6f70bc81 100644 --- a/example/C/Makefile +++ b/example/C/Makefile @@ -9,6 +9,6 @@ run: build ./example clean: - rm example_bp.h example_bp.c example + rm -f example_bp.h example_bp.c example .PHONY: build run clean diff --git a/example/CPP-optimization-mode/Makefile b/example/CPP-optimization-mode/Makefile index c453ca4d..d943ce08 100644 --- a/example/CPP-optimization-mode/Makefile +++ b/example/CPP-optimization-mode/Makefile @@ -19,6 +19,6 @@ run: build ./example clean: - rm example_bp.h example_bp.c example *.o || true + rm -f example_bp.h example_bp.c example *.o $(BITPROTO_LIB_PATH)/*.o .PHONY: build run clean diff --git a/example/CPP-optimization-mode/example_bp.c b/example/CPP-optimization-mode/example_bp.c index 4cf17e0d..721c8ee8 100644 --- a/example/CPP-optimization-mode/example_bp.c +++ b/example/CPP-optimization-mode/example_bp.c @@ -1,8 +1,16 @@ // Code generated by bitproto. DO NOT EDIT. #include "example_bp.h" +#if !defined(BP_BIG_ENDIAN) && defined(__BYTE_ORDER__) && \ + (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#define BP_BIG_ENDIAN 1 +#endif +#ifdef BP_BIG_ENDIAN +#include +#endif int EncodeDrone(struct Drone *m, unsigned char *s) { +#ifndef BP_BIG_ENDIAN s[0] = (((unsigned char *)&((*m).status))[0] ) & 7; s[0] |= (((unsigned char *)&((*m).position.latitude))[0] << 3) & 248; s[1] = (((unsigned char *)&((*m).position.latitude))[0] >> 5) & 7; @@ -145,10 +153,155 @@ int EncodeDrone(struct Drone *m, unsigned char *s) { s[65] = (((unsigned char *)&((*m).pressure_sensor.pressures[1]))[1] >> 4) & 15; s[65] |= (((unsigned char *)&((*m).pressure_sensor.pressures[1]))[2] << 4) & 240; s[66] = (((unsigned char *)&((*m).pressure_sensor.pressures[1]))[2] >> 4) & 15; +#else + s[0] = ((uint8_t)((*m).status)) & 7; + s[0] |= ((uint32_t)((*m).position.latitude)<< 3) & 248; + s[1] = ((uint32_t)((*m).position.latitude)>> 5) & 7; + s[1] |= ((uint32_t)((*m).position.latitude)>> 5) & 248; + s[2] = ((uint32_t)((*m).position.latitude)>> 13) & 7; + s[2] |= ((uint32_t)((*m).position.latitude)>> 13) & 248; + s[3] = ((uint32_t)((*m).position.latitude)>> 21) & 7; + s[3] |= ((uint32_t)((*m).position.latitude)>> 21) & 248; + s[4] = ((uint32_t)((*m).position.latitude)>> 29) & 7; + s[4] |= ((uint32_t)((*m).position.longitude)<< 3) & 248; + s[5] = ((uint32_t)((*m).position.longitude)>> 5) & 7; + s[5] |= ((uint32_t)((*m).position.longitude)>> 5) & 248; + s[6] = ((uint32_t)((*m).position.longitude)>> 13) & 7; + s[6] |= ((uint32_t)((*m).position.longitude)>> 13) & 248; + s[7] = ((uint32_t)((*m).position.longitude)>> 21) & 7; + s[7] |= ((uint32_t)((*m).position.longitude)>> 21) & 248; + s[8] = ((uint32_t)((*m).position.longitude)>> 29) & 7; + s[8] |= ((uint32_t)((*m).position.altitude)<< 3) & 248; + s[9] = ((uint32_t)((*m).position.altitude)>> 5) & 7; + s[9] |= ((uint32_t)((*m).position.altitude)>> 5) & 248; + s[10] = ((uint32_t)((*m).position.altitude)>> 13) & 7; + s[10] |= ((uint32_t)((*m).position.altitude)>> 13) & 248; + s[11] = ((uint32_t)((*m).position.altitude)>> 21) & 7; + s[11] |= ((uint32_t)((*m).position.altitude)>> 21) & 248; + s[12] = ((uint32_t)((*m).position.altitude)>> 29) & 7; + s[12] |= ((uint32_t)((*m).flight.pose.yaw)<< 3) & 248; + s[13] = ((uint32_t)((*m).flight.pose.yaw)>> 5) & 7; + s[13] |= ((uint32_t)((*m).flight.pose.yaw)>> 5) & 248; + s[14] = ((uint32_t)((*m).flight.pose.yaw)>> 13) & 7; + s[14] |= ((uint32_t)((*m).flight.pose.yaw)>> 13) & 248; + s[15] = ((uint32_t)((*m).flight.pose.yaw)>> 21) & 7; + s[15] |= ((uint32_t)((*m).flight.pose.yaw)>> 21) & 248; + s[16] = ((uint32_t)((*m).flight.pose.yaw)>> 29) & 7; + s[16] |= ((uint32_t)((*m).flight.pose.pitch)<< 3) & 248; + s[17] = ((uint32_t)((*m).flight.pose.pitch)>> 5) & 7; + s[17] |= ((uint32_t)((*m).flight.pose.pitch)>> 5) & 248; + s[18] = ((uint32_t)((*m).flight.pose.pitch)>> 13) & 7; + s[18] |= ((uint32_t)((*m).flight.pose.pitch)>> 13) & 248; + s[19] = ((uint32_t)((*m).flight.pose.pitch)>> 21) & 7; + s[19] |= ((uint32_t)((*m).flight.pose.pitch)>> 21) & 248; + s[20] = ((uint32_t)((*m).flight.pose.pitch)>> 29) & 7; + s[20] |= ((uint32_t)((*m).flight.pose.roll)<< 3) & 248; + s[21] = ((uint32_t)((*m).flight.pose.roll)>> 5) & 7; + s[21] |= ((uint32_t)((*m).flight.pose.roll)>> 5) & 248; + s[22] = ((uint32_t)((*m).flight.pose.roll)>> 13) & 7; + s[22] |= ((uint32_t)((*m).flight.pose.roll)>> 13) & 248; + s[23] = ((uint32_t)((*m).flight.pose.roll)>> 21) & 7; + s[23] |= ((uint32_t)((*m).flight.pose.roll)>> 21) & 248; + s[24] = ((uint32_t)((*m).flight.pose.roll)>> 29) & 7; + s[24] |= ((uint32_t)((*m).flight.velocity[0])<< 3) & 248; + s[25] = ((uint32_t)((*m).flight.velocity[0])>> 5) & 7; + s[25] |= ((uint32_t)((*m).flight.velocity[0])>> 5) & 248; + s[26] = ((uint32_t)((*m).flight.velocity[0])>> 13) & 7; + s[26] |= ((uint32_t)((*m).flight.velocity[0])>> 13) & 248; + s[27] = ((uint32_t)((*m).flight.velocity[0])>> 21) & 7; + s[27] |= ((uint32_t)((*m).flight.velocity[0])>> 21) & 248; + s[28] = ((uint32_t)((*m).flight.velocity[0])>> 29) & 7; + s[28] |= ((uint32_t)((*m).flight.velocity[1])<< 3) & 248; + s[29] = ((uint32_t)((*m).flight.velocity[1])>> 5) & 7; + s[29] |= ((uint32_t)((*m).flight.velocity[1])>> 5) & 248; + s[30] = ((uint32_t)((*m).flight.velocity[1])>> 13) & 7; + s[30] |= ((uint32_t)((*m).flight.velocity[1])>> 13) & 248; + s[31] = ((uint32_t)((*m).flight.velocity[1])>> 21) & 7; + s[31] |= ((uint32_t)((*m).flight.velocity[1])>> 21) & 248; + s[32] = ((uint32_t)((*m).flight.velocity[1])>> 29) & 7; + s[32] |= ((uint32_t)((*m).flight.velocity[2])<< 3) & 248; + s[33] = ((uint32_t)((*m).flight.velocity[2])>> 5) & 7; + s[33] |= ((uint32_t)((*m).flight.velocity[2])>> 5) & 248; + s[34] = ((uint32_t)((*m).flight.velocity[2])>> 13) & 7; + s[34] |= ((uint32_t)((*m).flight.velocity[2])>> 13) & 248; + s[35] = ((uint32_t)((*m).flight.velocity[2])>> 21) & 7; + s[35] |= ((uint32_t)((*m).flight.velocity[2])>> 21) & 248; + s[36] = ((uint32_t)((*m).flight.velocity[2])>> 29) & 7; + s[36] |= ((uint32_t)((*m).flight.acceleration[0])<< 3) & 248; + s[37] = ((uint32_t)((*m).flight.acceleration[0])>> 5) & 7; + s[37] |= ((uint32_t)((*m).flight.acceleration[0])>> 5) & 248; + s[38] = ((uint32_t)((*m).flight.acceleration[0])>> 13) & 7; + s[38] |= ((uint32_t)((*m).flight.acceleration[0])>> 13) & 248; + s[39] = ((uint32_t)((*m).flight.acceleration[0])>> 21) & 7; + s[39] |= ((uint32_t)((*m).flight.acceleration[0])>> 21) & 248; + s[40] = ((uint32_t)((*m).flight.acceleration[0])>> 29) & 7; + s[40] |= ((uint32_t)((*m).flight.acceleration[1])<< 3) & 248; + s[41] = ((uint32_t)((*m).flight.acceleration[1])>> 5) & 7; + s[41] |= ((uint32_t)((*m).flight.acceleration[1])>> 5) & 248; + s[42] = ((uint32_t)((*m).flight.acceleration[1])>> 13) & 7; + s[42] |= ((uint32_t)((*m).flight.acceleration[1])>> 13) & 248; + s[43] = ((uint32_t)((*m).flight.acceleration[1])>> 21) & 7; + s[43] |= ((uint32_t)((*m).flight.acceleration[1])>> 21) & 248; + s[44] = ((uint32_t)((*m).flight.acceleration[1])>> 29) & 7; + s[44] |= ((uint32_t)((*m).flight.acceleration[2])<< 3) & 248; + s[45] = ((uint32_t)((*m).flight.acceleration[2])>> 5) & 7; + s[45] |= ((uint32_t)((*m).flight.acceleration[2])>> 5) & 248; + s[46] = ((uint32_t)((*m).flight.acceleration[2])>> 13) & 7; + s[46] |= ((uint32_t)((*m).flight.acceleration[2])>> 13) & 248; + s[47] = ((uint32_t)((*m).flight.acceleration[2])>> 21) & 7; + s[47] |= ((uint32_t)((*m).flight.acceleration[2])>> 21) & 248; + s[48] = ((uint32_t)((*m).flight.acceleration[2])>> 29) & 7; + s[48] |= ((uint8_t)((*m).propellers[0].id)<< 3) & 248; + s[49] = ((uint8_t)((*m).propellers[0].id)>> 5) & 7; + s[49] |= ((uint8_t)((*m).propellers[0].status)<< 3) & 24; + s[49] |= ((uint8_t)((*m).propellers[0].direction)<< 5) & 96; + s[49] |= ((uint8_t)((*m).propellers[1].id)<< 7) & 128; + s[50] = ((uint8_t)((*m).propellers[1].id)>> 1) & 127; + s[50] |= ((uint8_t)((*m).propellers[1].status)<< 7) & 128; + s[51] = ((uint8_t)((*m).propellers[1].status)>> 1) & 1; + s[51] |= ((uint8_t)((*m).propellers[1].direction)<< 1) & 6; + s[51] |= ((uint8_t)((*m).propellers[2].id)<< 3) & 248; + s[52] = ((uint8_t)((*m).propellers[2].id)>> 5) & 7; + s[52] |= ((uint8_t)((*m).propellers[2].status)<< 3) & 24; + s[52] |= ((uint8_t)((*m).propellers[2].direction)<< 5) & 96; + s[52] |= ((uint8_t)((*m).propellers[3].id)<< 7) & 128; + s[53] = ((uint8_t)((*m).propellers[3].id)>> 1) & 127; + s[53] |= ((uint8_t)((*m).propellers[3].status)<< 7) & 128; + s[54] = ((uint8_t)((*m).propellers[3].status)>> 1) & 1; + s[54] |= ((uint8_t)((*m).propellers[3].direction)<< 1) & 6; + s[54] |= ((uint8_t)((*m).power.battery)<< 3) & 248; + s[55] = ((uint8_t)((*m).power.battery)>> 5) & 7; + s[55] |= ((uint8_t)((*m).power.status)<< 3) & 24; + s[55] |= ((uint8_t)((*m).power.is_charging)<< 5) & 32; + s[55] |= ((uint8_t)((*m).network.signal)<< 6) & 192; + s[56] = ((uint8_t)((*m).network.signal)>> 2) & 3; + s[56] |= ((uint32_t)((*m).network.heartbeat_at)<< 2) & 252; + s[57] = ((uint32_t)((*m).network.heartbeat_at)>> 6) & 3; + s[57] |= ((uint32_t)((*m).network.heartbeat_at)>> 6) & 252; + s[58] = ((uint32_t)((*m).network.heartbeat_at)>> 14) & 3; + s[58] |= ((uint32_t)((*m).network.heartbeat_at)>> 14) & 252; + s[59] = ((uint32_t)((*m).network.heartbeat_at)>> 22) & 3; + s[59] |= ((uint32_t)((*m).network.heartbeat_at)>> 22) & 252; + s[60] = ((uint32_t)((*m).network.heartbeat_at)>> 30) & 3; + s[60] |= ((uint8_t)((*m).landing_gear.status)<< 2) & 12; + s[60] |= ((uint32_t)((*m).pressure_sensor.pressures[0])<< 4) & 240; + s[61] = ((uint32_t)((*m).pressure_sensor.pressures[0])>> 4) & 15; + s[61] |= ((uint32_t)((*m).pressure_sensor.pressures[0])>> 4) & 240; + s[62] = ((uint32_t)((*m).pressure_sensor.pressures[0])>> 12) & 15; + s[62] |= ((uint32_t)((*m).pressure_sensor.pressures[0])>> 12) & 240; + s[63] = ((uint32_t)((*m).pressure_sensor.pressures[0])>> 20) & 15; + s[63] |= ((uint32_t)((*m).pressure_sensor.pressures[1])<< 4) & 240; + s[64] = ((uint32_t)((*m).pressure_sensor.pressures[1])>> 4) & 15; + s[64] |= ((uint32_t)((*m).pressure_sensor.pressures[1])>> 4) & 240; + s[65] = ((uint32_t)((*m).pressure_sensor.pressures[1])>> 12) & 15; + s[65] |= ((uint32_t)((*m).pressure_sensor.pressures[1])>> 12) & 240; + s[66] = ((uint32_t)((*m).pressure_sensor.pressures[1])>> 20) & 15; +#endif return 0; } int DecodeDrone(struct Drone *m, unsigned char *s) { +#ifndef BP_BIG_ENDIAN ((unsigned char *)&((*m).status))[0] = (s[0] ) & 7; ((unsigned char *)&((*m).position.latitude))[0] = (s[0] >> 3) & 31; ((unsigned char *)&((*m).position.latitude))[0] |= (s[1] << 5) & 224; @@ -293,5 +446,152 @@ int DecodeDrone(struct Drone *m, unsigned char *s) { ((unsigned char *)&((*m).pressure_sensor.pressures[1]))[2] = (s[65] >> 4) & 15; ((unsigned char *)&((*m).pressure_sensor.pressures[1]))[2] |= (s[66] << 4) & 240; if (((*m).pressure_sensor.pressures[1] >> 23) & 1) (*m).pressure_sensor.pressures[1] |= -16777216; +#else + memset(m, 0, sizeof(*m)); + (*m).status |= (DroneStatus)(((unsigned)(s[0])) & 7); + (*m).position.latitude |= (uint32_t)(((unsigned)(s[0])>> 3) & 31); + (*m).position.latitude |= (uint32_t)(((unsigned)(s[1])<< 5) & 224); + (*m).position.latitude |= (uint32_t)((uint32_t)(((unsigned)(s[1])>> 3) & 31) << 8); + (*m).position.latitude |= (uint32_t)((uint32_t)(((unsigned)(s[2])<< 5) & 224) << 8); + (*m).position.latitude |= (uint32_t)((uint32_t)(((unsigned)(s[2])>> 3) & 31) << 16); + (*m).position.latitude |= (uint32_t)((uint32_t)(((unsigned)(s[3])<< 5) & 224) << 16); + (*m).position.latitude |= (uint32_t)((uint32_t)(((unsigned)(s[3])>> 3) & 31) << 24); + (*m).position.latitude |= (uint32_t)((uint32_t)(((unsigned)(s[4])<< 5) & 224) << 24); + (*m).position.longitude |= (uint32_t)(((unsigned)(s[4])>> 3) & 31); + (*m).position.longitude |= (uint32_t)(((unsigned)(s[5])<< 5) & 224); + (*m).position.longitude |= (uint32_t)((uint32_t)(((unsigned)(s[5])>> 3) & 31) << 8); + (*m).position.longitude |= (uint32_t)((uint32_t)(((unsigned)(s[6])<< 5) & 224) << 8); + (*m).position.longitude |= (uint32_t)((uint32_t)(((unsigned)(s[6])>> 3) & 31) << 16); + (*m).position.longitude |= (uint32_t)((uint32_t)(((unsigned)(s[7])<< 5) & 224) << 16); + (*m).position.longitude |= (uint32_t)((uint32_t)(((unsigned)(s[7])>> 3) & 31) << 24); + (*m).position.longitude |= (uint32_t)((uint32_t)(((unsigned)(s[8])<< 5) & 224) << 24); + (*m).position.altitude |= (uint32_t)(((unsigned)(s[8])>> 3) & 31); + (*m).position.altitude |= (uint32_t)(((unsigned)(s[9])<< 5) & 224); + (*m).position.altitude |= (uint32_t)((uint32_t)(((unsigned)(s[9])>> 3) & 31) << 8); + (*m).position.altitude |= (uint32_t)((uint32_t)(((unsigned)(s[10])<< 5) & 224) << 8); + (*m).position.altitude |= (uint32_t)((uint32_t)(((unsigned)(s[10])>> 3) & 31) << 16); + (*m).position.altitude |= (uint32_t)((uint32_t)(((unsigned)(s[11])<< 5) & 224) << 16); + (*m).position.altitude |= (uint32_t)((uint32_t)(((unsigned)(s[11])>> 3) & 31) << 24); + (*m).position.altitude |= (uint32_t)((uint32_t)(((unsigned)(s[12])<< 5) & 224) << 24); + (*m).flight.pose.yaw |= (int32_t)(((unsigned)(s[12])>> 3) & 31); + (*m).flight.pose.yaw |= (int32_t)(((unsigned)(s[13])<< 5) & 224); + (*m).flight.pose.yaw |= (int32_t)((uint32_t)(((unsigned)(s[13])>> 3) & 31) << 8); + (*m).flight.pose.yaw |= (int32_t)((uint32_t)(((unsigned)(s[14])<< 5) & 224) << 8); + (*m).flight.pose.yaw |= (int32_t)((uint32_t)(((unsigned)(s[14])>> 3) & 31) << 16); + (*m).flight.pose.yaw |= (int32_t)((uint32_t)(((unsigned)(s[15])<< 5) & 224) << 16); + (*m).flight.pose.yaw |= (int32_t)((uint32_t)(((unsigned)(s[15])>> 3) & 31) << 24); + (*m).flight.pose.yaw |= (int32_t)((uint32_t)(((unsigned)(s[16])<< 5) & 224) << 24); + (*m).flight.pose.pitch |= (int32_t)(((unsigned)(s[16])>> 3) & 31); + (*m).flight.pose.pitch |= (int32_t)(((unsigned)(s[17])<< 5) & 224); + (*m).flight.pose.pitch |= (int32_t)((uint32_t)(((unsigned)(s[17])>> 3) & 31) << 8); + (*m).flight.pose.pitch |= (int32_t)((uint32_t)(((unsigned)(s[18])<< 5) & 224) << 8); + (*m).flight.pose.pitch |= (int32_t)((uint32_t)(((unsigned)(s[18])>> 3) & 31) << 16); + (*m).flight.pose.pitch |= (int32_t)((uint32_t)(((unsigned)(s[19])<< 5) & 224) << 16); + (*m).flight.pose.pitch |= (int32_t)((uint32_t)(((unsigned)(s[19])>> 3) & 31) << 24); + (*m).flight.pose.pitch |= (int32_t)((uint32_t)(((unsigned)(s[20])<< 5) & 224) << 24); + (*m).flight.pose.roll |= (int32_t)(((unsigned)(s[20])>> 3) & 31); + (*m).flight.pose.roll |= (int32_t)(((unsigned)(s[21])<< 5) & 224); + (*m).flight.pose.roll |= (int32_t)((uint32_t)(((unsigned)(s[21])>> 3) & 31) << 8); + (*m).flight.pose.roll |= (int32_t)((uint32_t)(((unsigned)(s[22])<< 5) & 224) << 8); + (*m).flight.pose.roll |= (int32_t)((uint32_t)(((unsigned)(s[22])>> 3) & 31) << 16); + (*m).flight.pose.roll |= (int32_t)((uint32_t)(((unsigned)(s[23])<< 5) & 224) << 16); + (*m).flight.pose.roll |= (int32_t)((uint32_t)(((unsigned)(s[23])>> 3) & 31) << 24); + (*m).flight.pose.roll |= (int32_t)((uint32_t)(((unsigned)(s[24])<< 5) & 224) << 24); + (*m).flight.velocity[0] |= (int32_t)(((unsigned)(s[24])>> 3) & 31); + (*m).flight.velocity[0] |= (int32_t)(((unsigned)(s[25])<< 5) & 224); + (*m).flight.velocity[0] |= (int32_t)((uint32_t)(((unsigned)(s[25])>> 3) & 31) << 8); + (*m).flight.velocity[0] |= (int32_t)((uint32_t)(((unsigned)(s[26])<< 5) & 224) << 8); + (*m).flight.velocity[0] |= (int32_t)((uint32_t)(((unsigned)(s[26])>> 3) & 31) << 16); + (*m).flight.velocity[0] |= (int32_t)((uint32_t)(((unsigned)(s[27])<< 5) & 224) << 16); + (*m).flight.velocity[0] |= (int32_t)((uint32_t)(((unsigned)(s[27])>> 3) & 31) << 24); + (*m).flight.velocity[0] |= (int32_t)((uint32_t)(((unsigned)(s[28])<< 5) & 224) << 24); + (*m).flight.velocity[1] |= (int32_t)(((unsigned)(s[28])>> 3) & 31); + (*m).flight.velocity[1] |= (int32_t)(((unsigned)(s[29])<< 5) & 224); + (*m).flight.velocity[1] |= (int32_t)((uint32_t)(((unsigned)(s[29])>> 3) & 31) << 8); + (*m).flight.velocity[1] |= (int32_t)((uint32_t)(((unsigned)(s[30])<< 5) & 224) << 8); + (*m).flight.velocity[1] |= (int32_t)((uint32_t)(((unsigned)(s[30])>> 3) & 31) << 16); + (*m).flight.velocity[1] |= (int32_t)((uint32_t)(((unsigned)(s[31])<< 5) & 224) << 16); + (*m).flight.velocity[1] |= (int32_t)((uint32_t)(((unsigned)(s[31])>> 3) & 31) << 24); + (*m).flight.velocity[1] |= (int32_t)((uint32_t)(((unsigned)(s[32])<< 5) & 224) << 24); + (*m).flight.velocity[2] |= (int32_t)(((unsigned)(s[32])>> 3) & 31); + (*m).flight.velocity[2] |= (int32_t)(((unsigned)(s[33])<< 5) & 224); + (*m).flight.velocity[2] |= (int32_t)((uint32_t)(((unsigned)(s[33])>> 3) & 31) << 8); + (*m).flight.velocity[2] |= (int32_t)((uint32_t)(((unsigned)(s[34])<< 5) & 224) << 8); + (*m).flight.velocity[2] |= (int32_t)((uint32_t)(((unsigned)(s[34])>> 3) & 31) << 16); + (*m).flight.velocity[2] |= (int32_t)((uint32_t)(((unsigned)(s[35])<< 5) & 224) << 16); + (*m).flight.velocity[2] |= (int32_t)((uint32_t)(((unsigned)(s[35])>> 3) & 31) << 24); + (*m).flight.velocity[2] |= (int32_t)((uint32_t)(((unsigned)(s[36])<< 5) & 224) << 24); + (*m).flight.acceleration[0] |= (int32_t)(((unsigned)(s[36])>> 3) & 31); + (*m).flight.acceleration[0] |= (int32_t)(((unsigned)(s[37])<< 5) & 224); + (*m).flight.acceleration[0] |= (int32_t)((uint32_t)(((unsigned)(s[37])>> 3) & 31) << 8); + (*m).flight.acceleration[0] |= (int32_t)((uint32_t)(((unsigned)(s[38])<< 5) & 224) << 8); + (*m).flight.acceleration[0] |= (int32_t)((uint32_t)(((unsigned)(s[38])>> 3) & 31) << 16); + (*m).flight.acceleration[0] |= (int32_t)((uint32_t)(((unsigned)(s[39])<< 5) & 224) << 16); + (*m).flight.acceleration[0] |= (int32_t)((uint32_t)(((unsigned)(s[39])>> 3) & 31) << 24); + (*m).flight.acceleration[0] |= (int32_t)((uint32_t)(((unsigned)(s[40])<< 5) & 224) << 24); + (*m).flight.acceleration[1] |= (int32_t)(((unsigned)(s[40])>> 3) & 31); + (*m).flight.acceleration[1] |= (int32_t)(((unsigned)(s[41])<< 5) & 224); + (*m).flight.acceleration[1] |= (int32_t)((uint32_t)(((unsigned)(s[41])>> 3) & 31) << 8); + (*m).flight.acceleration[1] |= (int32_t)((uint32_t)(((unsigned)(s[42])<< 5) & 224) << 8); + (*m).flight.acceleration[1] |= (int32_t)((uint32_t)(((unsigned)(s[42])>> 3) & 31) << 16); + (*m).flight.acceleration[1] |= (int32_t)((uint32_t)(((unsigned)(s[43])<< 5) & 224) << 16); + (*m).flight.acceleration[1] |= (int32_t)((uint32_t)(((unsigned)(s[43])>> 3) & 31) << 24); + (*m).flight.acceleration[1] |= (int32_t)((uint32_t)(((unsigned)(s[44])<< 5) & 224) << 24); + (*m).flight.acceleration[2] |= (int32_t)(((unsigned)(s[44])>> 3) & 31); + (*m).flight.acceleration[2] |= (int32_t)(((unsigned)(s[45])<< 5) & 224); + (*m).flight.acceleration[2] |= (int32_t)((uint32_t)(((unsigned)(s[45])>> 3) & 31) << 8); + (*m).flight.acceleration[2] |= (int32_t)((uint32_t)(((unsigned)(s[46])<< 5) & 224) << 8); + (*m).flight.acceleration[2] |= (int32_t)((uint32_t)(((unsigned)(s[46])>> 3) & 31) << 16); + (*m).flight.acceleration[2] |= (int32_t)((uint32_t)(((unsigned)(s[47])<< 5) & 224) << 16); + (*m).flight.acceleration[2] |= (int32_t)((uint32_t)(((unsigned)(s[47])>> 3) & 31) << 24); + (*m).flight.acceleration[2] |= (int32_t)((uint32_t)(((unsigned)(s[48])<< 5) & 224) << 24); + (*m).propellers[0].id |= (uint8_t)(((unsigned)(s[48])>> 3) & 31); + (*m).propellers[0].id |= (uint8_t)(((unsigned)(s[49])<< 5) & 224); + (*m).propellers[0].status |= (PropellerStatus)(((unsigned)(s[49])>> 3) & 3); + (*m).propellers[0].direction |= (RotatingDirection)(((unsigned)(s[49])>> 5) & 3); + (*m).propellers[1].id |= (uint8_t)(((unsigned)(s[49])>> 7) & 1); + (*m).propellers[1].id |= (uint8_t)(((unsigned)(s[50])<< 1) & 254); + (*m).propellers[1].status |= (PropellerStatus)(((unsigned)(s[50])>> 7) & 1); + (*m).propellers[1].status |= (PropellerStatus)(((unsigned)(s[51])<< 1) & 2); + (*m).propellers[1].direction |= (RotatingDirection)(((unsigned)(s[51])>> 1) & 3); + (*m).propellers[2].id |= (uint8_t)(((unsigned)(s[51])>> 3) & 31); + (*m).propellers[2].id |= (uint8_t)(((unsigned)(s[52])<< 5) & 224); + (*m).propellers[2].status |= (PropellerStatus)(((unsigned)(s[52])>> 3) & 3); + (*m).propellers[2].direction |= (RotatingDirection)(((unsigned)(s[52])>> 5) & 3); + (*m).propellers[3].id |= (uint8_t)(((unsigned)(s[52])>> 7) & 1); + (*m).propellers[3].id |= (uint8_t)(((unsigned)(s[53])<< 1) & 254); + (*m).propellers[3].status |= (PropellerStatus)(((unsigned)(s[53])>> 7) & 1); + (*m).propellers[3].status |= (PropellerStatus)(((unsigned)(s[54])<< 1) & 2); + (*m).propellers[3].direction |= (RotatingDirection)(((unsigned)(s[54])>> 1) & 3); + (*m).power.battery |= (uint8_t)(((unsigned)(s[54])>> 3) & 31); + (*m).power.battery |= (uint8_t)(((unsigned)(s[55])<< 5) & 224); + (*m).power.status |= (PowerStatus)(((unsigned)(s[55])>> 3) & 3); + (*m).power.is_charging |= (bool)(((unsigned)(s[55])>> 5) & 1); + (*m).network.signal |= (uint8_t)(((unsigned)(s[55])>> 6) & 3); + (*m).network.signal |= (uint8_t)(((unsigned)(s[56])<< 2) & 12); + (*m).network.heartbeat_at |= (Timestamp)(((unsigned)(s[56])>> 2) & 63); + (*m).network.heartbeat_at |= (Timestamp)(((unsigned)(s[57])<< 6) & 192); + (*m).network.heartbeat_at |= (Timestamp)((uint32_t)(((unsigned)(s[57])>> 2) & 63) << 8); + (*m).network.heartbeat_at |= (Timestamp)((uint32_t)(((unsigned)(s[58])<< 6) & 192) << 8); + (*m).network.heartbeat_at |= (Timestamp)((uint32_t)(((unsigned)(s[58])>> 2) & 63) << 16); + (*m).network.heartbeat_at |= (Timestamp)((uint32_t)(((unsigned)(s[59])<< 6) & 192) << 16); + (*m).network.heartbeat_at |= (Timestamp)((uint32_t)(((unsigned)(s[59])>> 2) & 63) << 24); + (*m).network.heartbeat_at |= (Timestamp)((uint32_t)(((unsigned)(s[60])<< 6) & 192) << 24); + (*m).landing_gear.status |= (LandingGearStatus)(((unsigned)(s[60])>> 2) & 3); + (*m).pressure_sensor.pressures[0] |= (int32_t)(((unsigned)(s[60])>> 4) & 15); + (*m).pressure_sensor.pressures[0] |= (int32_t)(((unsigned)(s[61])<< 4) & 240); + (*m).pressure_sensor.pressures[0] |= (int32_t)((uint32_t)(((unsigned)(s[61])>> 4) & 15) << 8); + (*m).pressure_sensor.pressures[0] |= (int32_t)((uint32_t)(((unsigned)(s[62])<< 4) & 240) << 8); + (*m).pressure_sensor.pressures[0] |= (int32_t)((uint32_t)(((unsigned)(s[62])>> 4) & 15) << 16); + (*m).pressure_sensor.pressures[0] |= (int32_t)((uint32_t)(((unsigned)(s[63])<< 4) & 240) << 16); + if (((*m).pressure_sensor.pressures[0] >> 23) & 1) (*m).pressure_sensor.pressures[0] |= -16777216; + (*m).pressure_sensor.pressures[1] |= (int32_t)(((unsigned)(s[63])>> 4) & 15); + (*m).pressure_sensor.pressures[1] |= (int32_t)(((unsigned)(s[64])<< 4) & 240); + (*m).pressure_sensor.pressures[1] |= (int32_t)((uint32_t)(((unsigned)(s[64])>> 4) & 15) << 8); + (*m).pressure_sensor.pressures[1] |= (int32_t)((uint32_t)(((unsigned)(s[65])<< 4) & 240) << 8); + (*m).pressure_sensor.pressures[1] |= (int32_t)((uint32_t)(((unsigned)(s[65])>> 4) & 15) << 16); + (*m).pressure_sensor.pressures[1] |= (int32_t)((uint32_t)(((unsigned)(s[66])<< 4) & 240) << 16); + if (((*m).pressure_sensor.pressures[1] >> 23) & 1) (*m).pressure_sensor.pressures[1] |= -16777216; +#endif return 0; } \ No newline at end of file diff --git a/example/CPP/Makefile b/example/CPP/Makefile index bc382a02..532f0600 100644 --- a/example/CPP/Makefile +++ b/example/CPP/Makefile @@ -22,6 +22,6 @@ run: build ./example clean: - rm example_bp.h example_bp.c example *.o || true + rm -f example_bp.h example_bp.c example *.o $(BITPROTO_LIB_PATH)/*.o .PHONY: build run clean diff --git a/example/Makefile b/example/Makefile index 5bd3d84e..8d19b238 100644 --- a/example/Makefile +++ b/example/Makefile @@ -25,3 +25,13 @@ run: build cd Go-optimization-mode && ./example cd CPP && ./example cd CPP-optimization-mode && ./example + +clean: + cd C && $(MAKE) clean + cd C-optimization-mode && $(MAKE) clean + cd CPP && $(MAKE) clean + cd CPP-optimization-mode && $(MAKE) clean + rm -f Go/example Go-optimization-mode/example + rm -f ../lib/c/*.o + +.PHONY: bp build run clean diff --git a/lib/c/bitproto.c b/lib/c/bitproto.c index 14de4452..b92d01f0 100644 --- a/lib/c/bitproto.c +++ b/lib/c/bitproto.c @@ -3,6 +3,16 @@ #include "bitproto.h" +// Big-endian host detection. bitproto's wire format is little-endian (the i-th +// byte on the wire holds the i-th least-significant bits of a field). On a +// big-endian host the in-memory byte order of an integer is reversed relative +// to the wire, so the byte-copy fast paths below need an endian-neutral +// fallback. Users may force this by predefining BP_BIG_ENDIAN. +#if !defined(BP_BIG_ENDIAN) && defined(__BYTE_ORDER__) && \ + (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#define BP_BIG_ENDIAN 1 +#endif + /////////////////// // Implementations /////////////////// @@ -10,11 +20,27 @@ // BpMin returns the smaller one int. static inline int BpMin(int a, int b) { return (a < b) ? a : b; } +#ifdef BP_BIG_ENDIAN +// BpBaseTypeStorageSize returns the number of bytes a base integer type +// occupies in C memory for a given nbits. Mirrors the compiler's +// get_nbits_of_integer rounding: 8->1, 16->2, 32->4, 64->8. +static inline int BpBaseTypeStorageSize(int nbits) { + if (nbits <= 8) return 1; + if (nbits <= 16) return 2; + if (nbits <= 32) return 4; + return 8; +} +#endif + // BpMinTriple returns the smaller one of given three integers. static inline int BpMinTriple(int a, int b, int c) { return (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c); } +#ifndef BP_BIG_ENDIAN +// These helpers only gate the little-endian array batch-copy fast path, which +// is disabled on big-endian hosts (see BpEndecodeArray). + // BpIsNbitsStandard returns true if given nbits is one of 8/16/32/64. static inline bool BpIsNbitsStandard(int nbits) { return nbits == 8 || nbits == 16 || nbits == 32 || nbits == 64; @@ -26,6 +52,7 @@ static inline bool BpIsBaseIntegerType(int flag) { return flag == BP_TYPE_BYTE || flag == BP_TYPE_UINT || flag == BP_TYPE_ENUM || flag == BP_TYPE_INT; } +#endif // BpEndecodeMessage process given message at data with provided message // descriptor. It iterates all message fields to process. @@ -137,8 +164,18 @@ void BpEndecodeArray(struct BpArrayDescriptor *descriptor, unsigned char *data_ptr = (unsigned char *)data; - if (BpIsNbitsStandard(element_nbits) && - (BpIsBaseIntegerType(flag) || BpIsBaseIntegerType(to_flag))) { + if ( +#ifndef BP_BIG_ENDIAN + BpIsNbitsStandard(element_nbits) && + (BpIsBaseIntegerType(flag) || BpIsBaseIntegerType(to_flag)) +#else + // On big-endian the contiguous-memory batch copy below is invalid: + // each element's bytes must be reversed individually, so fall through + // to the per-element loop (each element is staged endian-neutrally in + // BpEndecodeBaseType). + 0 +#endif + ) { // Performance improvement for C arrays of integers (byte/uint/int): // Since arrays in C are contiguous in memory layout, so we call // BpCopyBufferBits only once instead of calling it one by one @@ -223,6 +260,11 @@ void BpCopyBufferBits(int n, unsigned char *dst, unsigned char *src, int di, // Number of bits to process during batch copy. int bits = n + si; +#ifndef BP_BIG_ENDIAN + // These fast paths load multiple bytes through a native-endian + // integer cast, which only matches the little-endian wire order on + // a little-endian host. On big-endian we skip them and fall back to + // the endian-neutral single-byte path below. if (bits >= 32) { // Copy as an uint32 integer. // This way, performance faster x2 than bits copy approach. @@ -232,7 +274,9 @@ void BpCopyBufferBits(int n, unsigned char *dst, unsigned char *src, int di, // Copy as an uint16 integer. ((uint16_t *)dst)[0] = ((uint16_t *)(src))[0] >> si; c = 16 - si; - } else if (bits >= 8) { + } else +#endif + if (bits >= 8) { // Copy as an unsigned char. dst[0] = (src[0] >> si) & 0xff; c = 8 - si; @@ -283,14 +327,35 @@ void BpCopyBufferBits(int n, unsigned char *dst, unsigned char *src, int di, } // BpEndecodeBaseType process given base type at given data. -// This function guarantees to work geven a nbits > 64 is passed in. +// This function guarantees to work geven a nbits > 64 is passed in (the +// batch-array path used on little-endian only; on big-endian arrays are processed +// element by element so nbits here is always a single base type's <= 64 bits). void BpEndecodeBaseType(int nbits, struct BpProcessorContext *ctx, void *data) { +#ifdef BP_BIG_ENDIAN + // Stage the field through a little-endian byte view so the wire stays + // little-endian regardless of host byte order. A single base integer is at + // most 64 bits, so 8 bytes of staging is always enough. + int size = BpBaseTypeStorageSize(nbits); + unsigned char le[8] = {0}; + unsigned char *p = (unsigned char *)data; + if (ctx->is_encode) { + // Native big-endian bytes -> little-endian staging buffer. + for (int k = 0; k < size; k++) le[k] = p[size - 1 - k]; + BpCopyBufferBits(nbits, ctx->s, le, ctx->i, 0); + } else { + BpCopyBufferBits(nbits, le, ctx->s, 0, ctx->i); + // Little-endian staging buffer -> native big-endian bytes. + for (int k = 0; k < size; k++) p[size - 1 - k] = le[k]; + } + ctx->i += nbits; +#else if (ctx->is_encode) { BpCopyBufferBits(nbits, ctx->s, (unsigned char *)data, ctx->i, 0); } else { BpCopyBufferBits(nbits, (unsigned char *)data, ctx->s, 0, ctx->i); } ctx->i += nbits; +#endif } // BpHandleIntSignAfterEndecode processes signed integer at given data after diff --git a/lib/py/bitprotolib/bp.py b/lib/py/bitprotolib/bp.py index 14d7efa9..9ded05e8 100644 --- a/lib/py/bitprotolib/bp.py +++ b/lib/py/bitprotolib/bp.py @@ -192,7 +192,7 @@ class MessageBase(Accessor): def to_dict(self) -> Dict[str, Any]: """Converts this message to a dict.""" return asdict( - self, dict_factory=getattr(self, "dict_factory", dict) # type:ignore + self, dict_factory=getattr(self, "dict_factory", dict) # type: ignore ) def to_json( diff --git a/tests/test_encoding/encoding-cases/arrays/c/main.c b/tests/test_encoding/encoding-cases/arrays/c/main.c index d438ee2b..a770bb7a 100644 --- a/tests/test_encoding/encoding-cases/arrays/c/main.c +++ b/tests/test_encoding/encoding-cases/arrays/c/main.c @@ -43,7 +43,7 @@ int main(void) { assert(m1.g.number == m.g.number); assert(m1.g.ok == m.g.ok); for (int i = 0; i < 7; i++) - for (int j = 0; j < 7; j++) assert(m1.t[i][j] = m.t[i][j]); + for (int j = 0; j < 7; j++) assert(m1.t[i][j] == m.t[i][j]); assert(m1.x[0] == m.x[0]); assert(m1.x[1] == m.x[1]); assert(m1.x[2] == m.x[2]); diff --git a/tests/test_encoding/test_endian.py b/tests/test_encoding/test_endian.py new file mode 100644 index 00000000..d0d12e04 --- /dev/null +++ b/tests/test_encoding/test_endian.py @@ -0,0 +1,320 @@ +"""Tests for big-endian host support. + +These run on a little-endian CI host without emulation, exploiting two facts: + +1. The optimization-mode big-endian code path is *endian-neutral* (it extracts + field bytes by value, with explicit shifts), so compiling the generated code + with ``-DBP_BIG_ENDIAN`` and running it on a little-endian host still produces + the correct, byte-identical wire output. ``test_opt_mode_big_endian_branch`` + compiles each optimization-mode case both ways and asserts the wire bytes + match each other and the (endian-independent) Python reference. + +2. The runtime/non-optimization big-endian path *is* host-dependent (it reverses + a field's bytes assuming the host is big-endian), so it cannot be exercised + by compiling native structs on a little-endian host. Instead + ``test_runtime_big_endian_layout`` feeds it memory laid out *by hand* as + big-endian and checks the wire bytes; that is host-independent because the + raw bytes are supplied directly. + +A genuine big-endian end-to-end run (e.g. qemu-user on s390x) is the gold +standard for CI but is intentionally out of scope here. +""" + +import os +import shutil +import subprocess + +import pytest + +HERE = os.path.dirname(__file__) +CASES_DIR = os.path.join(HERE, "encoding-cases") +LIB_C_DIR = os.path.abspath(os.path.join(HERE, "..", "..", "lib", "c")) + +# Optimization-mode cases that emit encoded wire bytes to stdout (the "consts" +# case supports optimization mode but prints nothing, so it is excluded here). +OPT_MODE_CASES = [ + "nested", + "arrays", + "scatter", + "enums", + "signed", +] + +_HAVE_TOOLS = all(shutil.which(t) for t in ("make", "bitproto", "gcc")) +requires_tools = pytest.mark.skipif( + not _HAVE_TOOLS, reason="needs make, bitproto and gcc on PATH" +) + + +def _make(case: str, target: str, **env_extra: str) -> bytes: + env = dict(os.environ, **env_extra) + return subprocess.check_output( + f"make -s --no-print-directory {target}", + shell=True, + cwd=os.path.join(CASES_DIR, case), + env=env, + ) + + +def _clean(case: str) -> None: + subprocess.check_call( + "make -s --no-print-directory clean", + shell=True, + cwd=os.path.join(CASES_DIR, case), + ) + + +@requires_tools +@pytest.mark.parametrize("case", OPT_MODE_CASES) +def test_opt_mode_big_endian_branch(case: str) -> None: + """The optimization-mode #else (big-endian) branch must produce the same + wire bytes as the little-endian branch (which the rest of the suite already + validates against the Python/Go reference encoders). + """ + try: + little = _make(case, "run-c", OPTIMIZATION_MODE_ARGS="-O").strip() + _clean(case) # force a fresh re-generate / re-compile + big = _make( + case, + "run-c", + OPTIMIZATION_MODE_ARGS="-O", + CC_OPTIMIZATION_ARG="-DBP_BIG_ENDIAN", + ).strip() + # Best effort: also compare against the endian-independent Python + # encoder when a "python" interpreter is available to the Makefile. + reference = None + if shutil.which("python"): + reference = _make(case, "run-py").strip() + finally: + _clean(case) + + # The big-endian branch compiled and round-tripped (run-c asserts the + # round-trip internally) ... + assert big, "big-endian build produced no output" + # ... and emits wire bytes identical to the little-endian branch. + assert little == big, f"{case}: LE and BE wire bytes differ" + if reference is not None: + assert reference == big, f"{case}: BE wire bytes differ from python" + + +# A self-contained, host-independent check of the runtime big-endian path: +# feed BpEndecodeBaseType memory laid out as big-endian and assert the wire is +# the expected little-endian byte sequence. +_RUNTIME_BE_TEST_C = r""" +#include +#include +#include "bitproto.h" + +static void enc(int nbits, unsigned char *data, unsigned char *want, int n) { + unsigned char s[16] = {0}; + struct BpProcessorContext ctx = BpProcessorContext(true, s); + BpEndecodeBaseType(nbits, &ctx, data); + assert(memcmp(s, want, n) == 0); +} + +static void dec(int nbits, unsigned char *wire, unsigned char *want, int n) { + unsigned char data[16] = {0}; + struct BpProcessorContext ctx = BpProcessorContext(false, wire); + BpEndecodeBaseType(nbits, &ctx, data); + assert(memcmp(data, want, n) == 0); +} + +int main(void) { + /* uint32 0x12345678: BE memory {12 34 56 78} <-> LE wire {78 56 34 12} */ + enc(32, (unsigned char[]){0x12,0x34,0x56,0x78}, + (unsigned char[]){0x78,0x56,0x34,0x12}, 4); + dec(32, (unsigned char[]){0x78,0x56,0x34,0x12}, + (unsigned char[]){0x12,0x34,0x56,0x78}, 4); + /* uint12 0x0ABC in a uint16: BE memory {0A BC} <-> LE wire {BC 0A} */ + enc(12, (unsigned char[]){0x0A,0xBC}, (unsigned char[]){0xBC,0x0A}, 2); + dec(12, (unsigned char[]){0xBC,0x0A}, (unsigned char[]){0x0A,0xBC}, 2); + /* uint20 0xABCDE in a uint32 (storage 4 != ceil(20/8)=3) */ + enc(20, (unsigned char[]){0x00,0x0A,0xBC,0xDE}, + (unsigned char[]){0xDE,0xBC,0x0A}, 3); + dec(20, (unsigned char[]){0xDE,0xBC,0x0A}, + (unsigned char[]){0x00,0x0A,0xBC,0xDE}, 4); + return 0; +} +""" + + +# Self-contained test of BpEndecodeArray on big-endian: feeds memory laid out +# as big-endian through the array processor and verifies each element's bytes +# are reversed independently (no batch-copy on big-endian). +_RUNTIME_BE_ARRAY_TEST_C = r""" +#include +#include +#include "bitproto.h" + +int main(void) { + unsigned char s[16] = {0}; + struct BpProcessorContext ctx; + + /* Two uint32 elements in big-endian memory. + * data[0..3] = 0x12 0x34 0x56 0x78 (BE uint32 = 0x12345678) + * data[4..7] = 0x9A 0xBC 0xDE 0xF0 (BE uint32 = 0x9ABCDEF0) + */ + unsigned char data[] = {0x12,0x34,0x56,0x78, 0x9A,0xBC,0xDE,0xF0}; + /* Expected wire: each element reversed independently. + * want[0..3] = 0x78 0x56 0x34 0x12 + * want[4..7] = 0xF0 0xDE 0xBC 0x9A + */ + unsigned char want[] = {0x78,0x56,0x34,0x12, 0xF0,0xDE,0xBC,0x9A}; + + struct BpType element_type = {BP_TYPE_UINT, 32, 4, NULL, NULL, 0}; + struct BpArrayDescriptor desc = BpArrayDescriptor(false, 2, element_type); + + /* Encode: BE memory -> LE wire */ + memset(s, 0, 16); + ctx = BpProcessorContext(true, s); + BpEndecodeArray(&desc, &ctx, data); + assert(ctx.i == 64); + assert(memcmp(s, want, 8) == 0); + + /* Decode: LE wire -> BE memory */ + unsigned char decoded[8] = {0}; + ctx = BpProcessorContext(false, want); + BpEndecodeArray(&desc, &ctx, decoded); + assert(ctx.i == 64); + assert(memcmp(decoded, data, 8) == 0); + + return 0; +} +""" + + +# Self-contained test for edge cases of BpEndecodeBaseType on big-endian: +# different bit widths (uint8, uint64) and non-zero ctx->i offset (simulating +# consecutive fields, which exercises BpCopyBufferBits with shifted positions). +_RUNTIME_BE_EDGE_CASES_C = r""" +#include +#include +#include "bitproto.h" + +static void enc(int nbits, unsigned char *data, unsigned char *want, int n) { + unsigned char s[16] = {0}; + struct BpProcessorContext ctx = BpProcessorContext(true, s); + BpEndecodeBaseType(nbits, &ctx, data); + assert(memcmp(s, want, n) == 0); +} + +static void dec(int nbits, unsigned char *wire, unsigned char *want, int n) { + unsigned char data[16] = {0}; + struct BpProcessorContext ctx = BpProcessorContext(false, wire); + BpEndecodeBaseType(nbits, &ctx, data); + assert(memcmp(data, want, n) == 0); +} + +int main(void) { + /* uint8: single byte, trivial — no reversal needed */ + enc(8, (unsigned char[]){0xAB}, (unsigned char[]){0xAB}, 1); + dec(8, (unsigned char[]){0xAB}, (unsigned char[]){0xAB}, 1); + + /* uint64: full 8-byte reversal */ + enc(64, (unsigned char[]){0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, + (unsigned char[]){0xEF,0xCD,0xAB,0x89,0x67,0x45,0x23,0x01}, 8); + dec(64, (unsigned char[]){0xEF,0xCD,0xAB,0x89,0x67,0x45,0x23,0x01}, + (unsigned char[]){0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, 8); + + /* Two consecutive uint16 fields: tests BpCopyBufferBits with non-zero + * ctx->i offset (simulating multi-field messages). + * Field 1 at byte 0: BE {0x12,0x34} <-> LE wire {0x34,0x12} + * Field 2 at byte 2: BE {0xAB,0xCD} <-> LE wire {0xCD,0xAB} + */ + { + unsigned char s[4] = {0}; + struct BpProcessorContext ctx = BpProcessorContext(true, s); + BpEndecodeBaseType(16, &ctx, (unsigned char[]){0x12,0x34}); + assert(ctx.i == 16); + BpEndecodeBaseType(16, &ctx, (unsigned char[]){0xAB,0xCD}); + assert(ctx.i == 32); + unsigned char want[] = {0x34,0x12,0xCD,0xAB}; + assert(memcmp(s, want, 4) == 0); + } + { + unsigned char data[4] = {0}; + struct BpProcessorContext ctx = BpProcessorContext(false, + ((unsigned char[]){0x34,0x12,0xCD,0xAB})); + BpEndecodeBaseType(16, &ctx, data); + assert(ctx.i == 16); + BpEndecodeBaseType(16, &ctx, data + 2); + assert(ctx.i == 32); + unsigned char want[] = {0x12,0x34,0xAB,0xCD}; + assert(memcmp(data, want, 4) == 0); + } + + return 0; +} +""" + + +@requires_tools +def test_runtime_big_endian_layout(tmp_path) -> None: + """The non-optimization runtime big-endian path maps big-endian field memory + to little-endian wire bytes (and back), verified host-independently. + """ + src = tmp_path / "be_runtime_test.c" + src.write_text(_RUNTIME_BE_TEST_C) + binpath = tmp_path / "be_runtime_test" + subprocess.check_call( + [ + "gcc", + "-DBP_BIG_ENDIAN", + "-I", + LIB_C_DIR, + str(src), + os.path.join(LIB_C_DIR, "bitproto.c"), + "-o", + str(binpath), + ] + ) + subprocess.check_call([str(binpath)]) + subprocess.check_call([str(binpath)]) + + +@requires_tools +def test_runtime_big_endian_array(tmp_path) -> None: + """BpEndecodeArray on big-endian processes elements independently (no + batch copy). Two uint32 elements in big-endian memory are encoded to + little-endian wire bytes and decoded back correctly. + """ + src = tmp_path / "be_array_test.c" + src.write_text(_RUNTIME_BE_ARRAY_TEST_C) + binpath = tmp_path / "be_array_test" + subprocess.check_call( + [ + "gcc", + "-DBP_BIG_ENDIAN", + "-I", + LIB_C_DIR, + str(src), + os.path.join(LIB_C_DIR, "bitproto.c"), + "-o", + str(binpath), + ] + ) + subprocess.check_call([str(binpath)]) + + +@requires_tools +def test_runtime_big_endian_edge_cases(tmp_path) -> None: + """Big-endian BpEndecodeBaseType edge cases: uint8 (trivial), uint64 + (full 8-byte reversal), and two consecutive uint16 fields (exercising + BpCopyBufferBits with non-zero ctx->i offset). + """ + src = tmp_path / "be_edge_cases.c" + src.write_text(_RUNTIME_BE_EDGE_CASES_C) + binpath = tmp_path / "be_edge_cases" + subprocess.check_call( + [ + "gcc", + "-DBP_BIG_ENDIAN", + "-I", + LIB_C_DIR, + str(src), + os.path.join(LIB_C_DIR, "bitproto.c"), + "-o", + str(binpath), + ] + ) + subprocess.check_call([str(binpath)])