Last active
April 4, 2019 04:12
-
-
Save kkspeed/af7723644f555d89a735f53a1ef8775a to your computer and use it in GitHub Desktop.
Protobuf 3 decode raw
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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