make sections more self-contained and add basic listing utility

This commit is contained in:
Alain Zscheile 2023-01-08 18:50:04 +01:00
parent 7a67c9eb59
commit e530001a05
6 changed files with 507 additions and 48 deletions

315
Cargo.lock generated
View file

@ -2,6 +2,106 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
[[package]]
name = "clap"
version = "4.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
dependencies = [
"bitflags",
"clap_derive",
"clap_lex",
"is-terminal",
"once_cell",
"strsim",
"termcolor",
]
[[package]]
name = "clap-num"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488557e97528174edaa2ee268b23a809e0c598213a4bbcb4f34575a46fda147e"
dependencies = [
"num-traits",
]
[[package]]
name = "clap_derive"
version = "4.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
[[package]]
name = "int-enum"
version = "0.5.0"
@ -23,12 +123,70 @@ dependencies = [
"syn",
]
[[package]]
name = "io-lifetimes"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "is-terminal"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys",
]
[[package]]
name = "libc"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "memmap2"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc"
dependencies = [
"libc",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
[[package]]
name = "os_str_bytes"
version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "proc-macro-crate"
version = "1.2.1"
@ -40,6 +198,30 @@ dependencies = [
"toml",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.49"
@ -58,12 +240,32 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rustix"
version = "0.36.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "serde"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.107"
@ -75,6 +277,15 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.38"
@ -110,6 +321,100 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
[[package]]
name = "xxhash-rust"
version = "0.8.6"
@ -123,3 +428,13 @@ dependencies = [
"int-enum",
"xxhash-rust",
]
[[package]]
name = "yglnk-ls"
version = "0.1.0"
dependencies = [
"clap",
"clap-num",
"memmap2",
"yglnk-core",
]

View file

@ -10,10 +10,12 @@ pub struct HashTableSettings {
pub struct HashTableHeader {
pub strtab_link: u32,
pub nbuckets: u32,
pub nchains: u32,
pub nblf: u16,
pub settings: HashTableSettings,
}
#[derive(Clone)]
pub struct HashTableRef<'a> {
strtab: &'a [u8],
bloom: &'a [u8],
@ -22,26 +24,33 @@ pub struct HashTableRef<'a> {
settings: HashTableSettings,
}
#[derive(Clone)]
pub struct HashTableIter<'a> {
strtab: &'a [u8],
chains: &'a [u8],
}
impl HashTableHeader {
pub fn parse(data: &[u8]) -> Option<Self> {
if data.len() < 16 {
if data.len() < 20 {
return None;
}
let mut seed = u64::from(data[11]) << 32;
seed += u64::from(u32::from_be_bytes(data[12..16].try_into().unwrap()));
let mut seed = u64::from(data[15]) << 32;
seed += u64::from(u32::from_be_bytes(data[16..20].try_into().unwrap()));
Some(Self {
strtab_link: u32::from_be_bytes(data[0..4].try_into().unwrap()),
nbuckets: u32::from_be_bytes(data[4..8].try_into().unwrap()),
nblf: u16::from_be_bytes(data[8..10].try_into().unwrap()),
nchains: u32::from_be_bytes(data[8..12].try_into().unwrap()),
nblf: u16::from_be_bytes(data[12..14].try_into().unwrap()),
settings: HashTableSettings {
blshift: data[10],
blshift: data[14],
seed,
},
})
}
pub fn tabsize(&self) -> usize {
let tmp: usize = 4 + 2 * usize::try_from(self.nbuckets).unwrap() + usize::from(self.nblf);
let tmp: usize = 4 + usize::try_from(self.nbuckets).unwrap() + 8 * usize::try_from(self.nchains).unwrap() + 2 * usize::from(self.nblf);
4 * tmp
}
}
@ -58,25 +67,24 @@ pub fn hash_trf(h: u64, items: usize, div: usize) -> usize {
impl<'a> HashTableRef<'a> {
/// `location` should be the offset where the hash table is present (in units of 16 bytes)
pub fn parse(data: &'a [u8], location: u32, entsize: u16, entcount: u16) -> Option<Self> {
pub fn parse(data: &'a [u8], location: u32) -> Option<Self> {
let alldata = data;
let uf = <usize as TryFrom<u32>>::try_from;
let offset = 16 * uf(location).unwrap();
let data = &data[offset..offset + 16 * usize::from(entsize) * usize::from(entcount)];
let offset = crate::decode_location(location);
let data = data.get(offset..)?;
let header = HashTableHeader::parse(data)?;
if data.len() < header.tabsize() {
return None;
}
let data = data.get(..header.tabsize())?;
let bloom_end: usize = 16 + 8 * usize::from(header.nblf);
let buckets_end = bloom_end + 4 * uf(header.nbuckets).unwrap();
let chains_end = buckets_end + 32 * uf(header.nchains).unwrap();
Some(HashTableRef {
settings: header.settings,
strtab: alldata.get(usize::try_from(header.strtab_link).ok()?..)?,
bloom: &data[16..bloom_end],
buckets: &data[bloom_end..buckets_end],
chains: &data[buckets_end..],
chains: &data[buckets_end..chains_end],
})
}
@ -122,6 +130,34 @@ impl<'a> HashTableRef<'a> {
return None;
}
pub fn iter(&self) -> HashTableIter<'a> {
HashTableIter {
strtab: self.strtab,
chains: self.chains,
}
}
}
impl<'a> Iterator for HashTableIter<'a> {
type Item = (u64, &'a [u8], u32, u64, u64);
fn next(&mut self) -> Option<Self::Item> {
if self.chains.len() < 32 {
return None;
}
let (i, j) = self.chains.split_at(32);
self.chains = j;
let e_name_ix = usize::try_from(u32::from_be_bytes(i[8..12].try_into().unwrap())).ok()?;
let e_name = trunc_key_at0(self.strtab.get(e_name_ix..)?);
Some((
u64::from_be_bytes(i[0..8].try_into().unwrap()),
e_name,
u32::from_be_bytes(i[12..16].try_into().unwrap()),
u64::from_be_bytes(i[16..24].try_into().unwrap()),
u64::from_be_bytes(i[24..32].try_into().unwrap()),
))
}
}
impl HashTableSettings {

View file

@ -1,7 +1,7 @@
#![no_std]
#![forbid(unsafe_code)]
use int_enum::IntEnum;
pub use int_enum::{IntEnum, IntEnumError};
pub mod hilbert;
@ -18,6 +18,7 @@ pub enum FileType {
Text = 0x0000_0001,
}
#[derive(Clone, Copy, Debug)]
pub struct FileHeader {
pub magic: [u8; 4],
pub generator: u32,
@ -75,30 +76,69 @@ pub enum Ntt01 {
#[derive(Clone, Copy, Debug)]
pub struct LinearTableEntry<R> {
pub name: u32,
pub name: R,
pub typ: u32,
pub location: u32,
pub entsize: u16,
pub entcount: u16,
pub meta: u32,
pub rest: R,
}
pub fn linear_table_iter(
data: &[u8],
entsize: u16,
entcount: u16,
) -> impl Iterator<Item = LinearTableEntry<&'_ [u8]>> {
assert!(entsize > 0);
data.chunks(usize::from(entsize) * 16)
.take(entcount.into())
.map(|i| LinearTableEntry {
name: u32::from_be_bytes(i[0..4].try_into().unwrap()),
#[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 = decode_location(u32::from_be_bytes(i[0..4].try_into().unwrap()));
let name = trunc_key_at0(&self.strtab[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()),
entsize: u16::from_be_bytes(i[12..14].try_into().unwrap()),
entcount: u16::from_be_bytes(i[14..16].try_into().unwrap()),
meta: u32::from_be_bytes(i[12..16].try_into().unwrap()),
rest: &i[16..],
})
}
}
#[derive(Clone, Copy)]
@ -108,11 +148,7 @@ pub struct X2dhc<'a> {
}
impl<'a> X2dhc<'a> {
pub fn parse(data: &'a [u8], entsize: u16, entcount: u16) -> Option<Self> {
if entsize != 1 {
return None;
}
let data = data.get(..(16 * usize::from(entcount)))?;
pub fn parse(data: &'a [u8]) -> Option<Self> {
if data.len() < 32 {
return None;
}
@ -121,19 +157,16 @@ impl<'a> X2dhc<'a> {
return None;
}
let exp_len = 1usize.checked_shl(xybits.into())?.checked_mul(16)?;
if (data.len() - 16) != exp_len {
return None;
}
Some(Self {
xybits,
data: &data[16..],
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 = 16 * usize::try_from(hilbert::xy2d(2 * self.xybits, x.into(), y.into())).unwrap();
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()),

View file

@ -0,0 +1,14 @@
[package]
name = "yglnk-ls"
version = "0.1.0"
edition = "2021"
license = "Apache-2.0"
[dependencies]
clap-num = "1.0"
memmap2 = "0.5"
yglnk-core.path = "../yglnk-core"
[dependencies.clap]
version = "4.0"
features = ["derive"]

View file

@ -0,0 +1,62 @@
use clap::Subcommand;
use clap_num::maybe_hex;
use std::path::PathBuf;
#[derive(clap::Parser)]
struct Cli {
/// the file to operate on
#[arg(short, long)]
file: PathBuf,
/// section location
#[arg(short, long, default_value_t = 2, value_parser = maybe_hex::<u32>)]
location: u32,
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
/// list linear table
Linear,
/// list hash table
Hash,
/// list 2D "hilbert curve" table
X2dhc,
}
fn typ_to_txt(typ: u32) -> String {
use yglnk_core::IntEnum;
match yglnk_core::Type::from_int(typ) {
Ok(x) => format!("{:?}", x),
Err(_) => format!("{:08x}", x),
}
}
fn main() {
let cli = <Cli as clap::Parser>::parse();
let fh = std::fs::File::open(&cli.file).expect("unable to open specified file");
let data = unsafe { memmap2::MmapOptions::new().map(&fh)? };
match &cli.command {
Command::Linear => {
let ltr = yglnk_core::LinearTableRef::parse(data, cli.location).expect("unable to parse table header");
println!("name\ttyp\tlocation\tmeta\trest");
for i in ltr {
println!("{:?}\t{}\t{:08x}\t{:08x}\t{:x}", ltr.name, typ_to_txt(ltr.typ), ltr.location, ltr.meta, ltr.rest);
}
},
Command::Hash => {
let ht = yglnk_core::HashTableRef::parse(data, cli.location).expect("unable to parse table header");
println!("hash\tname\ttyp\tL\tR");
for (hash, name, typ, val_l, val_r) in ht.iter() {
println!("{:016x}\t{:?}\t{}\t{:016x}\t{:016x}", hash, name, typ_to_txt(typ), val_l, val_r);
}
},
Command::X2dhc => unimplemented!(),
}
}

View file

@ -20,7 +20,6 @@ All integers are encoded as big-endian.
```
header := magic[4b] generator[4b] type[4b] version[4b]
tstr_loc[4b] entsize[2b] entcount[2b] reserved[8b]
```
The file magic at offset 0 is "YgLn" = 0x5967'4c63. The header itself forms a section, and contains the types of the top-level sections. After the first 32 bytes follows the primary linear table.
@ -77,7 +76,8 @@ A hash table or alternatively "hilbert curve" table (see corresponding sections)
A simple list of entries. An entry containing all-zeros indicates the end of the list. The actual location decoded resides at `location << 4`, because names and sections are aligned to 16 byte boundaries. `rest` contains potentially additional data, also aligned to 16 bytes. `entsize * entcount << 4` is the length of each entry.
```
list entry := name[4b] type[4b] location[4b] entsize[2b] entcount[2b] rest[*]
list header := strtab_link[4b] entcount[2b] entsize[2b] reserved[8b]
list entry := name[4b] type[4b] location[4b] meta[4b] rest[*]
```
## Hash tables
@ -85,8 +85,8 @@ list entry := name[4b] type[4b] location[4b] entsize[2b] entcount[2b] rest[*]
A hash table, using 64bit-xxHash. chains are traversed in order, the last bit of the first 8b of a chain entry is used to indicate if another entry follows (0 = last entry), the rest contains the hash.
```
ht header := strtab_link[4b] nbuckets[4b] nblf[2b] blshift[1b] seed[5b]
ht body := bloom[8b * nlbf] buckets[4b * nbuckets] chains[32b **]
ht header := strtab_link[4b] nbuckets[4b] nchains[4b] nblf[2b] blshift[1b] seed[5b]
ht body := bloom[8b * nlbf] buckets[4b * nbuckets] chains[32b * nchains]
chain entry := hash[8b] name[4b] type[4b] value[16b]
```
@ -97,7 +97,7 @@ The reason this table doesn't allow more data per entry is that performance hing
```
2dt header := xybits[1b] reserved[15b]
2dt entry := meta[8b] location[8b]
2dt entry := meta[8b] location[4b] length[4b]
```
# Examples
@ -105,15 +105,14 @@ The reason this table doesn't allow more data per entry is that performance hing
## 01
```
@ 0000
00000000: 5967 4c63 0000 0000 0000 0001 0000 0000
magic generator type version
@ linear table:
00000010: 0000 0005 0001 0002 0000 0000 0000 0000
tstr_loc esiz ecnt reserved
strtab_ln esiz ecnt reserved
@ 0002 linear table:
name type location esiz ecnt
name type location esiz ecnt
00000020: 0000 0001 0000 0010 0000 0004 0001 0001
.strtab strtab