Skip to content

Instantly share code, notes, and snippets.

@YuriGor
Created September 3, 2023 22:51
Show Gist options
  • Save YuriGor/df275e6112bf436ead3c11a99fde8ef6 to your computer and use it in GitHub Desktop.
Save YuriGor/df275e6112bf436ead3c11a99fde8ef6 to your computer and use it in GitHub Desktop.
variable_row_array (in short: use array of slices instead)
// https://users.rust-lang.org/t/memory-friendly-2d-array-with-variable-length-rows/99330
pub struct VariableRowArray<T, const SIZE: usize, const ROWS: usize> {
data: [T; SIZE], // The main data array where rows are stored.
index: [usize; ROWS], // An index array to track the start of each row in the data array.
num_rows: usize, // The current number of rows in the array.
}
impl<T: Default + std::marker::Copy, const SIZE: usize, const ROWS: usize>
VariableRowArray<T, SIZE, ROWS>
{
pub fn new() -> Self {
let def = T::default();
VariableRowArray {
data: [def; SIZE],
index: [0; ROWS],
num_rows: 0,
}
}
pub const fn new_with_data(rows: &[&[T]]) -> Self {
let mut r = 0;
let mut def = None;
while r < ROWS && r < rows.len() {
if rows[r].len() > 0 {
def = Some(rows[r][0]);
break;
}
r += 1;
}
if let Some(def) = def {
let mut data = [def; SIZE];
let mut index = [0; ROWS];
let mut num_rows: usize = 0;
while num_rows < ROWS && num_rows < rows.len() {
let row = rows[num_rows];
let start = if num_rows > 0 {
index[num_rows - 1]
} else {
0 // first row always has 0 start index so we don't track it
};
let mut c = 0;
while c < row.len() {
data[start + c] = row[c];
c += 1;
}
index[num_rows] = start + row.len();
num_rows += 1;
}
VariableRowArray {
data,
index,
num_rows,
}
} else {
panic!("At least one not empty row is required");
}
}
pub fn add_row(&mut self, row: &[T]) {
let start = self.num_rows.checked_sub(1).map_or(0, |i| self.index[i]);
let end = start + row.len();
self.data[start..end].copy_from_slice(row);
self.index[self.num_rows] = end;
self.num_rows += 1;
}
pub fn get_row(&self, row_index: usize) -> Option<&[T]> {
if row_index >= self.num_rows {
return None;
}
let start = row_index.checked_sub(1).map_or(0, |i| self.index[i]);
let end = self.index[row_index];
Some(&self.data[start..end])
}
pub fn get(&self, row_index: usize, col_index: usize) -> Option<T> {
if row_index >= self.num_rows {
return None;
}
let start = row_index.checked_sub(1).map_or(0, |i| self.index[i]);
let end = self.index[row_index];
if col_index >= end - start {
return None;
}
Some(self.data[start + col_index])
}
pub fn num_rows(&self) -> usize {
self.num_rows
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment