android-abx 0.4.0

Android Binary XML (ABX) parser using winnow
Documentation
//! Hand-built ABX wire-format helpers.
#![allow(dead_code)]

use android_abx::MAGIC;

pub fn u16_be(v: u16) -> [u8; 2] {
    v.to_be_bytes()
}
pub fn i32_be(v: i32) -> [u8; 4] {
    v.to_be_bytes()
}
pub fn i64_be(v: i64) -> [u8; 8] {
    v.to_be_bytes()
}
pub fn f32_be(v: f32) -> [u8; 4] {
    v.to_be_bytes()
}
pub fn f64_be(v: f64) -> [u8; 8] {
    v.to_be_bytes()
}

/// `u16` length + UTF-8 bytes.
pub fn utf(s: &str) -> Vec<u8> {
    let bytes = s.as_bytes();
    let mut out = u16_be(bytes.len() as u16).to_vec();
    out.extend_from_slice(bytes);
    out
}

/// New interned string: `0xFFFF` + utf.
pub fn interned_new(s: &str) -> Vec<u8> {
    let mut out = u16_be(0xFFFF).to_vec();
    out.extend_from_slice(&utf(s));
    out
}

/// Interned back-reference.
pub fn interned_ref(idx: u16) -> Vec<u8> {
    u16_be(idx).to_vec()
}

/// Prepend `ABX\0`.
pub fn with_magic(body: &[u8]) -> Vec<u8> {
    let mut out = MAGIC.to_vec();
    out.extend_from_slice(body);
    out
}

pub const CMD_START_DOCUMENT: u8 = 0x00;
pub const CMD_END_DOCUMENT: u8 = 0x01;
pub const CMD_START_TAG: u8 = 0x02;
pub const CMD_END_TAG: u8 = 0x03;
pub const CMD_TEXT: u8 = 0x04;
pub const CMD_CDSECT: u8 = 0x05;
pub const CMD_ENTITY_REF: u8 = 0x06;
pub const CMD_IGNORABLE_WHITESPACE: u8 = 0x07;
pub const CMD_PROCESSING_INSTRUCTION: u8 = 0x08;
pub const CMD_COMMENT: u8 = 0x09;
pub const CMD_DOCDECL: u8 = 0x0A;
pub const CMD_ATTRIBUTE: u8 = 0x0F;

pub const TYPE_NULL: u8 = 0x10;
pub const TYPE_STRING: u8 = 0x20;
pub const TYPE_STRING_INTERNED: u8 = 0x30;
pub const TYPE_BYTES_HEX: u8 = 0x40;
pub const TYPE_BYTES_BASE64: u8 = 0x50;
pub const TYPE_INT: u8 = 0x60;
pub const TYPE_INT_HEX: u8 = 0x70;
pub const TYPE_LONG: u8 = 0x80;
pub const TYPE_LONG_HEX: u8 = 0x90;
pub const TYPE_FLOAT: u8 = 0xA0;
pub const TYPE_DOUBLE: u8 = 0xB0;
pub const TYPE_BOOLEAN_TRUE: u8 = 0xC0;
pub const TYPE_BOOLEAN_FALSE: u8 = 0xD0;

pub fn start_tag(name: &str) -> Vec<u8> {
    let mut out = vec![TYPE_STRING_INTERNED | CMD_START_TAG];
    out.extend(interned_new(name));
    out
}

pub fn end_tag(name: &str) -> Vec<u8> {
    let mut out = vec![TYPE_STRING_INTERNED | CMD_END_TAG];
    out.extend(interned_new(name));
    out
}

pub fn text(s: &str) -> Vec<u8> {
    let mut out = vec![TYPE_STRING | CMD_TEXT];
    out.extend(utf(s));
    out
}

/// Text token as `AbxWriter` emits it (`TYPE_STRING`).
pub fn text_token(cmd: u8, s: &str) -> Vec<u8> {
    let mut out = vec![TYPE_STRING | cmd];
    out.extend(utf(s));
    out
}

/// `TYPE_NULL` text token, decodable but never emitted by `AbxWriter`.
pub fn null_text_token(cmd: u8) -> Vec<u8> {
    vec![TYPE_NULL | cmd]
}

pub fn attr_string(name: &str, value: &str) -> Vec<u8> {
    let mut out = vec![TYPE_STRING | CMD_ATTRIBUTE];
    out.extend(interned_new(name));
    out.extend(utf(value));
    out
}

pub fn attr_int(name: &str, value: i32) -> Vec<u8> {
    let mut out = vec![TYPE_INT | CMD_ATTRIBUTE];
    out.extend(interned_new(name));
    out.extend(i32_be(value));
    out
}

pub fn attr_int_hex(name: &str, value: u32) -> Vec<u8> {
    let mut out = vec![TYPE_INT_HEX | CMD_ATTRIBUTE];
    out.extend(interned_new(name));
    out.extend(i32_be(value as i32));
    out
}

pub fn attr_long(name: &str, value: i64) -> Vec<u8> {
    let mut out = vec![TYPE_LONG | CMD_ATTRIBUTE];
    out.extend(interned_new(name));
    out.extend(i64_be(value));
    out
}

pub fn attr_long_hex(name: &str, value: u64) -> Vec<u8> {
    let mut out = vec![TYPE_LONG_HEX | CMD_ATTRIBUTE];
    out.extend(interned_new(name));
    out.extend(i64_be(value as i64));
    out
}

pub fn attr_float(name: &str, value: f32) -> Vec<u8> {
    let mut out = vec![TYPE_FLOAT | CMD_ATTRIBUTE];
    out.extend(interned_new(name));
    out.extend(f32_be(value));
    out
}

pub fn attr_double(name: &str, value: f64) -> Vec<u8> {
    let mut out = vec![TYPE_DOUBLE | CMD_ATTRIBUTE];
    out.extend(interned_new(name));
    out.extend(f64_be(value));
    out
}

pub fn attr_bool(name: &str, value: bool) -> Vec<u8> {
    let ty = if value {
        TYPE_BOOLEAN_TRUE
    } else {
        TYPE_BOOLEAN_FALSE
    };
    let mut out = vec![ty | CMD_ATTRIBUTE];
    out.extend(interned_new(name));
    out
}

pub fn attr_null(name: &str) -> Vec<u8> {
    let mut out = vec![TYPE_NULL | CMD_ATTRIBUTE];
    out.extend(interned_new(name));
    out
}

pub fn attr_bytes_hex(name: &str, value: &[u8]) -> Vec<u8> {
    let mut out = vec![TYPE_BYTES_HEX | CMD_ATTRIBUTE];
    out.extend(interned_new(name));
    out.extend(u16_be(value.len() as u16));
    out.extend_from_slice(value);
    out
}

pub fn attr_bytes_base64(name: &str, value: &[u8]) -> Vec<u8> {
    let mut out = vec![TYPE_BYTES_BASE64 | CMD_ATTRIBUTE];
    out.extend(interned_new(name));
    out.extend(u16_be(value.len() as u16));
    out.extend_from_slice(value);
    out
}

/// Magic + `StartDocument` + `events` + `EndDocument`.
pub fn document(parts: &[Vec<u8>]) -> Vec<u8> {
    let mut body = vec![CMD_START_DOCUMENT | TYPE_NULL];
    for p in parts {
        body.extend_from_slice(p);
    }
    body.push(CMD_END_DOCUMENT | TYPE_NULL);
    with_magic(&body)
}