1use rkyv::{Archive, Deserialize, Serialize};
7
8use crate::address::ChunkAddress;
9use crate::dtype::DType;
10
11#[derive(Debug, Clone, Archive, Serialize, Deserialize)]
13pub enum AttributeValue {
14 Bool(bool),
16 Int8(i8),
18 Int16(i16),
20 Int32(i32),
22 Int64(i64),
24 UInt8(u8),
26 UInt16(u16),
28 UInt32(u32),
30 UInt64(u64),
32 Float32(f32),
34 Float64(f64),
36 String(String),
38}
39
40impl PartialEq for AttributeValue {
41 fn eq(&self, other: &Self) -> bool {
42 match (self, other) {
43 (Self::Bool(a), Self::Bool(b)) => a == b,
44 (Self::Int8(a), Self::Int8(b)) => a == b,
45 (Self::Int16(a), Self::Int16(b)) => a == b,
46 (Self::Int32(a), Self::Int32(b)) => a == b,
47 (Self::Int64(a), Self::Int64(b)) => a == b,
48 (Self::UInt8(a), Self::UInt8(b)) => a == b,
49 (Self::UInt16(a), Self::UInt16(b)) => a == b,
50 (Self::UInt32(a), Self::UInt32(b)) => a == b,
51 (Self::UInt64(a), Self::UInt64(b)) => a == b,
52 (Self::Float32(a), Self::Float32(b)) => a.to_bits() == b.to_bits(),
53 (Self::Float64(a), Self::Float64(b)) => a.to_bits() == b.to_bits(),
54 (Self::String(a), Self::String(b)) => a == b,
55 _ => false,
56 }
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Default, Archive, Serialize, Deserialize)]
62pub enum AttrIndexKind {
63 #[default]
65 U16,
66 U32,
68 U64,
70}
71
72#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
78pub enum Attributes {
79 U16(Vec<(u16, u16)>),
81 U32(Vec<(u32, u32)>),
83 U64(Vec<(u64, u64)>),
85}
86
87impl Attributes {
88 pub fn empty(kind: AttrIndexKind) -> Self {
90 match kind {
91 AttrIndexKind::U16 => Self::U16(Vec::new()),
92 AttrIndexKind::U32 => Self::U32(Vec::new()),
93 AttrIndexKind::U64 => Self::U64(Vec::new()),
94 }
95 }
96
97 pub fn get(&self, key_idx: usize) -> Option<usize> {
99 match self {
100 Self::U16(v) => v
101 .binary_search_by_key(&(key_idx as u16), |(k, _)| *k)
102 .ok()
103 .map(|pos| v[pos].1 as usize),
104 Self::U32(v) => v
105 .binary_search_by_key(&(key_idx as u32), |(k, _)| *k)
106 .ok()
107 .map(|pos| v[pos].1 as usize),
108 Self::U64(v) => v
109 .binary_search_by_key(&(key_idx as u64), |(k, _)| *k)
110 .ok()
111 .map(|pos| v[pos].1 as usize),
112 }
113 }
114
115 pub fn upsert(&mut self, key_idx: usize, val_idx: usize) {
117 match self {
118 Self::U16(v) => {
119 let (ki, vi) = (key_idx as u16, val_idx as u16);
120 match v.binary_search_by_key(&ki, |(k, _)| *k) {
121 Ok(pos) => v[pos].1 = vi,
122 Err(pos) => v.insert(pos, (ki, vi)),
123 }
124 }
125 Self::U32(v) => {
126 let (ki, vi) = (key_idx as u32, val_idx as u32);
127 match v.binary_search_by_key(&ki, |(k, _)| *k) {
128 Ok(pos) => v[pos].1 = vi,
129 Err(pos) => v.insert(pos, (ki, vi)),
130 }
131 }
132 Self::U64(v) => {
133 let (ki, vi) = (key_idx as u64, val_idx as u64);
134 match v.binary_search_by_key(&ki, |(k, _)| *k) {
135 Ok(pos) => v[pos].1 = vi,
136 Err(pos) => v.insert(pos, (ki, vi)),
137 }
138 }
139 }
140 }
141
142 pub fn iter_entries(&self) -> Box<dyn Iterator<Item = (usize, usize)> + '_> {
144 match self {
145 Self::U16(v) => Box::new(v.iter().map(|&(k, v)| (k as usize, v as usize))),
146 Self::U32(v) => Box::new(v.iter().map(|&(k, v)| (k as usize, v as usize))),
147 Self::U64(v) => Box::new(v.iter().map(|&(k, v)| (k as usize, v as usize))),
148 }
149 }
150
151 pub fn max_index(&self) -> usize {
153 match self {
154 Self::U16(_) => u16::MAX as usize,
155 Self::U32(_) => u32::MAX as usize,
156 Self::U64(_) => usize::MAX,
157 }
158 }
159}
160
161#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
167pub struct ArrayLayout {
168 pub shape: Vec<u32>,
170 pub dimension_names: Vec<String>,
172 pub storage: StorageLayout,
174}
175
176#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
182pub struct ChunkEntry {
183 pub coord: Vec<u32>,
185 pub address: ChunkAddress,
187}
188
189#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
194pub struct StorageLayout {
195 pub chunk_shape: Vec<u32>,
197 pub chunks: Vec<ChunkEntry>,
202}
203
204#[derive(Debug, Clone, Archive, Serialize, Deserialize)]
210pub enum FillValue {
211 Bool(bool),
213 Int(i64),
215 UInt(u64),
217 Float(f64),
219 String(String),
221 TimestampNs(i64),
224}
225
226impl PartialEq for FillValue {
227 fn eq(&self, other: &Self) -> bool {
228 match (self, other) {
229 (Self::Bool(a), Self::Bool(b)) => a == b,
230 (Self::Int(a), Self::Int(b)) => a == b,
231 (Self::UInt(a), Self::UInt(b)) => a == b,
232 (Self::Float(a), Self::Float(b)) => a.to_bits() == b.to_bits(),
234 (Self::String(a), Self::String(b)) => a == b,
235 (Self::TimestampNs(a), Self::TimestampNs(b)) => a == b,
236 _ => false,
237 }
238 }
239}
240
241#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize)]
246pub struct ArrayMeta {
247 pub name: String,
249 pub dtype: DType,
251 pub layout: ArrayLayout,
253 pub fill_value: Option<FillValue>,
255 pub deleted: bool,
259 pub attributes: Attributes,
265}
266
267impl ArrayLayout {
268 pub fn get_chunk(&self, coord: &[u32]) -> Option<&ChunkAddress> {
270 self.storage
271 .chunks
272 .iter()
273 .find(|e| e.coord.as_slice() == coord)
274 .map(|e| &e.address)
275 }
276
277 pub fn all_addresses(&self) -> Vec<&ChunkAddress> {
279 self.storage.chunks.iter().map(|e| &e.address).collect()
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286 use crate::address::BlockId;
287
288 fn sample_addr(block: u32, offset: u32, size: u32) -> ChunkAddress {
289 ChunkAddress {
290 block_id: BlockId(block),
291 offset,
292 size,
293 }
294 }
295
296 fn chunk(coord: Vec<u32>, block: u32, offset: u32, size: u32) -> ChunkEntry {
297 ChunkEntry {
298 coord,
299 address: sample_addr(block, offset, size),
300 }
301 }
302
303 #[test]
304 fn flat_layout_addresses() {
305 let layout = ArrayLayout {
306 shape: vec![100],
307 dimension_names: vec!["x".into()],
308 storage: StorageLayout {
309 chunk_shape: vec![100],
310 chunks: vec![chunk(vec![0], 0, 0, 400)],
311 },
312 };
313 assert_eq!(layout.all_addresses().len(), 1);
314 assert!(layout.get_chunk(&[0]).is_some());
315 assert!(layout.get_chunk(&[1]).is_none());
316 }
317
318 #[test]
319 fn chunked_layout_lookup() {
320 let layout = ArrayLayout {
321 shape: vec![128, 128],
322 dimension_names: vec!["x".into(), "y".into()],
323 storage: StorageLayout {
324 chunk_shape: vec![64, 64],
325 chunks: vec![
326 chunk(vec![0, 0], 3, 0, 1000),
327 chunk(vec![0, 1], 7, 2000, 1000),
328 ],
329 },
330 };
331 let addr = layout.get_chunk(&[0, 1]).unwrap();
332 assert_eq!(addr.block_id, BlockId(7));
333 assert_eq!(addr.offset, 2000);
334
335 assert!(layout.get_chunk(&[1, 0]).is_none());
336 }
337
338 #[test]
339 fn chunked_all_addresses() {
340 let layout = ArrayLayout {
341 shape: vec![30],
342 dimension_names: vec!["t".into()],
343 storage: StorageLayout {
344 chunk_shape: vec![10],
345 chunks: vec![
346 chunk(vec![0], 0, 0, 500),
347 chunk(vec![1], 0, 500, 500),
348 chunk(vec![2], 1, 0, 500),
349 ],
350 },
351 };
352 assert_eq!(layout.all_addresses().len(), 3);
353 }
354}