Add RevBuilder for reverse message encoding
Generate RevBuilder structs in codegen alongside ProtoBuilder. RevBuilder writes fields into a buffer starting from the end, allowing fields to be set in reverse declaration order. This avoids reallocation and produces valid protobuf data matching the forward layout. Update generated interop files and add comprehensive tests for wire format, nesting, map entries, overflow handling, and buffer marking utilities.
This commit is contained in:
@@ -8,7 +8,7 @@ use std::collections::{HashMap, HashSet};
|
||||
use std::str;
|
||||
|
||||
const DATA_IMPORTS: &str = "#[allow(unused, unused_imports, unused_assignments, unused_variables, non_camel_case_types)]\n\
|
||||
use roto_runtime::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator, RotoMessage};\n\
|
||||
use roto_runtime::{ProtoAccessor, ProtoBuilder, RevBuilder, Result, RotoError, read_varint, RepeatedFieldIterator, RotoMessage};\n\
|
||||
use core::str;\n\
|
||||
use bytes::{Bytes, BytesMut, Buf, BufMut};\n";
|
||||
const SERVICE_IMPORTS: &str = "#[allow(unused, unused_imports, unused_assignments, unused_variables, non_camel_case_types)]\n\
|
||||
@@ -460,6 +460,59 @@ fn write_message(msg_proto: &DescriptorProto, output: &mut String) {
|
||||
|
||||
output.push_str(&format!(" pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {{\n self.builder.finish()\n }}\n}}\n\n"));
|
||||
|
||||
// RevBuilder struct — reverse encoding, one `_written: bool` flag per field
|
||||
output.push_str(&format!("pub struct {}RevBuilder<'b> {{\n", msg_name));
|
||||
output.push_str(" builder: roto_runtime::RevBuilder<'b>,\n");
|
||||
for (field_name, _, _, _, _) in &builder_fields {
|
||||
output.push_str(&format!(" {}_written: bool,\n", field_name));
|
||||
}
|
||||
output.push_str(&format!("}}\n\nimpl<'b> {}RevBuilder<'b> {{\n", msg_name));
|
||||
|
||||
// Constructor
|
||||
output.push_str(&format!(
|
||||
" pub fn builder(buf: &mut [u8]) -> {}RevBuilder<'_> {{\n {}RevBuilder {{\n",
|
||||
msg_name, msg_name
|
||||
));
|
||||
output.push_str(" builder: roto_runtime::RevBuilder::new(buf),\n");
|
||||
for (field_name, _, _, _, _) in &builder_fields {
|
||||
output.push_str(&format!(" {}_written: false,\n", field_name));
|
||||
}
|
||||
output.push_str(" }\n }\n\n");
|
||||
|
||||
// Per-field setters — same as ProtoBuilder but using RevBuilder methods
|
||||
for (field_name, safe_name, tag, rust_type, method) in &builder_fields {
|
||||
output.push_str(&format!(
|
||||
" pub fn {}(mut self, value: {}) -> roto_runtime::Result<Self> {{\n self.builder.{}({}, value)?;\n self.{}_written = true;\n Ok(self)\n }}\n\n",
|
||||
safe_name, rust_type, method, tag, field_name
|
||||
));
|
||||
}
|
||||
|
||||
// with() — copies unseen fields from an existing message (iterate in reverse for RevBuilder)
|
||||
output.push_str(&format!(
|
||||
" pub fn with(mut self, msg: &{}<'_>) -> roto_runtime::Result<Self> {{\n",
|
||||
msg_name
|
||||
));
|
||||
output.push_str(" let fields: Vec<_> = msg.accessor.raw_fields().collect();\n");
|
||||
output.push_str(" for item in fields.into_iter().rev() {\n");
|
||||
output.push_str(" let (field_number, raw_bytes) = item?;\n");
|
||||
output.push_str(" let is_written = match field_number {\n");
|
||||
for (field_name, _, tag, _, _) in &builder_fields {
|
||||
output.push_str(&format!(
|
||||
" {} => self.{}_written,\n",
|
||||
tag, field_name
|
||||
));
|
||||
}
|
||||
output.push_str(" _ => false,\n");
|
||||
output.push_str(" };\n");
|
||||
output.push_str(" if !is_written {\n");
|
||||
output.push_str(" self.builder.write_raw(raw_bytes)?;\n");
|
||||
output.push_str(" }\n");
|
||||
output.push_str(" }\n");
|
||||
output.push_str(" Ok(self)\n");
|
||||
output.push_str(" }\n\n");
|
||||
|
||||
output.push_str(&format!(" pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {{\n self.builder.finish()\n }}\n}}\n\n"));
|
||||
|
||||
output.push_str(&format!("pub struct Owned{} {{\n", msg_name));
|
||||
output.push_str(" pub data: bytes::Bytes,\n");
|
||||
output.push_str("}\n\n");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub const DATA_IMPORTS: &str = "use roto_runtime::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator, RotoMessage};\nuse core::str;\nuse bytes::{Bytes, BytesMut, Buf, BufMut};\n";
|
||||
pub const DATA_IMPORTS: &str = "use roto_runtime::{ProtoAccessor, ProtoBuilder, RevBuilder, Result, RotoError, read_varint, RepeatedFieldIterator, RotoMessage};\nuse core::str;\nuse bytes::{Bytes, BytesMut, Buf, BufMut};\n";
|
||||
|
||||
pub fn to_pascal_case(s: &str) -> String {
|
||||
s.split('_')
|
||||
|
||||
Reference in New Issue
Block a user