diff --git a/src/chunk.rs b/src/chunk.rs index e92530a..c19f754 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -25,7 +25,7 @@ use crate::protocol::packets::s2c::play::{ BlockUpdate, ChunkDataAndUpdateLight, S2cPlayPacket, UpdateSectionBlocks, }; use crate::protocol::{Encode, VarInt, VarLong}; -use crate::util::log2_ceil; +use crate::util::bits_needed; mod paletted_container; @@ -556,7 +556,7 @@ impl LoadedChunk { |b| b.to_raw().into(), 4, 8, - log2_ceil(BlockState::max_raw().into()), + bits_needed(BlockState::max_raw().into()), ) .unwrap(); @@ -566,7 +566,7 @@ impl LoadedChunk { |b| b.0.into(), 0, 3, - log2_ceil(biome_registry_len), + bits_needed(biome_registry_len - 1), ) .unwrap(); } @@ -612,7 +612,7 @@ impl LoadedChunk { debug_assert_eq!(bits.count_ones(), 1); - let idx = i * USIZE_BITS + log2_ceil(bits); + let idx = i * USIZE_BITS + bits.trailing_zeros() as usize; let block = sect.block_states.get(idx); let global_x = pos.x * 16 + (idx % 16) as i32; diff --git a/src/chunk/paletted_container.rs b/src/chunk/paletted_container.rs index 5648019..c87a255 100644 --- a/src/chunk/paletted_container.rs +++ b/src/chunk/paletted_container.rs @@ -5,7 +5,7 @@ use arrayvec::ArrayVec; use crate::chunk::{compact_u64s_len, encode_compact_u64s}; use crate::protocol::{Encode, VarInt}; -use crate::util::log2_ceil; +use crate::util::bits_needed; /// `HALF_LEN` must be equal to `ceil(LEN / 2)`. #[derive(Clone, Debug)] @@ -17,7 +17,8 @@ pub enum PalettedContainer { #[derive(Clone, Debug)] pub struct Indirect { - /// Each element is a unique instance of `T`. + /// Each element is a unique instance of `T`. The length of the palette is + /// always ≥2. palette: ArrayVec, /// Each half-byte is an index into `palette`. indices: [u8; HALF_LEN], @@ -147,7 +148,7 @@ impl /// force conversion to the direct representation while encoding. /// - **`direct_bits`**: The minimum number of bits required to represent /// all instances of the element type. If `N` is the total number of - /// possible values, then `DIRECT_BITS` is `ceil(log2(N))`. + /// possible values, then `DIRECT_BITS` is `floor(log2(N - 1)) + 1`. pub fn encode_mc_format( &self, mut writer: W, @@ -177,7 +178,7 @@ impl VarInt(0).encode(&mut writer)?; } Self::Indirect(ind) => { - let bits_per_entry = min_indirect_bits.max(log2_ceil(ind.palette.len())); + let bits_per_entry = min_indirect_bits.max(bits_needed(ind.palette.len() - 1)); // Encode as direct if necessary. if bits_per_entry > max_indirect_bits { diff --git a/src/util.rs b/src/util.rs index cf89093..99b7f4e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -127,15 +127,12 @@ pub fn ray_box_intersect(ro: Vec3, rd: Vec3, bb: Aabb) -> Option< } } -/// Calculates the log base 2 rounded up. -pub(crate) const fn log2_ceil(n: usize) -> usize { - debug_assert!(n != 0); - - // TODO: replace with `n.wrapping_next_power_of_two().trailing_zeros()`. - match n.checked_next_power_of_two() { - Some(n) => n.trailing_zeros() as usize, - None => 0_u64.trailing_zeros() as usize, - } +/// Calculates the minimum number of bits needed to represent the integer `n`. +/// Also known as `floor(log2(n)) + 1`. +/// +/// This returns `0` if `n` is `0`. +pub(crate) const fn bits_needed(n: usize) -> usize { + (usize::BITS - n.leading_zeros()) as _ } #[cfg(test)]