use rkyv::{Archive, Deserialize, Serialize};
use crate::address::ChunkAddress;
use crate::dtype::DType;
#[derive(Debug, Clone, Archive, Serialize, Deserialize)]
pub enum AttributeValue {
Bool(bool),
Int8(i8),
Int16(i16),
Int32(i32),
Int64(i64),
UInt8(u8),
UInt16(u16),
UInt32(u32),
UInt64(u64),
Float32(f32),
Float64(f64),
String(String),
Binary(Vec<u8>),
BoolList(Vec<bool>),
Int8List(Vec<i8>),
Int16List(Vec<i16>),
Int32List(Vec<i32>),
Int64List(Vec<i64>),
UInt8List(Vec<u8>),
UInt16List(Vec<u16>),
UInt32List(Vec<u32>),
UInt64List(Vec<u64>),
Float32List(Vec<f32>),
Float64List(Vec<f64>),
StringList(Vec<String>),
BinaryList(Vec<Vec<u8>>),
}
impl PartialEq for AttributeValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Bool(a), Self::Bool(b)) => a == b,
(Self::Int8(a), Self::Int8(b)) => a == b,
(Self::Int16(a), Self::Int16(b)) => a == b,
(Self::Int32(a), Self::Int32(b)) => a == b,
(Self::Int64(a), Self::Int64(b)) => a == b,
(Self::UInt8(a), Self::UInt8(b)) => a == b,
(Self::UInt16(a), Self::UInt16(b)) => a == b,
(Self::UInt32(a), Self::UInt32(b)) => a == b,
(Self::UInt64(a), Self::UInt64(b)) => a == b,
(Self::Float32(a), Self::Float32(b)) => a.to_bits() == b.to_bits(),
(Self::Float64(a), Self::Float64(b)) => a.to_bits() == b.to_bits(),
(Self::String(a), Self::String(b)) => a == b,
(Self::Binary(a), Self::Binary(b)) => a == b,
(Self::BoolList(a), Self::BoolList(b)) => a == b,
(Self::Int8List(a), Self::Int8List(b)) => a == b,
(Self::Int16List(a), Self::Int16List(b)) => a == b,
(Self::Int32List(a), Self::Int32List(b)) => a == b,
(Self::Int64List(a), Self::Int64List(b)) => a == b,
(Self::UInt8List(a), Self::UInt8List(b)) => a == b,
(Self::UInt16List(a), Self::UInt16List(b)) => a == b,
(Self::UInt32List(a), Self::UInt32List(b)) => a == b,
(Self::UInt64List(a), Self::UInt64List(b)) => a == b,
(Self::Float32List(a), Self::Float32List(b)) => {
a.len() == b.len() && a.iter().zip(b).all(|(x, y)| x.to_bits() == y.to_bits())
}
(Self::Float64List(a), Self::Float64List(b)) => {
a.len() == b.len() && a.iter().zip(b).all(|(x, y)| x.to_bits() == y.to_bits())
}
(Self::StringList(a), Self::StringList(b)) => a == b,
(Self::BinaryList(a), Self::BinaryList(b)) => a == b,
_ => false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default, Archive, Serialize, Deserialize)]
pub enum AttrIndexKind {
#[default]
U16,
U32,
U64,
}
#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
pub enum Attributes {
U16(Vec<(u16, u16)>),
U32(Vec<(u32, u32)>),
U64(Vec<(u64, u64)>),
}
impl Attributes {
pub fn empty(kind: AttrIndexKind) -> Self {
match kind {
AttrIndexKind::U16 => Self::U16(Vec::new()),
AttrIndexKind::U32 => Self::U32(Vec::new()),
AttrIndexKind::U64 => Self::U64(Vec::new()),
}
}
pub fn get(&self, key_idx: usize) -> Option<usize> {
match self {
Self::U16(v) => v
.binary_search_by_key(&(key_idx as u16), |(k, _)| *k)
.ok()
.map(|pos| v[pos].1 as usize),
Self::U32(v) => v
.binary_search_by_key(&(key_idx as u32), |(k, _)| *k)
.ok()
.map(|pos| v[pos].1 as usize),
Self::U64(v) => v
.binary_search_by_key(&(key_idx as u64), |(k, _)| *k)
.ok()
.map(|pos| v[pos].1 as usize),
}
}
pub fn upsert(&mut self, key_idx: usize, val_idx: usize) {
match self {
Self::U16(v) => {
let (ki, vi) = (key_idx as u16, val_idx as u16);
match v.binary_search_by_key(&ki, |(k, _)| *k) {
Ok(pos) => v[pos].1 = vi,
Err(pos) => v.insert(pos, (ki, vi)),
}
}
Self::U32(v) => {
let (ki, vi) = (key_idx as u32, val_idx as u32);
match v.binary_search_by_key(&ki, |(k, _)| *k) {
Ok(pos) => v[pos].1 = vi,
Err(pos) => v.insert(pos, (ki, vi)),
}
}
Self::U64(v) => {
let (ki, vi) = (key_idx as u64, val_idx as u64);
match v.binary_search_by_key(&ki, |(k, _)| *k) {
Ok(pos) => v[pos].1 = vi,
Err(pos) => v.insert(pos, (ki, vi)),
}
}
}
}
pub fn iter_entries(&self) -> Box<dyn Iterator<Item = (usize, usize)> + '_> {
match self {
Self::U16(v) => Box::new(v.iter().map(|&(k, v)| (k as usize, v as usize))),
Self::U32(v) => Box::new(v.iter().map(|&(k, v)| (k as usize, v as usize))),
Self::U64(v) => Box::new(v.iter().map(|&(k, v)| (k as usize, v as usize))),
}
}
pub fn max_index(&self) -> usize {
match self {
Self::U16(_) => u16::MAX as usize,
Self::U32(_) => u32::MAX as usize,
Self::U64(_) => usize::MAX,
}
}
}
#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
pub struct ArrayLayout {
pub shape: Vec<u32>,
pub dimension_names: Vec<String>,
pub storage: StorageLayout,
}
#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
pub struct ChunkEntry {
pub coord: Vec<u32>,
pub address: ChunkAddress,
}
#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
pub struct StorageLayout {
pub chunk_shape: Vec<u32>,
pub chunks: Vec<ChunkEntry>,
}
#[derive(Debug, Clone, Archive, Serialize, Deserialize)]
pub enum FillValue {
Bool(bool),
Int(i64),
UInt(u64),
Float(f64),
String(String),
TimestampNs(i64),
}
impl PartialEq for FillValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Bool(a), Self::Bool(b)) => a == b,
(Self::Int(a), Self::Int(b)) => a == b,
(Self::UInt(a), Self::UInt(b)) => a == b,
(Self::Float(a), Self::Float(b)) => a.to_bits() == b.to_bits(),
(Self::String(a), Self::String(b)) => a == b,
(Self::TimestampNs(a), Self::TimestampNs(b)) => a == b,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
pub struct ArrayMeta {
pub name: String,
pub dtype: DType,
pub layout: ArrayLayout,
pub fill_value: Option<FillValue>,
pub deleted: bool,
pub attributes: Attributes,
}
impl ArrayLayout {
pub fn get_chunk(&self, coord: &[u32]) -> Option<&ChunkAddress> {
self.storage
.chunks
.iter()
.find(|e| e.coord.as_slice() == coord)
.map(|e| &e.address)
}
pub fn all_addresses(&self) -> Vec<&ChunkAddress> {
self.storage.chunks.iter().map(|e| &e.address).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::address::BlockId;
fn sample_addr(block: u32, offset: u32, size: u32) -> ChunkAddress {
ChunkAddress {
block_id: BlockId(block),
offset,
size,
}
}
fn chunk(coord: Vec<u32>, block: u32, offset: u32, size: u32) -> ChunkEntry {
ChunkEntry {
coord,
address: sample_addr(block, offset, size),
}
}
#[test]
fn attribute_value_binary_and_list_equality() {
assert_eq!(
AttributeValue::Binary(vec![1, 2, 3]),
AttributeValue::Binary(vec![1, 2, 3])
);
assert_ne!(
AttributeValue::Binary(vec![1, 2, 3]),
AttributeValue::Binary(vec![1, 2])
);
assert_eq!(
AttributeValue::Int32List(vec![1, 2, 3]),
AttributeValue::Int32List(vec![1, 2, 3])
);
assert_eq!(
AttributeValue::StringList(vec!["a".into(), "b".into()]),
AttributeValue::StringList(vec!["a".into(), "b".into()])
);
assert_eq!(
AttributeValue::BinaryList(vec![vec![0], vec![1, 2]]),
AttributeValue::BinaryList(vec![vec![0], vec![1, 2]])
);
assert_ne!(
AttributeValue::Int32List(vec![1]),
AttributeValue::Int64List(vec![1])
);
}
#[test]
fn attribute_value_float_list_compares_by_bits() {
assert_eq!(
AttributeValue::Float64List(vec![f64::NAN, 1.0]),
AttributeValue::Float64List(vec![f64::NAN, 1.0])
);
assert_ne!(
AttributeValue::Float32List(vec![0.0]),
AttributeValue::Float32List(vec![-0.0])
);
assert_ne!(
AttributeValue::Float64List(vec![1.0, 2.0]),
AttributeValue::Float64List(vec![1.0])
);
}
#[test]
fn flat_layout_addresses() {
let layout = ArrayLayout {
shape: vec![100],
dimension_names: vec!["x".into()],
storage: StorageLayout {
chunk_shape: vec![100],
chunks: vec![chunk(vec![0], 0, 0, 400)],
},
};
assert_eq!(layout.all_addresses().len(), 1);
assert!(layout.get_chunk(&[0]).is_some());
assert!(layout.get_chunk(&[1]).is_none());
}
#[test]
fn chunked_layout_lookup() {
let layout = ArrayLayout {
shape: vec![128, 128],
dimension_names: vec!["x".into(), "y".into()],
storage: StorageLayout {
chunk_shape: vec![64, 64],
chunks: vec![
chunk(vec![0, 0], 3, 0, 1000),
chunk(vec![0, 1], 7, 2000, 1000),
],
},
};
let addr = layout.get_chunk(&[0, 1]).unwrap();
assert_eq!(addr.block_id, BlockId(7));
assert_eq!(addr.offset, 2000);
assert!(layout.get_chunk(&[1, 0]).is_none());
}
#[test]
fn chunked_all_addresses() {
let layout = ArrayLayout {
shape: vec![30],
dimension_names: vec!["t".into()],
storage: StorageLayout {
chunk_shape: vec![10],
chunks: vec![
chunk(vec![0], 0, 0, 500),
chunk(vec![1], 0, 500, 500),
chunk(vec![2], 1, 0, 500),
],
},
};
assert_eq!(layout.all_addresses().len(), 3);
}
}