use crate::hash::PreHashedMap;
use crate::{
BdatVersion, Cell, ColumnDef, ColumnMap, Label, LegacyTable, ModernCell, Row, RowRef,
RowRefMut, Table, TableAccessor, TableBuilder,
};
use super::{FormatConvertError, TableInner};
#[derive(Debug, Clone, PartialEq)]
pub struct ModernTable<'b> {
pub(crate) name: Label,
pub(crate) base_id: usize,
pub(crate) columns: ColumnMap,
pub(crate) rows: Vec<Row<'b>>,
#[cfg(feature = "hash-table")]
row_hash_table: PreHashedMap<u32, usize>,
}
impl<'b> ModernTable<'b> {
pub(crate) fn new(builder: TableBuilder<'b>) -> Self {
Self {
name: builder.name,
columns: builder.columns,
base_id: builder
.rows
.iter()
.map(|r| r.id())
.min()
.unwrap_or_default(),
#[cfg(feature = "hash-table")]
row_hash_table: builder
.rows
.iter()
.filter_map(|r| Some((r.id_hash()?, r.id())))
.collect(),
rows: builder.rows,
}
}
#[cfg(feature = "hash-table")]
pub fn get_row_by_hash(&self, hash_id: u32) -> Option<RowRef<'_, 'b, ModernCell<'_, 'b>>> {
self.row_hash_table
.get(&hash_id)
.and_then(|&id| self.get_row(id))
}
#[cfg(feature = "hash-table")]
pub fn row_by_hash(&self, hash_id: u32) -> RowRef<'_, 'b, ModernCell<'_, 'b>> {
self.get_row_by_hash(hash_id)
.expect("no row with given hash")
}
pub fn rows(&self) -> impl Iterator<Item = RowRef<'_, 'b, ModernCell<'_, 'b>>> {
self.rows.iter().map(|row| RowRef::new(row, &self.columns))
}
pub fn rows_mut(&mut self) -> impl Iterator<Item = RowRefMut<'_, 'b>> {
self.rows
.iter_mut()
.map(|row| RowRefMut::new(row, &self.columns))
}
pub fn into_rows(self) -> impl Iterator<Item = Row<'b>> {
self.rows.into_iter()
}
pub fn columns(&self) -> impl Iterator<Item = &ColumnDef> {
self.columns.as_slice().iter()
}
pub fn columns_mut(&mut self) -> impl Iterator<Item = &mut ColumnDef> {
self.columns.as_mut_slice().iter_mut()
}
pub fn into_columns(self) -> impl Iterator<Item = ColumnDef> {
self.columns.into_raw().into_iter()
}
}
impl<'t, 'b: 't> TableAccessor<'t, 'b> for ModernTable<'b> {
type Cell = ModernCell<'t, 'b>;
fn name(&self) -> &Label {
&self.name
}
fn set_name(&mut self, name: Label) {
self.name = name;
}
fn base_id(&self) -> usize {
self.base_id
}
fn row(&self, id: usize) -> RowRef<'_, 'b, ModernCell<'_, 'b>> {
self.get_row(id).expect("row not found")
}
fn get_row(&self, id: usize) -> Option<RowRef<'_, 'b, ModernCell<'_, 'b>>> {
let index = id.checked_sub(self.base_id)?;
self.rows
.get(index)
.map(|row| RowRef::new(row, &self.columns))
}
fn get_row_mut(&mut self, id: usize) -> Option<RowRefMut<'_, 'b>> {
let index = id.checked_sub(self.base_id)?;
self.rows
.get_mut(index)
.map(|row| RowRefMut::new(row, &self.columns))
}
fn row_count(&self) -> usize {
self.rows.len()
}
fn column_count(&self) -> usize {
self.columns.as_slice().len()
}
}
impl<'b> From<ModernTable<'b>> for TableBuilder<'b> {
fn from(value: ModernTable<'b>) -> Self {
Self {
name: value.name,
columns: value.columns,
rows: value.rows,
}
}
}
impl<'b> From<ModernTable<'b>> for Table<'b> {
fn from(value: ModernTable<'b>) -> Self {
Self {
inner: TableInner::Modern(value),
}
}
}
impl<'b> TryFrom<LegacyTable<'b>> for ModernTable<'b> {
type Error = FormatConvertError;
fn try_from(value: LegacyTable<'b>) -> Result<Self, Self::Error> {
if let Some(col) = value
.columns()
.find(|c| !c.value_type().is_supported(BdatVersion::Modern))
{
return Err(FormatConvertError::UnsupportedValueType(col.value_type()));
}
if value
.rows()
.any(|r| r.cells().any(|c| !matches!(c, Cell::Single(_))))
{
return Err(FormatConvertError::UnsupportedCell);
}
Ok(ModernTable::new(TableBuilder::from(value)))
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "hash-table")]
#[test]
fn test_hash_table() {
use crate::{Cell, ColumnDef, Label, Row, TableBuilder, Value, ValueType};
let table = TableBuilder::with_name(Label::Hash(0xDEADBEEF))
.add_column(ColumnDef::new(ValueType::HashRef, 0.into()))
.add_column(ColumnDef::new(ValueType::UnsignedInt, 1.into()))
.add_row(Row::new(
1,
vec![
Cell::Single(Value::HashRef(0xabcdef01)),
Cell::Single(Value::UnsignedInt(256)),
],
))
.add_row(Row::new(
2,
vec![
Cell::Single(Value::HashRef(0xdeadbeef)),
Cell::Single(Value::UnsignedInt(100)),
],
))
.build_modern();
assert_eq!(1, table.get_row_by_hash(0xabcdef01).unwrap().id());
assert_eq!(2, table.get_row_by_hash(0xdeadbeef).unwrap().id());
assert_eq!(
256,
table
.get_row_by_hash(0xabcdef01)
.unwrap()
.get(Label::Hash(1))
.get_as::<u32>()
);
assert_eq!(
100,
table
.get_row_by_hash(0xdeadbeef)
.unwrap()
.get(Label::Hash(1))
.get_as::<u32>()
);
}
}