You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
7.3 KiB
215 lines
7.3 KiB
use bitvec::prelude::*; |
|
|
|
#[derive(Clone)] |
|
pub struct PaletteStorage<T: Copy + Eq> { |
|
len: usize, |
|
bits: usize, |
|
data: BitVec, |
|
entries: Vec<PaletteEntry<T>>, |
|
} |
|
|
|
impl<T: Copy + Eq> PaletteStorage<T> { |
|
pub fn new(len: usize, fill: T) -> Self { |
|
Self { |
|
len, |
|
bits: 0, |
|
data: bitvec![], |
|
entries: vec![PaletteEntry { |
|
value: Some(fill), |
|
used: len, |
|
}], |
|
} |
|
} |
|
|
|
pub fn new_with_default(len: usize) -> Self |
|
where |
|
T: Default, |
|
{ |
|
Self::new(len, Default::default()) |
|
} |
|
|
|
/// Gets the length of this `PaletteStorage`. That is, how many |
|
/// elements can be accessed via the `get` and `set` functions. |
|
pub fn len(&self) -> usize { |
|
self.len |
|
} |
|
|
|
/// Gets the number of bits each element takes up when stored. |
|
/// |
|
/// More bits means more palette entries can be stored, so more unique |
|
/// values can be stored in this `PaletteStorage`, at the cost of more |
|
/// memory. This will automatically increase as necessary. |
|
pub fn bits(&self) -> usize { |
|
self.bits |
|
} |
|
|
|
/// Gets the number of times the specific value occurs in this `PaletteStorage`. |
|
pub fn get_used_count(&self, value: T) -> usize { |
|
self.find_existing_entry(value).map(|e| e.used).unwrap_or(0) |
|
} |
|
|
|
pub fn get(&self, index: usize) -> T { |
|
let palette_index = self.get_palette_index(index); |
|
let entry = &self.entries[palette_index]; |
|
entry.value.expect("invalid palette entry") |
|
} |
|
|
|
pub fn set(&mut self, index: usize, value: T) -> T { |
|
let prev_palette_index = self.get_palette_index(index); |
|
let prev_entry = &mut self.entries[prev_palette_index]; |
|
let prev_value = prev_entry.value.expect("invalid palette entry"); |
|
|
|
// If entry found at `index` already has this value, return early. |
|
if prev_value == value { |
|
return value; |
|
} |
|
|
|
// Reduce the number of times the previously used palette for this |
|
// `index` is in use. This potentially allows this entry to be reused. |
|
prev_entry.used -= 1; |
|
|
|
// Find a palette entry for this value. Resizes palette if necessary. |
|
let (new_palette_index, new_entry) = self.find_or_create_entry(value); |
|
// increase the number of times it's in use |
|
new_entry.used += 1; |
|
|
|
// Update the palette index in the actual `data` BitVec. |
|
self.set_palette_index(index, new_palette_index); |
|
|
|
prev_value |
|
} |
|
|
|
/// Gets the bit range into `data` for the specified index. |
|
fn bit_range(&self, index: usize) -> std::ops::Range<usize> { |
|
(index * self.bits)..((index + 1) * self.bits) |
|
} |
|
|
|
/// Looks up the palette index (into `entries`) at the specified index. |
|
fn get_palette_index(&self, index: usize) -> usize { |
|
if self.bits > 0 { |
|
let bit_range = self.bit_range(index); |
|
self.data[bit_range].load() |
|
} else { |
|
0 |
|
} |
|
} |
|
|
|
fn set_palette_index(&mut self, index: usize, value: usize) { |
|
let bit_range = self.bit_range(index); |
|
self.data[bit_range].store(value) |
|
} |
|
|
|
fn find_existing_entry(&self, value: T) -> Option<&PaletteEntry<T>> { |
|
self.entries.iter().find(|e| e.value == Some(value)) |
|
} |
|
|
|
fn find_or_create_entry(&mut self, value: T) -> (usize, &mut PaletteEntry<T>) { |
|
match self.find_entry_index(value) { |
|
// The palette entry already exists, so just return it. |
|
FindResult::Existing(index) => { |
|
let entry = &mut self.entries[index]; |
|
(index, entry) |
|
} |
|
// The entry didn't exist, but we found one we can reuse. |
|
FindResult::Uninitialized(index) | FindResult::Unused(index) => { |
|
let entry = &mut self.entries[index]; |
|
entry.value = Some(value); |
|
(index, entry) |
|
} |
|
// Everything in the palette is already in use. RESIZE! |
|
FindResult::None => { |
|
let index = self.entries.len(); |
|
self.resize_palette(self.bits + 1); |
|
let entry = &mut self.entries[index]; |
|
entry.value = Some(value); |
|
(index, entry) |
|
} |
|
} |
|
} |
|
|
|
fn find_entry_index(&self, value: T) -> FindResult { |
|
let mut result = FindResult::None; |
|
for (index, entry) in self.entries.iter().enumerate() { |
|
match entry { |
|
// If the specified value is found in the palette, return it immediately. |
|
PaletteEntry { value: v, .. } if *v == Some(value) => { |
|
return FindResult::Existing(index) |
|
} |
|
// Store the first uninitialized entry in case we don't find the specified value. |
|
PaletteEntry { value: None, .. } => match result { |
|
FindResult::Existing(_) => unreachable!(), |
|
FindResult::Uninitialized(_) => {} |
|
_ => result = FindResult::Uninitialized(index), |
|
}, |
|
// Otherwise, pick the first initialized entry that's currently used. |
|
PaletteEntry { used: 0, .. } => match result { |
|
FindResult::Existing(_) => unreachable!(), |
|
FindResult::Uninitialized(_) | FindResult::Unused(_) => {} |
|
FindResult::None => result = FindResult::Unused(index), |
|
}, |
|
_ => {} |
|
} |
|
} |
|
result |
|
} |
|
|
|
fn resize_palette(&mut self, new_bits: usize) { |
|
if new_bits == self.bits { |
|
return; |
|
} |
|
|
|
// TODO: Revisit this to see if we can optimize it. |
|
// Create a new data BitVec and copy the bits from the old one over. |
|
let mut new_data = BitVec::with_capacity(self.len * new_bits); |
|
if new_bits == 0 { |
|
// Nothing to do if we have nothing to copy to. |
|
} else if self.bits == 0 { |
|
// No data to copy from, so just fill with zeroes. |
|
new_data.resize(self.len * new_bits, false); |
|
} else if new_bits > self.bits { |
|
let additional_bits = new_bits - self.bits; |
|
for chunk in self.data.chunks_exact(self.bits) { |
|
new_data.extend(chunk); |
|
for _ in 0..additional_bits { |
|
new_data.push(false); |
|
} |
|
} |
|
} else { |
|
for chunk in self.data.chunks_exact(self.bits) { |
|
new_data.extend(&chunk[0..new_bits]); |
|
} |
|
} |
|
self.data = new_data; |
|
self.bits = new_bits; |
|
|
|
// Resize the palette itself. |
|
let num_entries = 2usize.pow(new_bits as u32); |
|
self.entries.resize_with(num_entries, Default::default); |
|
} |
|
} |
|
|
|
enum FindResult { |
|
Existing(usize), |
|
Uninitialized(usize), |
|
Unused(usize), |
|
None, |
|
} |
|
|
|
// #[derive(Default)] |
|
#[derive(Clone)] |
|
struct PaletteEntry<T> { |
|
value: Option<T>, |
|
used: usize, |
|
} |
|
|
|
// NOTE: For some weird reason, deriving `Default` doesn't quite do what we want |
|
// when later calling `Vec::resize_with`, so we're manually implementing |
|
// the trait instead. [insert confused noises here] |
|
impl<T> Default for PaletteEntry<T> { |
|
fn default() -> Self { |
|
Self { |
|
value: None, |
|
used: 0, |
|
} |
|
} |
|
}
|
|
|