diff --git a/Cargo.lock b/Cargo.lock index 35c3405..222353c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,7 @@ name = "haya_nbt" version = "0.1.0" dependencies = [ "haya_mutf8", + "haya_str", "itoa", "mser", "ryu", diff --git a/haya_data/Cargo.toml b/haya_data/Cargo.toml index a5f9cac..32f6189 100644 --- a/haya_data/Cargo.toml +++ b/haya_data/Cargo.toml @@ -18,7 +18,6 @@ default = [] mser = { workspace = true, default-features = false } [build-dependencies] -mser = { workspace = true, default-features = false } itoa = "1" ryu = "1" nested = "0.1" diff --git a/haya_data/README.md b/haya_data/README.md index acb462d..e785a64 100644 --- a/haya_data/README.md +++ b/haya_data/README.md @@ -18,9 +18,6 @@ fn main() { oak_log::decode((state.id() - block::oak_log.state_index()) as _).axis(), prop_axis_x_y_z::z ); - let state: block_state = encode_state!(oak_log(oak_log::new().with_axis(prop_axis_x_y_z::z))); - assert_eq!(decode_state!(oak_log(state)).axis(), prop_axis_x_y_z::z); - assert_eq!(state.to_fluid().to_fluid(), fluid::empty); let x = block::mud.state_default(); let b = x.to_block(); diff --git a/haya_data/build.rs b/haya_data/build.rs index 6f2ba44..61f7035 100644 --- a/haya_data/build.rs +++ b/haya_data/build.rs @@ -1,12 +1,14 @@ use core::iter::repeat_n; use core::num::NonZeroUsize; -use mser::*; use nested::ZString; use std::collections::HashMap; use std::collections::hash_map::Entry; use std::env::var_os; use std::path::PathBuf; +const V21MAX: usize = 0x1FFFFF; +const V7MAX: usize = 0x7F; + #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[must_use] enum Repr { @@ -56,7 +58,6 @@ fn main() -> std::io::Result<()> { let out = PathBuf::from(var_os("OUT_DIR").unwrap()); let path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("generated"); let mut w = String::with_capacity(0x80000); - let mut name_buf = Vec::with_capacity(0x80000); let mut data = String::with_capacity(0x40000); let mut gen_hash = GenerateHash::new(); @@ -94,33 +95,21 @@ fn main() -> std::io::Result<()> { let gat = read(&mut data, path.join("game_event_tags.txt"))?; let gat = ett.end..ett.end + gat; - let block_names = registries(&mut w, &mut name_buf, &data[reg], &mut gen_hash); - registries(&mut w, &mut name_buf, &data[pac], &mut gen_hash); + let block_names = registries(&mut w, &data[reg], &mut gen_hash); + registries(&mut w, &data[pac], &mut gen_hash); item(&mut w, &data[ite]); - entity(&mut w, &mut name_buf, &data[ent]); - - let bs_repr = block_state( - &mut w, - &mut name_buf, - &data[blo], - &mut gen_hash, - &block_names, - ); - fluid_state(&mut w, &data[flu], bs_repr); + entity(&mut w, &data[ent]); + + let (bs_repr, bl_props, bs_size) = block_state(&mut w, &data[blo], &mut gen_hash, &block_names); + fluid_state(&mut w, &data[flu], bs_repr, &bl_props, &bs_size); block_tags(&mut w, &data[blt]); item_tags(&mut w, &data[itt]); entity_tags(&mut w, &data[ett]); game_event_tags(&mut w, &data[gat]); - w += "const NAMES: &[u8; "; - w += itoa::Buffer::new().format(name_buf.len()); - w += "] = include_bytes!(\""; - w += "data"; - w += ".bin\");\n"; std::fs::write(out.join("data.rs"), w)?; - std::fs::write(out.join("data.bin"), name_buf)?; Ok(()) } @@ -137,12 +126,7 @@ fn version(w: &mut String, data: &str) { *w += ";\n"; } -fn registries<'a>( - w: &mut String, - name_buf: &mut Vec, - data: &'a str, - gen_hash: &mut GenerateHash, -) -> Vec<&'a str> { +fn registries<'a>(w: &mut String, data: &'a str, gen_hash: &mut GenerateHash) -> Vec<&'a str> { let mut zhash = Vec::<&str>::new(); let mut iter = data.split('\n'); let mut block_names = Vec::<&str>::new(); @@ -209,7 +193,7 @@ fn registries<'a>( *w += "::core::option::Option::Some(x) => ::core::result::Result::Ok(x),\n"; *w += "::core::option::Option::None => ::core::result::Result::Err(::mser::Error),\n"; *w += "}\n}\n}\n"; - impl_name(w, gen_hash, name_buf, repr, &zhash, &name); + impl_name(w, gen_hash, repr, &zhash, &name); impl_common(w, &name, repr, size, 0); } block_names @@ -293,7 +277,13 @@ fn impl_common(w: &mut String, name: &str, repr: Repr, size: usize, def: u32) { *w += "}\n"; } -fn fluid_state(w: &mut String, data: &str, bs_repr: Repr) { +fn fluid_state( + w: &mut String, + data: &str, + bs_repr: Repr, + bl_props: &[u32], + bs_size: &[NonZeroUsize], +) { let mut ib = itoa::Buffer::new(); let mut iter = data.split('\n'); let (name, size, repr) = head(iter.next(), "fluid_state"); @@ -312,62 +302,51 @@ fn fluid_state(w: &mut String, data: &str, bs_repr: Repr) { *w += "}\n"; let (_, size, _) = head(iter.next(), "fluid_to_block"); list_ty(w, "FLUID_STATE_TO_BLOCK", bs_repr, size); - list( - w, - (&mut iter) - .take(size) - .map(|x| parse_hex::(x.as_bytes()).0), - ); + list(w, (&mut iter).take(size).map(parse_u32)); *w += ";\n"; let (_, size, _) = head(iter.next(), "fluid_state_level"); list_ty(w, "FLUID_STATE_LEVEL", Repr::U8, size); - list( - w, - (&mut iter) - .take(size) - .map(|x| parse_hex::(x.as_bytes()).0), - ); + list(w, (&mut iter).take(size).map(parse_u32)); *w += ";\n"; let (_, size, _) = head(iter.next(), "fluid_state_falling"); list_ty(w, "FLUID_STATE_FALLING", Repr::U8, size); - list( - w, - (&mut iter) - .take(size) - .map(|x| parse_hex::(x.as_bytes()).0), - ); + list(w, (&mut iter).take(size).map(parse_u32)); *w += ";\n"; let (_, size, _) = head(iter.next(), "fluid_state_to_fluid"); list_ty(w, "FLUID_STATE_TO_FLUID", Repr::U8, size); - list( - w, - (&mut iter) - .take(size) - .map(|x| parse_hex::(x.as_bytes()).0), - ); + list(w, (&mut iter).take(size).map(parse_u32)); *w += ";\n"; let (_, fs_size, fs_repr) = head(iter.next(), "fluid_state_array"); - *w += "const FLUID_STATE_ARRAY: [&["; - *w += fs_repr.to_int(); - *w += "]; "; - *w += ib.format(fs_size); - *w += "] = [\n"; - for x in (&mut iter).take(fs_size).map(|arr| { - arr.split_ascii_whitespace() - .map(|x| parse_hex::(x.as_bytes()).0) - }) { - *w += "&"; - list(w, x); - *w += ",\n"; - } - *w += "];\n"; + let arr = (&mut iter) + .take(fs_size) + .map(|arr| hex_line(arr).collect::>()) + .collect::>(); let (_, size, _) = head(iter.next(), "block_to_fluid_state"); - list_ty(w, "FLUID_STATE_INDEX", fs_repr, size); - list(w, read_rl(size, &mut iter)); + + let mut out = Vec::::new(); + assert_eq!(size, bl_props.len()); + for (bound, prop) in read_rl(size, &mut iter).zip(bl_props.iter().copied()) { + let bounds = &*arr[bound as usize]; + let len = bs_size[prop as usize].get(); + match bounds[..] { + [] => { + out.extend(repeat_n(0, len)); + } + [b] => { + out.extend(repeat_n(b, len)); + } + ref bounds => { + assert_eq!(len, bounds.len()); + out.extend(bounds.iter().copied()); + } + } + } + list_ty(w, "FLUID_STATE_INDEX", fs_repr, out.len()); + list(w, out.into_iter()); *w += ";\n"; } @@ -391,11 +370,7 @@ fn block_tags(w: &mut String, data: &str) { *w += "(self) -> bool {\n"; if list.split_ascii_whitespace().next().is_some() { *w += "matches!(self as raw_block,\n"; - list_match_or( - w, - list.split_ascii_whitespace() - .map(|x| parse_hex::(x.as_bytes()).0), - ); + list_match_or(w, hex_line(list)); *w += "\n)"; } else { *w += "false"; @@ -425,11 +400,7 @@ fn item_tags(w: &mut String, data: &str) { *w += "(self) -> bool {\n"; if list.split_ascii_whitespace().next().is_some() { *w += "matches!(self as raw_item,\n"; - list_match_or( - w, - list.split_ascii_whitespace() - .map(|x| parse_hex::(x.as_bytes()).0), - ); + list_match_or(w, hex_line(list)); *w += "\n)"; } else { *w += "false"; @@ -459,11 +430,7 @@ fn entity_tags(w: &mut String, data: &str) { *w += "(self) -> bool {\n"; if list.split_ascii_whitespace().next().is_some() { *w += "matches!(self as raw_entity_type,\n"; - list_match_or( - w, - list.split_ascii_whitespace() - .map(|x| parse_hex::(x.as_bytes()).0), - ); + list_match_or(w, hex_line(list)); *w += "\n)"; } else { *w += "false"; @@ -493,11 +460,7 @@ fn game_event_tags(w: &mut String, data: &str) { *w += "(self) -> bool {\n"; if list.split_ascii_whitespace().next().is_some() { *w += "matches!(self as raw_game_event,\n"; - list_match_or( - w, - list.split_ascii_whitespace() - .map(|x| parse_hex::(x.as_bytes()).0), - ); + list_match_or(w, hex_line(list)); *w += "\n)"; } else { *w += "false"; @@ -509,11 +472,10 @@ fn game_event_tags(w: &mut String, data: &str) { fn block_state( w: &mut String, - name_buf: &mut Vec, data: &str, gen_hash: &mut GenerateHash, block_names: &[&str], -) -> Repr { +) -> (Repr, Vec, Vec) { let mut ib = itoa::Buffer::new(); let mut iter = data.split('\n'); @@ -548,6 +510,7 @@ fn block_state( } let (name_kv, size_kv, repr_kv) = head(iter.next(), "block_state_property"); + assert!(Repr::U8 == repr_kv); let kv = (0..size_kv) .map(|_| { @@ -559,8 +522,6 @@ fn block_state( vec.into_boxed_slice() }) .collect::>(); - drop(pk3); - drop(pv3); enum_head(w, repr_k, name_k); for &s in &pk2 { @@ -630,7 +591,7 @@ fn block_state( } *w += "}\n"; impl_common(w, name, repr, x.len(), 0); - impl_name(w, gen_hash, name_buf, repr, x, name); + impl_name(w, gen_hash, repr, x, name); val_names.push(name); } @@ -702,8 +663,8 @@ fn block_state( *w += "}\n"; impl_common(w, name_kv, repr_kv, size_kv, 0); - impl_name(w, gen_hash, name_buf, repr_k, &pk2, name_k); - impl_name(w, gen_hash, name_buf, repr_v, &pv2, name_v); + impl_name(w, gen_hash, repr_k, &pk2, name_k); + impl_name(w, gen_hash, repr_v, &pv2, name_v); *w += "impl "; *w += name_kv; @@ -713,11 +674,11 @@ fn block_state( list(w, kv.iter().map(|x| x[0])); *w += ";\n"; - *w += "const V: [&'static ["; + *w += "const V: &[&'static ["; *w += repr_v.to_int(); *w += "]; "; *w += ib.format(size_kv); - *w += "] = ["; + *w += "] = &["; for data in &kv { *w += "&["; for &v in &data[1..] { @@ -1128,11 +1089,11 @@ fn block_state( list(w, bl_props.iter().copied()); *w += ";\n"; - *w += "const PROPS: [&'static ["; + *w += "const PROPS: &[&["; *w += repr_kv.to_int(); *w += "]; "; *w += ib.format(bs_properties.len()); - *w += "] = [\n"; + *w += "] = &[\n"; for prop in &bs_properties { *w += "&["; for &x in &**prop { @@ -1229,7 +1190,7 @@ fn block_state( *w += "}\n"; let (_, size, _) = head(iter.next(), "block_item_to_block"); - *w += "const ITEM: [raw_block; item::MAX as usize + 1] = "; + *w += "const ITEM: &[raw_block; item::MAX as usize + 1] = "; let mut out = Vec::with_capacity(size); let _: u32 = read_rl(size, &mut iter).fold(0, |prev, x| { out.push(x.wrapping_add(prev)); @@ -1242,35 +1203,27 @@ fn block_state( let mut f32t = Vec::with_capacity(size); for _ in 0..size { - let (x, _) = parse_hex::(iter.next().unwrap().as_bytes()); + let x = parse_u32(iter.next().unwrap()); f32t.push(x); } let (_, size, _) = head(iter.next(), "float64_table"); let mut f64t = Vec::with_capacity(size); for _ in 0..size { - let (x, _) = parse_hex::(iter.next().unwrap().as_bytes()); + let x = parse_u64(iter.next().unwrap()); let x = f64::from_be_bytes(x.to_be_bytes()); f64t.push(x); } let (_, size, shape_repr) = head(iter.next(), "shape_table"); - *w += "const SHAPES: [&[[f64; 6]]; "; + *w += "const SHAPES: &[&[[f64; 6]]; "; *w += ib.format(size); - *w += "] = ["; + *w += "] = &["; let mut shape = Vec::new(); let mut rb = ryu::Buffer::new(); for _ in 0..size { - let mut s = iter.next().unwrap().as_bytes(); - loop { - let (x, y) = parse_hex::(s); - if y == 0 { - break; - } - s = &s[y..]; - if let [b' ', rest @ ..] = s { - s = rest; - } + let s = hex_line(iter.next().unwrap()); + for x in s { shape.push(f64t[x as usize]); } *w += "&["; @@ -1303,23 +1256,19 @@ fn block_state( let mut bs_ettings = Vec::with_capacity(size); for _ in 0..size { - let mut s = iter.next().unwrap().as_bytes(); + let mut s = hex_line(iter.next().unwrap()); let mut settings = [0_u32; 5]; for s1 in &mut settings { - let (x, y) = parse_hex::(s); - s = &s[y..]; - if let [b' ', rest @ ..] = s { - s = rest; - } + let x = s.next().unwrap(); *s1 = f32t[x as usize]; } bs_ettings.push(settings); } - *w += "const BLOCK_SETTINGS: ["; + *w += "const BLOCK_SETTINGS: &["; *w += "[f32; 5]"; *w += "; "; *w += ib.format(size); - *w += "] = ["; + *w += "] = &["; for &x in bs_ettings.iter() { *w += "["; for x in x { @@ -1360,22 +1309,18 @@ fn block_state( list_ty(w, "BLOCK_STATE_BOUNDS", Repr::U64, size); let mut out = Vec::with_capacity(size); for _ in 0..size { - let mut s = iter.next().unwrap().as_bytes(); + let s = iter.next().unwrap(); if s.is_empty() { out.push(0u64); continue; } - let (n1, y) = parse_hex::(s); - s = &s[y + 1..]; - let (n2, y) = parse_hex::(s); - s = &s[y + 1..]; - let (n3, y) = parse_hex::(s); - s = &s[y + 1..]; - let (n4, y) = parse_hex::(s); - s = &s[y + 1..]; - let (m5, y) = parse_hex::(s); - s = &s[y + 1..]; - let (m6, _) = parse_hex::(s); + let mut line = hex_line(s); + let n1 = line.next().unwrap() as u8; + let n2 = line.next().unwrap() as u8; + let n3 = line.next().unwrap() as u8; + let n4 = line.next().unwrap() as u8; + let m5 = line.next().unwrap() as u16; + let m6 = line.next().unwrap() as u16; let [n5, n6] = m5.to_le_bytes(); let [n7, n8] = m6.to_le_bytes(); let v = u64::from_le_bytes([n1, n2, n3, n4, n5, n6, n7, n8]); @@ -1388,11 +1333,7 @@ fn block_state( let prop_bounds = (&mut iter) .take(bsb_size) - .map(|arr| { - arr.split_ascii_whitespace() - .map(|x| parse_hex::(x.as_bytes()).0) - .collect::>() - }) + .map(|arr| hex_line(arr).collect::>()) .collect::>(); let (_, size, _) = head(iter.next(), "block_state_static_bounds"); @@ -1400,14 +1341,10 @@ fn block_state( list_ty(w, "BLOCK_STATE_BOUNDS_INDEX", Repr::U16, bs_size); let mut out = Vec::with_capacity(bs_size); - for (i, b_idx) in read_rl(size, &mut iter).enumerate() { - let bounds = &*prop_bounds[b_idx as usize]; - let props = &*bs_properties[bl_props[i] as usize]; - let mut len = 1; - for &prop in props { - let prop = &*kv[prop as usize]; - len *= prop.len() - 1; - } + assert_eq!(size, bl_props.len()); + for (bounds, prop) in read_rl(size, &mut iter).zip(bl_props.iter().copied()) { + let bounds = &*prop_bounds[bounds as usize]; + let len = bs_prop_size[prop as usize].get(); match bounds[..] { [] => { out.extend(repeat_n(0, len)); @@ -1425,59 +1362,37 @@ fn block_state( list(w, out.into_iter()); *w += ";\n"; - bs_repr + (bs_repr, bl_props, bs_prop_size) } fn item(w: &mut String, data: &str) { let mut iter = data.split('\n'); let (_, size, _) = head(iter.next(), "item_max_count"); list_ty(w, "ITEM_MAX_COUNT", Repr::U8, size); - let mut x = size; - let mut out = Vec::new(); - loop { - if x == 0 { - break; - } - let next = iter.next().unwrap().as_bytes(); - let (n, count) = match next.first().copied() { - Some(b'~') => { - let (a, b) = parse_hex::(&next[1..]); - let next = &next[b + 2..]; - let n = parse_hex::(next); - (n.0, a as usize) - } - _ => { - let n = parse_hex::(next); - (n.0, 1) - } - }; - out.extend(core::iter::repeat_n(n, count)); - x = x.checked_sub(count).unwrap(); - } - list(w, out.iter().copied()); + list(w, read_rl(size, &mut iter).map(|x| x as u8)); *w += ";\n"; } -fn entity(w: &mut String, _: &mut [u8], data: &str) { +fn entity(w: &mut String, data: &str) { let mut ib = itoa::Buffer::new(); let mut iter = data.split('\n'); let (_, size, _) = head(iter.next(), "entity_type_height"); - *w += "const ENTITY_HEIGHT: [f32; "; + *w += "const ENTITY_HEIGHT: &[f32; "; *w += ib.format(size); *w += "] = "; list(w, read_rl(size, &mut iter).map(f32::from_bits)); *w += ";\n"; let (_, size, _) = head(iter.next(), "entity_type_width"); - *w += "const ENTITY_WIDTH: [f32; "; + *w += "const ENTITY_WIDTH: &[f32; "; *w += ib.format(size); *w += "] = "; list(w, read_rl(size, &mut iter).map(f32::from_bits)); *w += ";\n"; let (_, size, _) = head(iter.next(), "entity_type_fixed"); - *w += "const ENTITY_FIXED: [u8; "; + *w += "const ENTITY_FIXED: &[u8; "; *w += ib.format(size); *w += "] = "; list(w, read_rl(size, &mut iter)); @@ -1491,7 +1406,7 @@ fn head<'a>(raw: Option<&'a str>, expected: &str) -> (&'a str, usize, Repr) { }; let (name, rest) = first.split_once(';').unwrap(); let (_ty, size) = rest.split_once(';').unwrap(); - let (size, _) = parse_hex::(size.as_bytes()); + let size = parse_u32(size); let size = size as usize; if !expected.is_empty() { assert_eq!(expected, name); @@ -1499,14 +1414,7 @@ fn head<'a>(raw: Option<&'a str>, expected: &str) -> (&'a str, usize, Repr) { (name, size, Repr::new(size)) } -fn impl_name( - w: &mut String, - g: &mut GenerateHash, - name_buf: &mut Vec, - repr: Repr, - names: &[&str], - name: &str, -) { +fn impl_name(w: &mut String, g: &mut GenerateHash, repr: Repr, names: &[&str], name: &str) { let mut ib = itoa::Buffer::new(); *w += "impl "; *w += name; @@ -1524,41 +1432,20 @@ fn impl_name( list_ty(w, "VALS", repr, state.map.len()); list(w, state.map.iter().map(|&ele| ele.unwrap())); *w += ";\n"; - while !name_buf.len().is_multiple_of(8) { - name_buf.push(0); - } - let start = name_buf.len(); - name_buf.reserve(names.len() * 24); - let mut offset = names.len() * 8; - for &name in names { - let a = offset as u32; - let b = name.len() as u32; - let c = (a as u64) | ((b as u64) << 32); - name_buf.extend(c.to_le_bytes()); - offset += name.len(); - } + *w += "const N: &[&str; "; + *w += ib.format(names.len()); + *w += "] = &[\n"; for &val in names { - name_buf.extend(val.as_bytes()); + *w += "\""; + *w += val; + *w += "\",\n"; } - *w += "const N: *const u8 = "; - if start != 0 { - *w += "unsafe { NAMES.as_ptr().add("; - *w += ib.format(start); - *w += ") }" - } else { - *w += "NAMES.as_ptr()"; - } - *w += ";\n"; + *w += "];\n"; *w += "#[inline] #[must_use] pub const fn name(self) -> &'static str { unsafe { -let packed = u64::from_le_bytes(*Self::N.add(8 * self as usize).cast::<[u8; 8]>()); -let len = (packed >> 32) as usize; -let offset = (packed as u32) as usize; -::core::str::from_utf8_unchecked( -::core::slice::from_raw_parts(Self::N.add(offset), len) -) +*Self::N.as_ptr().add(self as usize) } } #[inline] @@ -1584,7 +1471,7 @@ pub fn parse(name: &[u8]) -> ::core::option::Option {\n"; _ => unimplemented!(), } - *w += ">(Self::DISPS, Self::N, Self::VALS, name) {\n"; + *w += ">(Self::DISPS, Self::N.as_ptr(), Self::VALS, name) {\n"; *w += "::core::option::Option::Some(x) => unsafe { ::core::option::Option::Some(::core::mem::transmute::<"; *w += repr.to_int(); *w += ", Self>(x)) }, @@ -1634,10 +1521,9 @@ fn struct_head(w: &mut String, repr: Repr, name: &str) { *w += ");\n"; } -fn hex_line(x: &str) -> impl Iterator + '_ { - x.as_bytes() - .split(|&x| x == b' ') - .map(|x| parse_hex::(x).0) +fn hex_line(x: &str) -> impl Iterator + Clone + '_ { + x.split_ascii_whitespace() + .map(|n| u32::from_str_radix(n, 16).expect("parse hex")) } struct GenerateHash { @@ -1777,18 +1663,18 @@ impl<'a, T: Iterator> Iterator for RunLength { if self.take == 0 { None } else if self.count == 0 { - let next = self.i.next()?.as_bytes(); + let next = self.i.next()?; self.take -= 1; - match next.first().copied() { - Some(b'~') => { - let (a, b) = parse_hex::(&next[1..]); - let next = &next[b + 2..]; - let (ctx, _) = parse_hex::(next); + match next.strip_prefix('~') { + Some(rest) => { + let mut rest = hex_line(rest); + let a = rest.next().unwrap(); + let ctx = rest.next().unwrap(); self.count = a as usize - 1; self.prev = ctx; Some(ctx) } - _ => Some(parse_hex::(next).0), + _ => Some(parse_u32(next)), } } else { self.take -= 1; @@ -1811,16 +1697,11 @@ fn list_ty(w: &mut String, name: &str, repr: Repr, size: usize) { *w += "#[allow(clippy::large_const_arrays)]\n"; *w += "const "; *w += name; - w.push(':'); - w.push(' '); - w.push('['); + *w += ": &["; *w += repr.to_int(); *w += "; "; *w += itoa::Buffer::new().format(size); - w.push(']'); - w.push(' '); - w.push('='); - w.push(' '); + *w += "] = "; } fn list(w: &mut String, mut iter: impl Iterator) { @@ -1828,14 +1709,12 @@ fn list(w: &mut String, mut iter: impl Iterator) { let first = match first { Some(x) => x, None => { - w.push('['); - w.push(']'); + *w += "&[]"; return; } }; let mut c = 0usize; - w.push('['); - w.push('\n'); + *w += "&[\n"; let mut b = itoa::Buffer::new(); let mut r = ryu::Buffer::new(); first.format(w, &mut b, &mut r); @@ -1949,3 +1828,30 @@ impl Format for f32 { w.push_str(b.format(*self)); } } + +fn parse_u32(n: &str) -> u32 { + u32::from_str_radix(n.trim_ascii(), 16).expect("parse hex") +} + +fn parse_u64(n: &str) -> u64 { + u64::from_str_radix(n.trim_ascii(), 16).expect("parse hex") +} + +const fn hash128(n: &[u8], seed: u64) -> [u64; 2] { + const M: u64 = 0xc6a4a7935bd1e995; + const N: u128 = 0xdbe6d5d5fe4cce213198a2e03707344u128; + let mut h: u64 = seed ^ ((n.len() as u64).wrapping_mul(M)); + let mut i = 0; + while i + 8 <= n.len() { + h ^= u64::from_le_bytes(unsafe { *(n.as_ptr().add(i) as *const [u8; 8]) }).wrapping_mul(M); + i += 8; + } + while i < n.len() { + h ^= (unsafe { *n.as_ptr().add(i) } as u64) << ((i & 7) * 8); + i += 1; + } + let h = (h as u128).wrapping_mul(N); + let h = h ^ (h >> 64); + let h = h.wrapping_mul(N); + [(h >> 64) as u64, h as u64] +} diff --git a/haya_data/src/lib.rs b/haya_data/src/lib.rs index a58b7d5..5675ec4 100644 --- a/haya_data/src/lib.rs +++ b/haya_data/src/lib.rs @@ -2,34 +2,15 @@ #![allow(non_camel_case_types, clippy::manual_map, non_upper_case_globals)] use core::hint::assert_unchecked; -use mser::{cold_path, hash128}; +use mser::cold_path; include!(concat!(env!("OUT_DIR"), "/data.rs")); -/// `block_name(prop_expr)` -#[macro_export] -macro_rules! encode_state { - ($b:ident($x:expr)) => { - $crate::block_state::new( - $x.encode() as $crate::raw_block_state + $crate::block::$b.state_index(), - ) - .unwrap() - }; -} - -/// `block_name(prop_expr)` -#[macro_export] -macro_rules! decode_state { - ($b:ident($x:expr)) => { - $crate::$b::decode(($x.id() - $crate::block::$b.state_index()) as _) - }; -} - #[inline] fn name_u8( - disps: [u64; N], - names: *const u8, - vals: [u8; M], + disps: &'static [u64; N], + names: *const &'static str, + vals: &'static [u8; M], name: &[u8], ) -> Option { let [a, b] = hash128(name, K); @@ -41,18 +22,15 @@ fn name_u8( let d2 = d as u32; let index = d2.wrapping_add(f1.wrapping_mul(d1)).wrapping_add(f2) % (M as u32); let v = unsafe { *vals.get_unchecked(index as usize) }; - let packed = unsafe { u64::from_le_bytes(*names.add(8 * v as usize).cast::<[u8; 8]>()) }; - let len = (packed >> 32) as usize; - let offset = (packed as u32) as usize; - let k = unsafe { core::slice::from_raw_parts(names.add(offset), len) }; - if name == k { Some(v) } else { None } + let k = unsafe { *names.add(v as usize) }; + if name == k.as_bytes() { Some(v) } else { None } } #[inline] fn name_u16( - disps: [u64; N], - names: *const u8, - vals: [u16; M], + disps: &'static [u64; N], + names: *const &'static str, + vals: &'static [u16; M], name: &[u8], ) -> Option { let [a, b] = hash128(name, K); @@ -65,11 +43,8 @@ fn name_u16( let index = d2.wrapping_add(f1.wrapping_mul(d1)).wrapping_add(f2); let index = (index % (M as u32)) as usize; let v = unsafe { *vals.get_unchecked(index) }; - let packed = unsafe { u64::from_le_bytes(*names.add(8 * v as usize).cast::<[u8; 8]>()) }; - let len = (packed >> 32) as usize; - let offset = (packed as u32) as usize; - let k = unsafe { core::slice::from_raw_parts(names.add(offset), len) }; - if name == k { Some(v) } else { None } + let k = unsafe { *names.add(v as usize) }; + if name == k.as_bytes() { Some(v) } else { None } } fn make_block_state( @@ -196,23 +171,7 @@ impl block_state { #[inline] pub const fn to_fluid(self) -> fluid_state { - unsafe { - // 1 - let b = self.to_block(); - // 2 - let i = *FLUID_STATE_INDEX.as_ptr().add(b as usize); - // 3 - let a = *FLUID_STATE_ARRAY.as_ptr().add(i as usize); - if a.len() == 1 { - // 4 - fluid_state(*a.as_ptr()) - } else { - // 4 - let o = b.state_index(); - // 5 - fluid_state(*a.as_ptr().add((self.id() - o) as usize)) - } - } + unsafe { fluid_state(*FLUID_STATE_INDEX.as_ptr().add(self.0 as usize)) } } #[inline] @@ -623,123 +582,176 @@ impl core::fmt::Debug for fluid_state { } } -#[test] -fn test_block_state() { - assert!( - block_state::new(block_state::MAX) - .unwrap() - .collision_shape() - .is_some() - ); - - assert_eq!(game_event::block_activate.name(), "block_activate"); - assert_eq!( - sound_event::block_bamboo_wood_pressure_plate_click_on, - sound_event::parse(b"block.bamboo_wood_pressure_plate.click_on").unwrap() - ); - - let white_concrete_bs = encode_state!(white_concrete(white_concrete::new())); - assert_eq!(white_concrete_bs.side_solid_full(), Some(0b111111)); - assert_eq!(white_concrete_bs.side_solid_rigid(), Some(0b111111)); - assert_eq!(white_concrete_bs.side_solid_center(), Some(0b111111)); - assert_eq!(white_concrete_bs.full_cube(), Some(true)); - assert_eq!(white_concrete_bs.solid(), Some(true)); - assert!(white_concrete_bs.tool_required()); - - let b = white_concrete_bs.to_block(); - assert_eq!(b.name(), "white_concrete"); - assert_eq!(Some(b), block::parse(b.name().as_bytes())); - - let air_bl = block::air; - assert_eq!(air_bl.name(), "air"); - assert_eq!(Some(air_bl), block::parse(air_bl.name().as_bytes())); - - let air_bs = air_bl.state_default(); - assert_eq!(air_bs.side_solid_full(), Some(0)); - assert_eq!(air_bs.side_solid_rigid(), Some(0)); - assert_eq!(air_bs.side_solid_center(), Some(0)); - assert_eq!(air_bs.full_cube(), Some(false)); - - let oak_sapling_bs = encode_state!(oak_sapling(oak_sapling::new())); - let b = oak_sapling_bs.to_block(); - assert_eq!(b.name(), "oak_sapling"); - assert_eq!(Some(b), block::parse(b.name().as_bytes())); - - assert_eq!(oak_sapling_bs.side_solid_full(), Some(0)); - assert_eq!(oak_sapling_bs.side_solid_rigid(), Some(0)); - assert_eq!(oak_sapling_bs.side_solid_center(), Some(0)); - assert_eq!(oak_sapling_bs.full_cube(), Some(false)); - - let mud_bs = block::mud.state_default(); - let b = mud_bs.to_block(); - assert_eq!(b.name(), "mud"); - assert_eq!(Some(b), block::parse(b"mud")); - - assert_eq!(mud_bs.side_solid_full(), Some(0b111111)); - assert_eq!(mud_bs.side_solid_rigid(), Some(0b111111)); - assert_eq!(mud_bs.side_solid_center(), Some(0b111111)); - assert!(!mud_bs.redstone_power_source()); - assert_eq!(mud_bs.opaque_full_cube(), Some(true)); - assert_eq!(mud_bs.full_cube(), Some(false)); - - let cactus_bs = block::cactus.state_default(); - assert_eq!(cactus_bs.side_solid_full(), Some(0b000000)); - assert_eq!(cactus_bs.side_solid_rigid(), Some(0b000000)); - assert_eq!(cactus_bs.side_solid_center(), Some(0b000001)); - assert_eq!(cactus_bs.full_cube(), Some(false)); - assert!(!cactus_bs.exceeds_cube()); - assert!(!cactus_bs.tool_required()); - assert_eq!(cactus_bs.full_cube(), Some(false)); - assert_eq!( - block_state::parse(block::redstone_wire, &mut [][..]), - block::redstone_wire.state_default() - ); - assert_eq!( - block_state::parse( - block::redstone_wire, - &mut [( - block_state_property_key::parse(b"east").unwrap(), - block_state_property_value::parse(b"side").unwrap() - )][..] - ), - encode_state!(redstone_wire( - decode_state!(redstone_wire(block::redstone_wire.state_default())) - .with_east(prop_east_u_s_n::side) - )) - ); - assert_eq!( - block_state::parse( - block::redstone_wire, - &mut [ - ( +const fn hash128(n: &[u8], seed: u64) -> [u64; 2] { + const M: u64 = 0xc6a4a7935bd1e995; + const N: u128 = 0xdbe6d5d5fe4cce213198a2e03707344u128; + let mut h: u64 = seed ^ ((n.len() as u64).wrapping_mul(M)); + let mut i = 0; + while i + 8 <= n.len() { + h ^= u64::from_le_bytes(unsafe { *(n.as_ptr().add(i) as *const [u8; 8]) }).wrapping_mul(M); + i += 8; + } + while i < n.len() { + h ^= (unsafe { *n.as_ptr().add(i) } as u64) << ((i & 7) * 8); + i += 1; + } + let h = (h as u128).wrapping_mul(N); + let h = h ^ (h >> 64); + let h = h.wrapping_mul(N); + [(h >> 64) as u64, h as u64] +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_parse() { + assert_eq!(game_event::block_activate.name(), "block_activate"); + assert_eq!( + sound_event::block_bamboo_wood_pressure_plate_click_on, + sound_event::parse(b"block.bamboo_wood_pressure_plate.click_on").unwrap() + ); + } + + #[test] + fn test_air() { + let air_bl = block::air; + assert_eq!(air_bl.name(), "air"); + assert_eq!(Some(air_bl), block::parse(air_bl.name().as_bytes())); + + let air_bs = air_bl.state_default(); + assert_eq!(air_bs.side_solid_full(), Some(0)); + assert_eq!(air_bs.side_solid_rigid(), Some(0)); + assert_eq!(air_bs.side_solid_center(), Some(0)); + assert_eq!(air_bs.full_cube(), Some(false)); + } + + #[test] + fn test_block_state() { + assert!( + block::firefly_bush + .state_default() + .collision_shape() + .is_some() + ); + + let white_concrete_bs = block_state::new( + (white_concrete::new()).encode() as raw_block_state + + block::white_concrete.state_index(), + ) + .unwrap(); + assert_eq!(white_concrete_bs.side_solid_full(), Some(0b111111)); + assert_eq!(white_concrete_bs.side_solid_rigid(), Some(0b111111)); + assert_eq!(white_concrete_bs.side_solid_center(), Some(0b111111)); + assert_eq!(white_concrete_bs.full_cube(), Some(true)); + assert_eq!(white_concrete_bs.solid(), Some(true)); + assert!(white_concrete_bs.tool_required()); + + let b = white_concrete_bs.to_block(); + assert_eq!(b.name(), "white_concrete"); + assert_eq!(Some(b), block::parse(b.name().as_bytes())); + + let oak_sapling_bs = block_state::new( + (oak_sapling::new()).encode() as raw_block_state + block::oak_sapling.state_index(), + ) + .unwrap(); + let b = oak_sapling_bs.to_block(); + assert_eq!(b.name(), "oak_sapling"); + assert_eq!(Some(b), block::parse(b.name().as_bytes())); + + assert_eq!(oak_sapling_bs.side_solid_full(), Some(0)); + assert_eq!(oak_sapling_bs.side_solid_rigid(), Some(0)); + assert_eq!(oak_sapling_bs.side_solid_center(), Some(0)); + assert_eq!(oak_sapling_bs.full_cube(), Some(false)); + + let mud_bs = block::mud.state_default(); + let b = mud_bs.to_block(); + assert_eq!(b.name(), "mud"); + assert_eq!(Some(b), block::parse(b"mud")); + + assert_eq!(mud_bs.side_solid_full(), Some(0b111111)); + assert_eq!(mud_bs.side_solid_rigid(), Some(0b111111)); + assert_eq!(mud_bs.side_solid_center(), Some(0b111111)); + assert!(!mud_bs.redstone_power_source()); + assert_eq!(mud_bs.opaque_full_cube(), Some(true)); + assert_eq!(mud_bs.full_cube(), Some(false)); + + let cactus_bs = block::cactus.state_default(); + assert_eq!(cactus_bs.side_solid_full(), Some(0b000000)); + assert_eq!(cactus_bs.side_solid_rigid(), Some(0b000000)); + assert_eq!(cactus_bs.side_solid_center(), Some(0b000001)); + assert_eq!(cactus_bs.full_cube(), Some(false)); + assert!(!cactus_bs.exceeds_cube()); + assert!(!cactus_bs.tool_required()); + assert_eq!(cactus_bs.full_cube(), Some(false)); + assert_eq!( + block_state::parse(block::redstone_wire, &mut [][..]), + block::redstone_wire.state_default() + ); + let a = block_state::new( + (redstone_wire::decode( + ((block::redstone_wire.state_default()).id() - block::redstone_wire.state_index()) + as _, + ) + .with_east(prop_east_u_s_n::side)) + .encode() as raw_block_state + + block::redstone_wire.state_index(), + ) + .unwrap(); + assert_eq!( + block_state::parse( + block::redstone_wire, + &mut [( block_state_property_key::parse(b"east").unwrap(), block_state_property_value::parse(b"side").unwrap() - ), - ( - block_state_property_key::parse(b"power").unwrap(), - block_state_property_value::parse(b"11").unwrap() - ) - ][..] - ), - encode_state!(redstone_wire( - decode_state!(redstone_wire(block::redstone_wire.state_default())) - .with_east(prop_east_u_s_n::side) - .with_power(prop_power::d_11) - )) - ); - assert_eq!( - block::spruce_slab.state_default().to_fluid(), - fluid_state::empty - ); - assert_eq!( - encode_state!(spruce_slab( - decode_state!(spruce_slab(block::spruce_slab.state_default())) - .with_waterlogged(prop_waterlogged::r#true) - )) - .to_fluid(), - fluid_state::water_s_8 - ); - assert!(!block::dispenser.is_air()); - assert!(block::dispenser.state_default().opaque_full_cube().unwrap()); - assert_eq!(block::fire.state_default().opacity().unwrap(), 0); + )][..] + ), + a + ); + let a = block_state::new( + (redstone_wire::decode( + ((block::redstone_wire.state_default()).id() - block::redstone_wire.state_index()) + as _, + ) + .with_east(prop_east_u_s_n::side) + .with_power(prop_power::d_11)) + .encode() as raw_block_state + + block::redstone_wire.state_index(), + ) + .unwrap(); + assert_eq!( + block_state::parse( + block::redstone_wire, + &mut [ + ( + block_state_property_key::parse(b"east").unwrap(), + block_state_property_value::parse(b"side").unwrap() + ), + ( + block_state_property_key::parse(b"power").unwrap(), + block_state_property_value::parse(b"11").unwrap() + ) + ][..] + ), + a + ); + assert_eq!( + block::spruce_slab.state_default().to_fluid(), + fluid_state::empty + ); + let a = block_state::new( + (spruce_slab::decode( + ((block::spruce_slab.state_default()).id() - block::spruce_slab.state_index()) as _, + ) + .with_waterlogged(prop_waterlogged::r#true)) + .encode() as raw_block_state + + block::spruce_slab.state_index(), + ) + .unwrap(); + assert_eq!(a.to_fluid(), fluid_state::water_s_8); + assert!(!block::dispenser.is_air()); + assert!(block::dispenser.state_default().opaque_full_cube().unwrap()); + assert_eq!(block::fire.state_default().opacity().unwrap(), 0); + } } diff --git a/haya_ident/src/lib.rs b/haya_ident/src/lib.rs index aded9f7..1257de0 100644 --- a/haya_ident/src/lib.rs +++ b/haya_ident/src/lib.rs @@ -129,7 +129,7 @@ impl Identifier { let path = path.to_owned().into_boxed_str(); Self(Inner::Full { namespace, path }) } - None => match HayaStr::new(path) { + None => match HayaStr::copy_from(path) { Ok(path) => Self(Inner::Thin { path }), Err(_) => Self(Inner::Heap { path: path.to_owned().into_boxed_str(), diff --git a/haya_mutf8/src/lib.rs b/haya_mutf8/src/lib.rs index 9560f0c..48753d8 100644 --- a/haya_mutf8/src/lib.rs +++ b/haya_mutf8/src/lib.rs @@ -23,26 +23,15 @@ const CHAR_WIDTH: &[u8; 256] = &[ ]; #[must_use] -pub const fn mutf8_is_ascii(bytes: &[u8]) -> bool { - !has_zero_nonascii(bytes) -} - -#[must_use] -pub const fn mutf8_is_utf8(bytes: &[u8]) -> bool { - let mut index = 0; - while index < bytes.len() { - let byte = bytes[index]; - let w = unsafe { *CHAR_WIDTH.as_ptr().add(byte as usize) }; - if w == 0 { - return false; - } - index += w as usize; +pub const fn as_mutf8_ascii(bytes: &[u8]) -> Option<&str> { + if !contains_zero_or_nonascii(bytes) { + unsafe { Some(core::str::from_utf8_unchecked(bytes)) } + } else { + None } - - true } -const fn has_zero_nonascii(bytes: &[u8]) -> bool { +const fn contains_zero_or_nonascii(bytes: &[u8]) -> bool { let mut i = 0; let mut flag = false; while i < bytes.len() { diff --git a/haya_nbt/Cargo.toml b/haya_nbt/Cargo.toml index 84e082a..854e282 100644 --- a/haya_nbt/Cargo.toml +++ b/haya_nbt/Cargo.toml @@ -10,6 +10,7 @@ edition.workspace = true [dependencies] mser = { workspace = true } haya_mutf8 = { workspace = true } +haya_str = { workspace = true } unicode_names2 = "2" itoa = "1" diff --git a/haya_nbt/src/compound.rs b/haya_nbt/src/compound.rs index 394adb4..65477c9 100644 --- a/haya_nbt/src/compound.rs +++ b/haya_nbt/src/compound.rs @@ -1,22 +1,95 @@ -use crate::{RefStringTag, StringTag, Tag, TagType}; -use alloc::boxed::Box; +use crate::string::DecodeMutf8; +use crate::{Compound, Name, RefStringTag, Tag, TagType}; +use alloc::borrow::ToOwned; +use alloc::string::String; use alloc::vec::Vec; +use haya_mutf8::{Mutf8, as_mutf8_ascii, decode_mutf8_len}; +use haya_str::HayaStr; use mser::{Error, Read, UnsafeWriter, Write}; -#[derive(Clone)] -#[repr(transparent)] -pub struct Compound(Vec<(Box, Tag)>); +enum CowVec { + Thin(HayaStr), + Heap(Vec), +} + +impl Read<'_> for Name { + fn read(buf: &mut &'_ [u8]) -> Result { + let len = u16::read(buf)? as usize; + let data = match buf.split_at_checked(len) { + Some((x, y)) => { + *buf = y; + x + } + None => return Err(Error), + }; + if let Some(x) = as_mutf8_ascii(data) { + Ok(Self::new(x)) + } else { + let len = decode_mutf8_len(data)?; + let mut ptr = if len <= haya_str::MAX { + CowVec::Thin(HayaStr::new()) + } else { + CowVec::Heap(Vec::with_capacity(len)) + }; + unsafe { + mser::write_unchecked( + match &mut ptr { + CowVec::Thin(s) => s.as_mut_ptr(), + CowVec::Heap(x) => x.as_mut_ptr(), + }, + &(DecodeMutf8(Mutf8::new_unchecked(data), len)), + ); + match ptr { + CowVec::Thin(mut s) => { + s.set_len(len); + Ok(Self::Thin(s)) + } + CowVec::Heap(mut x) => { + x.set_len(len); + Ok(Self::Heap(String::from_utf8_unchecked(x).into_boxed_str())) + } + } + } + } + } +} + +impl AsRef for Name { + fn as_ref(&self) -> &str { + match self { + Self::Thin(x) => x, + Self::Heap(x) => x, + } + } +} + +impl core::ops::Deref for Name { + type Target = str; -impl AsRef<[(Box, Tag)]> for Compound { + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + +impl Name { + pub fn new(s: &str) -> Self { + match HayaStr::copy_from(s) { + Ok(x) => Self::Thin(x), + Err(_) => Self::Heap(s.to_owned().into_boxed_str()), + } + } +} + +impl AsRef<[(Name, Tag)]> for Compound { #[inline] - fn as_ref(&self) -> &[(Box, Tag)] { + fn as_ref(&self) -> &[(Name, Tag)] { self.0.as_slice() } } -impl AsMut<[(Box, Tag)]> for Compound { +impl AsMut<[(Name, Tag)]> for Compound { #[inline] - fn as_mut(&mut self) -> &mut [(Box, Tag)] { + fn as_mut(&mut self) -> &mut [(Name, Tag)] { self.0.as_mut_slice() } } @@ -78,37 +151,7 @@ impl Write for Compound { } } -#[derive(Clone)] -pub struct CompoundNamed(pub Box, pub Compound); - -impl Read<'_> for CompoundNamed { - #[inline] - fn read(n: &mut &[u8]) -> Result { - if matches!(TagType::read(n)?, TagType::Compound) { - Ok(Self(StringTag::read(n)?.0, Compound::read(n)?)) - } else { - Err(Error) - } - } -} - -impl Write for CompoundNamed { - #[inline] - unsafe fn write(&self, w: &mut UnsafeWriter) { - unsafe { - TagType::Compound.write(w); - RefStringTag(&self.0).write(w); - self.1.write(w); - } - } - - #[inline] - fn len_s(&self) -> usize { - 1 + Write::len_s(&RefStringTag(&self.0)) + Write::len_s(&self.1) - } -} - -impl>, V> FromIterator<(K, V)> for Compound +impl, V> FromIterator<(K, V)> for Compound where Tag: From, { @@ -153,12 +196,12 @@ impl Compound { } #[inline] - pub fn iter(&self) -> core::slice::Iter<'_, (Box, Tag)> { + pub fn iter(&self) -> core::slice::Iter<'_, (Name, Tag)> { self.0.iter() } #[inline] - pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, (Box, Tag)> { + pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, (Name, Tag)> { self.0.iter_mut() } @@ -196,7 +239,7 @@ impl Compound { } #[inline] - pub fn push(&mut self, k: Box, v: Tag) { + pub fn push(&mut self, k: Name, v: Tag) { self.0.push((k, v)); } @@ -234,7 +277,7 @@ impl Compound { } #[inline] - pub fn into_inner(self) -> Vec<(Box, Tag)> { + pub fn into_inner(self) -> Vec<(Name, Tag)> { self.0 } } @@ -242,24 +285,17 @@ impl Compound { impl Read<'_> for Compound { #[inline] fn read(buf: &mut &[u8]) -> Result { - let mut compound = Self(Vec::new()); - loop { - let ty = TagType::read(buf)?; - if matches!(ty, TagType::End) { - compound.0.shrink_to_fit(); - mser::cold_path(); - return Ok(compound); - } - let k = StringTag::read(buf)?.0; - let v = ty.tag(buf)?; - compound.push(k, v); + match TagType::Compound.tag(buf) { + Ok(Tag::Compound(x)) => Ok(x), + Ok(_) => Err(Error), + Err(e) => Err(e), } } } -impl From, Tag)>> for Compound { +impl From> for Compound { #[inline] - fn from(value: Vec<(Box, Tag)>) -> Self { + fn from(value: Vec<(Name, Tag)>) -> Self { Self(value) } } diff --git a/haya_nbt/src/int_array.rs b/haya_nbt/src/int_array.rs index 2b80e1b..cf98edd 100644 --- a/haya_nbt/src/int_array.rs +++ b/haya_nbt/src/int_array.rs @@ -7,7 +7,7 @@ pub(crate) struct IntArray(pub Vec); impl<'a> Read<'a> for IntArray { fn read(buf: &mut &'a [u8]) -> Result { let len = u32::read(buf)? as usize; - let data = match buf.split_at_checked(len * 4) { + let data = match buf.split_at_checked(len.checked_mul(4).ok_or(Error)?) { Some((x, y)) => { *buf = y; x diff --git a/haya_nbt/src/lib.rs b/haya_nbt/src/lib.rs index 672f78b..787131a 100644 --- a/haya_nbt/src/lib.rs +++ b/haya_nbt/src/lib.rs @@ -11,14 +11,14 @@ mod string; mod stringify; use self::byte_array::ByteArray; -pub use self::compound::{Compound, CompoundNamed}; use self::int_array::IntArray; -pub use self::list::{List, ListInfo}; +use self::list::ListRec; use self::long_array::LongArray; pub use self::string::{RefStringTag, StringTag, StringTagRaw}; -pub use self::stringify::StringifyCompound; +pub use self::stringify::CompoundStringify; use alloc::boxed::Box; use alloc::vec::Vec; +use haya_str::HayaStr; use mser::{Error, Read, UnsafeWriter, Write}; #[derive(Clone, Copy, PartialEq, Eq)] @@ -39,6 +39,36 @@ pub enum TagType { LongArray, } +#[derive(Clone, Debug)] +#[repr(transparent)] +pub struct Compound(Vec<(Name, Tag)>); + +#[derive(Clone, Debug)] +pub enum Name { + Thin(HayaStr), + Heap(Box), +} + +#[derive(Clone, Copy)] +pub struct ListInfo(pub TagType, pub u32); + +#[derive(Clone, Debug)] +pub enum ListTag { + None, + Byte(Vec), + Short(Vec), + Int(Vec), + Long(Vec), + Float(Vec), + Double(Vec), + String(Vec>), + ByteArray(Vec>), + IntArray(Vec>), + LongArray(Vec>), + List(Vec), + Compound(Vec), +} + impl TagType { pub fn bool(self, buf: &mut &[u8]) -> Result { match self { @@ -69,7 +99,7 @@ impl TagType { let ListInfo(tag, len) = ListInfo::read(buf)?; let len = len as usize; match tag { - TagType::Int => match buf.split_at_checked(len * 4) { + TagType::Int => match buf.split_at_checked(len.checked_mul(4).ok_or(Error)?) { Some((slice, y)) => unsafe { *buf = y; Ok(list::int_list(len, slice)) @@ -83,8 +113,8 @@ impl TagType { } } - pub fn tag(self, n: &mut &[u8]) -> Result { - Ok(match self { + fn tag_no_rec(self, n: &mut &[u8]) -> Result, Error> { + Ok(Ok(match self { Self::Byte => Tag::from(i8::read(n)?), Self::Short => Tag::from(i16::read(n)?), Self::Int => Tag::from(i32::read(n)?), @@ -93,18 +123,168 @@ impl TagType { Self::Double => Tag::from(f64::read(n)?), Self::ByteArray => Tag::from(ByteArray::read(n)?.0), Self::String => Tag::from(StringTag::read(n)?.0), - Self::List => { - let info = ListInfo::read(n)?; - Tag::from(info.list(n)?) - } - Self::Compound => Tag::from(Compound::read(n)?), Self::IntArray => Tag::from(IntArray::read(n)?.0), Self::LongArray => Tag::from(LongArray::read(n)?.0), + Self::List => return Ok(Err(TagRec::List)), + Self::Compound => return Ok(Err(TagRec::Compound)), Self::End => return Err(Error), - }) + })) + } +} + +impl TagType { + pub fn tag(self, n: &mut &[u8]) -> Result { + let t = match self.tag_no_rec(n)? { + Ok(x) => return Ok(x), + Err(TagRec::List) => { + let l = ListInfo::read(n)?; + match l.list_no_rec(n)? { + Ok(x) => return Ok(Tag::List(x)), + Err(e) => match e { + ListRec::Compound => Entry::ListCompound(Vec::new(), l.1), + ListRec::List => Entry::ListList(Vec::new(), l.1), + }, + } + } + Err(TagRec::Compound) => Entry::Compound(Compound::new()), + }; + read_tag(n, t, 512) + } +} + +enum Entry { + Compound(Compound), + ListCompound(Vec, u32), + ListList(Vec, u32), +} + +fn read_tag(buf: &mut &[u8], mut next: Entry, max_depth: usize) -> Result { + let mut blocks = Vec::::new(); + let mut names = Vec::::new(); + loop { + if max_depth == blocks.len() { + return Err(Error); + } + match next { + Entry::Compound(mut compound) => { + let ty = TagType::read(buf)?; + if let TagType::End = ty { + next = match blocks.pop() { + Some(Entry::Compound(mut c)) => { + let k = match names.pop() { + Some(x) => x, + None => return Err(Error), + }; + c.push(k, Tag::Compound(compound)); + Entry::Compound(c) + } + Some(Entry::ListCompound(mut l, len)) => { + l.push(compound); + Entry::ListCompound(l, len) + } + Some(Entry::ListList(_, _)) => return Err(Error), + None => return Ok(Tag::Compound(compound)), + }; + } else { + let name = Name::read(buf)?; + match ty.tag_no_rec(buf)? { + Ok(t) => { + compound.push(name, t); + next = Entry::Compound(compound); + } + Err(TagRec::Compound) => { + names.push(name); + blocks.push(Entry::Compound(compound)); + next = Entry::Compound(Compound::new()); + } + Err(TagRec::List) => { + let l = ListInfo::read(buf)?; + match l.list_no_rec(buf)? { + Ok(x) => { + compound.push(name, Tag::List(x)); + next = Entry::Compound(compound); + } + Err(e) => { + names.push(name); + blocks.push(Entry::Compound(compound)); + next = match e { + ListRec::Compound => Entry::ListCompound(Vec::new(), l.1), + ListRec::List => Entry::ListList(Vec::new(), l.1), + }; + } + } + } + }; + } + } + Entry::ListCompound(compounds, len) => { + if len == 0 { + next = match blocks.pop() { + Some(Entry::Compound(mut x)) => { + let k = match names.pop() { + Some(x) => x, + None => return Err(Error), + }; + x.push(k, Tag::List(ListTag::Compound(compounds))); + Entry::Compound(x) + } + Some(Entry::ListList(mut lists, len)) => { + lists.push(ListTag::Compound(compounds)); + Entry::ListList(lists, len) + } + Some(Entry::ListCompound(_, _)) => return Err(Error), + None => return Ok(Tag::List(ListTag::Compound(compounds))), + }; + } else { + blocks.push(Entry::ListCompound(compounds, len - 1)); + next = Entry::Compound(Compound::new()); + } + } + Entry::ListList(mut lists, len) => { + if len == 0 { + next = match blocks.pop() { + Some(Entry::Compound(mut x)) => { + let k = match names.pop() { + Some(x) => x, + None => return Err(Error), + }; + x.push(k, Tag::List(ListTag::List(lists))); + Entry::Compound(x) + } + Some(Entry::ListList(mut x, len)) => { + x.push(ListTag::List(lists)); + Entry::ListList(x, len) + } + Some(Entry::ListCompound(_, _)) => return Err(Error), + None => return Ok(Tag::List(ListTag::List(lists))), + }; + } else { + let l = ListInfo::read(buf)?; + match l.list_no_rec(buf)? { + Ok(x) => { + lists.push(x); + next = Entry::ListList(lists, len - 1); + } + Err(e) => { + blocks.push(Entry::ListList(lists, len - 1)); + next = match e { + ListRec::Compound => Entry::ListCompound(Vec::new(), l.1), + ListRec::List => Entry::ListList(Vec::new(), l.1), + }; + } + } + } + } + } } } +#[derive(Clone, Copy)] +enum TagRec { + List, + Compound, +} + impl Read<'_> for TagType { #[inline] fn read(buf: &mut &'_ [u8]) -> Result { @@ -129,7 +309,7 @@ impl Write for TagType { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum Tag { Byte(i8), Short(i16), @@ -141,7 +321,7 @@ pub enum Tag { ByteArray(Vec), IntArray(Vec), LongArray(Vec), - List(List), + List(ListTag), Compound(Compound), } @@ -193,11 +373,11 @@ impl Write for Tag { } Tag::IntArray(x) => { (x.len() as u32).write(w); - x.iter().write(w); + x.iter().for_each(|i| i.write(w)); } Tag::LongArray(x) => { (x.len() as u32).write(w); - x.iter().write(w); + x.iter().for_each(|i| i.write(w)); } Tag::List(x) => x.write(w), Tag::Compound(x) => x.write(w), @@ -332,9 +512,9 @@ impl From> for Tag { } } -impl From for Tag { +impl From for Tag { #[inline] - fn from(value: List) -> Self { + fn from(value: ListTag) -> Self { Self::List(value) } } @@ -365,3 +545,33 @@ impl Read<'_> for Tag { TagType::read(buf)?.tag(buf) } } + +#[derive(Clone)] +pub struct CompoundNamed(pub Name, pub Compound); + +impl Read<'_> for CompoundNamed { + #[inline] + fn read(n: &mut &[u8]) -> Result { + if matches!(TagType::read(n)?, TagType::Compound) { + Ok(Self(Name::read(n)?, Compound::read(n)?)) + } else { + Err(Error) + } + } +} + +impl Write for CompoundNamed { + #[inline] + unsafe fn write(&self, w: &mut UnsafeWriter) { + unsafe { + TagType::Compound.write(w); + RefStringTag(&self.0).write(w); + self.1.write(w); + } + } + + #[inline] + fn len_s(&self) -> usize { + 1 + Write::len_s(&RefStringTag(&self.0)) + Write::len_s(&self.1) + } +} diff --git a/haya_nbt/src/list.rs b/haya_nbt/src/list.rs index fc03645..028dd0f 100644 --- a/haya_nbt/src/list.rs +++ b/haya_nbt/src/list.rs @@ -1,25 +1,10 @@ -use crate::{ByteArray, Compound, IntArray, LongArray, RefStringTag, StringTag, TagType}; +use crate::{ + ByteArray, Compound, IntArray, ListInfo, ListTag, LongArray, RefStringTag, StringTag, TagType, +}; use alloc::boxed::Box; use alloc::vec::Vec; use mser::{Error, Read, UnsafeWriter, Write}; -#[derive(Clone)] -pub enum List { - None, - Byte(Vec), - Short(Vec), - Int(Vec), - Long(Vec), - Float(Vec), - Double(Vec), - String(Vec>), - ByteArray(Vec>), - IntArray(Vec>), - LongArray(Vec>), - List(Vec), - Compound(Vec), -} - #[derive(Clone)] pub(crate) enum ListPrimitive { Byte(Vec), @@ -30,7 +15,7 @@ pub(crate) enum ListPrimitive { Double(Vec), } -impl From for List { +impl From for ListTag { fn from(value: ListPrimitive) -> Self { match value { ListPrimitive::Byte(x) => Self::Byte(x), @@ -42,8 +27,6 @@ impl From for List { } } } -#[derive(Clone, Copy)] -pub struct ListInfo(pub TagType, pub u32); impl<'a> Read<'a> for ListInfo { fn read(buf: &mut &'a [u8]) -> Result { @@ -66,7 +49,7 @@ impl Write for ListInfo { } } -impl From> for List { +impl From> for ListTag { #[inline] fn from(value: Vec) -> Self { let mut me = core::mem::ManuallyDrop::new(value); @@ -76,91 +59,91 @@ impl From> for List { } } -impl From> for List { +impl From> for ListTag { #[inline] fn from(value: Vec) -> Self { Self::Byte(value) } } -impl From> for List { +impl From> for ListTag { #[inline] fn from(value: Vec) -> Self { Self::Short(value) } } -impl From> for List { +impl From> for ListTag { #[inline] fn from(value: Vec) -> Self { Self::Int(value) } } -impl From> for List { +impl From> for ListTag { #[inline] fn from(value: Vec) -> Self { Self::Long(value) } } -impl From> for List { +impl From> for ListTag { #[inline] fn from(value: Vec) -> Self { Self::Float(value) } } -impl From> for List { +impl From> for ListTag { #[inline] fn from(value: Vec) -> Self { Self::Double(value) } } -impl From>> for List { +impl From>> for ListTag { #[inline] fn from(value: Vec>) -> Self { Self::String(value) } } -impl From>> for List { +impl From>> for ListTag { #[inline] fn from(value: Vec>) -> Self { Self::ByteArray(value) } } -impl From>> for List { +impl From>> for ListTag { #[inline] fn from(value: Vec>) -> Self { Self::IntArray(value) } } -impl From>> for List { +impl From>> for ListTag { #[inline] fn from(value: Vec>) -> Self { Self::LongArray(value) } } -impl From> for List { +impl From> for ListTag { #[inline] - fn from(value: Vec) -> Self { + fn from(value: Vec) -> Self { Self::List(value) } } -impl From> for List { +impl From> for ListTag { #[inline] fn from(value: Vec) -> Self { Self::Compound(value) } } -impl List { +impl ListTag { pub fn list_info(&self) -> ListInfo { match self { Self::None => ListInfo(TagType::End, 0), @@ -180,7 +163,7 @@ impl List { } } -impl Write for List { +impl Write for ListTag { unsafe fn write(&self, w: &mut UnsafeWriter) { unsafe { self.list_info().write(w); @@ -210,26 +193,26 @@ impl Write for List { Self::ByteArray(x) => { x.iter().for_each(|y| { (y.len() as u32).write(w); - y.iter().write(w); + crate::byte_array::i8_to_u8_slice(y).write(w); }); } Self::IntArray(x) => { x.iter().for_each(|y| { (y.len() as u32).write(w); - y.iter().write(w); + y.iter().for_each(|z| z.write(w)); }); } Self::LongArray(x) => { x.iter().for_each(|y| { (y.len() as u32).write(w); - y.iter().write(w); + y.iter().for_each(|z| z.write(w)); }); } Self::List(x) => { - x.iter().write(w); + x.iter().for_each(|y| y.write(w)); } Self::Compound(x) => { - x.iter().write(w); + x.iter().for_each(|y| y.write(w)); } } } @@ -248,173 +231,177 @@ impl Write for List { Self::ByteArray(x) => x.len() * 4 + x.iter().map(|x| x.len()).sum::(), Self::IntArray(x) => x.len() * 4 + x.iter().map(|x| x.len()).sum::() * 4, Self::LongArray(x) => x.len() * 4 + x.iter().map(|x| x.len()).sum::() * 8, - Self::List(x) => x.iter().len_s(), - Self::Compound(x) => x.iter().len_s(), + Self::List(x) => x.iter().map(|x| x.len_s()).sum::(), + Self::Compound(x) => x.iter().map(|x| x.len_s()).sum::(), } } } +#[derive(Clone, Copy)] +pub(crate) enum ListRec { + Compound, + List, +} + impl ListInfo { - pub fn list(self, n: &mut &[u8]) -> Result { + pub(crate) fn list_no_rec(self, n: &mut &[u8]) -> Result, Error> { let len = self.1 as usize; match self.0 { - TagType::End => Ok(List::None), + TagType::End => Ok(Ok(ListTag::None)), TagType::Byte => match n.split_at_checked(len) { Some((x, y)) => { *n = y; - Ok(List::Byte(Vec::from(crate::byte_array::u8_to_i8_slice(x)))) + Ok(Ok(ListTag::Byte(Vec::from( + crate::byte_array::u8_to_i8_slice(x), + )))) } None => Err(Error), }, - TagType::Short => match n.split_at_checked(len * 2) { - Some((slice, y)) => { + TagType::Short => match n.split_at_checked(len.checked_mul(2).ok_or(Error)?) { + Some((slice, y)) => unsafe { *n = y; - let mut v = Vec::with_capacity(len); - let s = unsafe { v.spare_capacity_mut().assume_init_mut() }; - for index in 0..len { - unsafe { - *s.get_unchecked_mut(index) = i16::from_be_bytes( - *slice.as_ptr().add(index * 2).cast::<[u8; 2]>(), - ); - } - } - unsafe { v.set_len(len) } - Ok(List::Short(v)) - } + Ok(Ok(ListTag::Short(short_list(len, slice)))) + }, None => Err(Error), }, - TagType::Int => match n.split_at_checked(len * 4) { + TagType::Int => match n.split_at_checked(len.checked_mul(4).ok_or(Error)?) { Some((slice, y)) => unsafe { *n = y; - Ok(List::Int(int_list(len, slice))) + Ok(Ok(ListTag::Int(int_list(len, slice)))) }, None => Err(Error), }, - TagType::Long => match n.split_at_checked(len * 8) { - Some((slice, y)) => { + TagType::Long => match n.split_at_checked(len.checked_mul(8).ok_or(Error)?) { + Some((slice, y)) => unsafe { *n = y; - let mut v = Vec::with_capacity(len); - let s = unsafe { v.spare_capacity_mut().assume_init_mut() }; - for index in 0..len { - unsafe { - *s.get_unchecked_mut(index) = i64::from_be_bytes( - *slice.as_ptr().add(index * 8).cast::<[u8; 8]>(), - ); - } - } - unsafe { v.set_len(len) } - Ok(List::Long(v)) - } + Ok(Ok(ListTag::Long(long_list(len, slice)))) + }, None => Err(Error), }, - TagType::Float => match n.split_at_checked(len * 4) { - Some((slice, y)) => { + TagType::Float => match n.split_at_checked(len.checked_mul(4).ok_or(Error)?) { + Some((slice, y)) => unsafe { *n = y; - let mut v = Vec::with_capacity(len); - let s = unsafe { v.spare_capacity_mut().assume_init_mut() }; - for index in 0..len { - unsafe { - *s.get_unchecked_mut(index) = f32::from_be_bytes( - *slice.as_ptr().add(index * 4).cast::<[u8; 4]>(), - ); - } - } - unsafe { v.set_len(len) } - Ok(List::Float(v)) - } + Ok(Ok(ListTag::Float(f32_list(len, slice)))) + }, None => Err(Error), }, - TagType::Double => match n.split_at_checked(len * 8) { - Some((slice, y)) => { + TagType::Double => match n.split_at_checked(len.checked_mul(8).ok_or(Error)?) { + Some((slice, y)) => unsafe { *n = y; - let mut v = Vec::with_capacity(len); - let s = unsafe { v.spare_capacity_mut().assume_init_mut() }; - for index in 0..len { - unsafe { - *s.get_unchecked_mut(index) = f64::from_be_bytes( - *slice.as_ptr().add(index * 8).cast::<[u8; 8]>(), - ); - } - } - unsafe { v.set_len(len) } - Ok(List::Double(v)) - } + Ok(Ok(ListTag::Double(f64_list(len, slice)))) + }, None => Err(Error), }, TagType::ByteArray => { - if len * 4 > n.len() { + if len.checked_mul(4).ok_or(Error)? > n.len() { return Err(Error); } let mut list = Vec::with_capacity(len); for _ in 0..len { list.push(ByteArray::read(n)?.0); } - Ok(List::ByteArray(list)) + Ok(Ok(ListTag::ByteArray(list))) } TagType::String => { - if len * 2 > n.len() { + if len.checked_mul(2).ok_or(Error)? > n.len() { return Err(Error); } let mut list = Vec::with_capacity(len); for _ in 0..len { list.push(StringTag::read(n)?.0); } - Ok(List::String(list)) - } - TagType::List => { - if len << 2 > n.len() { - return Err(Error); - } - let mut list = Vec::with_capacity(len); - for _ in 0..len { - let info = ListInfo::read(n)?; - list.push(info.list(n)?); - } - Ok(List::List(list)) - } - TagType::Compound => { - if len > n.len() { - return Err(Error); - } - let mut list = Vec::with_capacity(len); - for _ in 0..len { - list.push(Compound::read(n)?); - } - Ok(List::Compound(list)) + Ok(Ok(ListTag::String(list))) } + TagType::List => Ok(Err(ListRec::List)), + TagType::Compound => Ok(Err(ListRec::Compound)), TagType::IntArray => { - if len * 4 > n.len() { + if len.checked_mul(4).ok_or(Error)? > n.len() { return Err(Error); } let mut list = Vec::with_capacity(len); for _ in 0..len { list.push(IntArray::read(n)?.0); } - Ok(List::IntArray(list)) + Ok(Ok(ListTag::IntArray(list))) } TagType::LongArray => { - if len * 4 > n.len() { + if len.checked_mul(4).ok_or(Error)? > n.len() { return Err(Error); } let mut list = Vec::with_capacity(len); for _ in 0..len { list.push(LongArray::read(n)?.0); } - Ok(List::LongArray(list)) + Ok(Ok(ListTag::LongArray(list))) } } } } -pub unsafe fn int_list(len: usize, slice: &[u8]) -> Vec { +pub(crate) unsafe fn long_list(len: usize, slice: &[u8]) -> Vec { + debug_assert_eq!(len * 8, slice.len()); + + let mut v = Vec::::with_capacity(len); + let s = v.as_mut_ptr(); + for index in 0..len { + unsafe { + *s.add(index) = i64::from_be_bytes(*slice.as_ptr().add(index * 8).cast::<[u8; 8]>()); + } + } + unsafe { v.set_len(len) } + v +} + +pub(crate) unsafe fn int_list(len: usize, slice: &[u8]) -> Vec { debug_assert_eq!(len * 4, slice.len()); - let mut v = Vec::with_capacity(len); - let s = unsafe { v.spare_capacity_mut().assume_init_mut() }; + let mut v = Vec::::with_capacity(len); + let s = v.as_mut_ptr(); + for index in 0..len { + unsafe { + *s.add(index) = i32::from_be_bytes(*slice.as_ptr().add(index * 4).cast::<[u8; 4]>()); + } + } + unsafe { v.set_len(len) } + v +} + +pub(crate) unsafe fn short_list(len: usize, slice: &[u8]) -> Vec { + debug_assert_eq!(len * 2, slice.len()); + + let mut v = Vec::::with_capacity(len); + let s = v.as_mut_ptr(); + for index in 0..len { + unsafe { + *s.add(index) = i16::from_be_bytes(*slice.as_ptr().add(index * 2).cast::<[u8; 2]>()); + } + } + unsafe { v.set_len(len) } + v +} + +pub(crate) unsafe fn f32_list(len: usize, slice: &[u8]) -> Vec { + debug_assert_eq!(len * 4, slice.len()); + + let mut v = Vec::::with_capacity(len); + let s = v.as_mut_ptr(); + for index in 0..len { + unsafe { + *s.add(index) = f32::from_be_bytes(*slice.as_ptr().add(index * 4).cast::<[u8; 4]>()); + } + } + unsafe { v.set_len(len) } + v +} + +pub(crate) unsafe fn f64_list(len: usize, slice: &[u8]) -> Vec { + debug_assert_eq!(len * 8, slice.len()); + + let mut v = Vec::::with_capacity(len); + let s = v.as_mut_ptr(); for index in 0..len { unsafe { - *s.get_unchecked_mut(index) = - i32::from_be_bytes(*slice.as_ptr().add(index * 4).cast::<[u8; 4]>()); + *s.add(index) = f64::from_be_bytes(*slice.as_ptr().add(index * 8).cast::<[u8; 8]>()); } } unsafe { v.set_len(len) } diff --git a/haya_nbt/src/long_array.rs b/haya_nbt/src/long_array.rs index 8dc39f1..f93a780 100644 --- a/haya_nbt/src/long_array.rs +++ b/haya_nbt/src/long_array.rs @@ -7,7 +7,7 @@ pub(crate) struct LongArray(pub Vec); impl<'a> Read<'a> for LongArray { fn read(buf: &mut &'a [u8]) -> Result { let len = u32::read(buf)? as usize; - let data = match buf.split_at_checked(len * 8) { + let data = match buf.split_at_checked(len.checked_mul(8).ok_or(Error)?) { Some((x, y)) => { *buf = y; x diff --git a/haya_nbt/src/string.rs b/haya_nbt/src/string.rs index d8854e2..0aefb22 100644 --- a/haya_nbt/src/string.rs +++ b/haya_nbt/src/string.rs @@ -1,31 +1,31 @@ use crate::{Error, Read, UnsafeWriter, Write}; +use alloc::borrow::ToOwned; use alloc::boxed::Box; use alloc::vec::Vec; use haya_mutf8::{ - Mutf8, decode_mutf8, decode_mutf8_len, encode_mutf8, encode_mutf8_len, mutf8_is_ascii, - mutf8_is_utf8, + Mutf8, as_mutf8_ascii, decode_mutf8, decode_mutf8_len, encode_mutf8, encode_mutf8_len, }; #[derive(Clone, Copy)] #[repr(transparent)] #[must_use] -pub struct StringTagRaw<'a>(&'a [u8]); +pub struct StringTagRaw<'a>(&'a str); impl<'a> StringTagRaw<'a> { pub const fn new(n: &'a [u8]) -> Option { - if mutf8_is_utf8(n) { - Some(Self(n)) + if let Some(s) = as_mutf8_ascii(n) { + Some(Self(s)) } else { None } } - pub const fn new_unchecked(n: &'a [u8]) -> Self { - debug_assert!(mutf8_is_utf8(n)); + pub const fn new_unchecked(n: &'a str) -> Self { + debug_assert!(as_mutf8_ascii(n.as_bytes()).is_some()); Self(n) } - pub const fn inner(&self) -> &'a [u8] { + pub const fn inner(&self) -> &'a str { self.0 } } @@ -56,8 +56,8 @@ impl<'a> Read<'a> for StringTagRaw<'a> { } None => return Err(Error), }; - if mutf8_is_ascii(data) { - Ok(Self(data)) + if let Some(x) = as_mutf8_ascii(data) { + Ok(Self(x)) } else { Err(Error) } @@ -73,8 +73,8 @@ impl<'a> Write for RefStringTag<'a> { #[inline] unsafe fn write(&self, w: &mut UnsafeWriter) { unsafe { - if mutf8_is_ascii(self.0.as_bytes()) { - StringTagRaw(self.0.as_bytes()).write(w); + if let Some(x) = StringTagRaw::new(self.0.as_bytes()) { + x.write(w); } else { (encode_mutf8_len(self.0) as u16).write(w); encode_mutf8(self.0, w); @@ -84,8 +84,8 @@ impl<'a> Write for RefStringTag<'a> { #[inline] fn len_s(&self) -> usize { - if mutf8_is_ascii(self.0.as_bytes()) { - StringTagRaw(self.0.as_bytes()).len_s() + if let Some(x) = StringTagRaw::new(self.0.as_bytes()) { + x.len_s() } else { encode_mutf8_len(self.0) + 2 } @@ -94,11 +94,7 @@ impl<'a> Write for RefStringTag<'a> { impl<'a> Read<'a> for RefStringTag<'a> { fn read(buf: &mut &'a [u8]) -> Result { - unsafe { - Ok(Self(core::str::from_utf8_unchecked( - StringTagRaw::read(buf)?.0, - ))) - } + Ok(Self(StringTagRaw::read(buf)?.0)) } } @@ -117,8 +113,8 @@ impl Read<'_> for StringTag { } None => return Err(Error), }; - if mutf8_is_ascii(data) { - unsafe { Ok(Self(Box::from(core::str::from_utf8_unchecked(data)))) } + if let Some(x) = as_mutf8_ascii(data) { + Ok(Self(x.to_owned().into_boxed_str())) } else { let len = decode_mutf8_len(data)?; let mut x = Vec::with_capacity(len); diff --git a/haya_nbt/src/stringify.rs b/haya_nbt/src/stringify.rs index a66e83b..2487982 100644 --- a/haya_nbt/src/stringify.rs +++ b/haya_nbt/src/stringify.rs @@ -1,5 +1,5 @@ use crate::list::ListPrimitive; -use crate::{Compound, Error, List, Read as _, Tag, TagArray, TagPrimitive}; +use crate::{Compound, Error, ListTag, Name, Read as _, Tag, TagArray, TagPrimitive}; use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; @@ -12,16 +12,16 @@ const LONG_ARRAY_PREFIX: &[u8; 3] = b"[L;"; #[derive(Clone)] #[repr(transparent)] -pub struct StringifyCompound(pub Compound); +pub struct CompoundStringify(pub Compound); -impl From for StringifyCompound { +impl From for CompoundStringify { #[inline] fn from(value: Compound) -> Self { Self(value) } } -impl StringifyCompound { +impl CompoundStringify { #[inline] pub fn decode(n: &str) -> Result { unsafe { decode(&mut n.as_bytes(), 512).map(Self) } @@ -156,7 +156,7 @@ impl TBuf { unsafe fn decode(n: &mut &[u8], max_depth: usize) -> Result { enum Bl { C(Compound), - L(List), + L(ListTag), } let mut tmp = TBuf(Vec::new()); @@ -224,19 +224,19 @@ unsafe fn decode(n: &mut &[u8], max_depth: usize) -> Result { match &mut bl { Bl::C(x) => x.shrink_to_fit(), Bl::L(x) => match x { - List::List(x) => x.shrink_to_fit(), - List::Compound(x) => x.shrink_to_fit(), - List::String(x) => x.shrink_to_fit(), - List::Int(x) => x.shrink_to_fit(), - List::Double(x) => x.shrink_to_fit(), - List::Byte(x) => x.shrink_to_fit(), - List::Short(x) => x.shrink_to_fit(), - List::Long(x) => x.shrink_to_fit(), - List::Float(x) => x.shrink_to_fit(), - List::ByteArray(x) => x.shrink_to_fit(), - List::IntArray(x) => x.shrink_to_fit(), - List::LongArray(x) => x.shrink_to_fit(), - List::None => (), + ListTag::List(x) => x.shrink_to_fit(), + ListTag::Compound(x) => x.shrink_to_fit(), + ListTag::String(x) => x.shrink_to_fit(), + ListTag::Int(x) => x.shrink_to_fit(), + ListTag::Double(x) => x.shrink_to_fit(), + ListTag::Byte(x) => x.shrink_to_fit(), + ListTag::Short(x) => x.shrink_to_fit(), + ListTag::Long(x) => x.shrink_to_fit(), + ListTag::Float(x) => x.shrink_to_fit(), + ListTag::ByteArray(x) => x.shrink_to_fit(), + ListTag::IntArray(x) => x.shrink_to_fit(), + ListTag::LongArray(x) => x.shrink_to_fit(), + ListTag::None => (), }, } match (blocks.last_mut(), bl) { @@ -246,7 +246,7 @@ unsafe fn decode(n: &mut &[u8], max_depth: usize) -> Result { let new_len = rest.len() - len; c.push( unsafe { - Box::from(from_utf8_unchecked( + Name::new(from_utf8_unchecked( rest.get_unchecked(rest.len() - len..rest.len()), )) }, @@ -261,10 +261,10 @@ unsafe fn decode(n: &mut &[u8], max_depth: usize) -> Result { _ => return Err(Error), }, (Some(Bl::L(l)), Bl::C(x)) => { - if let List::None = l { - *l = List::Compound(Vec::new()) + if let ListTag::None = l { + *l = ListTag::Compound(Vec::new()) } - if let List::Compound(l) = l { + if let ListTag::Compound(l) = l { l.push(x); continue; } else { @@ -272,10 +272,10 @@ unsafe fn decode(n: &mut &[u8], max_depth: usize) -> Result { } } (Some(Bl::L(l)), Bl::L(x)) => { - if let List::None = l { - *l = List::List(Vec::new()) + if let ListTag::None = l { + *l = ListTag::List(Vec::new()) } - if let List::List(l) = l { + if let ListTag::List(l) = l { l.push(x); continue; } else { @@ -334,7 +334,7 @@ unsafe fn decode(n: &mut &[u8], max_depth: usize) -> Result { Err(_) => { names.extend(kl); blocks.push(Bl::C(c)); - blocks.push(Bl::L(List::None)); + blocks.push(Bl::L(ListTag::None)); on_start = true; continue; } @@ -360,13 +360,13 @@ unsafe fn decode(n: &mut &[u8], max_depth: usize) -> Result { }, }; unsafe { - c.push(Box::from(from_utf8_unchecked(k)), t); + c.push(Name::new(from_utf8_unchecked(k)), t); } } Bl::L(mut l) => match peek(n)? { (b'{', rest) => { - if let List::None = &l { - l = List::Compound(Vec::new()); + if let ListTag::None = &l { + l = ListTag::Compound(Vec::new()); } blocks.push(Bl::L(l)); blocks.push(Bl::C(Compound::new())); @@ -379,29 +379,29 @@ unsafe fn decode(n: &mut &[u8], max_depth: usize) -> Result { Ok(arr) => { match arr { TagArray::Byte(b) => { - if let List::None = &l { - l = List::ByteArray(Vec::new()); + if let ListTag::None = &l { + l = ListTag::ByteArray(Vec::new()); } match &mut l { - List::ByteArray(x) => x.push(b), + ListTag::ByteArray(x) => x.push(b), _ => return Err(Error), } } TagArray::Int(b) => { - if let List::None = &l { - l = List::IntArray(Vec::new()); + if let ListTag::None = &l { + l = ListTag::IntArray(Vec::new()); } match &mut l { - List::IntArray(x) => x.push(b), + ListTag::IntArray(x) => x.push(b), _ => return Err(Error), } } TagArray::Long(b) => { - if let List::None = &l { - l = List::LongArray(Vec::new()); + if let ListTag::None = &l { + l = ListTag::LongArray(Vec::new()); } match &mut l { - List::LongArray(x) => x.push(b), + ListTag::LongArray(x) => x.push(b), _ => return Err(Error), } } @@ -410,14 +410,14 @@ unsafe fn decode(n: &mut &[u8], max_depth: usize) -> Result { } Err(_) => { match &l { - List::None => { - l = List::List(Vec::new()); + ListTag::None => { + l = ListTag::List(Vec::new()); } - List::List(_) => (), + ListTag::List(_) => (), _ => return Err(Error), } blocks.push(Bl::L(l)); - blocks.push(Bl::L(List::None)); + blocks.push(Bl::L(ListTag::None)); on_start = true; } } @@ -445,11 +445,11 @@ unsafe fn decode(n: &mut &[u8], max_depth: usize) -> Result { } }; let l = match tag { - Ok(x) => List::from(dec_list_primitive(n, &mut tmp, x)?), + Ok(x) => ListTag::from(dec_list_primitive(n, &mut tmp, x)?), Err(e) => { let mut list = vec![e]; dec_list_string(n, &mut tmp, &mut list)?; - List::String(list) + ListTag::String(list) } }; blocks.push(Bl::L(l)); @@ -1079,7 +1079,7 @@ const DELIMITER: &[u8] = b", "; fn encode(buf: &mut Vec, n: &Compound) { #[derive(Clone, Copy)] enum Bl<'a> { - C(&'a [(Box, Tag)]), + C(&'a [(Name, Tag)]), None, Byte(&'a [i8]), Short(&'a [i16]), @@ -1091,25 +1091,25 @@ fn encode(buf: &mut Vec, n: &Compound) { ByteArray(&'a [Vec]), IntArray(&'a [Vec]), LongArray(&'a [Vec]), - List(&'a [List]), + List(&'a [ListTag]), Compound(&'a [Compound]), } - impl<'a> From<&'a List> for Bl<'a> { - fn from(value: &'a List) -> Self { + impl<'a> From<&'a ListTag> for Bl<'a> { + fn from(value: &'a ListTag) -> Self { match value { - List::None => Self::None, - List::Byte(items) => Self::Byte(items), - List::Short(items) => Self::Short(items), - List::Int(items) => Self::Int(items), - List::Long(items) => Self::Long(items), - List::Float(items) => Self::Float(items), - List::Double(items) => Self::Double(items), - List::String(box_strs) => Self::String(box_strs), - List::ByteArray(items) => Self::ByteArray(items), - List::IntArray(items) => Self::IntArray(items), - List::LongArray(items) => Self::LongArray(items), - List::List(lists) => Self::List(lists), - List::Compound(compounds) => Self::Compound(compounds), + ListTag::None => Self::None, + ListTag::Byte(items) => Self::Byte(items), + ListTag::Short(items) => Self::Short(items), + ListTag::Int(items) => Self::Int(items), + ListTag::Long(items) => Self::Long(items), + ListTag::Float(items) => Self::Float(items), + ListTag::Double(items) => Self::Double(items), + ListTag::String(box_strs) => Self::String(box_strs), + ListTag::ByteArray(items) => Self::ByteArray(items), + ListTag::IntArray(items) => Self::IntArray(items), + ListTag::LongArray(items) => Self::LongArray(items), + ListTag::List(lists) => Self::List(lists), + ListTag::Compound(compounds) => Self::Compound(compounds), } } } diff --git a/haya_protocol/src/clientbound/common.rs b/haya_protocol/src/clientbound/common.rs index 87fd10c..c613dc2 100644 --- a/haya_protocol/src/clientbound/common.rs +++ b/haya_protocol/src/clientbound/common.rs @@ -1,6 +1,5 @@ -use crate::{Component, List, Rest, ServerLinkUntrustedEntry, TagNetworkEntry, Utf8}; +use crate::{Component, Dialog, List, Rest, ServerLinkUntrustedEntry, TagNetworkEntry, Utf8}; use haya_ident::Ident; -use haya_nbt::Tag; use mser::{ByteArray, V32}; use uuid::Uuid; @@ -80,5 +79,5 @@ pub struct ClearDialog {} #[derive(Clone, Serialize, Deserialize)] pub struct ShowDialog { - pub dialog: Tag, + pub dialog: Dialog, } diff --git a/haya_protocol/src/clientbound/login.rs b/haya_protocol/src/clientbound/login.rs index 198b77e..93d261b 100644 --- a/haya_protocol/src/clientbound/login.rs +++ b/haya_protocol/src/clientbound/login.rs @@ -1,11 +1,11 @@ use crate::profile::GameProfileRef; -use crate::{Rest, Utf8}; +use crate::{ComponentJson, Rest, Utf8}; use haya_ident::Ident; use mser::{ByteArray, V32}; #[derive(Clone, Serialize, Deserialize)] pub struct LoginDisconnect<'a> { - pub status: Utf8<'a>, + pub reason: ComponentJson<'a>, } #[derive(Clone, Serialize, Deserialize)] diff --git a/haya_protocol/src/lib.rs b/haya_protocol/src/lib.rs index f355ede..357efac 100644 --- a/haya_protocol/src/lib.rs +++ b/haya_protocol/src/lib.rs @@ -19,20 +19,17 @@ extern crate mser_macro; extern crate alloc; #[derive(Clone, Copy, Debug)] +#[repr(u8)] pub enum ClientIntent { - Status, - Login, - Transfer, + Status = 1, + Login = 2, + Transfer = 3, } impl Write for ClientIntent { unsafe fn write(&self, w: &mut UnsafeWriter) { unsafe { - w.write_byte(match self { - Self::Status => 1, - Self::Login => 2, - Self::Transfer => 3, - }); + w.write_byte(*self as u8); } } @@ -169,9 +166,15 @@ impl<'a, const MAX: usize> Write for Rest<'a, MAX> { } } +#[derive(Clone, Serialize, Deserialize)] +pub struct ComponentJson<'a>(pub Utf8<'a, 262144>); + #[derive(Clone, Serialize, Deserialize)] pub struct Component(pub Tag); +#[derive(Clone, Serialize, Deserialize)] +pub struct Dialog(pub Tag); + #[derive(Clone, Serialize, Deserialize)] pub struct RegistryKey<'a> { pub identifier: Ident<'a>, @@ -198,37 +201,37 @@ pub struct KnownPack<'a> { #[derive(Clone, Serialize, Deserialize)] pub struct ServerLinkUntrustedEntry<'a> { - pub ty: ServerLinkUntrustedEntryType, + pub ty: Either, pub url: Utf8<'a>, } #[derive(Clone)] -pub enum ServerLinkUntrustedEntryType { - Known(KnownLinkType), - Custom(Component), +pub enum Either { + Left(L), + Right(R), } -impl<'a> Read<'a> for ServerLinkUntrustedEntryType { +impl<'a, L: Read<'a>, R: Read<'a>> Read<'a> for Either { fn read(buf: &mut &'a [u8]) -> Result { if bool::read(buf)? { - Ok(Self::Known(KnownLinkType::read(buf)?)) + Ok(Self::Left(L::read(buf)?)) } else { - Ok(Self::Custom(Component::read(buf)?)) + Ok(Self::Right(R::read(buf)?)) } } } -impl Write for ServerLinkUntrustedEntryType { +impl Write for Either { unsafe fn write(&self, w: &mut UnsafeWriter) { unsafe { match self { - Self::Known(k) => { + Self::Left(l) => { true.write(w); - k.write(w); + l.write(w); } - Self::Custom(c) => { + Self::Right(r) => { false.write(w); - c.write(w); + r.write(w); } } } @@ -236,8 +239,8 @@ impl Write for ServerLinkUntrustedEntryType { fn len_s(&self) -> usize { match self { - Self::Known(k) => true.len_s() + k.len_s(), - Self::Custom(c) => false.len_s() + c.len_s(), + Self::Left(l) => true.len_s() + l.len_s(), + Self::Right(r) => false.len_s() + r.len_s(), } } } diff --git a/haya_ser/src/hex.rs b/haya_ser/src/hex.rs index bf237f8..783666e 100644 --- a/haya_ser/src/hex.rs +++ b/haya_ser/src/hex.rs @@ -1,61 +1,3 @@ -macro_rules! parse_impl { - ($($t:ident),* $(,)?) => { - $( - impl Integer for $t { - fn parse(buf: &[u8]) -> (Self, usize) { - let mut x = buf; - - let first = match x.first() { - Some(x) => *x, - None => return ($t::default(), 0), - }; - if first == b'+' { - unsafe { - x = x.get_unchecked(1..); - } - } - let mut out: $t = 0; - match x.split_first() { - Some((&dig, y)) => { - if let Some(dig) = hex_to_u8(dig) { - x = y; - out = out.wrapping_mul(16).wrapping_add(dig as _); - } else { - return ($t::default(), 0); - } - } - _ => return ($t::default(), 0), - } - while let Some((&dig, y)) = x.split_first() { - if let Some(dig) = hex_to_u8(dig) { - x = y; - out = out.wrapping_mul(16).wrapping_add(dig as _); - } else { - break; - } - } - (out, buf.len() - x.len()) - } - } - )* - }; -} - -pub trait Integer: Copy { - fn parse(buf: &[u8]) -> (Self, usize); -} - -pub fn parse_hex(n: &[u8]) -> (T, usize) { - T::parse(n) -} - -parse_impl! { - u8, - u16, - u32, - u64, -} - const HEX_DIG: &[u8; 16] = b"0123456789abcdef"; #[inline] diff --git a/haya_ser/src/lib.rs b/haya_ser/src/lib.rs index 935264e..650f313 100644 --- a/haya_ser/src/lib.rs +++ b/haya_ser/src/lib.rs @@ -7,10 +7,9 @@ mod varint; mod write; mod writer; -pub use self::hex::{hex_to_u8, parse_hex, u8_to_hex}; +pub use self::hex::{hex_to_u8, u8_to_hex}; pub use self::json::json_char_width_escaped; pub use self::varint::{V7MAX, V21, V21MAX, V32, V64}; -pub use self::write::{Write2, Write3}; pub use self::writer::UnsafeWriter; pub trait Write { @@ -24,7 +23,7 @@ pub trait Write { fn len_s(&self) -> usize; } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct Error; pub trait Read<'a>: Sized { @@ -48,25 +47,6 @@ pub unsafe fn write_unchecked(ptr: *mut u8, x: &(impl Write + ?Sized)) { #[cold] pub const fn cold_path() {} -pub const fn hash128(n: &[u8], seed: u64) -> [u64; 2] { - const M: u64 = 0xc6a4a7935bd1e995; - const N: u128 = 0xdbe6d5d5fe4cce213198a2e03707344u128; - let mut h: u64 = seed ^ ((n.len() as u64).wrapping_mul(M)); - let mut i = 0; - while i + 8 <= n.len() { - h ^= u64::from_le_bytes(unsafe { *(n.as_ptr().add(i) as *const [u8; 8]) }).wrapping_mul(M); - i += 8; - } - while i < n.len() { - h ^= (unsafe { *n.as_ptr().add(i) } as u64) << ((i & 7) * 8); - i += 1; - } - let h = (h as u128).wrapping_mul(N); - let h = h ^ (h >> 64); - let h = h.wrapping_mul(N); - [(h >> 64) as u64, h as u64] -} - #[derive(Clone, Copy, Debug)] pub struct ByteArray<'a, const MAX: usize = { usize::MAX }>(pub &'a [u8]); diff --git a/haya_ser/src/write.rs b/haya_ser/src/write.rs index 1364969..a07d297 100644 --- a/haya_ser/src/write.rs +++ b/haya_ser/src/write.rs @@ -41,72 +41,6 @@ macro_rules! non_zero { }; } -pub struct Write2<'a, A: ?Sized, B: ?Sized> { - pub a: &'a A, - pub b: &'a B, -} - -impl Write for Write2<'_, A, B> { - #[inline] - unsafe fn write(&self, w: &mut UnsafeWriter) { - unsafe { - self.a.write(w); - self.b.write(w); - } - } - - #[inline] - fn len_s(&self) -> usize { - self.a.len_s() + self.b.len_s() - } -} - -pub struct Write3<'a, A: ?Sized, B: ?Sized, C: ?Sized> { - pub a: &'a A, - pub b: &'a B, - pub c: &'a C, -} - -impl Write for Write3<'_, A, B, C> { - #[inline] - unsafe fn write(&self, w: &mut UnsafeWriter) { - unsafe { - self.a.write(w); - self.b.write(w); - self.c.write(w); - } - } - - #[inline] - fn len_s(&self) -> usize { - self.a.len_s() + self.b.len_s() + self.c.len_s() - } -} - -impl Write for core::slice::Iter<'_, T> { - #[inline(always)] - unsafe fn write(&self, w: &mut UnsafeWriter) { - self.clone().for_each(|x| unsafe { x.write(w) }); - } - - #[inline(always)] - fn len_s(&self) -> usize { - self.clone().map(|x| x.len_s()).sum() - } -} - -impl Write for core::slice::IterMut<'_, T> { - #[inline(always)] - unsafe fn write(&self, w: &mut UnsafeWriter) { - self.as_slice().iter().for_each(|x| unsafe { x.write(w) }); - } - - #[inline(always)] - fn len_s(&self) -> usize { - self.as_slice().iter().map(|x| x.len_s()).sum() - } -} - impl Write for bool { #[inline(always)] unsafe fn write(&self, w: &mut UnsafeWriter) { diff --git a/haya_str/src/lib.rs b/haya_str/src/lib.rs index 6c12916..52ae311 100644 --- a/haya_str/src/lib.rs +++ b/haya_str/src/lib.rs @@ -3,7 +3,7 @@ use core::mem::transmute; use core::ptr::copy_nonoverlapping; -const MAX: usize = 31; +pub const MAX: usize = 31; #[derive(Clone, Copy)] pub struct HayaStr { @@ -125,10 +125,7 @@ impl core::hash::Hash for HayaStr { impl Default for HayaStr { fn default() -> Self { - Self { - len: Len::N0, - data: [0; MAX], - } + Self::new() } } @@ -181,7 +178,27 @@ impl HayaStr { self.len = Len::N0; } - pub const fn new(s: &str) -> Result { + pub const fn new() -> Self { + Self { + len: Len::N0, + data: [0; MAX], + } + } + + /// # Safety + /// + /// `new_len` must be less than or equal to [MAX]. + /// `self[0..new_len]` must be initialized and is valid UTF-8. + /// + /// [`MAX`]: crate::MAX + pub const unsafe fn set_len(&mut self, new_len: usize) { + debug_assert!(new_len <= MAX); + unsafe { + self.len = transmute::(new_len as u8); + } + } + + pub const fn copy_from(s: &str) -> Result { if s.len() > MAX { Err(OutOfBoundsError) } else {