refactor(core): move LinearTable stuff into separate file; add iterator for X2dhc -> hilbert

This commit is contained in:
Alain Zscheile 2023-01-11 16:23:45 +01:00
parent c25c3f11ea
commit 7a54e48a09
6 changed files with 137 additions and 100 deletions

2
Cargo.lock generated
View file

@ -423,7 +423,7 @@ checksum = "735a71d46c4d68d71d4b24d03fdc2b98e38cea81730595801db779c04fe80d70"
[[package]]
name = "yglnk-core"
version = "0.1.0"
version = "0.0.1"
dependencies = [
"int-enum",
"xxhash-rust",

View file

@ -2,7 +2,7 @@
name = "yglnk-core"
description = "basic on-disk structured data helpers"
categories = ["parser-implementations"]
version = "0.0.0"
version = "0.0.1"
edition = "2021"
license = "Apache-2.0 OR ISC"

View file

@ -1,3 +1,5 @@
use super::decode_location;
/// rotate/flip a quadrant
fn rot(n: u32, x: &mut u32, y: &mut u32, rx: bool, ry: bool) {
if ry {
@ -32,6 +34,73 @@ pub fn xy2d(nh: u8, mut x: u32, mut y: u32) -> u32 {
.sum()
}
#[derive(Clone, Copy)]
pub struct X2dhc<'a> {
xybits: u8,
data: &'a [u8],
}
#[derive(Clone, Copy)]
pub enum X2dhcDim {
X(u8),
Y(u8),
}
impl X2dhcDim {
pub fn value(&self) -> u8 {
match *self {
X2dhcDim::X(x) => x,
X2dhcDim::Y(y) => y,
}
}
}
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)?,
})
}
fn lookup_internal(data: &'a [u8], xybits2: u8, x: u8, y: u8) -> (u64, u64) {
let loc = decode_location(xy2d(xybits2, x.into(), y.into()));
let dloc = &data[loc..loc + 16];
(
u64::from_be_bytes(dloc[0..8].try_into().unwrap()),
u64::from_be_bytes(dloc[8..16].try_into().unwrap()),
)
}
pub fn lookup(&self, x: u8, y: u8) -> (u64, u64) {
let xybr = 8 - self.xybits;
Self::lookup_internal(self.data, 2 * self.xybits, x >> xybr, y >> xybr)
}
pub fn iter_dim(&self, dim: X2dhcDim) -> impl Iterator<Item = ((u8, u8), (u64, u64))> + 'a {
let X2dhc { xybits, data } = *self;
let (xybr, xybdup) = (8 - xybits, 2 * xybits);
let xy = dim.value() >> xybr;
let xyv = xy << xybr;
(0u8..=(0xff >> xybr)).map(move |i| {
let xy2 = i << xybr;
let (x, y) = match dim {
X2dhcDim::X(_) => (xyv, xy2),
X2dhcDim::Y(_) => (xy2, xyv),
};
((x, y), Self::lookup_internal(data, xybdup, x, y))
})
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -5,6 +5,7 @@ pub use int_enum::{IntEnum, IntEnumError};
pub mod hash_table;
pub mod hilbert;
pub mod linear_table;
pub const MAGIC: [u8; 4] = [b'Y', b'g', b'L', b'n'];
@ -77,103 +78,6 @@ pub fn trunc_key_at0(key: &[u8]) -> &[u8] {
&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()),
)
}
}

View file

@ -0,0 +1,64 @@
use super::{decode_location, trunc_key_at0};
#[derive(Clone, Copy, Debug)]
pub struct Entry<R> {
pub name: R,
pub typ: u32,
pub location: u32,
pub meta: u32,
pub rest: R,
}
#[derive(Clone, Copy)]
pub struct Ref<'a> {
cklen: usize,
strtab: &'a [u8],
data: &'a [u8],
}
impl<'a> Ref<'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 Ref<'a> {
type Item = Entry<&'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(Entry {
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..],
})
}
}

View file

@ -65,7 +65,7 @@ fn main() {
match &cli.command {
Command::Linear => {
let ltr = yglnk_core::LinearTableRef::parse(data, cli.location)
let ltr = yglnk_core::linear_table::Ref::parse(data, cli.location)
.expect("unable to parse table header");
println!("location\tmeta\t\tname\t\ttyp\t\trest");
for i in ltr {