#[derive(Debug)]
pub struct VarintResult {
pub next_pos: usize,
pub varint_gar: Option<Vec<u8>>,
pub varint: Option<u64>,
pub varint_ohb: Option<u64>,
}
#[inline]
pub fn parse_varint(buf: &[u8], start: usize) -> VarintResult {
let buflen = buf.len();
assert!(start <= buflen);
if start == buflen {
return VarintResult {
next_pos: start,
varint_gar: Some(vec![]),
varint: None,
varint_ohb: None,
};
}
let mut v: u64 = 0;
let mut shift: u32 = 0;
let mut pos = start;
let mut too_big = false;
loop {
if pos >= buflen {
return VarintResult {
next_pos: buflen,
varint_gar: Some(buf[start..].to_vec()),
varint: None,
varint_ohb: None,
};
}
let b = buf[pos];
pos += 1;
let bits = (b & 0x7f) as u64;
if shift < 64 {
if shift == 63 && bits > 1 {
too_big = true;
} else {
v |= bits << shift;
}
} else {
if bits != 0 {
too_big = true;
}
}
shift += 7;
if b & 0x80 == 0 {
break; }
if shift > 70 {
while pos < buflen {
let b2 = buf[pos];
pos += 1;
if (b2 & 0x7f) != 0 {
too_big = true;
}
if b2 & 0x80 == 0 {
break;
}
}
break;
}
}
if too_big {
return VarintResult {
next_pos: buflen,
varint_gar: Some(buf[start..].to_vec()),
varint: None,
varint_ohb: None,
};
}
let last_b = buf[pos - 1];
let ohb = if last_b == 0x00 && pos > start + 1 {
let mut count: u64 = 1;
let mut p = pos - 2; while p > start && buf[p] == 0x80 {
count += 1;
p -= 1;
}
Some(count)
} else {
None
};
VarintResult {
next_pos: pos,
varint_gar: None,
varint: Some(v),
varint_ohb: ohb,
}
}
pub fn encode_varint_bytes(value: u64, ohb: Option<u64>) -> Vec<u8> {
let mut out = Vec::new();
write_varint_ohb(value, ohb, &mut out);
out
}
#[inline]
pub fn write_varint_ohb(value: u64, ohb: Option<u64>, out: &mut Vec<u8>) {
let mut v = value;
loop {
let b = (v & 0x7f) as u8;
v >>= 7;
if v != 0 {
out.push(b | 0x80);
} else {
out.push(b);
break;
}
}
if let Some(count) = ohb {
if count > 0 {
*out.last_mut().unwrap() |= 0x80; for _ in 0..count - 1 {
out.push(0x80);
}
out.push(0x00); }
}
}
#[derive(Debug, Clone)]
pub struct WiretagResult {
pub next_pos: usize,
pub wtag_gar: Option<Vec<u8>>,
pub wtype: Option<u32>,
pub wfield: Option<u64>,
pub wfield_ohb: Option<u64>,
pub wfield_oor: Option<bool>,
}
#[inline]
pub fn parse_wiretag(buf: &[u8], start: usize) -> WiretagResult {
let buflen = buf.len();
assert!(start < buflen, "parse_wiretag called at end of buffer");
let first_byte = buf[start];
let wtype = (first_byte & 0x07) as u32;
if wtype > 5 {
return WiretagResult {
next_pos: buflen,
wtag_gar: Some(buf[start..].to_vec()),
wtype: None,
wfield: None,
wfield_ohb: None,
wfield_oor: None,
};
}
let vr = parse_varint(buf, start);
if let Some(gar) = vr.varint_gar {
return WiretagResult {
next_pos: vr.next_pos,
wtag_gar: Some(gar),
wtype: None,
wfield: None,
wfield_ohb: None,
wfield_oor: None,
};
}
let raw = vr.varint.unwrap();
let field_number = raw >> 3;
let ohb = vr.varint_ohb;
let oor = if field_number == 0 || field_number >= (1 << 29) {
Some(true)
} else {
None
};
WiretagResult {
next_pos: vr.next_pos,
wtag_gar: None,
wtype: Some(wtype),
wfield: Some(field_number),
wfield_ohb: ohb,
wfield_oor: oor,
}
}