#[cfg(feature = "no-std")] use alloc::string::String;
#[cfg(feature = "no-std")] use alloc::vec::Vec;
use core::fmt;
use table::{Value, Table, TableId, Index};
use indexes::TableIndex;
use hashbrown::hash_map::{HashMap, Entry};
use std::rc::Rc;
use std::cell::RefCell;
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum Change {
Set{table: u64, row: Index, column: Index, value: Value},
Remove{table: u64, row: Index, column: Index, value: Value},
NewTable{id: u64, rows: u64, columns: u64},
RenameColumn{table: u64, column_ix: u64, column_alias: u64},
RemoveTable{id: u64, rows: u64, columns: u64},
}
impl fmt::Debug for Change {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Change::Set{table, row, column, value} => write!(f, "<set> #{:#x} [{:?} {:?} {:?}]", table, row, column, value),
Change::Remove{table, row, column, value} => write!(f, "<remove> #{:#x} [{:?} {:?}: {:?}]", table, row, column, value),
Change::NewTable{id, rows, columns} => write!(f, "<newtable> #{:#x} [{:?} x {:?}]", id, rows, columns),
Change::RenameColumn{table, column_ix, column_alias} => write!(f, "<renamecolumn> #{:#x} {:#x} -> {:#x}", table, column_ix, column_alias),
Change::RemoveTable{id, rows, columns} => write!(f, "<removetable> #{:#x} [{:?} x {:?}]", id, rows, columns),
}
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct Transaction {
pub tables: Vec<Change>,
pub adds: Vec<Change>,
pub removes: Vec<Change>,
pub names: Vec<Change>,
}
impl Transaction {
pub fn new() -> Transaction {
Transaction {
tables: Vec::new(),
adds: Vec::new(),
removes: Vec::new(),
names: Vec::new(),
}
}
pub fn from_changeset(changes: Vec<Change>) -> Transaction {
let mut txn = Transaction::new();
for change in changes {
match change {
Change::Set{..} => txn.adds.push(change),
Change::Remove{..} => txn.removes.push(change),
Change::RemoveTable{..} |
Change::NewTable{..} => txn.tables.push(change),
Change::RenameColumn{..} => txn.names.push(change),
}
}
txn
}
pub fn from_change(change: Change) -> Transaction {
let mut txn = Transaction::new();
match change {
Change::Set{..} => txn.adds.push(change),
Change::Remove{..} => txn.removes.push(change),
Change::RemoveTable{..} |
Change::NewTable{..} => txn.tables.push(change),
Change::RenameColumn{..} => txn.names.push(change),
}
txn
}
pub fn from_adds_removes(adds: Vec<(u64, Index, Index, String)>, removes: Vec<(u64, Index, Index, String)>) -> Transaction {
let mut txn = Transaction::new();
for (table, row, column, value) in adds {
txn.adds.push(Change::Set{table, row, column, value: Value::from_string(value)});
}
for (table, row, column, value) in removes {
txn.removes.push(Change::Remove{table, row, column, value: Value::from_string(value)});
}
txn
}
}
impl fmt::Debug for Transaction {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for ref table in &self.tables {
write!(f, "{:?}\n", table).unwrap();
}
for ref add in &self.adds {
write!(f, "{:?}\n", add).unwrap();
}
for ref remove in &self.removes {
write!(f, "{:?}\n", remove).unwrap();
}
for ref name in &self.names {
write!(f, "{:?}\n", name).unwrap();
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct Interner {
pub offset: usize,
pub tables: TableIndex,
pub names: HashMap<u64,String>,
pub changes: Vec<Change>,
pub changes_count: usize,
pub change_pointer: usize,
pub rollover: usize,
pub last_round: usize,
}
impl Interner {
pub fn new(change_capacity: usize, table_capacity: usize) -> Interner {
Interner {
offset: 0,
tables: TableIndex::new(table_capacity),
names: HashMap::new(),
changes: Vec::with_capacity(change_capacity),
changes_count: 0,
change_pointer: 0,
rollover: 0,
last_round: 0,
}
}
pub fn clear(&mut self) {
self.tables.clear();
self.changes.clear();
self.changes_count = 0;
self.change_pointer = 0;
}
pub fn process_transaction(&mut self, txn: &Transaction) {
for table in txn.tables.iter() {
self.intern_change(table);
}
for name in txn.names.iter() {
self.intern_change(name);
}
for remove in txn.removes.iter() {
self.intern_change(remove);
}
for add in txn.adds.iter() {
self.intern_change(add);
}
}
fn intern_change(&mut self, change: &Change) {
match change {
Change::Set{table, row, column, value} => {
let mut changed = false;
let mut alias: Option<u64> = None;
match self.tables.get(*table) {
Some(table_ref) => {
alias = table_ref.borrow().get_column_alias(column);
let old_value = table_ref.borrow_mut().set_cell(&row, &column, value.clone());
if old_value != *value {
changed = true;
}
if self.offset == 0 && changed == true {
match old_value {
Value::Empty => (),
_ => self.save_change(&Change::Remove{table: *table, row: row.clone(), column: column.clone(), value: old_value}),
}
}
}
None => (),
};
if changed == true {
match alias {
Some(id) => {
self.tables.changed_this_round.insert((table.clone(), Index::Alias(id)))
},
_ => false,
};
self.tables.changed_this_round.insert((table.clone(), column.clone()));
self.tables.changed_this_round.insert((table.clone(), Index::Index(0)));
}
},
Change::Remove{table, row, column, value} => {
},
Change::NewTable{id, rows, columns } => {
self.tables.insert(Table::new(*id, *rows, *columns));
}
Change::RemoveTable{id, rows: _, columns: _} => {
self.tables.remove(&id);
}
Change::RenameColumn{table, column_ix, column_alias} => {
match self.tables.get(*table) {
Some(table_ref) => {
table_ref.borrow_mut().set_column_alias(*column_alias, *column_ix);
}
None => (),
};
self.tables.changed_this_round.insert((*table, Index::Alias(*column_alias)));
},
}
if self.offset == 0 {
self.save_change(change);
}
}
fn save_change(&mut self, change: &Change) {
if self.changes.len() < self.changes.capacity() {
self.changes.push(change.clone());
} else if self.change_pointer == self.changes.capacity() {
self.change_pointer = 0;
self.rollover += 1;
self.changes[self.change_pointer] = change.clone();
} else {
self.changes[self.change_pointer] = change.clone();
}
self.change_pointer += 1;
self.changes_count += 1;
}
pub fn get_table(&self, table: u64) -> Option<&Rc<RefCell<Table>>> {
self.tables.get(table)
}
pub fn contains(&mut self, table: TableId) -> bool {
self.tables.contains(*table.unwrap())
}
pub fn get_column(&self, table: TableId, column: Index) -> Option<&Vec<Value>> {
match self.tables.get(*table.unwrap()) {
Some(stored_table) => {
match unsafe{(*stored_table.as_ptr()).get_column(&column)} {
Some(column) => Some(column),
None => None,
}
},
None => None,
}
}
pub fn len(&self) -> usize {
self.changes_count as usize
}
}