13 个版本 (7 个重大更新)

0.8.1 2022年11月22日
0.8.0 2020年9月26日
0.7.0 2020年5月18日
0.6.4 2019年11月20日
0.5.0 2017年2月17日

#18解析器实现

Download history 126820/week @ 2024-04-23 118506/week @ 2024-04-30 124736/week @ 2024-05-07 140332/week @ 2024-05-14 134829/week @ 2024-05-21 152492/week @ 2024-05-28 150597/week @ 2024-06-04 149498/week @ 2024-06-11 125898/week @ 2024-06-18 142948/week @ 2024-06-25 145270/week @ 2024-07-02 141483/week @ 2024-07-09 153331/week @ 2024-07-16 160400/week @ 2024-07-23 155652/week @ 2024-07-30 158602/week @ 2024-08-06

654,713 每月下载量
434 个 crate 中使用 (57 个直接使用)

MIT 许可证

85KB
953

quick-protobuf

一个用于序列化和反序列化 protobuf 文件的纯 Rust 库。

文档

描述

这个 crate 旨在提供一个简单而快速的(最小分配)protobuf 解析器实现。一般来说,你可能不需要直接使用这个 crate,否则你应该使用由 pb-rs 工具自动生成的模块。

示例

    1. 安装 pb-rs 二进制文件以将您的 proto 文件转换为与 quick-protobuf 兼容的源代码
cargo install pb-rs
pb-rs /path/to/your/protobuf/file.proto
# will generate a 
# /path/to/your/protobuf/file.rs
    1. 添加 quick-protobuf 依赖项
# Cargo.toml
[dependencies]
quick-protobuf = "0.6.2"
    1. 享受编程乐趣
extern crate quick_protobuf;

mod foo_bar; // (see 1.)

use quick_protobuf::Reader;

// We will suppose here that Foo and Bar are two messages defined in the .proto file
// and converted into rust structs
//
// FooBar is the root message defined like this:
// message FooBar {
//     repeated Foo foos = 1;
//     repeated Bar bars = 2;
// }
// FooBar is a message generated from a proto file
// in parcicular it contains a `from_reader` function
use foo_bar::FooBar;
use quick_protobuf::{MessageRead, BytesReader};

