yglnk/crates/yglnk-core/src/lib.rs
2023-01-09 09:30:47 +01:00

180 lines
4.7 KiB
Rust

#![no_std]
#![forbid(unsafe_code)]
pub use int_enum::{IntEnum, IntEnumError};
pub mod hash_table;
pub mod hilbert;
pub const MAGIC: [u8; 4] = [b'Y', b'g', b'L', b'n'];
#[derive(Clone, Copy, Debug, IntEnum)]
#[repr(u32)]
#[rustfmt::skip]
pub enum FileType {
None = 0x0000_0000,
Text = 0x0000_0001,
}
#[derive(Clone, Copy, Debug)]
pub struct FileHeader {
pub magic: [u8; 4],
pub generator: u32,
pub typ: u32,
pub version: u32,
}
impl FileHeader {
pub fn decode(data: [u8; 16]) -> Self {
Self {
magic: data[0..4].try_into().unwrap(),
generator: u32::from_be_bytes(data[4..8].try_into().unwrap()),
typ: u32::from_be_bytes(data[8..12].try_into().unwrap()),
version: u32::from_be_bytes(data[12..16].try_into().unwrap()),
}
}
pub fn encode(&self) -> [u8; 16] {
let mut data = [0u8; 16];
data[0..4].copy_from_slice(&self.magic);
data[4..8].copy_from_slice(&u32::to_be_bytes(self.generator));
data[8..12].copy_from_slice(&u32::to_be_bytes(self.typ));
data[12..16].copy_from_slice(&u32::to_be_bytes(self.version));
data
}
}
#[derive(Clone, Copy, Debug, IntEnum)]
#[repr(u32)]
#[rustfmt::skip]
pub enum Type {
PlainText = 0x0000_0000,
NestedText = 0x0000_0001,
StringTable = 0x0000_0010,
LinearPlain = 0x0000_0012,
HashPlain = 0x0000_0020,
HashLink = 0x0000_0021,
X2dhcPlain = 0x0000_0030,
X2dhcLink = 0x0000_0031,
}
#[derive(Clone, Copy, Debug, IntEnum)]
#[repr(u16)]
#[rustfmt::skip]
pub enum Ntt01 {
Div = 0x0000,
Group = 0x0001,
Header = 0x0002,
Quote = 0x0003,
Code = 0x0004,
}
pub fn trunc_key_at0(key: &[u8]) -> &[u8] {
let key_end = key.iter().take_while(|&&i| i != 0).count();
&key[..key_end]
}
#[derive(Clone, Copy, Debug)]
pub struct LinearTableEntry<R> {
pub name: R,
pub typ: u32,
pub location: u32,
pub meta: u32,
pub rest: R,
}
#[derive(Clone, Copy)]
pub struct LinearTableRef<'a> {
cklen: usize,
strtab: &'a [u8],
data: &'a [u8],
}
pub fn decode_location(location: u32) -> usize {
16 * usize::try_from(location).unwrap()
}
impl<'a> LinearTableRef<'a> {
pub fn parse(data: &'a [u8], location: u32) -> Option<Self> {
let sel = decode_location(location);
if data.len() < (sel + 16) {
return None;
}
let sel = &data[sel..];
let strtab_link = u32::from_be_bytes(sel[0..4].try_into().unwrap());
let entcount = u16::from_be_bytes(sel[4..6].try_into().unwrap());
let entsize = u16::from_be_bytes(sel[6..8].try_into().unwrap());
let strtab_loc = decode_location(strtab_link);
let dataspan = 16..(16 + 16 * usize::from(entsize) * usize::from(entcount));
if data.len() <= strtab_loc || entsize == 0 {
return None;
}
Some(Self {
cklen: 16 * usize::from(entsize),
strtab: &data[strtab_loc..],
data: sel.get(dataspan)?,
})
}
}
impl<'a> Iterator for LinearTableRef<'a> {
type Item = LinearTableEntry<&'a [u8]>;
fn next(&mut self) -> Option<Self::Item> {
if self.data.len() < self.cklen {
debug_assert!(self.data.is_empty());
return None;
}
let (i, j) = self.data.split_at(self.cklen);
self.data = j;
let name = usize::try_from(u32::from_be_bytes(i[0..4].try_into().unwrap())).unwrap();
let name = trunc_key_at0(self.strtab.get(name..)?);
Some(LinearTableEntry {
name,
typ: u32::from_be_bytes(i[4..8].try_into().unwrap()),
location: u32::from_be_bytes(i[8..12].try_into().unwrap()),
meta: u32::from_be_bytes(i[12..16].try_into().unwrap()),
rest: &i[16..],
})
}
}
#[derive(Clone, Copy)]
pub struct X2dhc<'a> {
xybits: u8,
data: &'a [u8],
}
impl<'a> X2dhc<'a> {
pub fn parse(data: &'a [u8]) -> Option<Self> {
if data.len() < 32 {
return None;
}
let xybits = data[0];
if xybits >= 8 {
return None;
}
let exp_len = 1usize.checked_shl(xybits.into())?.checked_mul(16)?;
Some(Self {
xybits,
data: data.get(16..16 + exp_len)?,
})
}
pub fn lookup(&self, x: u8, y: u8) -> (u64, u64) {
let xybr = 8 - self.xybits;
let (x, y) = (x >> xybr, y >> xybr);
let loc = decode_location(hilbert::xy2d(2 * self.xybits, x.into(), y.into()));
let dloc = &self.data[loc..loc + 16];
(
u64::from_be_bytes(dloc[0..8].try_into().unwrap()),
u64::from_be_bytes(dloc[8..16].try_into().unwrap()),
)
}
}