use assembly_core::buffer::{self, Buffer, Repr, LEI64};
pub use assembly_fdb_core::value::mem::{Field, MemContext};
use assembly_fdb_core::value::{
file::{FDBFieldValue, FileContext, IndirectValue},
owned::OwnedContext,
ValueMapperMut, ValueType,
};
use buffer::CastError;
use latin1str::Latin1Str;
mod c;
use crate::{
handle::{self, Handle, RefHandle, TryFromHandle},
util::compare_bytes,
};
use c::{
FDBBucketHeaderC, FDBColumnHeaderC, FDBFieldDataC, FDBHeaderC, FDBRowHeaderListEntryC,
FDBTableDataHeaderC, FDBTableDefHeaderC, FDBTableHeaderC,
};
use std::{
borrow::Cow,
convert::{Infallible, TryFrom},
};
pub mod iter;
use iter::{BucketIter, TableRowIter};
pub use iter::{FieldIter, RowHeaderIter, TableIter}; fn get_latin1_str(buf: &[u8], offset: u32) -> &Latin1Str {
let (_, haystack) = buf.split_at(offset as usize);
Latin1Str::from_bytes_until_nul(haystack)
}
#[derive(Copy, Clone)]
pub struct Database<'a> {
inner: Handle<'a, ()>,
}
impl<'a> Database<'a> {
pub fn new(buf: &'a [u8]) -> Self {
let inner = Handle::new(buf);
Self { inner }
}
pub fn header(self) -> Result<Header<'a>, CastError> {
let inner = self.inner.try_map_cast(0)?;
Ok(Header { inner })
}
pub fn tables(self) -> Result<Tables<'a>, CastError> {
let header = self.header()?;
let tables = header.tables()?;
Ok(tables)
}
}
#[derive(Copy, Clone)]
pub struct Header<'a> {
inner: RefHandle<'a, FDBHeaderC>,
}
impl<'a> Header<'a> {
fn tables(self) -> Result<Tables<'a>, CastError> {
let header = self.inner.map_extract();
let inner = self.inner.try_map_cast_array(header.into_raw().tables)?;
Ok(Tables { inner })
}
}
fn map_table_header<'a>(handle: RefHandle<'a, FDBTableHeaderC>) -> Result<Table<'a>, CastError> {
let table_header = handle.into_raw().extract();
let def_header: &'a FDBTableDefHeaderC =
handle.buf().try_cast(table_header.table_def_header_addr)?;
let def_header = def_header.extract();
let data_header: &'a FDBTableDataHeaderC =
handle.buf().try_cast(table_header.table_data_header_addr)?;
let data_header = data_header.extract();
let name = get_latin1_str(handle.buf(), def_header.table_name_addr);
let columns: RefHandle<'a, [FDBColumnHeaderC]> =
handle.try_map_cast_slice(def_header.column_header_list_addr, def_header.column_count)?;
let buckets: RefHandle<'a, [FDBBucketHeaderC]> =
handle.try_map_cast_array(data_header.buckets)?;
Ok(Table::new(handle.wrap(InnerTable {
name,
columns: columns.raw(),
buckets: buckets.raw(),
})))
}
#[derive(Copy, Clone)]
pub struct Tables<'a> {
inner: RefHandle<'a, [FDBTableHeaderC]>,
}
impl<'a> Tables<'a> {
pub fn len(self) -> usize {
self.inner.into_raw().len()
}
pub fn is_empty(self) -> bool {
self.inner.into_raw().len() == 0
}
pub fn get(self, index: usize) -> Option<Result<Table<'a>, CastError>> {
self.inner.get(index).map(map_table_header)
}
pub fn iter(&self) -> TableIter<'a> {
TableIter::new(&self.inner)
}
pub fn by_name(&self, name: &str) -> Option<Result<Table<'a>, CastError>> {
let bytes = name.as_bytes();
self.inner
.into_raw()
.binary_search_by(|table_header| {
let def_header_addr = table_header.table_def_header_addr.extract();
let def_header =
buffer::cast::<FDBTableDefHeaderC>(self.inner.buf(), def_header_addr);
let name_addr = def_header.table_name_addr.extract() as usize;
let name_bytes = &self.inner.buf()[name_addr..];
compare_bytes(bytes, name_bytes)
})
.ok()
.and_then(|index| self.get(index))
}
}
fn map_column_header<'a>(buf: &'a [u8]) -> impl Fn(&'a FDBColumnHeaderC) -> Column<'a> + Copy {
move |header: &FDBColumnHeaderC| {
let column_header = header.extract();
let name = get_latin1_str(buf, column_header.column_name_addr);
let domain = ValueType::try_from(column_header.column_data_type).unwrap();
Column { name, domain }
}
}
fn get_row_header_list_entry(buf: &[u8], addr: u32) -> Option<&FDBRowHeaderListEntryC> {
if addr == u32::MAX {
None
} else {
Some(buf.cast::<FDBRowHeaderListEntryC>(addr))
}
}
#[derive(Copy, Clone)]
struct InnerTable<'a> {
name: &'a Latin1Str,
columns: &'a [FDBColumnHeaderC],
buckets: &'a [FDBBucketHeaderC],
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct Table<'a> {
inner: Handle<'a, InnerTable<'a>>,
}
impl<'a> Table<'a> {
fn new(inner: Handle<'a, InnerTable<'a>>) -> Self {
Self { inner }
}
pub fn name_raw(&self) -> &'a Latin1Str {
self.inner.raw.name
}
pub fn name(&self) -> Cow<'a, str> {
self.inner.raw.name.decode()
}
pub fn index_iter(&self, id: u32) -> impl Iterator<Item = Row<'a>> {
let bucket: usize = id as usize % self.bucket_count();
self.bucket_at(bucket).into_iter().flat_map(move |b| {
b.row_iter()
.filter(move |r| r.field_at(0) == Some(Field::Integer(id as i32)))
})
}
pub fn bucket_index_iter(&self, id: u32) -> TableRowIter<'a> {
let bucket: usize = id as usize % self.bucket_count();
TableRowIter::new(BucketIter::new(
&self.inner.map_val(|r| &r.buckets[bucket..bucket + 1]),
))
}
pub fn column_at(&self, index: usize) -> Option<Column<'a>> {
self.inner
.raw
.columns
.get(index)
.map(map_column_header(self.inner.mem))
}
pub fn column_iter(&self) -> impl Iterator<Item = Column<'a>> + Clone {
self.inner
.raw
.columns
.iter()
.map(map_column_header(self.inner.mem))
}
pub fn column_count(&self) -> usize {
self.inner.raw.columns.len()
}
pub fn bucket_at(&self, index: usize) -> Option<Bucket<'a>> {
self.inner
.map_val(|raw| raw.buckets)
.get(index)
.map(|e| {
e.map_extract()
.map_val(|r| r.row_header_list_head_addr)
.map(get_row_header_list_entry)
.transpose()
})
.map(Bucket::new)
}
pub fn bucket_for_hash(&self, hash: u32) -> Bucket<'a> {
let index = hash as usize % self.inner.raw.buckets.len();
self.bucket_at(index).unwrap()
}
pub fn bucket_iter(&self) -> BucketIter<'a> {
BucketIter::new(&self.inner.map_val(|r| r.buckets))
}
pub fn bucket_count(&self) -> usize {
self.inner.raw.buckets.len()
}
pub fn row_iter(&self) -> TableRowIter<'a> {
TableRowIter::new(self.bucket_iter())
}
}
pub struct Column<'a> {
name: &'a Latin1Str,
domain: ValueType,
}
impl<'a> Column<'a> {
pub fn name(&self) -> Cow<'a, str> {
self.name.decode()
}
pub fn name_raw(&self) -> &'a Latin1Str {
self.name
}
pub fn value_type(&self) -> ValueType {
self.domain
}
}
#[derive(Debug, Copy, Clone)]
pub struct Bucket<'a> {
inner: Option<RefHandle<'a, FDBRowHeaderListEntryC>>,
}
impl<'a> Bucket<'a> {
pub fn row_iter(&self) -> RowHeaderIter<'a> {
RowHeaderIter::new(self.inner)
}
pub fn is_empty(&self) -> bool {
self.inner.is_none()
}
fn new(inner: Option<RefHandle<'a, FDBRowHeaderListEntryC>>) -> Self {
Self { inner }
}
}
#[derive(Copy, Clone)]
pub struct Row<'a> {
inner: RefHandle<'a, [FDBFieldDataC]>,
}
fn get_field<'a>(buf: &'a [u8], data: &'a FDBFieldDataC) -> Field<'a> {
let data_type = ValueType::try_from(data.data_type.extract()).unwrap();
let bytes = data.value.0;
get_field_raw(buf, data_type, bytes)
}
fn get_field_raw(buf: &[u8], data_type: ValueType, bytes: [u8; 4]) -> Field {
match data_type {
ValueType::Nothing => Field::Nothing,
ValueType::Integer => Field::Integer(i32::from_le_bytes(bytes)),
ValueType::Float => Field::Float(f32::from_le_bytes(bytes)),
ValueType::Text => {
let addr = u32::from_le_bytes(bytes);
let text = get_latin1_str(buf, addr);
Field::Text(text)
}
ValueType::Boolean => Field::Boolean(bytes != [0, 0, 0, 0]),
ValueType::BigInt => {
let addr = u32::from_le_bytes(bytes);
let val = buf.cast::<LEI64>(addr).extract();
Field::BigInt(val)
}
ValueType::VarChar => {
let addr = u32::from_le_bytes(bytes);
let text = get_latin1_str(buf, addr);
Field::VarChar(text)
}
}
}
impl<'a> Row<'a> {
fn new(inner: RefHandle<'a, [FDBFieldDataC]>) -> Self {
Self { inner }
}
pub fn field_at(&self, index: usize) -> Option<Field<'a>> {
self.inner.get(index).map(|f| f.map(get_field).into_raw())
}
pub fn field_iter(&self) -> FieldIter<'a> {
FieldIter::new(self.inner)
}
pub fn field_count(&self) -> usize {
self.inner.raw().len()
}
}
impl<'a> IntoIterator for Row<'a> {
type Item = Field<'a>;
type IntoIter = FieldIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.field_iter()
}
}
pub struct MemToOwned;
impl<'a> ValueMapperMut<MemContext<'a>, OwnedContext> for MemToOwned {
fn map_string(&mut self, from: &&'a Latin1Str) -> String {
from.decode().into_owned()
}
fn map_i64(&mut self, from: &i64) -> i64 {
*from
}
fn map_xml(&mut self, from: &&'a Latin1Str) -> String {
from.decode().into_owned()
}
}
struct MemFromFile<'a>(&'a [u8]);
impl<'a> ValueMapperMut<FileContext, MemContext<'a>> for MemFromFile<'a> {
fn map_string(&mut self, from: &IndirectValue) -> &'a Latin1Str {
handle::get_string(self.0, from.addr).unwrap()
}
fn map_i64(&mut self, from: &IndirectValue) -> i64 {
handle::get_i64(self.0, from.addr).unwrap()
}
fn map_xml(&mut self, from: &IndirectValue) -> &'a Latin1Str {
handle::get_string(self.0, from.addr).unwrap()
}
}
impl<'a> TryFromHandle<'a, FDBFieldValue> for Field<'a> {
type Error = Infallible;
fn try_from(value: Handle<'a, FDBFieldValue>) -> Result<Self, Self::Error> {
let mut mem = MemFromFile(value.buf());
Ok(value.raw().map(&mut mem))
}
}