const MARKER: &[u8] = b"\xab\xcd\xefMaxMind.com";
const SEPARATOR: [u8; 16] = [0; 16];
const NODE_COUNT: u32 = 1;
const RECORD_SIZE: u16 = 24;
const STRING: u8 = 2;
const UINT16: u8 = 5;
const UINT32: u8 = 6;
pub(super) const MAP: u8 = 7;
const UINT64: u8 = 9;
const ARRAY: u8 = 11;
const SPILL_ONE: u8 = 0x1d;
const SPILL_TWO: u8 = 0x1e;
pub(super) const IPV4: u16 = 4;
pub(super) fn database(data: &[u8], root: usize, database_type: &str, ip_version: u16) -> Vec<u8> {
let slot = NODE_COUNT + u32::try_from(SEPARATOR.len()).unwrap() + u32::try_from(root).unwrap();
let mut file = Vec::new();
for _ in 0..2 {
file.extend_from_slice(&slot.to_be_bytes()[1..]);
}
file.extend_from_slice(&SEPARATOR);
file.extend_from_slice(data);
file.extend_from_slice(MARKER);
file.extend_from_slice(&metadata(database_type, ip_version));
file
}
fn metadata(database_type: &str, ip_version: u16) -> Vec<u8> {
map(&[
("binary_format_major_version", uint16(2)),
("binary_format_minor_version", uint16(0)),
("build_epoch", uint64(0)),
("database_type", string(database_type)),
("description", map(&[("en", string("fixture"))])),
("ip_version", uint16(ip_version)),
("languages", array(&[string("en")])),
("node_count", uint32(NODE_COUNT)),
("record_size", uint16(RECORD_SIZE)),
])
}
pub(super) fn header(kind: u8, size: usize) -> Vec<u8> {
match size {
0..=28 => vec![(kind << 5) | u8::try_from(size).unwrap()],
29..=284 => vec![(kind << 5) | SPILL_ONE, u8::try_from(size - 29).unwrap()],
285..=65_819 => {
let spilled = u16::try_from(size - 285).unwrap().to_be_bytes();
vec![(kind << 5) | SPILL_TWO, spilled[0], spilled[1]]
}
_ => panic!("test values stay inside the two-byte size form"),
}
}
fn extended(kind: u8, size: usize) -> Vec<u8> {
assert!(
size < usize::from(SPILL_ONE),
"extended values are small by construction"
);
vec![u8::try_from(size).unwrap(), kind - MAP]
}
fn trimmed(value: u64) -> Vec<u8> {
let bytes = value.to_be_bytes();
let first = bytes.iter().position(|&byte| byte != 0).unwrap_or(8);
bytes[first..].to_vec()
}
pub(super) fn string(value: &str) -> Vec<u8> {
let mut out = header(STRING, value.len());
out.extend_from_slice(value.as_bytes());
out
}
fn uint16(value: u16) -> Vec<u8> {
let bytes = trimmed(u64::from(value));
let mut out = header(UINT16, bytes.len());
out.extend_from_slice(&bytes);
out
}
pub(super) fn uint32(value: u32) -> Vec<u8> {
let bytes = trimmed(u64::from(value));
let mut out = header(UINT32, bytes.len());
out.extend_from_slice(&bytes);
out
}
fn uint64(value: u64) -> Vec<u8> {
let bytes = trimmed(value);
let mut out = extended(UINT64, bytes.len());
out.extend_from_slice(&bytes);
out
}
fn array(items: &[Vec<u8>]) -> Vec<u8> {
let mut out = extended(ARRAY, items.len());
for item in items {
out.extend_from_slice(item);
}
out
}
pub(super) fn map(pairs: &[(&str, Vec<u8>)]) -> Vec<u8> {
let mut out = header(MAP, pairs.len());
for (key, value) in pairs {
out.extend_from_slice(&string(key));
out.extend_from_slice(value);
}
out
}