fn main() {

    // bytes is a buffer on the data we want to deserialize
    // typically bytes is read from a `Read`:
    // r.read_to_end(&mut bytes).expect("cannot read bytes");
    let bytes: Vec<u8> = ...;

    // In the most simple form, we want to deserialize from a `&[u8]`
    let foobar = deserialize_from_slice(&bytes).expect("Cannot convert into a `FooBar`");

    // ...
    // ...

    // Alternatively, we can go lower level and work with a `BytesReader`
    // It gives more control of the bytes we are reading
    let mut reader = BytesReader::from_bytes(&bytes);

    // now using the generated module decoding is as easy as:
    let foobar = FooBar::from_reader(&mut reader, &bytes).expect("Cannot read FooBar");

    // if instead the buffer contains a length delimited stream of message we could use:
    // while !r.is_eof() {
    //     let foobar: FooBar = r.read_message(&bytes).expect(...);
    //     ...
    // }
    println!("Found {} foos and {} bars", foobar.foos.len(), foobar.bars.len());

    // Similarly, if we want to serialize the message you can use a `Writer` or use
    // `serialize_into_vec`
    let vec = serialize_into_vec(&foobar).expect("Cannot serialize `foobar`");

    // ... or for more control (more than one message)
    let mut buf = Vec::new();
    let mut writer = Writer::new(&mut buf);
    writer.write_message(&foobar).expect("Cannot write `foobar`);
}

消息 <-> 结构体

检查所有生成的代码的最佳方式是查看 codegen_example 数据

Proto 定义

enum FooEnum {
    FIRST_VALUE = 1;
    SECOND_VALUE = 2;
}
    
message BarMessage {
    required int32 b_required_int32 = 1;
}

message FooMessage {
    optional int32 f_int32 = 1;
    optional int64 f_int64 = 2;
    optional uint32 f_uint32 = 3;
    optional uint64 f_uint64 = 4;
    optional sint32 f_sint32 = 5;
    optional sint64 f_sint64 = 6;
    optional bool f_bool = 7;
    optional FooEnum f_FooEnum = 8;
    optional fixed64 f_fixed64 = 9;
    optional sfixed64 f_sfixed64 = 10;
    optional fixed32 f_fixed32 = 11;
    optional sfixed32 f_sfixed32 = 12;
    optional double f_double = 13;
    optional float f_float = 14;
    optional bytes f_bytes = 15;
    optional string f_string = 16;
    optional FooMessage f_self_message = 17;
    optional BarMessage f_bar_message = 18;
    repeated int32 f_repeated_int32 = 19;
    repeated int32 f_repeated_packed_int32 = 20 [ packed = true ];
}

生成的结构体

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum FooEnum {
    FIRST_VALUE = 1,
    SECOND_VALUE = 2,
}

#[derive(Debug, Default, PartialEq, Clone)]
pub struct BarMessage {                                 // all fields are owned: no lifetime parameter
    pub b_required_int32: i32,
}

#[derive(Debug, Default, PartialEq, Clone)]
pub struct FooMessage<'a> {                             // has borrowed fields: lifetime parameter
    pub f_int32: Option<i32>,
    pub f_int64: Option<i64>,
    pub f_uint32: Option<u32>,
    pub f_uint64: Option<u64>,
    pub f_sint32: Option<i32>,
    pub f_sint64: Option<i64>,
    pub f_bool: Option<bool>,
    pub f_FooEnum: Option<FooEnum>,
    pub f_fixed64: Option<u64>,
    pub f_sfixed64: Option<i64>,
    pub f_fixed32: Option<u32>,
    pub f_sfixed32: Option<i32>,
    pub f_double: Option<f64>,
    pub f_float: Option<f32>,
    pub f_bytes: Option<Cow<'a, [u8]>>,                 // bytes  -> Cow<[u8]>
    pub f_string: Option<Cow<'a, str>>                  // string -> Cow<str>
    pub f_self_message: Option<Box<FooMessage<'a>>>,    // reference cycle -> Boxed message
    pub f_bar_message: Option<BarMessage>,
    pub f_repeated_int32: Vec<i32>,                     // repeated: Vec
    pub f_repeated_packed_int32: Vec<i32>,              // repeated packed: Vec
}

利用 Rust 模块系统

嵌套消息

message A {
    message B {
        // ...
    }
}

由于 Rust 不允许结构体和模块共享相同的名称,我们使用 mod_Name 来表示嵌套消息。

pub struct A {
    //...
}

pub mod mod_A {
    pub struct B {
        // ...
    }
}

package a.b;

在这里我们可以使用相同的名称,但为了与嵌套消息保持一致性,模块也以 mod_ 为前缀。

pub mod mod_a {
    pub mod mod_b {
        // ...
    }
}

为什么不使用 rust-protobuf

这个库是广泛使用的 rust-protobuf 的替代品。

优点 / 缺点

  • 优点

    • 更快,尤其是在处理字符串、字节和重复的打包固定大小字段时(没有额外分配)
    • 不需要在您的机器上安装 protoc
    • 无需 trait 对象:更快/更简单的解析器
    • 生成的模块非常简单(约小10倍),因此您可以轻松理解所发生的情况
  • 缺点

    • 不太流行
      • 大多数rust-protobuf测试已迁移至此(参见v2v3
      • quick-protobuf现在被许多人使用,非常可靠
      • 一些缺少的功能
    • 不是rust-protobuf的直接替代品
      • 所有操作都需要明确处理,因此需要自己处理更多事情(例如Option解包,Cow管理)

贡献

任何帮助都受欢迎!(当然包括拉取请求、错误报告、缺少的功能等...)

许可证

MIT

依赖项