genegraph_storage/zzarr/
mod.rs1pub mod csr;
12
13pub use zarrs::array::ElementOwned;
14
15use std::ops::Range;
16use std::path::Path;
17use std::sync::Arc;
18
19use zarrs::array::codec::ZstdCodec;
20use zarrs::array::{Array, data_type};
21use zarrs::array::{ArraySubset, DataType, Element, FillValue};
22use zarrs::filesystem::FilesystemStore;
23use zarrs::storage::ReadableWritableListableStorageTraits;
24
25use crate::{StorageError, StorageResult};
26
27pub trait ZzarrElement: Element + ElementOwned + Clone {
30 fn zzarr_data_type() -> DataType;
32 fn zzarr_fill() -> FillValue;
34}
35
36macro_rules! zzarr_element {
37 ($t:ty, $dt:expr) => {
38 impl ZzarrElement for $t {
39 fn zzarr_data_type() -> DataType {
40 $dt()
41 }
42 fn zzarr_fill() -> FillValue {
43 FillValue::new((0 as $t).to_le_bytes().to_vec())
44 }
45 }
46 };
47}
48
49zzarr_element!(f32, data_type::float32);
50zzarr_element!(f64, data_type::float64);
51zzarr_element!(i64, data_type::int64);
52zzarr_element!(u64, data_type::uint64);
53
54fn store_for(path: &Path) -> StorageResult<Arc<dyn ReadableWritableListableStorageTraits>> {
55 let store: Arc<dyn ReadableWritableListableStorageTraits> =
56 Arc::new(FilesystemStore::new(path).map_err(|e| StorageError::Io(e.to_string()))?);
57 Ok(store)
58}
59
60fn map_open_err(err: impl std::fmt::Display) -> StorageError {
61 StorageError::UnsupportedFormat(format!("not a readable Zarr v3 array: {}", err))
62}
63
64fn map_err(err: impl std::fmt::Display) -> StorageError {
65 StorageError::Invalid(format!("zzarr error: {}", err))
66}
67
68pub struct ZarrArray {
70 array: Array<dyn ReadableWritableListableStorageTraits>,
71}
72
73impl std::fmt::Debug for ZarrArray {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 f.debug_struct("ZarrArray")
76 .field("shape", &self.array.shape())
77 .finish()
78 }
79}
80
81impl ZarrArray {
82 pub fn shape(&self) -> Vec<u64> {
84 self.array.shape().to_vec()
85 }
86
87 pub fn read_all<T: ElementOwned>(&self) -> StorageResult<Vec<T>> {
89 self.read_subset::<T>(
90 &(0..self.array.shape().len())
91 .map(|i| 0..self.array.shape()[i])
92 .collect::<Vec<_>>(),
93 )
94 }
95
96 pub fn read_subset<T: ElementOwned>(&self, ranges: &[Range<u64>]) -> StorageResult<Vec<T>> {
98 let subset = ArraySubset::new_with_ranges(ranges);
99 self.array
100 .retrieve_array_subset::<Vec<T>>(&subset)
101 .map_err(map_err)
102 }
103
104 pub fn write_subset<T: Element>(
107 &mut self,
108 ranges: &[Range<u64>],
109 values: &[T],
110 ) -> StorageResult<()> {
111 let subset = ArraySubset::new_with_ranges(ranges);
112 self.array
113 .store_array_subset(&subset, values)
114 .map_err(map_err)
115 }
116
117 pub fn append<T: Element>(&mut self, values: &[T]) -> StorageResult<()> {
124 let shape = self.array.shape().to_vec();
125 let trailing: u64 = shape[1..].iter().product::<u64>().max(1);
126 if shape.is_empty() || !(values.len() as u64).is_multiple_of(trailing) {
127 return Err(StorageError::Invalid(format!(
128 "append length {} does not tile trailing extent {}",
129 values.len(),
130 trailing
131 )));
132 }
133 let old_len = shape[0];
134 let added = values.len() as u64 / trailing;
135 let new_shape = shape
136 .iter()
137 .enumerate()
138 .map(|(i, s)| if i == 0 { old_len + added } else { *s })
139 .collect::<Vec<_>>();
140 self.array.set_shape(new_shape).map_err(map_err)?;
141 self.array.store_metadata().map_err(map_err)?;
142 let mut ranges: Vec<Range<u64>> = Vec::new();
143 ranges.push(old_len..old_len + added);
144 ranges.extend(shape[1..].iter().map(|s| 0..*s));
145 let subset = ArraySubset::new_with_ranges(&ranges);
146 self.array
147 .store_array_subset(&subset, values)
148 .map_err(map_err)
149 }
150}
151
152pub fn open(path: &Path) -> StorageResult<ZarrArray> {
157 if !path.join("zarr.json").is_file() {
158 return Err(StorageError::UnsupportedFormat(format!(
159 "not a Zarr v3 array (missing zarr.json): {}",
160 path.display()
161 )));
162 }
163 let store = store_for(path)?;
164 let array = Array::open(store, "/").map_err(map_open_err)?;
165 Ok(ZarrArray { array })
166}
167
168pub fn write_array<T: ZzarrElement>(
174 path: &Path,
175 shape: &[u64],
176 chunks: &[u64],
177 values: &[T],
178 compress: bool,
179) -> StorageResult<()> {
180 if path.join("zarr.json").is_file() {
181 return Err(StorageError::InvalidState(format!(
182 "zzarr array already exists at {}",
183 path.display()
184 )));
185 }
186 let total: u64 = shape.iter().product();
187 if total != values.len() as u64 {
188 return Err(StorageError::Invalid(format!(
189 "shape {:?} holds {} elements but {} values were given",
190 shape,
191 total,
192 values.len()
193 )));
194 }
195 std::fs::create_dir_all(path).map_err(|e| StorageError::Io(e.to_string()))?;
196 let store = store_for(path)?;
197 let mut builder = zarrs::array::ArrayBuilder::new(
198 shape.to_vec(),
199 chunks.to_vec(),
200 T::zzarr_data_type(),
201 T::zzarr_fill(),
202 );
203 if compress {
204 builder.bytes_to_bytes_codecs(vec![Arc::new(ZstdCodec::new(3, false))]);
205 }
206 let array = builder.build(store, "/").map_err(map_open_err)?;
207 array.store_metadata().map_err(map_err)?;
208 let subset_all = array.subset_all();
209 array
210 .store_array_subset(&subset_all, values)
211 .map_err(map_err)
212}