prost-0.11.9/.cargo_vcs_info.json0000644000000001360000000000100122730ustar { "git": { "sha1": "cab3c9459630bac040aecefa8d3afde7e598e50f" }, "path_in_vcs": "" }prost-0.11.9/.github/workflows/cifuzz.yml000064400000000000000000000012711046102023000165160ustar 00000000000000name: CIFuzz on: [pull_request] jobs: Fuzzing: runs-on: ubuntu-latest steps: - name: Build Fuzzers id: build uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master with: oss-fuzz-project-name: 'prost' dry-run: false language: rust - name: Run Fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master with: oss-fuzz-project-name: 'prost' fuzz-seconds: 300 dry-run: false language: rust - name: Upload Crash uses: actions/upload-artifact@v3 if: failure() && steps.build.outcome == 'success' with: name: artifacts path: ./out/artifacts prost-0.11.9/.github/workflows/continuous-integration-workflow.yaml000064400000000000000000000112471046102023000237500ustar 00000000000000name: continuous integration on: pull_request jobs: rustfmt: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v3 with: submodules: recursive - name: install toolchain uses: dtolnay/rust-toolchain@stable with: components: rustfmt - name: rustfmt run: cargo fmt --all --check # Disabled because downstream crates don't check this as well # minversions: # runs-on: ubuntu-latest # steps: # - uses: actions-rs/toolchain@v1 # with: # profile: minimal # toolchain: nightly # - uses: actions-rs/toolchain@v1 # with: # profile: minimal # toolchain: stable # - uses: actions/checkout@v3 # with: # submodules: recursive # - name: Install Protoc # uses: arduino/setup-protoc@v1 # with: # repo-token: ${{ secrets.GITHUB_TOKEN }} # - name: install ninja # uses: seanmiddleditch/gha-setup-ninja@v3 # - name: cargo update -Zminimal-versions # uses: actions-rs/cargo@v1 # with: # command: update # toolchain: nightly # args: -Zminimal-versions # - name: cargo check # uses: actions-rs/cargo@v1 # with: # command: check # args: --workspace --all-targets # toolchain: stable machete: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v3 with: submodules: recursive - name: install toolchain uses: dtolnay/rust-toolchain@stable - name: Install cargo-machete uses: baptiste0928/cargo-install@v1 with: crate: cargo-machete - name: Check unused dependencies run: cargo machete test: runs-on: ${{ matrix.os }} strategy: matrix: toolchain: - stable - "1.60" os: - ubuntu-latest - macos-latest - windows-latest steps: - name: checkout uses: actions/checkout@v3 with: submodules: recursive - name: install toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.toolchain }} - name: Install Protoc uses: arduino/setup-protoc@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: install ninja uses: seanmiddleditch/gha-setup-ninja@v3 - uses: Swatinem/rust-cache@v2 - name: test run: cargo test --workspace --all-targets - name: test no-default-features run: cargo test --no-default-features kani: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v3 with: submodules: recursive - name: Verify with Kani uses: model-checking/kani-github-action@v0.19 with: enable-propproof: true args: | --tests -p prost-types --default-unwind 3 \ --harness "tests::check_timestamp_roundtrip_via_system_time" # --default-unwind N roughly corresponds to how much effort # Kani will spend trying to prove correctness of the # program. Higher the number, more programs can be proven # correct. However, Kani will require more time and memory. If # Kani fails with "Failed Checks: unwinding assertion," this # number may need to be raised for Kani to succeed. no-std: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v3 with: submodules: recursive - name: install toolchain uses: dtolnay/rust-toolchain@nightly - name: Install Protoc uses: arduino/setup-protoc@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - uses: Swatinem/rust-cache@v2 - name: install cargo-no-std-check uses: baptiste0928/cargo-install@v1 with: crate: cargo-no-std-check - name: prost cargo-no-std-check run: cargo no-std-check --manifest-path Cargo.toml --no-default-features - name: prost-types cargo-no-std-check run: cargo no-std-check --manifest-path prost-types/Cargo.toml --no-default-features # prost-build depends on prost with --no-default-features, but when # prost-build is built through the workspace, prost typically has default # features enabled due to vagaries in Cargo workspace feature resolution. # This additional check ensures that prost-build does not rely on any of # prost's default features to compile. - name: prost-build check run: cargo check --manifest-path prost-build/Cargo.toml prost-0.11.9/.gitignore000064400000000000000000000000341046102023000130500ustar 00000000000000target Cargo.lock .DS_Storeprost-0.11.9/.gitmodules000064400000000000000000000002071046102023000132370ustar 00000000000000[submodule "prost-build/third-party/protobuf"] path = prost-build/third-party/protobuf url = git@github.com:protocolbuffers/protobuf prost-0.11.9/Cargo.toml0000644000000027660000000000100103040ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.60" name = "prost" version = "0.11.9" authors = [ "Dan Burkert ", "Lucio Franco ", ] description = "A Protocol Buffers implementation for the Rust Language." documentation = "https://docs.rs/prost" readme = "README.md" keywords = [ "protobuf", "serialization", ] categories = ["encoding"] license = "Apache-2.0" repository = "https://github.com/tokio-rs/prost" [profile.bench] debug = true [lib] bench = false [[bench]] name = "varint" harness = false [dependencies.bytes] version = "1" default-features = false [dependencies.prost-derive] version = "0.11.9" optional = true [dev-dependencies.criterion] version = "0.3" [dev-dependencies.env_logger] version = "0.8" default-features = false [dev-dependencies.log] version = "0.4" [dev-dependencies.proptest] version = "1" [dev-dependencies.rand] version = "0.8" [features] default = [ "prost-derive", "std", ] no-recursion-limit = [] std = [] prost-0.11.9/Cargo.toml.orig000064400000000000000000000026641046102023000137620ustar 00000000000000[package] name = "prost" version = "0.11.9" authors = [ "Dan Burkert ", "Lucio Franco ", ] license = "Apache-2.0" repository = "https://github.com/tokio-rs/prost" documentation = "https://docs.rs/prost" readme = "README.md" description = "A Protocol Buffers implementation for the Rust Language." keywords = ["protobuf", "serialization"] categories = ["encoding"] edition = "2021" rust-version = "1.60" [workspace] members = [ "conformance", "prost-build", "prost-derive", "prost-types", "protobuf", "tests", "tests-2015", "tests-no-std", "tests/single-include", ] exclude = [ # The fuzz crate can't be compiled or tested without the 'cargo fuzz' command, # so exclude it from normal builds. "fuzz", # Same counts for the afl fuzz targets "afl", ] [lib] # https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options bench = false [features] default = ["prost-derive", "std"] no-recursion-limit = [] std = [] [dependencies] bytes = { version = "1", default-features = false } prost-derive = { version = "0.11.9", path = "prost-derive", optional = true } [dev-dependencies] criterion = "0.3" env_logger = { version = "0.8", default-features = false } log = "0.4" proptest = "1" rand = "0.8" [profile.bench] debug = true [[bench]] name = "varint" harness = false prost-0.11.9/FUZZING.md000064400000000000000000000007521046102023000125450ustar 00000000000000# Fuzzing Prost ships a few fuzz tests, using both libfuzzer and aflfuzz. ## afl To run the afl fuzz tests, first install cargo-afl: cargo install -f afl Then build a fuzz target and run afl on it: cd fuzz/afl// cargo afl build --bin fuzz-target cargo afl fuzz -i in -o out target/debug/fuzz-target To reproduce a crash: cd fuzz/afl// cargo build --bin reproduce cargo run --bin reproduce -- out/crashes/ ## libfuzzer TODO prost-0.11.9/KANI.md000064400000000000000000000052341046102023000121330ustar 00000000000000# Kani This document describes how to **locally** install and use Kani, along with its experimental PropProof feature. Because of instability in Kani internals, the GitHub action is the recommended option if you are running in CI. Kani is a software verification tool that complements testing by proving the absence of certain classes of bugs like unwrap exceptions, overflows, and assertion failures. See the [Kani book](https://model-checking.github.io/kani/) for a full list of capabilities and limitations. ## Installing Kani and PropProof - The install instructions for Kani can be [found here](https://model-checking.github.io/kani/install-guide.html). Once Kani is installed, you can run with `cargo kani` for projects or `kani` for individual Rust files. - **[UNSTABLE]** To use PropProof, first download the source code from the Kani repository. ```bash git clone https://github.com/model-checking/kani.git --branch features/proptest propproof cd propproof; git submodule update --init --recursive ``` Then, use `.cargo/config.toml` enable it in the local directory you want to run Kani in. This will override the `proptest` import in your repo. ```bash cd $YOUR_REPO_LOCAL_PATH mkdir '.cargo' echo "paths =[\"$PATH_TO_PROPPROOF\"]" > .cargo/config.toml ``` **Please Note**: - `features/proptest` branch under Kani is likely not the final location for this code. If this instruction stops working, please consult the Kani documentation and file an issue on [the Kani repo](https://github.com/model-checking/kani.git). - The cargo config file will force cargo to always use PropProof. To use `proptest`, delete the file. ## Running Kani After installing Kani and PropProof, `cargo kani --tests` should automatically run `proptest!` harnesses inside your crate. Use `--harness` to run a specific harness, and `-p` for a specific sub-crate. If Kani returns with an error, you can use the concrete playback feature using `--enable-unstable --concrete-playback print` and paste in the code to your repository. Running this harness with `cargo test` will replay the input found by Kani that produced this crash. Please note that this feature is unstable and using `--concrete-playback inplace` to automatically inject a replay harness is not supported when using PropProof. ## Debugging CI Failure ```yaml - name: Verify with Kani uses: model-checking/kani-github-action@v0.xx with: enable-propproof: true args: | $KANI_ARGUMENTS ``` The above GitHub CI workflow is equivalent to `cargo kani $KANI_ARGUMENTS` with PropProof installed. To replicate issues locally, run `cargo kani` with the same arguments. prost-0.11.9/LICENSE000064400000000000000000000251371046102023000121000ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. prost-0.11.9/README.md000064400000000000000000000350071046102023000123470ustar 00000000000000![continuous integration](https://github.com/tokio-rs/prost/workflows/continuous%20integration/badge.svg) [![Documentation](https://docs.rs/prost/badge.svg)](https://docs.rs/prost/) [![Crate](https://img.shields.io/crates/v/prost.svg)](https://crates.io/crates/prost) [![Dependency Status](https://deps.rs/repo/github/tokio-rs/prost/status.svg)](https://deps.rs/repo/github/tokio-rs/prost) [![Discord](https://img.shields.io/discord/500028886025895936)](https://discord.gg/tokio) # *PROST!* `prost` is a [Protocol Buffers](https://developers.google.com/protocol-buffers/) implementation for the [Rust Language](https://www.rust-lang.org/). `prost` generates simple, idiomatic Rust code from `proto2` and `proto3` files. Compared to other Protocol Buffers implementations, `prost` * Generates simple, idiomatic, and readable Rust types by taking advantage of Rust `derive` attributes. * Retains comments from `.proto` files in generated Rust code. * Allows existing Rust types (not generated from a `.proto`) to be serialized and deserialized by adding attributes. * Uses the [`bytes::{Buf, BufMut}`](https://github.com/carllerche/bytes) abstractions for serialization instead of `std::io::{Read, Write}`. * Respects the Protobuf `package` specifier when organizing generated code into Rust modules. * Preserves unknown enum values during deserialization. * Does not include support for runtime reflection or message descriptors. ## Using `prost` in a Cargo Project First, add `prost` and its public dependencies to your `Cargo.toml`: ```ignore [dependencies] prost = "0.11" # Only necessary if using Protobuf well-known types: prost-types = "0.11" ``` The recommended way to add `.proto` compilation to a Cargo project is to use the `prost-build` library. See the [`prost-build` documentation](prost-build) for more details and examples. See the [snazzy repository](https://github.com/danburkert/snazzy) for a simple start-to-finish example. ### MSRV `prost` follows the `tokio-rs` projects MSRV model and supports 1.60. For more information on the tokio msrv policy you can check it out [here][tokio msrv] [tokio msrv]: https://github.com/tokio-rs/tokio/#supported-rust-versions ## Generated Code `prost` generates Rust code from source `.proto` files using the `proto2` or `proto3` syntax. `prost`'s goal is to make the generated code as simple as possible. ### `protoc` With `prost-build` v0.11 release, `protoc` will be required to invoke `compile_protos` (unless `skip_protoc` is enabled). Prost will no longer provide bundled a `protoc` or attempt to compile `protoc` for users. For install instructions for `protoc` please check out the [protobuf install] instructions. [protobuf install]: https://github.com/protocolbuffers/protobuf#protocol-compiler-installation ### Packages Prost can now generate code for `.proto` files that don't have a package spec. `prost` will translate the Protobuf package into a Rust module. For example, given the `package` specifier: [package]: https://developers.google.com/protocol-buffers/docs/proto#packages ```protobuf,ignore package foo.bar; ``` All Rust types generated from the file will be in the `foo::bar` module. ### Messages Given a simple message declaration: ```protobuf,ignore // Sample message. message Foo { } ``` `prost` will generate the following Rust struct: ```rust,ignore /// Sample message. #[derive(Clone, Debug, PartialEq, Message)] pub struct Foo { } ``` ### Fields Fields in Protobuf messages are translated into Rust as public struct fields of the corresponding type. #### Scalar Values Scalar value types are converted as follows: | Protobuf Type | Rust Type | | --- | --- | | `double` | `f64` | | `float` | `f32` | | `int32` | `i32` | | `int64` | `i64` | | `uint32` | `u32` | | `uint64` | `u64` | | `sint32` | `i32` | | `sint64` | `i64` | | `fixed32` | `u32` | | `fixed64` | `u64` | | `sfixed32` | `i32` | | `sfixed64` | `i64` | | `bool` | `bool` | | `string` | `String` | | `bytes` | `Vec` | #### Enumerations All `.proto` enumeration types convert to the Rust `i32` type. Additionally, each enumeration type gets a corresponding Rust `enum` type. For example, this `proto` enum: ```protobuf,ignore enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } ``` gets this corresponding Rust enum [^1]: ```rust,ignore pub enum PhoneType { Mobile = 0, Home = 1, Work = 2, } ``` [^1]: Annotations have been elided for clarity. See below for a full example. You can convert a `PhoneType` value to an `i32` by doing: ```rust,ignore PhoneType::Mobile as i32 ``` The `#[derive(::prost::Enumeration)]` annotation added to the generated `PhoneType` adds these associated functions to the type: ```rust,ignore impl PhoneType { pub fn is_valid(value: i32) -> bool { ... } pub fn from_i32(value: i32) -> Option { ... } } ``` so you can convert an `i32` to its corresponding `PhoneType` value by doing, for example: ```rust,ignore let phone_type = 2i32; match PhoneType::from_i32(phone_type) { Some(PhoneType::Mobile) => ..., Some(PhoneType::Home) => ..., Some(PhoneType::Work) => ..., None => ..., } ``` Additionally, wherever a `proto` enum is used as a field in a `Message`, the message will have 'accessor' methods to get/set the value of the field as the Rust enum type. For instance, this proto `PhoneNumber` message that has a field named `type` of type `PhoneType`: ```protobuf,ignore message PhoneNumber { string number = 1; PhoneType type = 2; } ``` will become the following Rust type [^2] with methods `type` and `set_type`: ```rust,ignore pub struct PhoneNumber { pub number: String, pub r#type: i32, // the `r#` is needed because `type` is a Rust keyword } impl PhoneNumber { pub fn r#type(&self) -> PhoneType { ... } pub fn set_type(&mut self, value: PhoneType) { ... } } ``` Note that the getter methods will return the Rust enum's default value if the field has an invalid `i32` value. The `enum` type isn't used directly as a field, because the Protobuf spec mandates that enumerations values are 'open', and decoding unrecognized enumeration values must be possible. [^2]: Annotations have been elided for clarity. See below for a full example. #### Field Modifiers Protobuf scalar value and enumeration message fields can have a modifier depending on the Protobuf version. Modifiers change the corresponding type of the Rust field: | `.proto` Version | Modifier | Rust Type | | --- | --- | --- | | `proto2` | `optional` | `Option` | | `proto2` | `required` | `T` | | `proto3` | default | `T` for scalar types, `Option` otherwise | | `proto3` | `optional` | `Option` | | `proto2`/`proto3` | `repeated` | `Vec` | Note that in `proto3` the default representation for all user-defined message types is `Option`, and for scalar types just `T` (during decoding, a missing value is populated by `T::default()`). If you need a witness of the presence of a scalar type `T`, use the `optional` modifier to enforce an `Option` representation in the generated Rust struct. #### Map Fields Map fields are converted to a Rust `HashMap` with key and value type converted from the Protobuf key and value types. #### Message Fields Message fields are converted to the corresponding struct type. The table of field modifiers above applies to message fields, except that `proto3` message fields without a modifier (the default) will be wrapped in an `Option`. Typically message fields are unboxed. `prost` will automatically box a message field if the field type and the parent type are recursively nested in order to avoid an infinite sized struct. #### Oneof Fields Oneof fields convert to a Rust enum. Protobuf `oneof`s types are not named, so `prost` uses the name of the `oneof` field for the resulting Rust enum, and defines the enum in a module under the struct. For example, a `proto3` message such as: ```protobuf,ignore message Foo { oneof widget { int32 quux = 1; string bar = 2; } } ``` generates the following Rust[^3]: ```rust,ignore pub struct Foo { pub widget: Option, } pub mod foo { pub enum Widget { Quux(i32), Bar(String), } } ``` `oneof` fields are always wrapped in an `Option`. [^3]: Annotations have been elided for clarity. See below for a full example. ### Services `prost-build` allows a custom code-generator to be used for processing `service` definitions. This can be used to output Rust traits according to an application's specific needs. ### Generated Code Example Example `.proto` file: ```protobuf,ignore syntax = "proto3"; package tutorial; message Person { string name = 1; int32 id = 2; // Unique ID number for this person. string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { string number = 1; PhoneType type = 2; } repeated PhoneNumber phones = 4; } // Our address book file is just one of these. message AddressBook { repeated Person people = 1; } ``` and the generated Rust code (`tutorial.rs`): ```rust,ignore #[derive(Clone, PartialEq, ::prost::Message)] pub struct Person { #[prost(string, tag="1")] pub name: ::prost::alloc::string::String, /// Unique ID number for this person. #[prost(int32, tag="2")] pub id: i32, #[prost(string, tag="3")] pub email: ::prost::alloc::string::String, #[prost(message, repeated, tag="4")] pub phones: ::prost::alloc::vec::Vec, } /// Nested message and enum types in `Person`. pub mod person { #[derive(Clone, PartialEq, ::prost::Message)] pub struct PhoneNumber { #[prost(string, tag="1")] pub number: ::prost::alloc::string::String, #[prost(enumeration="PhoneType", tag="2")] pub r#type: i32, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] pub enum PhoneType { Mobile = 0, Home = 1, Work = 2, } } /// Our address book file is just one of these. #[derive(Clone, PartialEq, ::prost::Message)] pub struct AddressBook { #[prost(message, repeated, tag="1")] pub people: ::prost::alloc::vec::Vec, } ``` ## Accessing the `protoc` `FileDescriptorSet` The `prost_build::Config::file_descriptor_set_path` option can be used to emit a file descriptor set during the build & code generation step. When used in conjunction with the `std::include_bytes` macro and the `prost_types::FileDescriptorSet` type, applications and libraries using Prost can implement introspection capabilities requiring details from the original `.proto` files. ## Using `prost` in a `no_std` Crate `prost` is compatible with `no_std` crates. To enable `no_std` support, disable the `std` features in `prost` and `prost-types`: ```ignore [dependencies] prost = { version = "0.6", default-features = false, features = ["prost-derive"] } # Only necessary if using Protobuf well-known types: prost-types = { version = "0.6", default-features = false } ``` Additionally, configure `prost-build` to output `BTreeMap`s instead of `HashMap`s for all Protobuf `map` fields in your `build.rs`: ```rust,ignore let mut config = prost_build::Config::new(); config.btree_map(&["."]); ``` When using edition 2015, it may be necessary to add an `extern crate core;` directive to the crate which includes `prost`-generated code. ## Serializing Existing Types `prost` uses a custom derive macro to handle encoding and decoding types, which means that if your existing Rust type is compatible with Protobuf types, you can serialize and deserialize it by adding the appropriate derive and field annotations. Currently the best documentation on adding annotations is to look at the generated code examples above. ### Tag Inference for Existing Types Prost automatically infers tags for the struct. Fields are tagged sequentially in the order they are specified, starting with `1`. You may skip tags which have been reserved, or where there are gaps between sequentially occurring tag values by specifying the tag number to skip to with the `tag` attribute on the first field after the gap. The following fields will be tagged sequentially starting from the next number. ```rust,ignore use prost; use prost::{Enumeration, Message}; #[derive(Clone, PartialEq, Message)] struct Person { #[prost(string, tag = "1")] pub id: String, // tag=1 // NOTE: Old "name" field has been removed // pub name: String, // tag=2 (Removed) #[prost(string, tag = "6")] pub given_name: String, // tag=6 #[prost(string)] pub family_name: String, // tag=7 #[prost(string)] pub formatted_name: String, // tag=8 #[prost(uint32, tag = "3")] pub age: u32, // tag=3 #[prost(uint32)] pub height: u32, // tag=4 #[prost(enumeration = "Gender")] pub gender: i32, // tag=5 // NOTE: Skip to less commonly occurring fields #[prost(string, tag = "16")] pub name_prefix: String, // tag=16 (eg. mr/mrs/ms) #[prost(string)] pub name_suffix: String, // tag=17 (eg. jr/esq) #[prost(string)] pub maiden_name: String, // tag=18 } #[derive(Clone, Copy, Debug, PartialEq, Eq, Enumeration)] pub enum Gender { Unknown = 0, Female = 1, Male = 2, } ``` ## Nix The prost project maintains flakes support for local development. Once you have nix and nix flakes setup you can just run `nix develop` to get a shell configured with the required dependencies to compile the whole project. ## FAQ 1. **Could `prost` be implemented as a serializer for [Serde](https://serde.rs/)?** Probably not, however I would like to hear from a Serde expert on the matter. There are two complications with trying to serialize Protobuf messages with Serde: - Protobuf fields require a numbered tag, and currently there appears to be no mechanism suitable for this in `serde`. - The mapping of Protobuf type to Rust type is not 1-to-1. As a result, trait-based approaches to dispatching don't work very well. Example: six different Protobuf field types correspond to a Rust `Vec`: `repeated int32`, `repeated sint32`, `repeated sfixed32`, and their packed counterparts. But it is possible to place `serde` derive tags onto the generated types, so the same structure can support both `prost` and `Serde`. 2. **I get errors when trying to run `cargo test` on MacOS** If the errors are about missing `autoreconf` or similar, you can probably fix them by running ```ignore brew install automake brew install libtool ``` ## License `prost` is distributed under the terms of the Apache License (Version 2.0). See [LICENSE](https://github.com/tokio-rs/prost/blob/master/LICENSE) for details. Copyright 2022 Dan Burkert & Tokio Contributors prost-0.11.9/benches/varint.rs000064400000000000000000000062771046102023000143570ustar 00000000000000use std::mem; use bytes::Buf; use criterion::{Criterion, Throughput}; use prost::encoding::{decode_varint, encode_varint, encoded_len_varint}; use rand::{rngs::StdRng, seq::SliceRandom, SeedableRng}; fn benchmark_varint(criterion: &mut Criterion, name: &str, mut values: Vec) { // Shuffle the values in a stable order. values.shuffle(&mut StdRng::seed_from_u64(0)); let name = format!("varint/{}", name); let encoded_len = values .iter() .cloned() .map(encoded_len_varint) .sum::() as u64; let decoded_len = (values.len() * mem::size_of::()) as u64; criterion .benchmark_group(&name) .bench_function("encode", { let encode_values = values.clone(); move |b| { let mut buf = Vec::::with_capacity(encode_values.len() * 10); b.iter(|| { buf.clear(); for &value in &encode_values { encode_varint(value, &mut buf); } criterion::black_box(&buf); }) } }) .throughput(Throughput::Bytes(encoded_len)); criterion .benchmark_group(&name) .bench_function("decode", { let decode_values = values.clone(); move |b| { let mut buf = Vec::with_capacity(decode_values.len() * 10); for &value in &decode_values { encode_varint(value, &mut buf); } b.iter(|| { let mut buf = &mut buf.as_slice(); while buf.has_remaining() { let result = decode_varint(&mut buf); debug_assert!(result.is_ok()); criterion::black_box(&result); } }) } }) .throughput(Throughput::Bytes(decoded_len)); criterion .benchmark_group(&name) .bench_function("encoded_len", move |b| { b.iter(|| { let mut sum = 0; for &value in &values { sum += encoded_len_varint(value); } criterion::black_box(sum); }) }) .throughput(Throughput::Bytes(decoded_len)); } fn main() { let mut criterion = Criterion::default().configure_from_args(); // Benchmark encoding and decoding 100 small (1 byte) varints. benchmark_varint(&mut criterion, "small", (0..100).collect()); // Benchmark encoding and decoding 100 medium (5 byte) varints. benchmark_varint(&mut criterion, "medium", (1 << 28..).take(100).collect()); // Benchmark encoding and decoding 100 large (10 byte) varints. benchmark_varint(&mut criterion, "large", (1 << 63..).take(100).collect()); // Benchmark encoding and decoding 100 varints of mixed width (average 5.5 bytes). benchmark_varint( &mut criterion, "mixed", (0..10) .flat_map(move |width| { let exponent = width * 7; (0..10).map(move |offset| offset + (1 << exponent)) }) .collect(), ); criterion.final_summary(); } prost-0.11.9/clippy.toml000064400000000000000000000000371046102023000132600ustar 00000000000000too-many-arguments-threshold=8 prost-0.11.9/flake.lock000064400000000000000000000020031046102023000130120ustar 00000000000000{ "nodes": { "flake-utils": { "locked": { "lastModified": 1667395993, "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", "owner": "numtide", "repo": "flake-utils", "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", "type": "github" }, "original": { "owner": "numtide", "repo": "flake-utils", "type": "github" } }, "nixpkgs": { "locked": { "lastModified": 1667969101, "narHash": "sha256-GL53T705HO7Q/KVfbb5STx8AxFs8YgaGY8pvAZC+O7U=", "owner": "NixOS", "repo": "nixpkgs", "rev": "bbf77421ac51a7c93f5f0f760da99e4dbce614fa", "type": "github" }, "original": { "owner": "NixOS", "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } }, "root": { "inputs": { "flake-utils": "flake-utils", "nixpkgs": "nixpkgs" } } }, "root": "root", "version": 7 } prost-0.11.9/flake.nix000064400000000000000000000010101046102023000126550ustar 00000000000000{ description = "Prost dependencies"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; }; in { devShells.default = pkgs.mkShell { packages = with pkgs; [ cargo rustc ]; buildInputs = with pkgs; [ pkg-config protobuf curl ]; }; }); } prost-0.11.9/prepare-release.sh000075500000000000000000000027711046102023000145050ustar 00000000000000#!/bin/bash # Script which automates modifying source version fields, and creating a release # commit and tag. The commit and tag are not automatically pushed, nor are the # crates published (see publish-release.sh). set -ex if [ "$#" -ne 1 ] then echo "Usage: $0 " exit 1 fi DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" VERSION="$1" MINOR="$( echo ${VERSION} | cut -d\. -f1-2 )" VERSION_MATCHER="([a-z0-9\\.-]+)" PROST_CRATE_MATCHER="(prost|prost-[a-z]+)" # Update the README.md. sed -i -E "s/${PROST_CRATE_MATCHER} = \"${VERSION_MATCHER}\"/\1 = \"${MINOR}\"/" "$DIR/README.md" # Update html_root_url attributes. sed -i -E "s~html_root_url = \"https://docs\.rs/${PROST_CRATE_MATCHER}/$VERSION_MATCHER\"~html_root_url = \"https://docs.rs/\1/${VERSION}\"~" \ "$DIR/src/lib.rs" \ "$DIR/prost-derive/src/lib.rs" \ "$DIR/prost-build/src/lib.rs" \ "$DIR/prost-types/src/lib.rs" # Update Cargo.toml version fields. sed -i -E "s/^version = \"${VERSION_MATCHER}\"$/version = \"${VERSION}\"/" \ "$DIR/Cargo.toml" \ "$DIR/prost-derive/Cargo.toml" \ "$DIR/prost-build/Cargo.toml" \ "$DIR/prost-types/Cargo.toml" # Update Cargo.toml dependency versions. sed -i -E "s/^${PROST_CRATE_MATCHER} = \{ version = \"${VERSION_MATCHER}\"/\1 = { version = \"${VERSION}\"/" \ "$DIR/Cargo.toml" \ "$DIR/prost-derive/Cargo.toml" \ "$DIR/prost-build/Cargo.toml" \ "$DIR/prost-types/Cargo.toml" git commit -a -m "release ${VERSION}" git tag -a "v${VERSION}" -m "release ${VERSION}" prost-0.11.9/publish-release.sh000075500000000000000000000007501046102023000145100ustar 00000000000000#!/bin/bash # Script which automates publishing a crates.io release of the prost crates. set -ex if [ "$#" -ne 0 ] then echo "Usage: $0" exit 1 fi DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" CRATES=( \ "prost-derive" \ "." \ "prost-types" \ "prost-build" \ ) for CRATE in "${CRATES[@]}"; do pushd "$DIR/$CRATE" echo "Publishing $CRATE" cargo publish echo "Sleeping 5 seconds...for the release to be visible" sleep 5 popd done prost-0.11.9/src/encoding.rs000064400000000000000000001536401046102023000140170ustar 00000000000000//! Utility functions and types for encoding and decoding Protobuf types. //! //! Meant to be used only from `Message` implementations. #![allow(clippy::implicit_hasher, clippy::ptr_arg)] use alloc::collections::BTreeMap; use alloc::format; use alloc::string::String; use alloc::vec::Vec; use core::cmp::min; use core::convert::TryFrom; use core::mem; use core::str; use core::u32; use core::usize; use ::bytes::{Buf, BufMut, Bytes}; use crate::DecodeError; use crate::Message; /// Encodes an integer value into LEB128 variable length format, and writes it to the buffer. /// The buffer must have enough remaining space (maximum 10 bytes). #[inline] pub fn encode_varint(mut value: u64, buf: &mut B) where B: BufMut, { loop { if value < 0x80 { buf.put_u8(value as u8); break; } else { buf.put_u8(((value & 0x7F) | 0x80) as u8); value >>= 7; } } } /// Decodes a LEB128-encoded variable length integer from the buffer. #[inline] pub fn decode_varint(buf: &mut B) -> Result where B: Buf, { let bytes = buf.chunk(); let len = bytes.len(); if len == 0 { return Err(DecodeError::new("invalid varint")); } let byte = bytes[0]; if byte < 0x80 { buf.advance(1); Ok(u64::from(byte)) } else if len > 10 || bytes[len - 1] < 0x80 { let (value, advance) = decode_varint_slice(bytes)?; buf.advance(advance); Ok(value) } else { decode_varint_slow(buf) } } /// Decodes a LEB128-encoded variable length integer from the slice, returning the value and the /// number of bytes read. /// /// Based loosely on [`ReadVarint64FromArray`][1] with a varint overflow check from /// [`ConsumeVarint`][2]. /// /// ## Safety /// /// The caller must ensure that `bytes` is non-empty and either `bytes.len() >= 10` or the last /// element in bytes is < `0x80`. /// /// [1]: https://github.com/google/protobuf/blob/3.3.x/src/google/protobuf/io/coded_stream.cc#L365-L406 /// [2]: https://github.com/protocolbuffers/protobuf-go/blob/v1.27.1/encoding/protowire/wire.go#L358 #[inline] fn decode_varint_slice(bytes: &[u8]) -> Result<(u64, usize), DecodeError> { // Fully unrolled varint decoding loop. Splitting into 32-bit pieces gives better performance. // Use assertions to ensure memory safety, but it should always be optimized after inline. assert!(!bytes.is_empty()); assert!(bytes.len() > 10 || bytes[bytes.len() - 1] < 0x80); let mut b: u8 = unsafe { *bytes.get_unchecked(0) }; let mut part0: u32 = u32::from(b); if b < 0x80 { return Ok((u64::from(part0), 1)); }; part0 -= 0x80; b = unsafe { *bytes.get_unchecked(1) }; part0 += u32::from(b) << 7; if b < 0x80 { return Ok((u64::from(part0), 2)); }; part0 -= 0x80 << 7; b = unsafe { *bytes.get_unchecked(2) }; part0 += u32::from(b) << 14; if b < 0x80 { return Ok((u64::from(part0), 3)); }; part0 -= 0x80 << 14; b = unsafe { *bytes.get_unchecked(3) }; part0 += u32::from(b) << 21; if b < 0x80 { return Ok((u64::from(part0), 4)); }; part0 -= 0x80 << 21; let value = u64::from(part0); b = unsafe { *bytes.get_unchecked(4) }; let mut part1: u32 = u32::from(b); if b < 0x80 { return Ok((value + (u64::from(part1) << 28), 5)); }; part1 -= 0x80; b = unsafe { *bytes.get_unchecked(5) }; part1 += u32::from(b) << 7; if b < 0x80 { return Ok((value + (u64::from(part1) << 28), 6)); }; part1 -= 0x80 << 7; b = unsafe { *bytes.get_unchecked(6) }; part1 += u32::from(b) << 14; if b < 0x80 { return Ok((value + (u64::from(part1) << 28), 7)); }; part1 -= 0x80 << 14; b = unsafe { *bytes.get_unchecked(7) }; part1 += u32::from(b) << 21; if b < 0x80 { return Ok((value + (u64::from(part1) << 28), 8)); }; part1 -= 0x80 << 21; let value = value + ((u64::from(part1)) << 28); b = unsafe { *bytes.get_unchecked(8) }; let mut part2: u32 = u32::from(b); if b < 0x80 { return Ok((value + (u64::from(part2) << 56), 9)); }; part2 -= 0x80; b = unsafe { *bytes.get_unchecked(9) }; part2 += u32::from(b) << 7; // Check for u64::MAX overflow. See [`ConsumeVarint`][1] for details. // [1]: https://github.com/protocolbuffers/protobuf-go/blob/v1.27.1/encoding/protowire/wire.go#L358 if b < 0x02 { return Ok((value + (u64::from(part2) << 56), 10)); }; // We have overrun the maximum size of a varint (10 bytes) or the final byte caused an overflow. // Assume the data is corrupt. Err(DecodeError::new("invalid varint")) } /// Decodes a LEB128-encoded variable length integer from the buffer, advancing the buffer as /// necessary. /// /// Contains a varint overflow check from [`ConsumeVarint`][1]. /// /// [1]: https://github.com/protocolbuffers/protobuf-go/blob/v1.27.1/encoding/protowire/wire.go#L358 #[inline(never)] #[cold] fn decode_varint_slow(buf: &mut B) -> Result where B: Buf, { let mut value = 0; for count in 0..min(10, buf.remaining()) { let byte = buf.get_u8(); value |= u64::from(byte & 0x7F) << (count * 7); if byte <= 0x7F { // Check for u64::MAX overflow. See [`ConsumeVarint`][1] for details. // [1]: https://github.com/protocolbuffers/protobuf-go/blob/v1.27.1/encoding/protowire/wire.go#L358 if count == 9 && byte >= 0x02 { return Err(DecodeError::new("invalid varint")); } else { return Ok(value); } } } Err(DecodeError::new("invalid varint")) } /// Additional information passed to every decode/merge function. /// /// The context should be passed by value and can be freely cloned. When passing /// to a function which is decoding a nested object, then use `enter_recursion`. #[derive(Clone, Debug)] #[cfg_attr(feature = "no-recursion-limit", derive(Default))] pub struct DecodeContext { /// How many times we can recurse in the current decode stack before we hit /// the recursion limit. /// /// The recursion limit is defined by `RECURSION_LIMIT` and cannot be /// customized. The recursion limit can be ignored by building the Prost /// crate with the `no-recursion-limit` feature. #[cfg(not(feature = "no-recursion-limit"))] recurse_count: u32, } #[cfg(not(feature = "no-recursion-limit"))] impl Default for DecodeContext { #[inline] fn default() -> DecodeContext { DecodeContext { recurse_count: crate::RECURSION_LIMIT, } } } impl DecodeContext { /// Call this function before recursively decoding. /// /// There is no `exit` function since this function creates a new `DecodeContext` /// to be used at the next level of recursion. Continue to use the old context // at the previous level of recursion. #[cfg(not(feature = "no-recursion-limit"))] #[inline] pub(crate) fn enter_recursion(&self) -> DecodeContext { DecodeContext { recurse_count: self.recurse_count - 1, } } #[cfg(feature = "no-recursion-limit")] #[inline] pub(crate) fn enter_recursion(&self) -> DecodeContext { DecodeContext {} } /// Checks whether the recursion limit has been reached in the stack of /// decodes described by the `DecodeContext` at `self.ctx`. /// /// Returns `Ok<()>` if it is ok to continue recursing. /// Returns `Err` if the recursion limit has been reached. #[cfg(not(feature = "no-recursion-limit"))] #[inline] pub(crate) fn limit_reached(&self) -> Result<(), DecodeError> { if self.recurse_count == 0 { Err(DecodeError::new("recursion limit reached")) } else { Ok(()) } } #[cfg(feature = "no-recursion-limit")] #[inline] #[allow(clippy::unnecessary_wraps)] // needed in other features pub(crate) fn limit_reached(&self) -> Result<(), DecodeError> { Ok(()) } } /// Returns the encoded length of the value in LEB128 variable length format. /// The returned value will be between 1 and 10, inclusive. #[inline] pub fn encoded_len_varint(value: u64) -> usize { // Based on [VarintSize64][1]. // [1]: https://github.com/google/protobuf/blob/3.3.x/src/google/protobuf/io/coded_stream.h#L1301-L1309 ((((value | 1).leading_zeros() ^ 63) * 9 + 73) / 64) as usize } #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] pub enum WireType { Varint = 0, SixtyFourBit = 1, LengthDelimited = 2, StartGroup = 3, EndGroup = 4, ThirtyTwoBit = 5, } pub const MIN_TAG: u32 = 1; pub const MAX_TAG: u32 = (1 << 29) - 1; impl TryFrom for WireType { type Error = DecodeError; #[inline] fn try_from(value: u64) -> Result { match value { 0 => Ok(WireType::Varint), 1 => Ok(WireType::SixtyFourBit), 2 => Ok(WireType::LengthDelimited), 3 => Ok(WireType::StartGroup), 4 => Ok(WireType::EndGroup), 5 => Ok(WireType::ThirtyTwoBit), _ => Err(DecodeError::new(format!( "invalid wire type value: {}", value ))), } } } /// Encodes a Protobuf field key, which consists of a wire type designator and /// the field tag. #[inline] pub fn encode_key(tag: u32, wire_type: WireType, buf: &mut B) where B: BufMut, { debug_assert!((MIN_TAG..=MAX_TAG).contains(&tag)); let key = (tag << 3) | wire_type as u32; encode_varint(u64::from(key), buf); } /// Decodes a Protobuf field key, which consists of a wire type designator and /// the field tag. #[inline(always)] pub fn decode_key(buf: &mut B) -> Result<(u32, WireType), DecodeError> where B: Buf, { let key = decode_varint(buf)?; if key > u64::from(u32::MAX) { return Err(DecodeError::new(format!("invalid key value: {}", key))); } let wire_type = WireType::try_from(key & 0x07)?; let tag = key as u32 >> 3; if tag < MIN_TAG { return Err(DecodeError::new("invalid tag value: 0")); } Ok((tag, wire_type)) } /// Returns the width of an encoded Protobuf field key with the given tag. /// The returned width will be between 1 and 5 bytes (inclusive). #[inline] pub fn key_len(tag: u32) -> usize { encoded_len_varint(u64::from(tag << 3)) } /// Checks that the expected wire type matches the actual wire type, /// or returns an error result. #[inline] pub fn check_wire_type(expected: WireType, actual: WireType) -> Result<(), DecodeError> { if expected != actual { return Err(DecodeError::new(format!( "invalid wire type: {:?} (expected {:?})", actual, expected ))); } Ok(()) } /// Helper function which abstracts reading a length delimiter prefix followed /// by decoding values until the length of bytes is exhausted. pub fn merge_loop( value: &mut T, buf: &mut B, ctx: DecodeContext, mut merge: M, ) -> Result<(), DecodeError> where M: FnMut(&mut T, &mut B, DecodeContext) -> Result<(), DecodeError>, B: Buf, { let len = decode_varint(buf)?; let remaining = buf.remaining(); if len > remaining as u64 { return Err(DecodeError::new("buffer underflow")); } let limit = remaining - len as usize; while buf.remaining() > limit { merge(value, buf, ctx.clone())?; } if buf.remaining() != limit { return Err(DecodeError::new("delimited length exceeded")); } Ok(()) } pub fn skip_field( wire_type: WireType, tag: u32, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { ctx.limit_reached()?; let len = match wire_type { WireType::Varint => decode_varint(buf).map(|_| 0)?, WireType::ThirtyTwoBit => 4, WireType::SixtyFourBit => 8, WireType::LengthDelimited => decode_varint(buf)?, WireType::StartGroup => loop { let (inner_tag, inner_wire_type) = decode_key(buf)?; match inner_wire_type { WireType::EndGroup => { if inner_tag != tag { return Err(DecodeError::new("unexpected end group tag")); } break 0; } _ => skip_field(inner_wire_type, inner_tag, buf, ctx.enter_recursion())?, } }, WireType::EndGroup => return Err(DecodeError::new("unexpected end group tag")), }; if len > buf.remaining() as u64 { return Err(DecodeError::new("buffer underflow")); } buf.advance(len as usize); Ok(()) } /// Helper macro which emits an `encode_repeated` function for the type. macro_rules! encode_repeated { ($ty:ty) => { pub fn encode_repeated(tag: u32, values: &[$ty], buf: &mut B) where B: BufMut, { for value in values { encode(tag, value, buf); } } }; } /// Helper macro which emits a `merge_repeated` function for the numeric type. macro_rules! merge_repeated_numeric { ($ty:ty, $wire_type:expr, $merge:ident, $merge_repeated:ident) => { pub fn $merge_repeated( wire_type: WireType, values: &mut Vec<$ty>, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { if wire_type == WireType::LengthDelimited { // Packed. merge_loop(values, buf, ctx, |values, buf, ctx| { let mut value = Default::default(); $merge($wire_type, &mut value, buf, ctx)?; values.push(value); Ok(()) }) } else { // Unpacked. check_wire_type($wire_type, wire_type)?; let mut value = Default::default(); $merge(wire_type, &mut value, buf, ctx)?; values.push(value); Ok(()) } } }; } /// Macro which emits a module containing a set of encoding functions for a /// variable width numeric type. macro_rules! varint { ($ty:ty, $proto_ty:ident) => ( varint!($ty, $proto_ty, to_uint64(value) { *value as u64 }, from_uint64(value) { value as $ty }); ); ($ty:ty, $proto_ty:ident, to_uint64($to_uint64_value:ident) $to_uint64:expr, from_uint64($from_uint64_value:ident) $from_uint64:expr) => ( pub mod $proto_ty { use crate::encoding::*; pub fn encode(tag: u32, $to_uint64_value: &$ty, buf: &mut B) where B: BufMut { encode_key(tag, WireType::Varint, buf); encode_varint($to_uint64, buf); } pub fn merge(wire_type: WireType, value: &mut $ty, buf: &mut B, _ctx: DecodeContext) -> Result<(), DecodeError> where B: Buf { check_wire_type(WireType::Varint, wire_type)?; let $from_uint64_value = decode_varint(buf)?; *value = $from_uint64; Ok(()) } encode_repeated!($ty); pub fn encode_packed(tag: u32, values: &[$ty], buf: &mut B) where B: BufMut { if values.is_empty() { return; } encode_key(tag, WireType::LengthDelimited, buf); let len: usize = values.iter().map(|$to_uint64_value| { encoded_len_varint($to_uint64) }).sum(); encode_varint(len as u64, buf); for $to_uint64_value in values { encode_varint($to_uint64, buf); } } merge_repeated_numeric!($ty, WireType::Varint, merge, merge_repeated); #[inline] pub fn encoded_len(tag: u32, $to_uint64_value: &$ty) -> usize { key_len(tag) + encoded_len_varint($to_uint64) } #[inline] pub fn encoded_len_repeated(tag: u32, values: &[$ty]) -> usize { key_len(tag) * values.len() + values.iter().map(|$to_uint64_value| { encoded_len_varint($to_uint64) }).sum::() } #[inline] pub fn encoded_len_packed(tag: u32, values: &[$ty]) -> usize { if values.is_empty() { 0 } else { let len = values.iter() .map(|$to_uint64_value| encoded_len_varint($to_uint64)) .sum::(); key_len(tag) + encoded_len_varint(len as u64) + len } } #[cfg(test)] mod test { use proptest::prelude::*; use crate::encoding::$proto_ty::*; use crate::encoding::test::{ check_collection_type, check_type, }; proptest! { #[test] fn check(value: $ty, tag in MIN_TAG..=MAX_TAG) { check_type(value, tag, WireType::Varint, encode, merge, encoded_len)?; } #[test] fn check_repeated(value: Vec<$ty>, tag in MIN_TAG..=MAX_TAG) { check_collection_type(value, tag, WireType::Varint, encode_repeated, merge_repeated, encoded_len_repeated)?; } #[test] fn check_packed(value: Vec<$ty>, tag in MIN_TAG..=MAX_TAG) { check_type(value, tag, WireType::LengthDelimited, encode_packed, merge_repeated, encoded_len_packed)?; } } } } ); } varint!(bool, bool, to_uint64(value) u64::from(*value), from_uint64(value) value != 0); varint!(i32, int32); varint!(i64, int64); varint!(u32, uint32); varint!(u64, uint64); varint!(i32, sint32, to_uint64(value) { ((value << 1) ^ (value >> 31)) as u32 as u64 }, from_uint64(value) { let value = value as u32; ((value >> 1) as i32) ^ (-((value & 1) as i32)) }); varint!(i64, sint64, to_uint64(value) { ((value << 1) ^ (value >> 63)) as u64 }, from_uint64(value) { ((value >> 1) as i64) ^ (-((value & 1) as i64)) }); /// Macro which emits a module containing a set of encoding functions for a /// fixed width numeric type. macro_rules! fixed_width { ($ty:ty, $width:expr, $wire_type:expr, $proto_ty:ident, $put:ident, $get:ident) => { pub mod $proto_ty { use crate::encoding::*; pub fn encode(tag: u32, value: &$ty, buf: &mut B) where B: BufMut, { encode_key(tag, $wire_type, buf); buf.$put(*value); } pub fn merge( wire_type: WireType, value: &mut $ty, buf: &mut B, _ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { check_wire_type($wire_type, wire_type)?; if buf.remaining() < $width { return Err(DecodeError::new("buffer underflow")); } *value = buf.$get(); Ok(()) } encode_repeated!($ty); pub fn encode_packed(tag: u32, values: &[$ty], buf: &mut B) where B: BufMut, { if values.is_empty() { return; } encode_key(tag, WireType::LengthDelimited, buf); let len = values.len() as u64 * $width; encode_varint(len as u64, buf); for value in values { buf.$put(*value); } } merge_repeated_numeric!($ty, $wire_type, merge, merge_repeated); #[inline] pub fn encoded_len(tag: u32, _: &$ty) -> usize { key_len(tag) + $width } #[inline] pub fn encoded_len_repeated(tag: u32, values: &[$ty]) -> usize { (key_len(tag) + $width) * values.len() } #[inline] pub fn encoded_len_packed(tag: u32, values: &[$ty]) -> usize { if values.is_empty() { 0 } else { let len = $width * values.len(); key_len(tag) + encoded_len_varint(len as u64) + len } } #[cfg(test)] mod test { use proptest::prelude::*; use super::super::test::{check_collection_type, check_type}; use super::*; proptest! { #[test] fn check(value: $ty, tag in MIN_TAG..=MAX_TAG) { check_type(value, tag, $wire_type, encode, merge, encoded_len)?; } #[test] fn check_repeated(value: Vec<$ty>, tag in MIN_TAG..=MAX_TAG) { check_collection_type(value, tag, $wire_type, encode_repeated, merge_repeated, encoded_len_repeated)?; } #[test] fn check_packed(value: Vec<$ty>, tag in MIN_TAG..=MAX_TAG) { check_type(value, tag, WireType::LengthDelimited, encode_packed, merge_repeated, encoded_len_packed)?; } } } } }; } fixed_width!( f32, 4, WireType::ThirtyTwoBit, float, put_f32_le, get_f32_le ); fixed_width!( f64, 8, WireType::SixtyFourBit, double, put_f64_le, get_f64_le ); fixed_width!( u32, 4, WireType::ThirtyTwoBit, fixed32, put_u32_le, get_u32_le ); fixed_width!( u64, 8, WireType::SixtyFourBit, fixed64, put_u64_le, get_u64_le ); fixed_width!( i32, 4, WireType::ThirtyTwoBit, sfixed32, put_i32_le, get_i32_le ); fixed_width!( i64, 8, WireType::SixtyFourBit, sfixed64, put_i64_le, get_i64_le ); /// Macro which emits encoding functions for a length-delimited type. macro_rules! length_delimited { ($ty:ty) => { encode_repeated!($ty); pub fn merge_repeated( wire_type: WireType, values: &mut Vec<$ty>, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { check_wire_type(WireType::LengthDelimited, wire_type)?; let mut value = Default::default(); merge(wire_type, &mut value, buf, ctx)?; values.push(value); Ok(()) } #[inline] pub fn encoded_len(tag: u32, value: &$ty) -> usize { key_len(tag) + encoded_len_varint(value.len() as u64) + value.len() } #[inline] pub fn encoded_len_repeated(tag: u32, values: &[$ty]) -> usize { key_len(tag) * values.len() + values .iter() .map(|value| encoded_len_varint(value.len() as u64) + value.len()) .sum::() } }; } pub mod string { use super::*; pub fn encode(tag: u32, value: &String, buf: &mut B) where B: BufMut, { encode_key(tag, WireType::LengthDelimited, buf); encode_varint(value.len() as u64, buf); buf.put_slice(value.as_bytes()); } pub fn merge( wire_type: WireType, value: &mut String, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { // ## Unsafety // // `string::merge` reuses `bytes::merge`, with an additional check of utf-8 // well-formedness. If the utf-8 is not well-formed, or if any other error occurs, then the // string is cleared, so as to avoid leaking a string field with invalid data. // // This implementation uses the unsafe `String::as_mut_vec` method instead of the safe // alternative of temporarily swapping an empty `String` into the field, because it results // in up to 10% better performance on the protobuf message decoding benchmarks. // // It's required when using `String::as_mut_vec` that invalid utf-8 data not be leaked into // the backing `String`. To enforce this, even in the event of a panic in `bytes::merge` or // in the buf implementation, a drop guard is used. unsafe { struct DropGuard<'a>(&'a mut Vec); impl<'a> Drop for DropGuard<'a> { #[inline] fn drop(&mut self) { self.0.clear(); } } let drop_guard = DropGuard(value.as_mut_vec()); bytes::merge_one_copy(wire_type, drop_guard.0, buf, ctx)?; match str::from_utf8(drop_guard.0) { Ok(_) => { // Success; do not clear the bytes. mem::forget(drop_guard); Ok(()) } Err(_) => Err(DecodeError::new( "invalid string value: data is not UTF-8 encoded", )), } } } length_delimited!(String); #[cfg(test)] mod test { use proptest::prelude::*; use super::super::test::{check_collection_type, check_type}; use super::*; proptest! { #[test] fn check(value: String, tag in MIN_TAG..=MAX_TAG) { super::test::check_type(value, tag, WireType::LengthDelimited, encode, merge, encoded_len)?; } #[test] fn check_repeated(value: Vec, tag in MIN_TAG..=MAX_TAG) { super::test::check_collection_type(value, tag, WireType::LengthDelimited, encode_repeated, merge_repeated, encoded_len_repeated)?; } } } } pub trait BytesAdapter: sealed::BytesAdapter {} mod sealed { use super::{Buf, BufMut}; pub trait BytesAdapter: Default + Sized + 'static { fn len(&self) -> usize; /// Replace contents of this buffer with the contents of another buffer. fn replace_with(&mut self, buf: B) where B: Buf; /// Appends this buffer to the (contents of) other buffer. fn append_to(&self, buf: &mut B) where B: BufMut; fn is_empty(&self) -> bool { self.len() == 0 } } } impl BytesAdapter for Bytes {} impl sealed::BytesAdapter for Bytes { fn len(&self) -> usize { Buf::remaining(self) } fn replace_with(&mut self, mut buf: B) where B: Buf, { *self = buf.copy_to_bytes(buf.remaining()); } fn append_to(&self, buf: &mut B) where B: BufMut, { buf.put(self.clone()) } } impl BytesAdapter for Vec {} impl sealed::BytesAdapter for Vec { fn len(&self) -> usize { Vec::len(self) } fn replace_with(&mut self, buf: B) where B: Buf, { self.clear(); self.reserve(buf.remaining()); self.put(buf); } fn append_to(&self, buf: &mut B) where B: BufMut, { buf.put(self.as_slice()) } } pub mod bytes { use super::*; pub fn encode(tag: u32, value: &A, buf: &mut B) where A: BytesAdapter, B: BufMut, { encode_key(tag, WireType::LengthDelimited, buf); encode_varint(value.len() as u64, buf); value.append_to(buf); } pub fn merge( wire_type: WireType, value: &mut A, buf: &mut B, _ctx: DecodeContext, ) -> Result<(), DecodeError> where A: BytesAdapter, B: Buf, { check_wire_type(WireType::LengthDelimited, wire_type)?; let len = decode_varint(buf)?; if len > buf.remaining() as u64 { return Err(DecodeError::new("buffer underflow")); } let len = len as usize; // Clear the existing value. This follows from the following rule in the encoding guide[1]: // // > Normally, an encoded message would never have more than one instance of a non-repeated // > field. However, parsers are expected to handle the case in which they do. For numeric // > types and strings, if the same field appears multiple times, the parser accepts the // > last value it sees. // // [1]: https://developers.google.com/protocol-buffers/docs/encoding#optional // // This is intended for A and B both being Bytes so it is zero-copy. // Some combinations of A and B types may cause a double-copy, // in which case merge_one_copy() should be used instead. value.replace_with(buf.copy_to_bytes(len)); Ok(()) } pub(super) fn merge_one_copy( wire_type: WireType, value: &mut A, buf: &mut B, _ctx: DecodeContext, ) -> Result<(), DecodeError> where A: BytesAdapter, B: Buf, { check_wire_type(WireType::LengthDelimited, wire_type)?; let len = decode_varint(buf)?; if len > buf.remaining() as u64 { return Err(DecodeError::new("buffer underflow")); } let len = len as usize; // If we must copy, make sure to copy only once. value.replace_with(buf.take(len)); Ok(()) } length_delimited!(impl BytesAdapter); #[cfg(test)] mod test { use proptest::prelude::*; use super::super::test::{check_collection_type, check_type}; use super::*; proptest! { #[test] fn check_vec(value: Vec, tag in MIN_TAG..=MAX_TAG) { super::test::check_type::, Vec>(value, tag, WireType::LengthDelimited, encode, merge, encoded_len)?; } #[test] fn check_bytes(value: Vec, tag in MIN_TAG..=MAX_TAG) { let value = Bytes::from(value); super::test::check_type::(value, tag, WireType::LengthDelimited, encode, merge, encoded_len)?; } #[test] fn check_repeated_vec(value: Vec>, tag in MIN_TAG..=MAX_TAG) { super::test::check_collection_type(value, tag, WireType::LengthDelimited, encode_repeated, merge_repeated, encoded_len_repeated)?; } #[test] fn check_repeated_bytes(value: Vec>, tag in MIN_TAG..=MAX_TAG) { let value = value.into_iter().map(Bytes::from).collect(); super::test::check_collection_type(value, tag, WireType::LengthDelimited, encode_repeated, merge_repeated, encoded_len_repeated)?; } } } } pub mod message { use super::*; pub fn encode(tag: u32, msg: &M, buf: &mut B) where M: Message, B: BufMut, { encode_key(tag, WireType::LengthDelimited, buf); encode_varint(msg.encoded_len() as u64, buf); msg.encode_raw(buf); } pub fn merge( wire_type: WireType, msg: &mut M, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where M: Message, B: Buf, { check_wire_type(WireType::LengthDelimited, wire_type)?; ctx.limit_reached()?; merge_loop( msg, buf, ctx.enter_recursion(), |msg: &mut M, buf: &mut B, ctx| { let (tag, wire_type) = decode_key(buf)?; msg.merge_field(tag, wire_type, buf, ctx) }, ) } pub fn encode_repeated(tag: u32, messages: &[M], buf: &mut B) where M: Message, B: BufMut, { for msg in messages { encode(tag, msg, buf); } } pub fn merge_repeated( wire_type: WireType, messages: &mut Vec, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where M: Message + Default, B: Buf, { check_wire_type(WireType::LengthDelimited, wire_type)?; let mut msg = M::default(); merge(WireType::LengthDelimited, &mut msg, buf, ctx)?; messages.push(msg); Ok(()) } #[inline] pub fn encoded_len(tag: u32, msg: &M) -> usize where M: Message, { let len = msg.encoded_len(); key_len(tag) + encoded_len_varint(len as u64) + len } #[inline] pub fn encoded_len_repeated(tag: u32, messages: &[M]) -> usize where M: Message, { key_len(tag) * messages.len() + messages .iter() .map(Message::encoded_len) .map(|len| len + encoded_len_varint(len as u64)) .sum::() } } pub mod group { use super::*; pub fn encode(tag: u32, msg: &M, buf: &mut B) where M: Message, B: BufMut, { encode_key(tag, WireType::StartGroup, buf); msg.encode_raw(buf); encode_key(tag, WireType::EndGroup, buf); } pub fn merge( tag: u32, wire_type: WireType, msg: &mut M, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where M: Message, B: Buf, { check_wire_type(WireType::StartGroup, wire_type)?; ctx.limit_reached()?; loop { let (field_tag, field_wire_type) = decode_key(buf)?; if field_wire_type == WireType::EndGroup { if field_tag != tag { return Err(DecodeError::new("unexpected end group tag")); } return Ok(()); } M::merge_field(msg, field_tag, field_wire_type, buf, ctx.enter_recursion())?; } } pub fn encode_repeated(tag: u32, messages: &[M], buf: &mut B) where M: Message, B: BufMut, { for msg in messages { encode(tag, msg, buf); } } pub fn merge_repeated( tag: u32, wire_type: WireType, messages: &mut Vec, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where M: Message + Default, B: Buf, { check_wire_type(WireType::StartGroup, wire_type)?; let mut msg = M::default(); merge(tag, WireType::StartGroup, &mut msg, buf, ctx)?; messages.push(msg); Ok(()) } #[inline] pub fn encoded_len(tag: u32, msg: &M) -> usize where M: Message, { 2 * key_len(tag) + msg.encoded_len() } #[inline] pub fn encoded_len_repeated(tag: u32, messages: &[M]) -> usize where M: Message, { 2 * key_len(tag) * messages.len() + messages.iter().map(Message::encoded_len).sum::() } } /// Rust doesn't have a `Map` trait, so macros are currently the best way to be /// generic over `HashMap` and `BTreeMap`. macro_rules! map { ($map_ty:ident) => { use crate::encoding::*; use core::hash::Hash; /// Generic protobuf map encode function. pub fn encode( key_encode: KE, key_encoded_len: KL, val_encode: VE, val_encoded_len: VL, tag: u32, values: &$map_ty, buf: &mut B, ) where K: Default + Eq + Hash + Ord, V: Default + PartialEq, B: BufMut, KE: Fn(u32, &K, &mut B), KL: Fn(u32, &K) -> usize, VE: Fn(u32, &V, &mut B), VL: Fn(u32, &V) -> usize, { encode_with_default( key_encode, key_encoded_len, val_encode, val_encoded_len, &V::default(), tag, values, buf, ) } /// Generic protobuf map merge function. pub fn merge( key_merge: KM, val_merge: VM, values: &mut $map_ty, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where K: Default + Eq + Hash + Ord, V: Default, B: Buf, KM: Fn(WireType, &mut K, &mut B, DecodeContext) -> Result<(), DecodeError>, VM: Fn(WireType, &mut V, &mut B, DecodeContext) -> Result<(), DecodeError>, { merge_with_default(key_merge, val_merge, V::default(), values, buf, ctx) } /// Generic protobuf map encode function. pub fn encoded_len( key_encoded_len: KL, val_encoded_len: VL, tag: u32, values: &$map_ty, ) -> usize where K: Default + Eq + Hash + Ord, V: Default + PartialEq, KL: Fn(u32, &K) -> usize, VL: Fn(u32, &V) -> usize, { encoded_len_with_default(key_encoded_len, val_encoded_len, &V::default(), tag, values) } /// Generic protobuf map encode function with an overridden value default. /// /// This is necessary because enumeration values can have a default value other /// than 0 in proto2. pub fn encode_with_default( key_encode: KE, key_encoded_len: KL, val_encode: VE, val_encoded_len: VL, val_default: &V, tag: u32, values: &$map_ty, buf: &mut B, ) where K: Default + Eq + Hash + Ord, V: PartialEq, B: BufMut, KE: Fn(u32, &K, &mut B), KL: Fn(u32, &K) -> usize, VE: Fn(u32, &V, &mut B), VL: Fn(u32, &V) -> usize, { for (key, val) in values.iter() { let skip_key = key == &K::default(); let skip_val = val == val_default; let len = (if skip_key { 0 } else { key_encoded_len(1, key) }) + (if skip_val { 0 } else { val_encoded_len(2, val) }); encode_key(tag, WireType::LengthDelimited, buf); encode_varint(len as u64, buf); if !skip_key { key_encode(1, key, buf); } if !skip_val { val_encode(2, val, buf); } } } /// Generic protobuf map merge function with an overridden value default. /// /// This is necessary because enumeration values can have a default value other /// than 0 in proto2. pub fn merge_with_default( key_merge: KM, val_merge: VM, val_default: V, values: &mut $map_ty, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where K: Default + Eq + Hash + Ord, B: Buf, KM: Fn(WireType, &mut K, &mut B, DecodeContext) -> Result<(), DecodeError>, VM: Fn(WireType, &mut V, &mut B, DecodeContext) -> Result<(), DecodeError>, { let mut key = Default::default(); let mut val = val_default; ctx.limit_reached()?; merge_loop( &mut (&mut key, &mut val), buf, ctx.enter_recursion(), |&mut (ref mut key, ref mut val), buf, ctx| { let (tag, wire_type) = decode_key(buf)?; match tag { 1 => key_merge(wire_type, key, buf, ctx), 2 => val_merge(wire_type, val, buf, ctx), _ => skip_field(wire_type, tag, buf, ctx), } }, )?; values.insert(key, val); Ok(()) } /// Generic protobuf map encode function with an overridden value default. /// /// This is necessary because enumeration values can have a default value other /// than 0 in proto2. pub fn encoded_len_with_default( key_encoded_len: KL, val_encoded_len: VL, val_default: &V, tag: u32, values: &$map_ty, ) -> usize where K: Default + Eq + Hash + Ord, V: PartialEq, KL: Fn(u32, &K) -> usize, VL: Fn(u32, &V) -> usize, { key_len(tag) * values.len() + values .iter() .map(|(key, val)| { let len = (if key == &K::default() { 0 } else { key_encoded_len(1, key) }) + (if val == val_default { 0 } else { val_encoded_len(2, val) }); encoded_len_varint(len as u64) + len }) .sum::() } }; } #[cfg(feature = "std")] pub mod hash_map { use std::collections::HashMap; map!(HashMap); } pub mod btree_map { map!(BTreeMap); } #[cfg(test)] mod test { use alloc::string::ToString; use core::borrow::Borrow; use core::fmt::Debug; use core::u64; use ::bytes::{Bytes, BytesMut}; use proptest::{prelude::*, test_runner::TestCaseResult}; use crate::encoding::*; pub fn check_type( value: T, tag: u32, wire_type: WireType, encode: fn(u32, &B, &mut BytesMut), merge: fn(WireType, &mut T, &mut Bytes, DecodeContext) -> Result<(), DecodeError>, encoded_len: fn(u32, &B) -> usize, ) -> TestCaseResult where T: Debug + Default + PartialEq + Borrow, B: ?Sized, { prop_assume!((MIN_TAG..=MAX_TAG).contains(&tag)); let expected_len = encoded_len(tag, value.borrow()); let mut buf = BytesMut::with_capacity(expected_len); encode(tag, value.borrow(), &mut buf); let mut buf = buf.freeze(); prop_assert_eq!( buf.remaining(), expected_len, "encoded_len wrong; expected: {}, actual: {}", expected_len, buf.remaining() ); if !buf.has_remaining() { // Short circuit for empty packed values. return Ok(()); } let (decoded_tag, decoded_wire_type) = decode_key(&mut buf).map_err(|error| TestCaseError::fail(error.to_string()))?; prop_assert_eq!( tag, decoded_tag, "decoded tag does not match; expected: {}, actual: {}", tag, decoded_tag ); prop_assert_eq!( wire_type, decoded_wire_type, "decoded wire type does not match; expected: {:?}, actual: {:?}", wire_type, decoded_wire_type, ); match wire_type { WireType::SixtyFourBit if buf.remaining() != 8 => Err(TestCaseError::fail(format!( "64bit wire type illegal remaining: {}, tag: {}", buf.remaining(), tag ))), WireType::ThirtyTwoBit if buf.remaining() != 4 => Err(TestCaseError::fail(format!( "32bit wire type illegal remaining: {}, tag: {}", buf.remaining(), tag ))), _ => Ok(()), }?; let mut roundtrip_value = T::default(); merge( wire_type, &mut roundtrip_value, &mut buf, DecodeContext::default(), ) .map_err(|error| TestCaseError::fail(error.to_string()))?; prop_assert!( !buf.has_remaining(), "expected buffer to be empty, remaining: {}", buf.remaining() ); prop_assert_eq!(value, roundtrip_value); Ok(()) } pub fn check_collection_type( value: T, tag: u32, wire_type: WireType, encode: E, mut merge: M, encoded_len: L, ) -> TestCaseResult where T: Debug + Default + PartialEq + Borrow, B: ?Sized, E: FnOnce(u32, &B, &mut BytesMut), M: FnMut(WireType, &mut T, &mut Bytes, DecodeContext) -> Result<(), DecodeError>, L: FnOnce(u32, &B) -> usize, { prop_assume!((MIN_TAG..=MAX_TAG).contains(&tag)); let expected_len = encoded_len(tag, value.borrow()); let mut buf = BytesMut::with_capacity(expected_len); encode(tag, value.borrow(), &mut buf); let mut buf = buf.freeze(); prop_assert_eq!( buf.remaining(), expected_len, "encoded_len wrong; expected: {}, actual: {}", expected_len, buf.remaining() ); let mut roundtrip_value = Default::default(); while buf.has_remaining() { let (decoded_tag, decoded_wire_type) = decode_key(&mut buf).map_err(|error| TestCaseError::fail(error.to_string()))?; prop_assert_eq!( tag, decoded_tag, "decoded tag does not match; expected: {}, actual: {}", tag, decoded_tag ); prop_assert_eq!( wire_type, decoded_wire_type, "decoded wire type does not match; expected: {:?}, actual: {:?}", wire_type, decoded_wire_type ); merge( wire_type, &mut roundtrip_value, &mut buf, DecodeContext::default(), ) .map_err(|error| TestCaseError::fail(error.to_string()))?; } prop_assert_eq!(value, roundtrip_value); Ok(()) } #[test] fn string_merge_invalid_utf8() { let mut s = String::new(); let buf = b"\x02\x80\x80"; let r = string::merge( WireType::LengthDelimited, &mut s, &mut &buf[..], DecodeContext::default(), ); r.expect_err("must be an error"); assert!(s.is_empty()); } #[test] fn varint() { fn check(value: u64, mut encoded: &[u8]) { // Small buffer. let mut buf = Vec::with_capacity(1); encode_varint(value, &mut buf); assert_eq!(buf, encoded); // Large buffer. let mut buf = Vec::with_capacity(100); encode_varint(value, &mut buf); assert_eq!(buf, encoded); assert_eq!(encoded_len_varint(value), encoded.len()); let roundtrip_value = decode_varint(&mut <&[u8]>::clone(&encoded)).expect("decoding failed"); assert_eq!(value, roundtrip_value); let roundtrip_value = decode_varint_slow(&mut encoded).expect("slow decoding failed"); assert_eq!(value, roundtrip_value); } check(2u64.pow(0) - 1, &[0x00]); check(2u64.pow(0), &[0x01]); check(2u64.pow(7) - 1, &[0x7F]); check(2u64.pow(7), &[0x80, 0x01]); check(300, &[0xAC, 0x02]); check(2u64.pow(14) - 1, &[0xFF, 0x7F]); check(2u64.pow(14), &[0x80, 0x80, 0x01]); check(2u64.pow(21) - 1, &[0xFF, 0xFF, 0x7F]); check(2u64.pow(21), &[0x80, 0x80, 0x80, 0x01]); check(2u64.pow(28) - 1, &[0xFF, 0xFF, 0xFF, 0x7F]); check(2u64.pow(28), &[0x80, 0x80, 0x80, 0x80, 0x01]); check(2u64.pow(35) - 1, &[0xFF, 0xFF, 0xFF, 0xFF, 0x7F]); check(2u64.pow(35), &[0x80, 0x80, 0x80, 0x80, 0x80, 0x01]); check(2u64.pow(42) - 1, &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F]); check(2u64.pow(42), &[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01]); check( 2u64.pow(49) - 1, &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F], ); check( 2u64.pow(49), &[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01], ); check( 2u64.pow(56) - 1, &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F], ); check( 2u64.pow(56), &[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01], ); check( 2u64.pow(63) - 1, &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F], ); check( 2u64.pow(63), &[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01], ); check( u64::MAX, &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01], ); } #[test] fn varint_overflow() { let mut u64_max_plus_one: &[u8] = &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02]; decode_varint(&mut u64_max_plus_one).expect_err("decoding u64::MAX + 1 succeeded"); decode_varint_slow(&mut u64_max_plus_one) .expect_err("slow decoding u64::MAX + 1 succeeded"); } /// This big bowl o' macro soup generates an encoding property test for each combination of map /// type, scalar map key, and value type. /// TODO: these tests take a long time to compile, can this be improved? #[cfg(feature = "std")] macro_rules! map_tests { (keys: $keys:tt, vals: $vals:tt) => { mod hash_map { map_tests!(@private HashMap, hash_map, $keys, $vals); } mod btree_map { map_tests!(@private BTreeMap, btree_map, $keys, $vals); } }; (@private $map_type:ident, $mod_name:ident, [$(($key_ty:ty, $key_proto:ident)),*], $vals:tt) => { $( mod $key_proto { use std::collections::$map_type; use proptest::prelude::*; use crate::encoding::*; use crate::encoding::test::check_collection_type; map_tests!(@private $map_type, $mod_name, ($key_ty, $key_proto), $vals); } )* }; (@private $map_type:ident, $mod_name:ident, ($key_ty:ty, $key_proto:ident), [$(($val_ty:ty, $val_proto:ident)),*]) => { $( proptest! { #[test] fn $val_proto(values: $map_type<$key_ty, $val_ty>, tag in MIN_TAG..=MAX_TAG) { check_collection_type(values, tag, WireType::LengthDelimited, |tag, values, buf| { $mod_name::encode($key_proto::encode, $key_proto::encoded_len, $val_proto::encode, $val_proto::encoded_len, tag, values, buf) }, |wire_type, values, buf, ctx| { check_wire_type(WireType::LengthDelimited, wire_type)?; $mod_name::merge($key_proto::merge, $val_proto::merge, values, buf, ctx) }, |tag, values| { $mod_name::encoded_len($key_proto::encoded_len, $val_proto::encoded_len, tag, values) })?; } } )* }; } #[cfg(feature = "std")] map_tests!(keys: [ (i32, int32), (i64, int64), (u32, uint32), (u64, uint64), (i32, sint32), (i64, sint64), (u32, fixed32), (u64, fixed64), (i32, sfixed32), (i64, sfixed64), (bool, bool), (String, string) ], vals: [ (f32, float), (f64, double), (i32, int32), (i64, int64), (u32, uint32), (u64, uint64), (i32, sint32), (i64, sint64), (u32, fixed32), (u64, fixed64), (i32, sfixed32), (i64, sfixed64), (bool, bool), (String, string), (Vec, bytes) ]); } prost-0.11.9/src/error.rs000064400000000000000000000074361046102023000133630ustar 00000000000000//! Protobuf encoding and decoding errors. use alloc::borrow::Cow; use alloc::boxed::Box; use alloc::vec::Vec; use core::fmt; /// A Protobuf message decoding error. /// /// `DecodeError` indicates that the input buffer does not contain a valid /// Protobuf message. The error details should be considered 'best effort': in /// general it is not possible to exactly pinpoint why data is malformed. #[derive(Clone, PartialEq, Eq)] pub struct DecodeError { inner: Box, } #[derive(Clone, PartialEq, Eq)] struct Inner { /// A 'best effort' root cause description. description: Cow<'static, str>, /// A stack of (message, field) name pairs, which identify the specific /// message type and field where decoding failed. The stack contains an /// entry per level of nesting. stack: Vec<(&'static str, &'static str)>, } impl DecodeError { /// Creates a new `DecodeError` with a 'best effort' root cause description. /// /// Meant to be used only by `Message` implementations. #[doc(hidden)] #[cold] pub fn new(description: impl Into>) -> DecodeError { DecodeError { inner: Box::new(Inner { description: description.into(), stack: Vec::new(), }), } } /// Pushes a (message, field) name location pair on to the location stack. /// /// Meant to be used only by `Message` implementations. #[doc(hidden)] pub fn push(&mut self, message: &'static str, field: &'static str) { self.inner.stack.push((message, field)); } } impl fmt::Debug for DecodeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DecodeError") .field("description", &self.inner.description) .field("stack", &self.inner.stack) .finish() } } impl fmt::Display for DecodeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("failed to decode Protobuf message: ")?; for &(message, field) in &self.inner.stack { write!(f, "{}.{}: ", message, field)?; } f.write_str(&self.inner.description) } } #[cfg(feature = "std")] impl std::error::Error for DecodeError {} #[cfg(feature = "std")] impl From for std::io::Error { fn from(error: DecodeError) -> std::io::Error { std::io::Error::new(std::io::ErrorKind::InvalidData, error) } } /// A Protobuf message encoding error. /// /// `EncodeError` always indicates that a message failed to encode because the /// provided buffer had insufficient capacity. Message encoding is otherwise /// infallible. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct EncodeError { required: usize, remaining: usize, } impl EncodeError { /// Creates a new `EncodeError`. pub(crate) fn new(required: usize, remaining: usize) -> EncodeError { EncodeError { required, remaining, } } /// Returns the required buffer capacity to encode the message. pub fn required_capacity(&self) -> usize { self.required } /// Returns the remaining length in the provided buffer at the time of encoding. pub fn remaining(&self) -> usize { self.remaining } } impl fmt::Display for EncodeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "failed to encode Protobuf message; insufficient buffer capacity (required: {}, remaining: {})", self.required, self.remaining ) } } #[cfg(feature = "std")] impl std::error::Error for EncodeError {} #[cfg(feature = "std")] impl From for std::io::Error { fn from(error: EncodeError) -> std::io::Error { std::io::Error::new(std::io::ErrorKind::InvalidInput, error) } } prost-0.11.9/src/lib.rs000064400000000000000000000056211046102023000127720ustar 00000000000000#![doc(html_root_url = "https://docs.rs/prost/0.11.9")] #![cfg_attr(not(feature = "std"), no_std)] #![doc = include_str!("../README.md")] // Re-export the alloc crate for use within derived code. #[doc(hidden)] pub extern crate alloc; // Re-export the bytes crate for use within derived code. pub use bytes; mod error; mod message; mod types; #[doc(hidden)] pub mod encoding; pub use crate::error::{DecodeError, EncodeError}; pub use crate::message::Message; use bytes::{Buf, BufMut}; use crate::encoding::{decode_varint, encode_varint, encoded_len_varint}; // See `encoding::DecodeContext` for more info. // 100 is the default recursion limit in the C++ implementation. #[cfg(not(feature = "no-recursion-limit"))] const RECURSION_LIMIT: u32 = 100; /// Encodes a length delimiter to the buffer. /// /// See [Message.encode_length_delimited] for more info. /// /// An error will be returned if the buffer does not have sufficient capacity to encode the /// delimiter. pub fn encode_length_delimiter(length: usize, buf: &mut B) -> Result<(), EncodeError> where B: BufMut, { let length = length as u64; let required = encoded_len_varint(length); let remaining = buf.remaining_mut(); if required > remaining { return Err(EncodeError::new(required, remaining)); } encode_varint(length, buf); Ok(()) } /// Returns the encoded length of a length delimiter. /// /// Applications may use this method to ensure sufficient buffer capacity before calling /// `encode_length_delimiter`. The returned size will be between 1 and 10, inclusive. pub fn length_delimiter_len(length: usize) -> usize { encoded_len_varint(length as u64) } /// Decodes a length delimiter from the buffer. /// /// This method allows the length delimiter to be decoded independently of the message, when the /// message is encoded with [Message.encode_length_delimited]. /// /// An error may be returned in two cases: /// /// * If the supplied buffer contains fewer than 10 bytes, then an error indicates that more /// input is required to decode the full delimiter. /// * If the supplied buffer contains more than 10 bytes, then the buffer contains an invalid /// delimiter, and typically the buffer should be considered corrupt. pub fn decode_length_delimiter(mut buf: B) -> Result where B: Buf, { let length = decode_varint(&mut buf)?; if length > usize::max_value() as u64 { return Err(DecodeError::new( "length delimiter exceeds maximum usize value", )); } Ok(length as usize) } // Re-export #[derive(Message, Enumeration, Oneof)]. // Based on serde's equivalent re-export [1], but enabled by default. // // [1]: https://github.com/serde-rs/serde/blob/v1.0.89/serde/src/lib.rs#L245-L256 #[cfg(feature = "prost-derive")] #[allow(unused_imports)] #[macro_use] extern crate prost_derive; #[cfg(feature = "prost-derive")] #[doc(hidden)] pub use prost_derive::*; prost-0.11.9/src/message.rs000064400000000000000000000121661046102023000136520ustar 00000000000000use alloc::boxed::Box; use alloc::vec::Vec; use core::fmt::Debug; use core::usize; use bytes::{Buf, BufMut}; use crate::encoding::{ decode_key, encode_varint, encoded_len_varint, message, DecodeContext, WireType, }; use crate::DecodeError; use crate::EncodeError; /// A Protocol Buffers message. pub trait Message: Debug + Send + Sync { /// Encodes the message to a buffer. /// /// This method will panic if the buffer has insufficient capacity. /// /// Meant to be used only by `Message` implementations. #[doc(hidden)] fn encode_raw(&self, buf: &mut B) where B: BufMut, Self: Sized; /// Decodes a field from a buffer, and merges it into `self`. /// /// Meant to be used only by `Message` implementations. #[doc(hidden)] fn merge_field( &mut self, tag: u32, wire_type: WireType, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, Self: Sized; /// Returns the encoded length of the message without a length delimiter. fn encoded_len(&self) -> usize; /// Encodes the message to a buffer. /// /// An error will be returned if the buffer does not have sufficient capacity. fn encode(&self, buf: &mut B) -> Result<(), EncodeError> where B: BufMut, Self: Sized, { let required = self.encoded_len(); let remaining = buf.remaining_mut(); if required > buf.remaining_mut() { return Err(EncodeError::new(required, remaining)); } self.encode_raw(buf); Ok(()) } /// Encodes the message to a newly allocated buffer. fn encode_to_vec(&self) -> Vec where Self: Sized, { let mut buf = Vec::with_capacity(self.encoded_len()); self.encode_raw(&mut buf); buf } /// Encodes the message with a length-delimiter to a buffer. /// /// An error will be returned if the buffer does not have sufficient capacity. fn encode_length_delimited(&self, buf: &mut B) -> Result<(), EncodeError> where B: BufMut, Self: Sized, { let len = self.encoded_len(); let required = len + encoded_len_varint(len as u64); let remaining = buf.remaining_mut(); if required > remaining { return Err(EncodeError::new(required, remaining)); } encode_varint(len as u64, buf); self.encode_raw(buf); Ok(()) } /// Encodes the message with a length-delimiter to a newly allocated buffer. fn encode_length_delimited_to_vec(&self) -> Vec where Self: Sized, { let len = self.encoded_len(); let mut buf = Vec::with_capacity(len + encoded_len_varint(len as u64)); encode_varint(len as u64, &mut buf); self.encode_raw(&mut buf); buf } /// Decodes an instance of the message from a buffer. /// /// The entire buffer will be consumed. fn decode(mut buf: B) -> Result where B: Buf, Self: Default, { let mut message = Self::default(); Self::merge(&mut message, &mut buf).map(|_| message) } /// Decodes a length-delimited instance of the message from the buffer. fn decode_length_delimited(buf: B) -> Result where B: Buf, Self: Default, { let mut message = Self::default(); message.merge_length_delimited(buf)?; Ok(message) } /// Decodes an instance of the message from a buffer, and merges it into `self`. /// /// The entire buffer will be consumed. fn merge(&mut self, mut buf: B) -> Result<(), DecodeError> where B: Buf, Self: Sized, { let ctx = DecodeContext::default(); while buf.has_remaining() { let (tag, wire_type) = decode_key(&mut buf)?; self.merge_field(tag, wire_type, &mut buf, ctx.clone())?; } Ok(()) } /// Decodes a length-delimited instance of the message from buffer, and /// merges it into `self`. fn merge_length_delimited(&mut self, mut buf: B) -> Result<(), DecodeError> where B: Buf, Self: Sized, { message::merge( WireType::LengthDelimited, self, &mut buf, DecodeContext::default(), ) } /// Clears the message, resetting all fields to their default. fn clear(&mut self); } impl Message for Box where M: Message, { fn encode_raw(&self, buf: &mut B) where B: BufMut, { (**self).encode_raw(buf) } fn merge_field( &mut self, tag: u32, wire_type: WireType, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { (**self).merge_field(tag, wire_type, buf, ctx) } fn encoded_len(&self) -> usize { (**self).encoded_len() } fn clear(&mut self) { (**self).clear() } } #[cfg(test)] mod tests { use super::*; const _MESSAGE_IS_OBJECT_SAFE: Option<&dyn Message> = None; } prost-0.11.9/src/types.rs000064400000000000000000000212421046102023000133650ustar 00000000000000//! Protocol Buffers well-known wrapper types. //! //! This module provides implementations of `Message` for Rust standard library types which //! correspond to a Protobuf well-known wrapper type. The remaining well-known types are defined in //! the `prost-types` crate in order to avoid a cyclic dependency between `prost` and //! `prost-build`. use alloc::string::String; use alloc::vec::Vec; use ::bytes::{Buf, BufMut, Bytes}; use crate::{ encoding::{ bool, bytes, double, float, int32, int64, skip_field, string, uint32, uint64, DecodeContext, WireType, }, DecodeError, Message, }; /// `google.protobuf.BoolValue` impl Message for bool { fn encode_raw(&self, buf: &mut B) where B: BufMut, { if *self { bool::encode(1, self, buf) } } fn merge_field( &mut self, tag: u32, wire_type: WireType, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { if tag == 1 { bool::merge(wire_type, self, buf, ctx) } else { skip_field(wire_type, tag, buf, ctx) } } fn encoded_len(&self) -> usize { if *self { 2 } else { 0 } } fn clear(&mut self) { *self = false; } } /// `google.protobuf.UInt32Value` impl Message for u32 { fn encode_raw(&self, buf: &mut B) where B: BufMut, { if *self != 0 { uint32::encode(1, self, buf) } } fn merge_field( &mut self, tag: u32, wire_type: WireType, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { if tag == 1 { uint32::merge(wire_type, self, buf, ctx) } else { skip_field(wire_type, tag, buf, ctx) } } fn encoded_len(&self) -> usize { if *self != 0 { uint32::encoded_len(1, self) } else { 0 } } fn clear(&mut self) { *self = 0; } } /// `google.protobuf.UInt64Value` impl Message for u64 { fn encode_raw(&self, buf: &mut B) where B: BufMut, { if *self != 0 { uint64::encode(1, self, buf) } } fn merge_field( &mut self, tag: u32, wire_type: WireType, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { if tag == 1 { uint64::merge(wire_type, self, buf, ctx) } else { skip_field(wire_type, tag, buf, ctx) } } fn encoded_len(&self) -> usize { if *self != 0 { uint64::encoded_len(1, self) } else { 0 } } fn clear(&mut self) { *self = 0; } } /// `google.protobuf.Int32Value` impl Message for i32 { fn encode_raw(&self, buf: &mut B) where B: BufMut, { if *self != 0 { int32::encode(1, self, buf) } } fn merge_field( &mut self, tag: u32, wire_type: WireType, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { if tag == 1 { int32::merge(wire_type, self, buf, ctx) } else { skip_field(wire_type, tag, buf, ctx) } } fn encoded_len(&self) -> usize { if *self != 0 { int32::encoded_len(1, self) } else { 0 } } fn clear(&mut self) { *self = 0; } } /// `google.protobuf.Int64Value` impl Message for i64 { fn encode_raw(&self, buf: &mut B) where B: BufMut, { if *self != 0 { int64::encode(1, self, buf) } } fn merge_field( &mut self, tag: u32, wire_type: WireType, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { if tag == 1 { int64::merge(wire_type, self, buf, ctx) } else { skip_field(wire_type, tag, buf, ctx) } } fn encoded_len(&self) -> usize { if *self != 0 { int64::encoded_len(1, self) } else { 0 } } fn clear(&mut self) { *self = 0; } } /// `google.protobuf.FloatValue` impl Message for f32 { fn encode_raw(&self, buf: &mut B) where B: BufMut, { if *self != 0.0 { float::encode(1, self, buf) } } fn merge_field( &mut self, tag: u32, wire_type: WireType, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { if tag == 1 { float::merge(wire_type, self, buf, ctx) } else { skip_field(wire_type, tag, buf, ctx) } } fn encoded_len(&self) -> usize { if *self != 0.0 { float::encoded_len(1, self) } else { 0 } } fn clear(&mut self) { *self = 0.0; } } /// `google.protobuf.DoubleValue` impl Message for f64 { fn encode_raw(&self, buf: &mut B) where B: BufMut, { if *self != 0.0 { double::encode(1, self, buf) } } fn merge_field( &mut self, tag: u32, wire_type: WireType, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { if tag == 1 { double::merge(wire_type, self, buf, ctx) } else { skip_field(wire_type, tag, buf, ctx) } } fn encoded_len(&self) -> usize { if *self != 0.0 { double::encoded_len(1, self) } else { 0 } } fn clear(&mut self) { *self = 0.0; } } /// `google.protobuf.StringValue` impl Message for String { fn encode_raw(&self, buf: &mut B) where B: BufMut, { if !self.is_empty() { string::encode(1, self, buf) } } fn merge_field( &mut self, tag: u32, wire_type: WireType, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { if tag == 1 { string::merge(wire_type, self, buf, ctx) } else { skip_field(wire_type, tag, buf, ctx) } } fn encoded_len(&self) -> usize { if !self.is_empty() { string::encoded_len(1, self) } else { 0 } } fn clear(&mut self) { self.clear(); } } /// `google.protobuf.BytesValue` impl Message for Vec { fn encode_raw(&self, buf: &mut B) where B: BufMut, { if !self.is_empty() { bytes::encode(1, self, buf) } } fn merge_field( &mut self, tag: u32, wire_type: WireType, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { if tag == 1 { bytes::merge(wire_type, self, buf, ctx) } else { skip_field(wire_type, tag, buf, ctx) } } fn encoded_len(&self) -> usize { if !self.is_empty() { bytes::encoded_len(1, self) } else { 0 } } fn clear(&mut self) { self.clear(); } } /// `google.protobuf.BytesValue` impl Message for Bytes { fn encode_raw(&self, buf: &mut B) where B: BufMut, { if !self.is_empty() { bytes::encode(1, self, buf) } } fn merge_field( &mut self, tag: u32, wire_type: WireType, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { if tag == 1 { bytes::merge(wire_type, self, buf, ctx) } else { skip_field(wire_type, tag, buf, ctx) } } fn encoded_len(&self) -> usize { if !self.is_empty() { bytes::encoded_len(1, self) } else { 0 } } fn clear(&mut self) { self.clear(); } } /// `google.protobuf.Empty` impl Message for () { fn encode_raw(&self, _buf: &mut B) where B: BufMut, { } fn merge_field( &mut self, tag: u32, wire_type: WireType, buf: &mut B, ctx: DecodeContext, ) -> Result<(), DecodeError> where B: Buf, { skip_field(wire_type, tag, buf, ctx) } fn encoded_len(&self) -> usize { 0 } fn clear(&mut self) {} }