From 118095d0cfdf181809e22fe24f6a2c5003b5aa75 Mon Sep 17 00:00:00 2001 From: tox Date: Wed, 6 May 2026 03:43:47 -0500 Subject: [PATCH 1/7] perf(lib): pass `WorldFlags` by value `WorldFlags` wraps `u8`. It's more efficient to pass arguments this small by value than by reference. --- slimeball-lib/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/slimeball-lib/src/lib.rs b/slimeball-lib/src/lib.rs index ac44e3e..c462c58 100644 --- a/slimeball-lib/src/lib.rs +++ b/slimeball-lib/src/lib.rs @@ -55,19 +55,19 @@ impl From for WorldFlags { } impl WorldFlags { - const fn poi_chunks(&self) -> bool { + const fn poi_chunks(self) -> bool { self.0 & 1 == 1 } - const fn fluid_ticks(&self) -> bool { + const fn fluid_ticks(self) -> bool { self.0 & 2 == 2 } - const fn block_ticks(&self) -> bool { + const fn block_ticks(self) -> bool { self.0 & 4 == 4 } - const fn other_flag_count(&self) -> u32 { + const fn other_flag_count(self) -> u32 { self.0.count_ones() - (self.poi_chunks() as u32) - (self.fluid_ticks() as u32) From 7336d5292dd30f7895a5cc94d2c9d6b3b501f249 Mon Sep 17 00:00:00 2001 From: tox Date: Wed, 6 May 2026 03:53:42 -0500 Subject: [PATCH 2/7] fix(lib): make use of auto-deref --- slimeball-lib/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slimeball-lib/src/lib.rs b/slimeball-lib/src/lib.rs index c462c58..f1ace50 100644 --- a/slimeball-lib/src/lib.rs +++ b/slimeball-lib/src/lib.rs @@ -235,7 +235,7 @@ fn read_chunks(buf: &mut impl Read, world_flags: WorldFlags) -> Result(buf: &mut impl Read) -> Result debug!("loading nbt, size {size} bytes"); let mut bytebuf = vec![0u8; size.try_into().unwrap()]; buf.read_exact(&mut bytebuf)?; - Ok(fastnbt::from_bytes(&*bytebuf)?) + Ok(fastnbt::from_bytes(&bytebuf)?) } #[derive(Deserialize, Debug, Clone)] From c3fbf8dd74b42f2e61ecb3d8683143787245bdc7 Mon Sep 17 00:00:00 2001 From: tox Date: Wed, 6 May 2026 04:16:20 -0500 Subject: [PATCH 3/7] perf(lib): avoid allocs from `zstd::decode_all()` Convenient though it may be, `zstd::decode_all()` repeatedly allocates memory for a new `result` vector[^1]. We can dodge these unnecessary allocations by calling `zstd::stream::copy_decode()` directly and allocating `uncompressed_chunks_size` bytes to an `uncompressed` vector once. I renamed the vector associated with `uncompressed_chunks_size` from `decoded` to `uncompressed` for consistency. [^1]: See code for `zstd::decode_all()`: ```rust pub fn decode_all(source: R) -> io::Result> { let mut result = Vec::new(); copy_decode(source, &mut result)?; Ok(result) } ``` --- slimeball-lib/src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/slimeball-lib/src/lib.rs b/slimeball-lib/src/lib.rs index f1ace50..5cf8556 100644 --- a/slimeball-lib/src/lib.rs +++ b/slimeball-lib/src/lib.rs @@ -127,16 +127,18 @@ fn read_compressed(buf: &mut impl BufRead) -> Result> { let mut compressed = vec![0; compressed_chunks_size]; buf.read_exact(&mut compressed)?; - let decoded = zstd::decode_all(&*compressed)?; - if decoded.len() != uncompressed_chunks_size { + let mut uncompressed = Vec::with_capacity(uncompressed_chunks_size); + zstd::stream::copy_decode(&*compressed, &mut uncompressed)?; + + if uncompressed.len() != uncompressed_chunks_size { return Err(Error::DecompressSize( uncompressed_chunks_size, - decoded.len(), + uncompressed.len(), )); } - Ok(decoded) + Ok(uncompressed) } fn read_chunks(buf: &mut impl Read, world_flags: WorldFlags) -> Result> { From 15c474ea675e9b51a29f9ce5c2528dff646f9944 Mon Sep 17 00:00:00 2001 From: tox Date: Wed, 6 May 2026 04:42:12 -0500 Subject: [PATCH 4/7] fix(lib): be more idiomatic --- slimeball-lib/src/lib.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/slimeball-lib/src/lib.rs b/slimeball-lib/src/lib.rs index 5cf8556..ef12619 100644 --- a/slimeball-lib/src/lib.rs +++ b/slimeball-lib/src/lib.rs @@ -285,13 +285,14 @@ pub struct PalettedContainer { // TODO: move this stuff to a different package as it's shared between Anvil and Slime impl PalettedContainer { pub fn get(&self, index: usize) -> Option<&T> { - if self.palette.len() == 1 { - return self.palette.get(0); + if let [only] = self.palette.as_slice() { + return Some(only); } - if index >= SIZE { - panic!("index {index} outside range for PalettedContainer of size {SIZE}"); - } + assert!( + index < SIZE, + "index {index} outside range for PalettedContainer of size {SIZE}" + ); // All indices are the same length. This length is set to the minimum amount // of bits required to represent the largest index in the palette, and then From 9b2eed52705acff87e6afb1abbb111f12b4d9c37 Mon Sep 17 00:00:00 2001 From: tox Date: Wed, 6 May 2026 04:44:53 -0500 Subject: [PATCH 5/7] chore(lib): semicolons --- slimeball-lib/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slimeball-lib/src/lib.rs b/slimeball-lib/src/lib.rs index ef12619..ba063b9 100644 --- a/slimeball-lib/src/lib.rs +++ b/slimeball-lib/src/lib.rs @@ -189,7 +189,7 @@ fn read_chunks(buf: &mut impl Read, world_flags: WorldFlags) -> Result Result Date: Wed, 6 May 2026 04:49:14 -0500 Subject: [PATCH 6/7] fix(lib): Use `chunk_index` for logs More informative logs and address a lint. --- slimeball-lib/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/slimeball-lib/src/lib.rs b/slimeball-lib/src/lib.rs index ba063b9..fa79282 100644 --- a/slimeball-lib/src/lib.rs +++ b/slimeball-lib/src/lib.rs @@ -150,7 +150,10 @@ fn read_chunks(buf: &mut impl Read, world_flags: WorldFlags) -> Result()?; let section_count = buf.read_i32::()?; - debug!("chunk {x}, {z}: {section_count} sections"); + debug!( + "Chunk {}/{chunks_to_read} at {x}, {z}: {section_count} sections", + chunk_index + 1 + ); let mut sections = Vec::with_capacity(section_count.try_into().unwrap()); for section in 0..section_count { From fc46d1744a778db208deb85625bec41350408a51 Mon Sep 17 00:00:00 2001 From: tox Date: Wed, 6 May 2026 04:50:58 -0500 Subject: [PATCH 7/7] chore(lib): consistently capitalize messages --- slimeball-lib/src/lib.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/slimeball-lib/src/lib.rs b/slimeball-lib/src/lib.rs index fa79282..b8fc408 100644 --- a/slimeball-lib/src/lib.rs +++ b/slimeball-lib/src/lib.rs @@ -158,11 +158,11 @@ fn read_chunks(buf: &mut impl Read, world_flags: WorldFlags) -> Result { - debug!("reading skylight"); + debug!("Reading skylight"); let mut sky_light = vec![0; 2048]; buf.read_exact(&mut sky_light)?; Some(sky_light) @@ -172,7 +172,7 @@ fn read_chunks(buf: &mut impl Read, world_flags: WorldFlags) -> Result { - debug!("reading blocklight"); + debug!("Reading blocklight"); let mut block_light = vec![0; 2048]; buf.read_exact(&mut block_light)?; Some(block_light) @@ -180,10 +180,10 @@ fn read_chunks(buf: &mut impl Read, world_flags: WorldFlags) -> Result None, }; - debug!("reading block states"); + debug!("Reading block states"); let block_states: PalettedContainer<4096, BlockState> = read_sized(buf)?; debug!("{:?}", block_states); - debug!("reading biomes"); + debug!("Reading biomes"); let biomes: fastnbt::Value = read_sized(buf)?; debug!("{:?}", biomes); @@ -195,7 +195,7 @@ fn read_chunks(buf: &mut impl Read, world_flags: WorldFlags) -> Result Result Result(buf: &mut impl Read) -> Result { let size = buf.read_i32::()?; - debug!("loading nbt, size {size} bytes"); + debug!("Loading nbt, size {size} bytes"); let mut bytebuf = vec![0u8; size.try_into().unwrap()]; buf.read_exact(&mut bytebuf)?; Ok(fastnbt::from_bytes(&bytebuf)?) @@ -294,7 +294,7 @@ impl PalettedContainer { assert!( index < SIZE, - "index {index} outside range for PalettedContainer of size {SIZE}" + "Index {index} outside range for PalettedContainer of size {SIZE}" ); // All indices are the same length. This length is set to the minimum amount