diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 2a19ded..86c7622 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -9,7 +9,7 @@ publish = false [dependencies] bytes = "0.5.6" -pb-jelly = "0.0.5" +pb-jelly = { path = "../pb-jelly" } proto_box_it = { path = "gen/rust/proto/proto_box_it" } proto_custom_type = { path = "gen/rust/proto/proto_custom_type" } proto_linked_list = { path = "gen/rust/proto/proto_linked_list" } diff --git a/examples/protos/zero_copy/basic.proto b/examples/protos/zero_copy/basic.proto index 5316d87..49eeb83 100644 --- a/examples/protos/zero_copy/basic.proto +++ b/examples/protos/zero_copy/basic.proto @@ -6,5 +6,5 @@ import "rust/extensions.proto"; message BytesMessage { optional bytes data = 1 [(rust.zero_copy)=true]; - optional string name = 2; + optional string name = 2 [(rust.zero_copy)=true]; } diff --git a/examples/src/zero_copy/main.rs b/examples/src/zero_copy/main.rs index cc27705..64366e6 100644 --- a/examples/src/zero_copy/main.rs +++ b/examples/src/zero_copy/main.rs @@ -13,7 +13,7 @@ fn main() -> std::io::Result<()> { // Create our Proto Struct let mut proto = BytesMessage::default(); proto.set_data(data); - proto.set_name("Parker".to_owned()); + proto.set_name("Parker".into()); // Serialize our proto let ser_bytes: Vec = proto.serialize_to_vec(); diff --git a/pb-jelly-gen/codegen/codegen.py b/pb-jelly-gen/codegen/codegen.py index b5f6539..a6a636e 100755 --- a/pb-jelly-gen/codegen/codegen.py +++ b/pb-jelly-gen/codegen/codegen.py @@ -66,6 +66,7 @@ BLOB_TYPE = "::pb_jelly::Lazy<::blob_pb::WrappedBlob>" VEC_SLICE_TYPE = "::pb_jelly::Lazy<::blob_pb::VecSlice>" LAZY_BYTES_TYPE = "::pb_jelly::Lazy<::bytes::Bytes>" +ZERO_COPY_STRING_TYPE = "::pb_jelly::StrBytes" # pull out `x` from every instance of `::x::y::z`, but not `y` or `z` CRATE_NAME_REGEX = re.compile(r"(?:^|\W)::(\w+)(?:::\w+)*") @@ -256,6 +257,12 @@ def is_lazy_bytes(self) -> bool: and self.field.options.Extensions[extensions_pb2.zero_copy] ) + def is_zero_copy_string(self) -> bool: + return ( + self.field.type == FieldDescriptorProto.TYPE_STRING + and self.field.options.Extensions[extensions_pb2.zero_copy] + ) + def is_boxed(self) -> bool: return ( self.field.type == FieldDescriptorProto.TYPE_MESSAGE @@ -327,7 +334,10 @@ def set_method(self) -> Tuple[Text, Text]: elif self.field.type == FieldDescriptorProto.TYPE_BOOL: return "bool", "v" elif self.field.type == FieldDescriptorProto.TYPE_STRING: - return "::std::string::String", "v" + if self.is_zero_copy_string(): + return ZERO_COPY_STRING_TYPE, "v" + else: + return "::std::string::String", "v" elif self.field.type == FieldDescriptorProto.TYPE_BYTES: if self.is_blob(): return BLOB_TYPE, "v" @@ -360,7 +370,10 @@ def take_method(self) -> Tuple[Optional[Text], Optional[Text]]: expr = "self.%s.take().unwrap_or_default()" % self.field.name if self.field.type == FieldDescriptorProto.TYPE_STRING: - return "::std::string::String", expr + if self.is_zero_copy_string(): + return ZERO_COPY_STRING_TYPE, expr + else: + return "::std::string::String", expr elif self.field.type == FieldDescriptorProto.TYPE_BYTES: if self.is_blob(): return BLOB_TYPE, expr @@ -410,10 +423,12 @@ def get_method(self) -> Tuple[Text, Text]: elif self.field.type == FieldDescriptorProto.TYPE_BOOL: return "bool", "self.%s.unwrap_or(false)" % name elif self.field.type == FieldDescriptorProto.TYPE_STRING: - return ( - "&str", - 'self.%s.as_ref().map(|ref s| s.as_str()).unwrap_or("")' % name, - ) + return_type = "&str" + if self.is_zero_copy_string(): + return_expr = 'self.%s.as_ref().map(|s| &**s).unwrap_or("")' % name + else: + return_expr = 'self.%s.as_ref().map(|ref s| s.as_str()).unwrap_or("")' % name + return (return_type, return_expr) elif self.field.type == FieldDescriptorProto.TYPE_BYTES: assert not ( self.is_blob() or self.is_grpc_slices() or self.is_lazy_bytes() @@ -446,6 +461,9 @@ def rust_type(self) -> Text: if self.is_lazy_bytes(): return LAZY_BYTES_TYPE + + if self.is_zero_copy_string(): + return ZERO_COPY_STRING_TYPE if typ in PRIMITIVE_TYPES: return PRIMITIVE_TYPES[typ][0] @@ -1561,7 +1579,7 @@ def get_cargo_toml_file(self, derive_serde: bool) -> Iterator[Tuple[Text, Text]] features = {u"serde": u' features = ["serde_derive"]'} versions = { u"lazy_static": u' version = "1.4.0" ', - u"pb-jelly": u' version = "0.0.5" ', + u"pb-jelly": u' path = "../../../../../pb-jelly" ', u"serde": u' version = "1.0.114" ', u"bytes": u' version = "0.5.6" ' } diff --git a/pb-jelly/Cargo.toml b/pb-jelly/Cargo.toml index 6fbe578..23786ef 100644 --- a/pb-jelly/Cargo.toml +++ b/pb-jelly/Cargo.toml @@ -17,3 +17,13 @@ categories = ["encoding", "parsing", "web-programming"] byteorder = "1.3.4" bytes = "0.5.6" serde = { version = "1.0.114", features = ["derive"] } + +[features] +# Skips UTF-8 validation when using zero-copy Strings +# +# The (protobuf specification)[https://developers.google.com/protocol-buffers/docs/proto3#scalar] +# states that Strings must contain UTF-8 encoded text, so theoretically, validating a chunk +# of bytes is UTF-8 on deserialization shouldn't be needed. As a precaution though, we do this +# validation step. But validation can take a relatively large amount of time, so for users +# who wish to skip this validation, we provide the `zero_copy_string_no_utf8_check` feature flag. +zero_copy_string_no_utf8_check = [] diff --git a/pb-jelly/src/buffer.rs b/pb-jelly/src/buffer.rs index c4981e6..22ed2c4 100644 --- a/pb-jelly/src/buffer.rs +++ b/pb-jelly/src/buffer.rs @@ -69,6 +69,7 @@ use std::any::{ type_name, Any, }; +use std::convert::TryFrom; use std::default::Default; use std::fmt::{ self, @@ -87,7 +88,10 @@ use bytes::{ Bytes, }; -use super::Message; +use super::{ + Message, + StrBytes, +}; /// A stand-in trait for any backing buffer store. Required to be object-safe for lazy evaluation. /// PbBuffers are expected to own references to the data they reference, and should be cheap @@ -196,6 +200,32 @@ impl Message for Lazy { } } +impl Message for StrBytes { + fn compute_size(&self) -> usize { + match self.bytes() { + None => 0, + Some(bytes) => bytes.len(), + } + } + + fn compute_grpc_slices_size(&self) -> usize { + self.compute_size() + } + + fn serialize(&self, w: &mut W) -> std::io::Result<()> { + if let Some(bytes) = self.bytes() { + w.write_buffer(bytes)?; + } + Ok(()) + } + + fn deserialize(&mut self, r: &mut R) -> std::io::Result<()> { + self.replace_bytes(Bytes::from_reader(r)?) + .map_err(|e| Error::new(ErrorKind::InvalidData, e.to_string()))?; + Ok(()) + } +} + impl<'a> PbBufferReader for Cursor<&'a [u8]> { fn split(&mut self, at: usize) -> Self { let pos = self.position() as usize; @@ -229,6 +259,23 @@ impl PbBufferReader for Cursor { } } +impl PbBuffer for StrBytes { + type Reader = Cursor; + + fn len(&self) -> usize { + self.len() + } + + fn into_reader(self) -> Self::Reader { + Cursor::new(self.into_bytes()) + } + + fn from_reader(reader: &mut R) -> Result { + let bytes: Bytes = reader.as_buffer()?; + StrBytes::try_from(bytes).map_err(|e| Error::new(ErrorKind::InvalidData, e.to_string())) + } +} + impl<'a> PbBufferWriter for Cursor<&'a mut Vec> { /// Note: this implementation freely copies the data out of `buf`. #[inline] diff --git a/pb-jelly/src/lib.rs b/pb-jelly/src/lib.rs index 5d35be4..d445b9f 100644 --- a/pb-jelly/src/lib.rs +++ b/pb-jelly/src/lib.rs @@ -53,6 +53,9 @@ pub use crate::base_types::{ mod descriptor; pub use crate::descriptor::MessageDescriptor; +mod str_bytes; +pub use crate::str_bytes::StrBytes; + #[cfg(test)] mod tests; diff --git a/pb-jelly/src/str_bytes.rs b/pb-jelly/src/str_bytes.rs new file mode 100644 index 0000000..ef24312 --- /dev/null +++ b/pb-jelly/src/str_bytes.rs @@ -0,0 +1,117 @@ +use bytes::Bytes; +use std::{ + convert::TryFrom, + fmt, + ops::Deref, +}; + +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct StrBytes { + bytes: Option, +} + +impl StrBytes { + pub fn new() -> StrBytes { + StrBytes { bytes: None } + } + + /// Length of the underlying byte store + pub fn len(&self) -> usize { + match self.bytes.as_ref() { + None => 0, + Some(bytes) => bytes.len(), + } + } + + pub fn bytes(&self) -> Option<&Bytes> { + self.bytes.as_ref() + } + + pub fn into_bytes(self) -> Bytes { + match self.bytes { + None => Bytes::new(), + Some(bytes) => bytes, + } + } + + pub fn replace_bytes(&mut self, bytes: Bytes) -> Result, std::str::Utf8Error> { + if cfg!(feature = "zero_copy_string_no_utf8_check") { + // If we have UTF-8 validation turned off, simply replace the bytes + return Ok(self.bytes.replace(bytes)); + } + + std::str::from_utf8(&bytes)?; + Ok(self.bytes.replace(bytes)) + } +} + +impl Deref for StrBytes { + type Target = str; + + fn deref(&self) -> &Self::Target { + match self.bytes.as_ref() { + // SAFTEY: We validate on creation the underlying bytes are valid UTF8 + Some(bytes) => unsafe { std::str::from_utf8_unchecked(&bytes) }, + None => "", + } + } +} + +#[cfg(not(feature = "zero_copy_string_no_utf8_check"))] +impl TryFrom for StrBytes { + type Error = std::str::Utf8Error; + + fn try_from(bytes: Bytes) -> Result { + std::str::from_utf8(&bytes)?; + Ok(StrBytes { bytes: Some(bytes) }) + } +} + +#[cfg(not(feature = "zero_copy_string_no_utf8_check"))] +impl TryFrom<&[u8]> for StrBytes { + type Error = std::str::Utf8Error; + + fn try_from(slice: &[u8]) -> Result { + std::str::from_utf8(&slice)?; + Ok(StrBytes { + bytes: Some(Bytes::copy_from_slice(slice)), + }) + } +} + +impl From<&str> for StrBytes { + fn from(string: &str) -> StrBytes { + StrBytes { + bytes: Some(Bytes::copy_from_slice(string.as_bytes())), + } + } +} + +impl From for StrBytes { + fn from(string: String) -> StrBytes { + StrBytes { + bytes: Some(Bytes::from(string)), + } + } +} + +impl fmt::Display for StrBytes { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +#[cfg(feature = "zero_copy_string_no_utf8_check")] +impl From for StrBytes { + fn from(bytes: Bytes) -> StrBytes { + StrBytes { bytes: Some(bytes) } + } +} + +#[cfg(feature = "zero_copy_string_no_utf8_check")] +impl From<&[u8]> for StrBytes { + fn from(slice: &[u8]) -> StrBytes { + StrBytes { bytes: Some(Bytes::copy_from_slice(slice)) } + } +} diff --git a/pb-test/Cargo.toml b/pb-test/Cargo.toml index 2b8b0f7..d506be7 100644 --- a/pb-test/Cargo.toml +++ b/pb-test/Cargo.toml @@ -8,7 +8,8 @@ edition = "2018" [dependencies] bytes = "0.5.6" -pb-jelly = "0.0.5" +# pb-jelly = "0.0.5" +pb-jelly = { path = "../pb-jelly" } pretty_assertions = "0.6.1" proto_pbtest = { path = "gen/pb-jelly/proto_pbtest" } walkdir = "2.3.1" diff --git a/pb-test/pb_test_gen/src/main.rs b/pb-test/pb_test_gen/src/main.rs index f4dc68f..c8ab3c0 100644 --- a/pb-test/pb_test_gen/src/main.rs +++ b/pb-test/pb_test_gen/src/main.rs @@ -47,6 +47,7 @@ fn main() -> std::io::Result<()> { .include("../proto/packages") .customize(Customize { carllerche_bytes_for_bytes: Some(true), + carllerche_bytes_for_string: Some(true), ..Default::default() }) .run() diff --git a/pb-test/proto/packages/pbtest/bench.proto b/pb-test/proto/packages/pbtest/bench.proto index b11c19c..9856be6 100644 --- a/pb-test/proto/packages/pbtest/bench.proto +++ b/pb-test/proto/packages/pbtest/bench.proto @@ -14,6 +14,11 @@ message VecData { optional bytes data = 1; } +message ZeroCopyStringMessage { + // Using the `zero_copy` option, the `string` type maps to `pb_jelly::StrBytes` + optional string data = 1 [(rust.zero_copy)=true]; +} + message StringMessage { optional string data = 1; } diff --git a/pb-test/src/bench.rs b/pb-test/src/bench.rs index 0ca1ac1..11b99c6 100644 --- a/pb-test/src/bench.rs +++ b/pb-test/src/bench.rs @@ -10,6 +10,7 @@ mod benches { BytesData, StringMessage, VecData, + ZeroCopyStringMessage, }; use test::Bencher; @@ -69,6 +70,28 @@ mod benches { }); } + #[bench] + fn bench_deserialize_zero_copy_string(b: &mut Bencher) { + let data = String::from(include_str!("../data/moby_dick.txt")); + + let mut proto = ZeroCopyStringMessage::default(); + proto.set_data(data.into()); + + let ser_bytes: Vec = proto.serialize_to_vec(); + + let bytes_buf = Bytes::from(ser_bytes); + + b.iter(|| { + // Convert our bytes::Bytes into a pb::PbBufferReader + let mut bytes_reader = bytes_buf.clone().into_reader(); + + // Deserialize our proto + let mut de_proto = ZeroCopyStringMessage::default(); + de_proto.deserialize(&mut bytes_reader).unwrap(); + assert!(de_proto.has_data()); + }); + } + #[bench] fn bench_deserialize_string(b: &mut Bencher) { let data = String::from(include_str!("../data/moby_dick.txt")); @@ -158,17 +181,18 @@ mod prost { mod rust_protobuf { use crate::gen::rust_protobuf::bench::{ BytesData, - StringMessage, + ZeroCopyStringMessage, }; use bytes::Bytes; use protobuf::{ + Chars, CodedInputStream, Message, }; use test::Bencher; #[bench] - fn bench_deserialize_rust_protobuf_bytes(b: &mut Bencher) { + fn bench_deserialize_rust_protobuf_zero_copy_bytes(b: &mut Bencher) { // Generate 4MB of data let data = Bytes::from(vec![42 as u8; 4 * 1024 * 1024]); @@ -200,12 +224,12 @@ mod rust_protobuf { } #[bench] - fn bench_deserialize_rust_protobuf_string(b: &mut Bencher) { + fn bench_deserialize_rust_protobuf_zero_copy_string(b: &mut Bencher) { let data = String::from(include_str!("../data/moby_dick.txt")); // Create our proto struct - let mut proto = StringMessage::new(); - proto.set_data(data); + let mut proto = ZeroCopyStringMessage::new(); + proto.set_data(Chars::from(data)); // Serialize the proto let csz = proto.compute_size(); @@ -222,7 +246,7 @@ mod rust_protobuf { b.iter(|| { // Deserialize our proto let mut input_stream = CodedInputStream::from_carllerche_bytes(&bytes_buf); - let mut de_proto = StringMessage::default(); + let mut de_proto = ZeroCopyStringMessage::default(); de_proto .merge_from(&mut input_stream) .expect("failed to decode rust_protobuf proto!"); diff --git a/pb-test/src/gen/rust_protobuf/bench.rs b/pb-test/src/gen/rust_protobuf/bench.rs index 44afcb0..b13e5df 100644 --- a/pb-test/src/gen/rust_protobuf/bench.rs +++ b/pb-test/src/gen/rust_protobuf/bench.rs @@ -1,4 +1,4 @@ -// This file is generated by rust-protobuf 2.17.0. Do not edit +// This file is generated by rust-protobuf 2.18.1. Do not edit // @generated // https://github.com/rust-lang/rust-clippy/issues/702 @@ -21,9 +21,9 @@ /// Generated files are compatible only with the same version /// of protobuf runtime. -// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_17_0; +// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_18_1; -#[derive(PartialEq, Clone, Default)] +#[derive(PartialEq,Clone,Default)] pub struct BytesData { // message fields data: ::std::option::Option<::bytes::Bytes>, @@ -45,6 +45,7 @@ impl BytesData { // optional bytes data = 1; + pub fn get_data(&self) -> &[u8] { match self.data.as_ref() { Some(v) => v, @@ -150,20 +151,18 @@ impl ::protobuf::Message for BytesData { } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { - static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = - ::protobuf::rt::LazyV2::INIT; + static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT; descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); - fields.push(::protobuf::reflect::accessor::make_option_accessor::< - _, - ::protobuf::types::ProtobufTypeCarllercheBytes, - >( - "data", |m: &BytesData| &m.data, |m: &mut BytesData| &mut m.data + fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeCarllercheBytes>( + "data", + |m: &BytesData| { &m.data }, + |m: &mut BytesData| { &mut m.data }, )); ::protobuf::reflect::MessageDescriptor::new_pb_name::( "BytesData", fields, - file_descriptor_proto(), + file_descriptor_proto() ) }) } @@ -193,7 +192,7 @@ impl ::protobuf::reflect::ProtobufValue for BytesData { } } -#[derive(PartialEq, Clone, Default)] +#[derive(PartialEq,Clone,Default)] pub struct VecData { // message fields data: ::std::option::Option<::bytes::Bytes>, @@ -215,6 +214,7 @@ impl VecData { // optional bytes data = 1; + pub fn get_data(&self) -> &[u8] { match self.data.as_ref() { Some(v) => v, @@ -320,17 +320,19 @@ impl ::protobuf::Message for VecData { } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { - static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = - ::protobuf::rt::LazyV2::INIT; + static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT; descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); - fields.push(::protobuf::reflect::accessor::make_option_accessor::< - _, - ::protobuf::types::ProtobufTypeCarllercheBytes, - >( - "data", |m: &VecData| &m.data, |m: &mut VecData| &mut m.data + fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeCarllercheBytes>( + "data", + |m: &VecData| { &m.data }, + |m: &mut VecData| { &mut m.data }, )); - ::protobuf::reflect::MessageDescriptor::new_pb_name::("VecData", fields, file_descriptor_proto()) + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "VecData", + fields, + file_descriptor_proto() + ) }) } @@ -359,10 +361,179 @@ impl ::protobuf::reflect::ProtobufValue for VecData { } } -#[derive(PartialEq, Clone, Default)] +#[derive(PartialEq,Clone,Default)] +pub struct ZeroCopyStringMessage { + // message fields + data: ::std::option::Option<::protobuf::Chars>, + // special fields + pub unknown_fields: ::protobuf::UnknownFields, + pub cached_size: ::protobuf::CachedSize, +} + +impl<'a> ::std::default::Default for &'a ZeroCopyStringMessage { + fn default() -> &'a ZeroCopyStringMessage { + ::default_instance() + } +} + +impl ZeroCopyStringMessage { + pub fn new() -> ZeroCopyStringMessage { + ::std::default::Default::default() + } + + // optional string data = 1; + + + pub fn get_data(&self) -> &str { + match self.data.as_ref() { + Some(v) => v, + None => "", + } + } + pub fn clear_data(&mut self) { + self.data = ::std::option::Option::None; + } + + pub fn has_data(&self) -> bool { + self.data.is_some() + } + + // Param is passed by value, moved + pub fn set_data(&mut self, v: ::protobuf::Chars) { + self.data = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_data(&mut self) -> &mut ::protobuf::Chars { + if self.data.is_none() { + self.data = ::std::option::Option::Some(::protobuf::Chars::new()); + } + self.data.as_mut().unwrap() + } + + // Take field + pub fn take_data(&mut self) -> ::protobuf::Chars { + self.data.take().unwrap_or_else(|| ::protobuf::Chars::new()) + } +} + +impl ::protobuf::Message for ZeroCopyStringMessage { + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { + while !is.eof()? { + let (field_number, wire_type) = is.read_tag_unpack()?; + match field_number { + 1 => { + ::protobuf::rt::read_singular_carllerche_string_into(wire_type, is, &mut self.data)?; + }, + _ => { + ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u32 { + let mut my_size = 0; + if let Some(ref v) = self.data.as_ref() { + my_size += ::protobuf::rt::string_size(1, &v); + } + my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); + self.cached_size.set(my_size); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { + if let Some(ref v) = self.data.as_ref() { + os.write_string(1, v)?; + } + os.write_unknown_fields(self.get_unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn get_cached_size(&self) -> u32 { + self.cached_size.get() + } + + fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { + &self.unknown_fields + } + + fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { + &mut self.unknown_fields + } + + fn as_any(&self) -> &dyn (::std::any::Any) { + self as &dyn (::std::any::Any) + } + fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) { + self as &mut dyn (::std::any::Any) + } + fn into_any(self: ::std::boxed::Box) -> ::std::boxed::Box { + self + } + + fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { + Self::descriptor_static() + } + + fn new() -> ZeroCopyStringMessage { + ZeroCopyStringMessage::new() + } + + fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT; + descriptor.get(|| { + let mut fields = ::std::vec::Vec::new(); + fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeCarllercheChars>( + "data", + |m: &ZeroCopyStringMessage| { &m.data }, + |m: &mut ZeroCopyStringMessage| { &mut m.data }, + )); + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "ZeroCopyStringMessage", + fields, + file_descriptor_proto() + ) + }) + } + + fn default_instance() -> &'static ZeroCopyStringMessage { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(ZeroCopyStringMessage::new) + } +} + +impl ::protobuf::Clear for ZeroCopyStringMessage { + fn clear(&mut self) { + self.data = ::std::option::Option::None; + self.unknown_fields.clear(); + } +} + +impl ::std::fmt::Debug for ZeroCopyStringMessage { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for ZeroCopyStringMessage { + fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { + ::protobuf::reflect::ReflectValueRef::Message(self) + } +} + +#[derive(PartialEq,Clone,Default)] pub struct StringMessage { // message fields - data: ::protobuf::SingularField<::std::string::String>, + data: ::std::option::Option<::protobuf::Chars>, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, @@ -381,14 +552,15 @@ impl StringMessage { // optional string data = 1; + pub fn get_data(&self) -> &str { match self.data.as_ref() { - Some(v) => &v, + Some(v) => v, None => "", } } pub fn clear_data(&mut self) { - self.data.clear(); + self.data = ::std::option::Option::None; } pub fn has_data(&self) -> bool { @@ -396,22 +568,22 @@ impl StringMessage { } // Param is passed by value, moved - pub fn set_data(&mut self, v: ::std::string::String) { - self.data = ::protobuf::SingularField::some(v); + pub fn set_data(&mut self, v: ::protobuf::Chars) { + self.data = ::std::option::Option::Some(v); } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. - pub fn mut_data(&mut self) -> &mut ::std::string::String { + pub fn mut_data(&mut self) -> &mut ::protobuf::Chars { if self.data.is_none() { - self.data.set_default(); + self.data = ::std::option::Option::Some(::protobuf::Chars::new()); } self.data.as_mut().unwrap() } // Take field - pub fn take_data(&mut self) -> ::std::string::String { - self.data.take().unwrap_or_else(|| ::std::string::String::new()) + pub fn take_data(&mut self) -> ::protobuf::Chars { + self.data.take().unwrap_or_else(|| ::protobuf::Chars::new()) } } @@ -425,7 +597,7 @@ impl ::protobuf::Message for StringMessage { let (field_number, wire_type) = is.read_tag_unpack()?; match field_number { 1 => { - ::protobuf::rt::read_singular_string_into(wire_type, is, &mut self.data)?; + ::protobuf::rt::read_singular_carllerche_string_into(wire_type, is, &mut self.data)?; }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; @@ -449,7 +621,7 @@ impl ::protobuf::Message for StringMessage { fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { if let Some(ref v) = self.data.as_ref() { - os.write_string(1, &v)?; + os.write_string(1, v)?; } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) @@ -486,22 +658,18 @@ impl ::protobuf::Message for StringMessage { } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { - static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = - ::protobuf::rt::LazyV2::INIT; + static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT; descriptor.get(|| { let mut fields = ::std::vec::Vec::new(); - fields.push(::protobuf::reflect::accessor::make_singular_field_accessor::< - _, - ::protobuf::types::ProtobufTypeString, - >( + fields.push(::protobuf::reflect::accessor::make_option_accessor::<_, ::protobuf::types::ProtobufTypeCarllercheChars>( "data", - |m: &StringMessage| &m.data, - |m: &mut StringMessage| &mut m.data, + |m: &StringMessage| { &m.data }, + |m: &mut StringMessage| { &mut m.data }, )); ::protobuf::reflect::MessageDescriptor::new_pb_name::( "StringMessage", fields, - file_descriptor_proto(), + file_descriptor_proto() ) }) } @@ -514,7 +682,7 @@ impl ::protobuf::Message for StringMessage { impl ::protobuf::Clear for StringMessage { fn clear(&mut self) { - self.data.clear(); + self.data = ::std::option::Option::None; self.unknown_fields.clear(); } } @@ -535,16 +703,19 @@ static file_descriptor_proto_data: &'static [u8] = b"\ \n\x12pbtest/bench.proto\x12\x06pbtest\x1a\x15rust/extensions.proto\"%\n\ \tBytesData\x12\x18\n\x04data\x18\x01\x20\x01(\x0cR\x04dataB\x04\xb8\xb5\ \x18\x01\"\x1d\n\x07VecData\x12\x12\n\x04data\x18\x01\x20\x01(\x0cR\x04d\ - ata\"#\n\rStringMessage\x12\x12\n\x04data\x18\x01\x20\x01(\tR\x04data\ + ata\"1\n\x15ZeroCopyStringMessage\x12\x18\n\x04data\x18\x01\x20\x01(\tR\ + \x04dataB\x04\xb8\xb5\x18\x01\"#\n\rStringMessage\x12\x12\n\x04data\x18\ + \x01\x20\x01(\tR\x04data\ "; -static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = - ::protobuf::rt::LazyV2::INIT; +static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto { ::protobuf::parse_from_bytes(file_descriptor_proto_data).unwrap() } pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto { - file_descriptor_proto_lazy.get(|| parse_descriptor_proto()) + file_descriptor_proto_lazy.get(|| { + parse_descriptor_proto() + }) }