Skip to content

Instantly share code, notes, and snippets.

@kkspeed
Last active April 4, 2019 04:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kkspeed/af7723644f555d89a735f53a1ef8775a to your computer and use it in GitHub Desktop.
Save kkspeed/af7723644f555d89a735f53a1ef8775a to your computer and use it in GitHub Desktop.
Protobuf 3 decode raw
use std::str::from_utf8;
fn main() {
println!(
"{}",
read_proto_raw(&[0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67]).unwrap()
);
println!(
"{}",
read_proto_raw(&[0x1a, 0x03, 0x08, 0x96, 0x01]).unwrap()
);
println!(
"{}",
read_proto_raw(&[0x22, 0x06, 0x03, 0x8E, 0x02, 0x9E, 0xA7, 0x05]).unwrap()
);
}
fn read_proto_raw(bytes: &[u8]) -> Result<String, String> {
let mut pos = 0;
let mut result = "".into();
while pos < bytes.len() {
let (varint, len) = read_varint(&bytes[pos..])?;
let field_type = varint & 0x7;
let field_tag = varint >> 3;
pos += len;
result = format!("{}{}: ", result, field_tag);
match field_type {
0 => {
let (num, len) = read_varint(&bytes[pos..])?;
result = format!("{}{}\n", result, num);
pos += len;
}
1 => {
let num = read_fixed64(&bytes[pos..])?;
result = format!("{}{}\n", result, num);
pos += 8;
}
2 => {
let (num, len) = read_varint(&bytes[pos..])?;
pos += len;
let candidate = &bytes[pos..pos + num as usize];
if let Ok(s) = from_utf8(candidate) {
result = format!("{}{}\n", result, s);
} else {
result = format!("{}{{\n", result);
if let Ok(r) = read_proto_raw(candidate) {
result = format!("{}{}", result, r);
} else {
let packed = read_packed(candidate)?;
for i in packed {
result = format!("{}{},\n", result, i);
}
}
result = format!("{}}}\n", result);
}
pos += num as usize;
}
5 => {
let num = read_fixed32(&bytes[pos..])?;
result = format!("{}{}\n", result, num);
pos += 4;
}
_ => {
return Err(format!(
"Unkknown field type: {} at: {}",
field_type,
pos - len
));
}
}
}
Ok(result)
}
fn read_varint(bytes: &[u8]) -> Result<(u64, usize), &str> {
let mut shift = 0;
let mut result: u64 = 0;
for i in 0..bytes.len() {
let tmp: u64 = (bytes[i] & 0x7f) as u64;
result |= tmp << shift;
shift += 7;
if bytes[i] & 0x80 == 0 {
return Ok((result, shift / 7));
}
}
Err("Stream not terminated.")
}
fn read_fixed64(bytes: &[u8]) -> Result<u64, &str> {
if bytes.len() < 8 {
return Err("Stream does not contain 4 bytes.");
}
let mut shift = 0;
let mut fixed: u64 = 0;
for i in 0..8 {
let tmp = bytes[i] as u64;
fixed |= tmp << shift;
shift += 8;
}
Ok(fixed)
}
fn read_fixed32(bytes: &[u8]) -> Result<u32, &str> {
if bytes.len() < 4 {
return Err("Stream does not contain 4 bytes.");
}
let mut shift = 0;
let mut fixed: u32 = 0;
for i in 0..4 {
let tmp = bytes[i] as u32;
fixed |= tmp << shift;
shift += 8;
}
Ok(fixed)
}
fn read_packed(bytes: &[u8]) -> Result<Vec<u64>, &str> {
if bytes.len() == 0 {
return Err("Bytes are empty.");
}
let mut len = 0;
let mut result = Vec::new();
while let Ok((num, l)) = read_varint(&bytes[len..]) {
result.push(num);
len += l;
}
if len == bytes.len() {
Ok(result)
} else {
Err("Not consumed everything...")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment