Skip to content

Instantly share code, notes, and snippets.

@jdiez17
Last active December 29, 2016 21:10
Show Gist options
  • Save jdiez17/0f98ef663fa5b74201f97384fd86b590 to your computer and use it in GitHub Desktop.
Save jdiez17/0f98ef663fa5b74201f97384fd86b590 to your computer and use it in GitHub Desktop.
use std::collections::HashMap;
use std::vec::Vec;
use std::iter::Iterator;
use std::str;
use byteorder::{ByteOrder, LittleEndian};
type SizeMap<'a> = HashMap<Vec<&'a str>, usize>;
fn element_size<'a, I>(path: I, bytes: &'a [u8]) -> (SizeMap<'a>, usize)
where I: IntoIterator<Item = &'a str> {
let el_type = bytes[0];
let el_name_len = bytes.iter().position(|&c| c == 0x00).unwrap();
let el_name = str::from_utf8(&bytes[1..el_name_len]).unwrap();
// TODO this does not need to be mut
let mut el_path: Vec<_> = path.into_iter().collect();
el_path.push(el_name);
let mut res = SizeMap::new();
let mut i = 1 /* el_type */ + el_name_len;
i += match el_type {
// double
0x01 => 4,
// string
0x02 => {
panic!("TODO");
}
// embedded document
0x03 => {
let (sz, incr) = document_size(el_path.clone(), &bytes[i..]);
res.extend(sz.into_iter());
incr
}
// array
0x04 => {
let (sz, incr) = document_size(el_path.clone(), &bytes[i..]);
res.extend(sz.into_iter());
incr
}
// binary
0x05 => {
panic!("TODO");
}
// undefined (?)
0x06 => 0,
// ObjectId
0x07 => 12,
// bool
0x08 => 1,
_ => panic!("Unknown element type {}", el_type)
};
res.insert(el_path, i);
(res, i)
}
fn document_size<'a, I>(path: I, bytes: &'a [u8]) -> (SizeMap<'a>, usize)
where I: IntoIterator<Item = &'a str> {
let doc_size = LittleEndian::read_u32(&bytes) as usize;
let el_path: Vec<_> = path.into_iter().collect();
let mut res = SizeMap::new();
let mut i = 4;
while bytes[i] != 0x00 {
let (sz, incr) = element_size(el_path.clone(), &bytes[i..]);
res.extend(sz.into_iter());
i += incr;
}
i += 1;
assert!(doc_size == i);
res.insert(el_path, i);
(res, i)
}
fn bson_size(bytes: &[u8]) -> SizeMap {
let (sz, _) = document_size(vec!["root"], &bytes[..]);
sz
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sizes() {
let doc = vec![
0x21, 0x00, 0x00, 0x00, // Document size
0x08, 0x66, 0x6f, 0x6f, 0x00, 0x01, // "foo" => true
0x08, 0x62, 0x61, 0x72, 0x00, 0x00, // "bar" => false
0x03, 0x62, 0x61, 0x7a, 0x00, // "baz" => {
0x0b, 0x00, 0x00, 0x00,
0x08, 0x71, 0x75, 0x78, 0x00, 0x01, // "qux" => true }
0x00,
0x00 // Document end
];
let mut expct = SizeMap::new();
expct.insert(vec!["root"], 0x21);
expct.insert(vec!["root", "foo"], 0x06);
expct.insert(vec!["root", "bar"], 0x06);
expct.insert(vec!["root", "baz"], 0x10);
expct.insert(vec!["root", "baz", "qux"], 0x06);
assert_eq!(expct, bson_size(&doc[..]));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment