diff --git a/crates/core/world/palette/src/container.rs b/crates/core/world/palette/src/container.rs new file mode 100644 index 0000000..602c128 --- /dev/null +++ b/crates/core/world/palette/src/container.rs @@ -0,0 +1 @@ +//! Paletted containers. diff --git a/crates/core/world/palette/src/lib.rs b/crates/core/world/palette/src/lib.rs index b5372e5..dd5256d 100644 --- a/crates/core/world/palette/src/lib.rs +++ b/crates/core/world/palette/src/lib.rs @@ -2,6 +2,7 @@ use std::{collections::HashMap, hash::Hash}; +pub mod container; mod iter; pub use iter::Iter; diff --git a/crates/util/packed-int-array/Cargo.toml b/crates/util/packed-int-array/Cargo.toml new file mode 100644 index 0000000..481a18d --- /dev/null +++ b/crates/util/packed-int-array/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "rimecraft-packed-int-array" +version = "0.1.0" +edition = "2021" +authors = ["JieningYu "] +description = "PackedIntegerArray in Rust" +repository = "https://github.com/rimecraft-rs/rimecraft/" +license = "AGPL-3.0-or-later" +categories = ["data-structures"] + +[badges] +maintenance = { status = "passively-maintained" } + +[dependencies] + +[lints] +workspace = true diff --git a/crates/util/packed-int-array/src/consts.rs b/crates/util/packed-int-array/src/consts.rs new file mode 100644 index 0000000..a7bc5ae --- /dev/null +++ b/crates/util/packed-int-array/src/consts.rs @@ -0,0 +1,194 @@ +pub const INDEX_PARAMS: [i32; 192] = [ + -1, + -1, + 0, + i32::MIN, + 0, + 0, + 1431655765, + 1431655765, + 0, + i32::MIN, + 0, + 1, + 858993459, + 858993459, + 0, + 715827882, + 715827882, + 0, + 613566756, + 613566756, + 0, + i32::MIN, + 0, + 2, + 477218588, + 477218588, + 0, + 429496729, + 429496729, + 0, + 390451572, + 390451572, + 0, + 357913941, + 357913941, + 0, + 330382099, + 330382099, + 0, + 306783378, + 306783378, + 0, + 286331153, + 286331153, + 0, + i32::MIN, + 0, + 3, + 252645135, + 252645135, + 0, + 238609294, + 238609294, + 0, + 226050910, + 226050910, + 0, + 214748364, + 214748364, + 0, + 204522252, + 204522252, + 0, + 195225786, + 195225786, + 0, + 186737708, + 186737708, + 0, + 178956970, + 178956970, + 0, + 171798691, + 171798691, + 0, + 165191049, + 165191049, + 0, + 159072862, + 159072862, + 0, + 153391689, + 153391689, + 0, + 148102320, + 148102320, + 0, + 143165576, + 143165576, + 0, + 138547332, + 138547332, + 0, + i32::MIN, + 0, + 4, + 130150524, + 130150524, + 0, + 126322567, + 126322567, + 0, + 122713351, + 122713351, + 0, + 119304647, + 119304647, + 0, + 116080197, + 116080197, + 0, + 113025455, + 113025455, + 0, + 110127366, + 110127366, + 0, + 107374182, + 107374182, + 0, + 104755299, + 104755299, + 0, + 102261126, + 102261126, + 0, + 99882960, + 99882960, + 0, + 97612893, + 97612893, + 0, + 95443717, + 95443717, + 0, + 93368854, + 93368854, + 0, + 91382282, + 91382282, + 0, + 89478485, + 89478485, + 0, + 87652393, + 87652393, + 0, + 85899345, + 85899345, + 0, + 84215045, + 84215045, + 0, + 82595524, + 82595524, + 0, + 81037118, + 81037118, + 0, + 79536431, + 79536431, + 0, + 78090314, + 78090314, + 0, + 76695844, + 76695844, + 0, + 75350303, + 75350303, + 0, + 74051160, + 74051160, + 0, + 72796055, + 72796055, + 0, + 71582788, + 71582788, + 0, + 70409299, + 70409299, + 0, + 69273666, + 69273666, + 0, + 68174084, + 68174084, + 0, + i32::MIN, + 0, + 5, +]; diff --git a/crates/util/packed-int-array/src/iter.rs b/crates/util/packed-int-array/src/iter.rs new file mode 100644 index 0000000..a0dc5f4 --- /dev/null +++ b/crates/util/packed-int-array/src/iter.rs @@ -0,0 +1,69 @@ +use crate::PackedIntArray; + +#[derive(Debug)] +pub(crate) struct IterInner { + pub l: u64, + pub j: usize, + pub times: usize, +} + +/// An iterator over a packed int array. +#[derive(Debug)] +pub struct Iter<'a> { + pub(crate) array: &'a PackedIntArray, + pub(crate) iter: std::slice::Iter<'a, u64>, + pub(crate) inner: IterInner, +} + +impl Iterator for Iter<'_> { + type Item = u32; + + fn next(&mut self) -> Option { + if self.inner.times >= self.array.len() { + return None; + } + + if self.inner.j < self.array.elements_per_long { + self.inner.j += 1; + let res = self.inner.l & self.array.max; + self.inner.l >>= self.array.element_bits; + self.inner.times += 1; + Some(res as u32) + } else { + self.inner.l = *self.iter.next()?; + self.next() + } + } +} + +/// An iterator over a packed int array. +#[derive(Debug)] +pub struct IntoIter { + pub(crate) element_bits: usize, + pub(crate) elements_per_long: usize, + pub(crate) max: u64, + pub(crate) iter: std::vec::IntoIter, + pub(crate) inner: IterInner, + pub(crate) len: usize, +} + +impl Iterator for IntoIter { + type Item = u32; + + fn next(&mut self) -> Option { + if self.inner.times >= self.len { + return None; + } + + if self.inner.j < self.elements_per_long { + self.inner.j += 1; + let res = self.inner.l & self.max; + self.inner.l >>= self.element_bits; + self.inner.times += 1; + Some(res as u32) + } else { + self.inner.l = self.iter.next()?; + self.next() + } + } +} diff --git a/crates/util/packed-int-array/src/lib.rs b/crates/util/packed-int-array/src/lib.rs new file mode 100644 index 0000000..161b7d6 --- /dev/null +++ b/crates/util/packed-int-array/src/lib.rs @@ -0,0 +1,206 @@ +//! `PackedIntegerArray` in Rust. + +mod consts; +mod iter; + +pub use iter::{IntoIter, Iter}; + +use crate::consts::INDEX_PARAMS; + +/// A packed container for storing small integers. +#[derive(Debug, Clone)] +pub struct PackedIntArray { + data: Vec, + element_bits: usize, + max: u64, + len: usize, + elements_per_long: usize, + + index_scale: isize, + index_offset: isize, + index_shift: isize, +} + +impl PackedIntArray { + /// Creates a new `PackedIntArray` with given `element_bits`, `len` and `raw` + /// packed data. + /// + /// # Panics + /// + /// - Panics if the given `element_bits` is not in range `(0, 32]`. + /// - Panics if length of the given raw data slice is not equal to + /// `(len + 64 / element_bits - 1) / (64 / element_bits)`. + pub fn from_packed(element_bits: usize, len: usize, raw: Option<&[u64]>) -> Self { + assert!( + 0 < element_bits && element_bits <= 32, + "element bits should in range (0, 32]" + ); + + let max = (1u64 << element_bits) - 1; + let elements_per_long = 64 / element_bits; + let i = 3 * (elements_per_long - 1); + let index_scale = INDEX_PARAMS[i] as isize; + let index_offset = INDEX_PARAMS[i + 1] as isize; + let index_shift = INDEX_PARAMS[i + 2] as isize; + let j = (len + elements_per_long - 1) / elements_per_long; + + if let Some(data) = raw { + assert_eq!(data.len(), j, "invalid length given for storage"); + } + + Self { + data: raw.map(Vec::from).unwrap_or_else(|| vec![0; j]), + element_bits, + max, + len, + elements_per_long, + index_scale, + index_offset, + index_shift, + } + } + + #[inline] + const fn storage_index(&self, index: usize) -> usize { + let l = self.index_scale as u32 as usize; + let m = self.index_offset as u32 as usize; + (index * l + m) >> 32 >> self.index_shift + } + + /// Sets the data at given `index` with given value and returns the old one. + /// + /// # Panics + /// + /// Panics if the given value is greater than the internal max value. + pub fn set(&mut self, index: usize, value: u32) -> Option { + assert!( + value as u64 <= self.max, + "given value {} could not be greater than max value {}", + value, + self.max + ); + + if index >= self.len { + return None; + } + + let i = self.storage_index(index); + let l = &mut self.data[i]; + let lo = *l; + let j = (index - i * self.elements_per_long) * self.element_bits; + *l = *l & !(self.max << j) | (value as u64 & self.max) << j; + Some((lo >> j & self.max) as u32) + } + + /// Gets the value at target index. + pub fn get(&self, index: usize) -> Option { + if index >= self.len { + return None; + } + let i = self.storage_index(index); + let l = self.data[i]; + let j = (index - i * self.elements_per_long) * self.element_bits; + Some((l >> j & self.max) as u32) + } + + /// Gets the inner packed data of this array. + #[inline] + pub fn data(&self) -> &[u64] { + &self.data + } + + /// Gets the inner packed mutable data of this array. + #[inline] + pub fn data_mut(&mut self) -> &mut [u64] { + &mut self.data + } + + /// Gets the length of this array. + #[inline] + pub fn len(&self) -> usize { + self.len + } + + /// Whether this array is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Gets an iterator over this array. + pub fn iter(&self) -> Iter<'_> { + if self.is_empty() { + Iter { + array: self, + iter: self.data.iter(), + inner: iter::IterInner { + l: 0, + j: self.elements_per_long, + times: 0, + }, + } + } else { + Iter { + array: self, + iter: self.data[1..].iter(), + inner: iter::IterInner { + l: self.data[0], + j: 0, + times: 0, + }, + } + } + } +} + +impl IntoIterator for PackedIntArray { + type Item = u32; + + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + if self.is_empty() { + IntoIter { + iter: self.data.into_iter(), + inner: iter::IterInner { + l: 0, + j: self.elements_per_long, + times: 0, + }, + element_bits: self.element_bits, + elements_per_long: self.elements_per_long, + max: self.max, + len: self.len, + } + } else { + let mut iter = self.data.into_iter(); + let first = iter.next().unwrap(); + IntoIter { + iter, + inner: iter::IterInner { + l: first, + j: 0, + times: 0, + }, + element_bits: self.element_bits, + elements_per_long: self.elements_per_long, + max: self.max, + len: self.len, + } + } + } +} + +impl<'a> IntoIterator for &'a PackedIntArray { + type Item = u32; + + type IntoIter = Iter<'a>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +#[cfg(test)] +mod tests; diff --git a/crates/util/packed-int-array/src/tests.rs b/crates/util/packed-int-array/src/tests.rs new file mode 100644 index 0000000..1f02d27 --- /dev/null +++ b/crates/util/packed-int-array/src/tests.rs @@ -0,0 +1,33 @@ +use crate::PackedIntArray; + +#[test] +fn swap() { + let mut array = PackedIntArray::from_packed(8, 16, None); + assert_eq!(array.len(), 16); + assert_eq!(array.max, u8::MAX as u64); + + array.set(0, 1); + assert_eq!(array.get(0), Some(1)); + array.set(0, 2); + assert_eq!(array.get(0), Some(2)); + array.set(15, 255); + assert_eq!(array.get(15), Some(255)); + + assert_eq!(array.set(15, 0), Some(255)); +} + +#[test] +fn iter() { + const ARRAY: [u32; 4] = [1, 2, 3, 4]; + let mut array = PackedIntArray::from_packed(8, 4, None); + for (i, j) in ARRAY.into_iter().enumerate() { + array.set(i, j); + } + + let mut iter = array.into_iter(); + assert_eq!(iter.next(), Some(ARRAY[0])); + assert_eq!(iter.next(), Some(ARRAY[1])); + assert_eq!(iter.next(), Some(ARRAY[2])); + assert_eq!(iter.next(), Some(ARRAY[3])); + assert_eq!(iter.next(), None); +}