extern crate nom;
use anyhow::{anyhow, Result};
use nom::{
branch::alt,
bytes::complete::{tag, take},
combinator::map,
multi::{count, many0},
number::complete::be_u32,
sequence::tuple,
IResult,
};
use serde::{Deserialize, Serialize};
type KTestTuple = (u32, Vec<String>, u32, u32, u32, Vec<KTestObject>);
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct KTestObject {
pub name: String,
pub num_bytes: u32,
pub bytes: Vec<u8>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct KTest {
pub version: u32,
pub args: Vec<String>,
pub sym_argvs: u32,
pub sym_argv_len: u32,
pub num_objects: u32,
pub objects: Vec<KTestObject>,
}
pub fn parse_ktest(input: &[u8]) -> Result<KTest> {
let (_, (version, args, sym_argvs, sym_argv_len, num_objects, objects)) =
match parse_ktest_binary(input) {
Ok(res) => res,
Err(e) => {
return Err(anyhow!("Could not parse binary {:}", e));
}
};
Ok(KTest {
version,
args,
sym_argvs,
sym_argv_len,
num_objects,
objects,
})
}
fn parse_ktest_binary(input: &[u8]) -> IResult<&[u8], KTestTuple> {
let (input, (_magic, version, args)) =
tuple((magic_number, extract_be_u32, extract_arguments))(input)?;
let (input, (sym_argvs, sym_argv_len)) = if version > 2 {
extract_sym_args(input)?
} else {
(input, (0, 0))
};
let (input, objects) = extract_objects(input)?;
Ok((
input,
(
version,
args,
sym_argvs,
sym_argv_len,
objects.len() as u32,
objects,
),
))
}
fn magic_number(input: &[u8]) -> IResult<&[u8], &[u8]> {
alt((tag(b"KTEST"), tag(b"BOUT\n")))(input)
}
fn extract_be_u32(input: &[u8]) -> IResult<&[u8], u32> {
be_u32(input)
}
fn extract_arguments(input: &[u8]) -> IResult<&[u8], Vec<String>> {
let (input, num) = extract_be_u32(input)?;
count(extract_argument, num as usize)(input)
}
fn extract_argument(input: &[u8]) -> IResult<&[u8], String> {
let (input, size) = extract_be_u32(input)?;
map(take(size), |arg: &[u8]| {
String::from_utf8(arg.to_owned()).unwrap()
})(input)
}
fn extract_sym_args(input: &[u8]) -> IResult<&[u8], (u32, u32)> {
tuple((extract_be_u32, extract_be_u32))(input)
}
fn extract_objects(input: &[u8]) -> IResult<&[u8], Vec<KTestObject>> {
let (input, _num) = extract_be_u32(input)?;
many0(extract_object)(input)
}
fn extract_object(input: &[u8]) -> IResult<&[u8], KTestObject> {
let (input, size_name) = extract_be_u32(input)?;
let (input, name) = map(take(size_name), |name: &[u8]| {
String::from_utf8(name.to_owned()).unwrap()
})(input)?;
let (input, size_bytes) = extract_be_u32(input)?;
let (input, bytes) = take(size_bytes)(input)?;
Ok((
input,
KTestObject {
name: name,
num_bytes: size_bytes,
bytes: bytes.to_vec(),
},
))
}
#[cfg(test)]
mod parser_tests {
use super::*;
#[test]
fn invalid_magic_number() {
let magic = [0, 0, 0, 0, 0];
let res = magic_number(&magic);
assert_eq!(res.is_err(), true);
}
#[test]
fn valid_magic_numbers() {
let ktest = b"KTEST";
let bout = b"BOUT\n";
assert_eq!(magic_number(ktest).is_ok(), true);
assert_eq!(magic_number(bout).is_ok(), true);
}
}