#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ComponentKind {
Raw,
Component,
}
pub fn detect_component_kind(wasm_bytes: &[u8]) -> ComponentKind {
if wasm_bytes.len() < 8 {
return ComponentKind::Raw;
}
if wasm_bytes[0..4] != [0x00, 0x61, 0x73, 0x6d] {
return ComponentKind::Raw;
}
if wasm_bytes[4..8] != [0x01, 0x00, 0x00, 0x00] {
return ComponentKind::Raw;
}
let mut pos = 8; while pos + 1 < wasm_bytes.len() {
let section_id = wasm_bytes[pos];
pos += 1;
let (size, end) = match decode_leb128(wasm_bytes, pos) {
Some(v) => v,
None => break, };
pos = end;
if section_id == 0x0b {
return ComponentKind::Component;
}
if pos + size as usize > wasm_bytes.len() {
break;
}
pos += size as usize;
}
ComponentKind::Raw
}
fn decode_leb128(bytes: &[u8], pos: usize) -> Option<(u64, usize)> {
let mut result: u64 = 0;
let mut shift: u32 = 0;
let mut current = pos;
loop {
if current >= bytes.len() {
return None;
}
let byte = bytes[current];
result |= ((byte & 0x7f) as u64) << shift;
current += 1;
if byte & 0x80 == 0 {
return Some((result, current));
}
shift += 7;
if shift > 63 {
return None; }
}
}
#[cfg(test)]
mod tests {
use super::*;
fn raw_wasm_header() -> Vec<u8> {
vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, ]
}
#[test]
fn detect_empty_is_raw() {
assert_eq!(detect_component_kind(&[]), ComponentKind::Raw);
}
#[test]
fn detect_too_short_is_raw() {
assert_eq!(detect_component_kind(&[0x00, 0x61]), ComponentKind::Raw);
}
#[test]
fn detect_header_only_is_raw() {
assert_eq!(
detect_component_kind(&raw_wasm_header()),
ComponentKind::Raw
);
}
#[test]
fn detect_header_with_code_section_is_raw() {
let mut bytes = raw_wasm_header();
bytes.extend_from_slice(&[10, 2, 1, 0]);
assert_eq!(detect_component_kind(&bytes), ComponentKind::Raw);
}
#[test]
fn detect_header_with_component_section() {
let mut bytes = raw_wasm_header();
bytes.extend_from_slice(&[0x0b, 2, 0x00, 0x00]);
assert_eq!(detect_component_kind(&bytes), ComponentKind::Component);
}
#[test]
fn detect_malformed_leb128_treated_as_raw() {
let mut bytes = raw_wasm_header();
bytes.extend_from_slice(&[10, 0xff, 0xff]);
assert_eq!(detect_component_kind(&bytes), ComponentKind::Raw);
}
#[test]
fn detect_section_body_overflows_treated_as_raw() {
let mut bytes = raw_wasm_header();
bytes.extend_from_slice(&[10, 0xff, 0x42]);
assert_eq!(detect_component_kind(&bytes), ComponentKind::Raw);
}
#[test]
fn decode_leb128_single_byte() {
let bytes = [0x7f];
let (val, pos) = decode_leb128(&bytes, 0).unwrap();
assert_eq!(val, 0x7f);
assert_eq!(pos, 1);
}
#[test]
fn decode_leb128_two_bytes() {
let bytes = [0x80, 0x01];
let (val, pos) = decode_leb128(&bytes, 0).unwrap();
assert_eq!(val, 128);
assert_eq!(pos, 2);
}
#[test]
fn decode_leb128_truncated_returns_none() {
let bytes = [0x80];
assert!(decode_leb128(&bytes, 0).is_none());
}
}