Implement EBML varint parser
This commit is contained in:
parent
5de9bf9aee
commit
112e7da3f3
1 changed files with 87 additions and 0 deletions
87
src/lib.rs
87
src/lib.rs
|
@ -1,10 +1,72 @@
|
|||
|
||||
extern crate futures;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
CorruptVarint
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum EbmlVarint {
|
||||
Value(u64),
|
||||
Unknown
|
||||
}
|
||||
|
||||
/// Try to parse an EBML varint starting at the start of the given slice.
|
||||
/// Returns an Err() if the format is corrupt.
|
||||
/// Returns Ok(None) if more bytes are needed to get a result.
|
||||
/// Returns Ok(Some((varint, next))) to return a varint value and
|
||||
/// the starting location fo the next
|
||||
pub fn decode_varint(bytes: &[u8]) -> Result<Option<(EbmlVarint, usize)>, Error> {
|
||||
let mut value: u64 = 0;
|
||||
let mut value_length = 1;
|
||||
let mut mask: u8 = 0x80;
|
||||
let mut unknown_marker: u64 = !0;
|
||||
|
||||
if bytes.len() == 0 {
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
// get length marker bit from first byte & parse first byte
|
||||
while mask > 0 {
|
||||
if (mask & bytes[0]) != 0 {
|
||||
value = (bytes[0] & !mask) as u64;
|
||||
unknown_marker = (mask - 1) as u64;
|
||||
break
|
||||
}
|
||||
value_length += 1;
|
||||
mask = mask >> 1;
|
||||
}
|
||||
|
||||
if mask == 0 {
|
||||
return Err(Error::CorruptVarint)
|
||||
}
|
||||
|
||||
// check we have enough data to parse
|
||||
if value_length > bytes.len() {
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
// decode remaining bytes
|
||||
for i in 1..value_length {
|
||||
value = (value << 8) + (bytes[i] as u64);
|
||||
unknown_marker = (unknown_marker << 8) + 0xFF;
|
||||
}
|
||||
|
||||
// determine result
|
||||
if value == unknown_marker {
|
||||
Ok(Some((EbmlVarint::Unknown, value_length)))
|
||||
} else {
|
||||
Ok(Some((EbmlVarint::Value(value), value_length)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use futures::future::{ok, Future};
|
||||
use super::{decode_varint, Error};
|
||||
use super::EbmlVarint::{Unknown, Value};
|
||||
|
||||
#[test]
|
||||
fn hello_futures() {
|
||||
|
@ -15,4 +77,29 @@ mod tests {
|
|||
|
||||
assert_eq!(string_result, "Hello, Futures!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fail_corrupted_varints() {
|
||||
assert_eq!(decode_varint(&[0]), Err(Error::CorruptVarint));
|
||||
assert_eq!(decode_varint(&[0, 0, 0]), Err(Error::CorruptVarint));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn incomplete_varints() {
|
||||
assert_eq!(decode_varint(&[]), Ok(None));
|
||||
assert_eq!(decode_varint(&[0x40]), Ok(None));
|
||||
assert_eq!(decode_varint(&[0x01, 0, 0]), Ok(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_varints() {
|
||||
assert_eq!(decode_varint(&[0xFF]), Ok(Some((Unknown, 1))));
|
||||
assert_eq!(decode_varint(&[0x7F, 0xFF]), Ok(Some((Unknown, 2))));
|
||||
assert_eq!(decode_varint(&[0x80]), Ok(Some((Value(0), 1))));
|
||||
assert_eq!(decode_varint(&[0x81]), Ok(Some((Value(1), 1))));
|
||||
assert_eq!(decode_varint(&[0x40, 52]), Ok(Some((Value(52), 2))));
|
||||
|
||||
// test extra data in buffer
|
||||
assert_eq!(decode_varint(&[0x83, 0x11]), Ok(Some((Value(3), 1))));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue