pub fn extract_field(encoded_message: &[u8], field_number: u32) -> Option<&[u8]> {
let mut index = 0;
while index < encoded_message.len() {
let (field_number_from_wire, wire_type) = extract_field_number_and_wire_type(&encoded_message[index..]);
let (field_length, varint_length) = extract_field_length(&encoded_message[index + 1..]);
if field_number_from_wire == field_number {
let start = index + 1 + varint_length;
let end = start + field_length as usize;
return Some(&encoded_message[start..end]);
}
index += 1 + varint_length + field_length as usize;
}
None
}
pub fn extract_multiple_fields<'a>(encoded_message: &'a[u8], field_numbers: &[u32]) -> Vec<(u32, &'a[u8])> {
let mut index = 0;
let mut result = Vec::new();
while index < encoded_message.len() {
let (field_number_from_wire, wire_type) = extract_field_number_and_wire_type(&encoded_message[index..]);
let (field_length, varint_length) = extract_field_length(&encoded_message[index + 1..]);
if field_numbers.contains(&field_number_from_wire) {
let start = index + 1 + varint_length;
let end = start + field_length as usize;
result.push((field_number_from_wire, &encoded_message[start..end]));
}
index += 1 + varint_length + field_length as usize;
}
result
}
pub fn extract_multiple_fields_lazy<'a>(encoded_message: &'a[u8], field_numbers: &[u32]) -> Vec<(u32, &'a[u8])> {
extract_multiple_fields(encoded_message, field_numbers)
}
fn extract_field_number_and_wire_type(data: &[u8]) -> (u32, u32) {
let field_number_and_wire_type = data[0];
let wire_type = field_number_and_wire_type & 0b0000_0111;
let field_number = field_number_and_wire_type >> 3;
(field_number as u32, wire_type as u32)
}
fn extract_field_length(data: &[u8]) -> (u32, usize) {
let mut field_length = 0u32;
let mut shift = 0;
let mut index = 0;
loop {
let byte = data[index];
field_length |= ((byte & 0b0111_1111) as u32) << shift;
shift += 7;
index += 1;
if byte & 0b1000_0000 == 0 {
break;
}
}
(field_length, index)
}
fn get_varint_length(value: u32) -> usize {
match value {
0..=0x7f => 1,
0x80..=0x3fff => 2,
0x4000..=0x1fffff => 3,
0x200000..=0xfffffff => 4,
_ => 5,
}
}
#[cfg(test)]
mod tests {
use prost::Message;
use super::*;
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct DeviceInfo {
#[prost(string, tag = "1")]
pub uid: ::prost::alloc::string::String,
#[prost(string, tag = "2")]
pub source_id: ::prost::alloc::string::String,
#[prost(string, tag = "3")]
pub meta_source: ::prost::alloc::string::String,
#[prost(string, optional, tag = "10")]
pub device_id: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "11")]
pub source_name: ::core::option::Option<::prost::alloc::string::String>,
}
fn device_info() -> DeviceInfo {
DeviceInfo {
uid: "uid".to_string(),
source_id: "source_id".to_string(),
meta_source: "meta_source".to_string(),
device_id: Some("device_id".to_string()),
source_name: Some("source_name".to_string()),
}
}
#[test]
fn test_extract_field() {
let device_info = device_info();
let encoded = device_info.encode_to_vec();
let field = extract_field(&encoded, 1).unwrap();
assert_eq!(field, b"uid");
}
#[test]
fn test_extract_multiple_fields() {
let device_info = device_info();
let encoded = device_info.encode_to_vec();
let fields = extract_multiple_fields(&encoded, &[1, 2]);
assert_eq!(fields.len(), 2);
assert_eq!(fields[0].0, 1);
assert_eq!(fields[0].1, b"uid");
assert_eq!(fields[1].0, 2);
assert_eq!(fields[1].1, b"source_id");
}
#[test]
fn test_replace_field() {
let mut device_info = device_info();
let mut encoded = device_info.encode_to_vec();
replace_field(&mut encoded, 1, b"new_uid");
let new_device_info = DeviceInfo::decode(&*encoded).unwrap();
assert_eq!(new_device_info.uid, "new_uid");
assert_eq!(new_device_info.source_id, "source_id");
}
#[test]
fn test_replace_multiple_fields() {
let mut device_info = device_info();
let mut encoded = device_info.encode_to_vec();
let mut field_values: Vec<(u32, &[u8])> = vec![(1, b"new_uid"), (2, b"new_source_id")];
replace_multiple_fields(&mut encoded, &mut field_values);
let new_device_info = DeviceInfo::decode(&*encoded).unwrap();
assert_eq!(new_device_info.uid, "new_uid");
assert_eq!(new_device_info.source_id, "new_source_id");
}
}