zarrs/lib.rs
1//! `zarrs` is Rust library for the [Zarr](https://zarr.dev) storage format for multidimensional arrays and metadata.
2//!
3//! If you are a Python user, check out [`zarrs-python`](https://github.com/zarrs/zarrs-python).
4//! It includes a high-performance codec pipeline for the reference [`zarr-python`](https://github.com/zarr-developers/zarr-python) implementation.
5//!
6//! `zarrs` supports [Zarr V3](https://zarr-specs.readthedocs.io/en/latest/v3/core/index.html) and a V3 compatible subset of [Zarr V2](https://zarr-specs.readthedocs.io/en/latest/v2/v2.0.html).
7//! It is fully up-to-date and conformant with the Zarr 3.1 specification with support for:
8//! - all *core extensions* (data types, codecs, chunk grids, chunk key encodings, storage transformers),
9//! - all accepted [Zarr Enhancement Proposals (ZEPs)](https://zarr.dev/zeps/) and several draft ZEPs:
10//! - ZEP 0003: Variable chunking
11//! - ZEP 0007: Strings
12//! - ZEP 0009: Zarr Extension Naming
13//! - various registered extensions from [zarr-developers/zarr-extensions/](https://github.com/zarr-developers/zarr-extensions/),
14//! - experimental codecs intended for future registration, and
15//! - user-defined custom extensions and stores.
16//!
17//! A changelog can be found [here](https://github.com/zarrs/zarrs/blob/main/CHANGELOG.md).
18//! Correctness issues with past versions are [detailed here](https://github.com/zarrs/zarrs/blob/main/doc/correctness_issues.md).
19//!
20//! Developed at the [Department of Materials Physics](https://physics.anu.edu.au/research/mp/), Australian National University, Canberra, Australia.
21//!
22//! ## Getting Started
23//! - Review the [Zarr version support](#zarr-version-support), [array extension support](#array-extension-support) (codecs, data types, etc.), [storage support](#storage-support), and the [`zarrs` ecosystem](#zarrs-ecosystem).
24//! - View the [the examples](#examples) below.
25//! - Read the [documentation](https://docs.rs/zarrs/latest/zarrs/) and [The `zarrs` Book].
26//!
27//! ### Zarr Version Support
28//!
29//! `zarrs` has first-class Zarr V3 support and additionally supports a *compatible subset* of Zarr V2 data that:
30//! - can be converted to V3 with only a metadata change, and
31//! - uses array metadata that is recognised and supported for encoding/decoding.
32//!
33//! `zarrs` supports forward conversion from Zarr V2 to V3. See [Converting Zarr V2 to V3](https://book.zarrs.dev/v2_to_v3.html) in [The `zarrs` Book], or try the [`zarrs_reencode`](https://github.com/zarrs/zarrs_tools/blob/main/docs/zarrs_reencode.md) CLI tool.
34//!
35//! ### Array Extension Support
36//!
37//! Extensions are grouped into three categories:
38//! - *Core*: defined in the Zarr V3 specification and are fully supported.
39//! - *Registered*: specified at <https://github.com/zarr-developers/zarr-extensions/>
40//! - Registered extensions listed in the below tables are fully supported unless otherwise indicated.
41//! - *Experimental*: indicated by 🚧 in the tables below and **recommended for evaluation only**.
42//! - Experimental extensions are either pending registration or have no formal specification outside of the `zarrs` docs.
43//! - Experimental extensions may be unrecognised or incompatible with other Zarr implementations.
44//! - Experimental extensions may change in future releases without maintaining backwards compatibility.
45//! - *Deprecated*: indicated by ~~strikethrough~~ in the tables below
46//! - Deprecated aliases will not be removed, but are not recommended for use in new arrays.
47//! - Deprecated extensions may be removed in future releases.
48//!
49//! `zarrs` will persist extension names if opening an existing array of creating an array from metadata.
50//!
51//! #### Data Types
52//!
53#![doc = include_str!("../doc/status/data_types.md")]
54//!
55//! #### Codecs
56//!
57#![doc = include_str!("../doc/status/codecs.md")]
58//!
59//! #### Chunk Grids
60//!
61#![doc = include_str!("../doc/status/chunk_grids.md")]
62//!
63//! #### Chunk Key Encodings
64//!
65#![doc = include_str!("../doc/status/chunk_key_encodings.md")]
66//!
67//! #### Storage Transformers
68//!
69#![doc = include_str!("../doc/status/storage_transformers.md")]
70//!
71//! ### Storage Support
72//!
73//! `zarrs` supports a huge range of stores (including custom stores) via the [`zarrs_storage`] API.
74//!
75#![doc = include_str!("../doc/status/stores.md")]
76//!
77//! [`opendal`]: https://docs.rs/opendal/latest/opendal/
78//! [`object_store`]: https://docs.rs/object_store/latest/object_store/
79//! [`object_store`]: https://docs.rs/object_store/latest/object_store/
80//! [`zarrs_icechunk`]: https://docs.rs/zarrs_icechunk/latest/zarrs_icechunk/
81//! [`zarrs_object_store`]: https://docs.rs/zarrs_object_store/latest/zarrs_object_store/
82//! [`zarrs_opendal`]: https://docs.rs/zarrs_opendal/latest/zarrs_opendal/
83//!
84//!
85//! The [`opendal`] and [`object_store`] crates are popular Rust storage backends that are fully supported via [`zarrs_opendal`] and [`zarrs_object_store`].
86//! These backends provide more feature complete HTTP stores than [`zarrs_http`].
87//!
88//! [`zarrs_icechunk`] implements the [Icechunk](https://icechunk.io) transactional storage engine, a storage specification for Zarr that supports [`object_store`] stores.
89//!
90//! The [`AsyncToSyncStorageAdapter`](crate::storage::storage_adapter::async_to_sync::AsyncToSyncStorageAdapter) enables some async stores to be used in a sync context.
91//!
92//! ## Logging
93//! `zarrs` logs information and warnings using the [`log`] crate.
94//! A logging implementation must be enabled to capture logs.
95//! See the [`log`] crate documentation for more details.
96//!
97//! ## Examples
98//! ### Create and Read a Zarr Hierarchy
99#![cfg_attr(feature = "ndarray", doc = "```rust")]
100#![cfg_attr(not(feature = "ndarray"), doc = "```rust,ignore")]
101//! # use std::{path::PathBuf, sync::Arc};
102//!
103//! // Create a filesystem store
104//! let store_path: PathBuf = "/path/to/hierarchy.zarr".into();
105//! # let store_path: PathBuf = "tests/data/array_write_read.zarr".into();
106//! let store: zarrs::storage::ReadableWritableListableStorage = Arc::new(
107//! // zarrs::filesystem requires the filesystem feature
108//! zarrs::filesystem::FilesystemStore::new(&store_path)?
109//! );
110//! # let store = Arc::new(zarrs::storage::store::MemoryStore::new());
111//!
112//! // Write the root group metadata
113//! zarrs::group::GroupBuilder::new()
114//! .build(store.clone(), "/")?
115//! // .attributes(...)
116//! .store_metadata()?;
117//!
118//! // Create a new sharded V3 array using the array builder
119//! let array = zarrs::array::ArrayBuilder::new(
120//! vec![3, 4], // array shape
121//! vec![2, 2], // regular chunk (shard) shape
122//! zarrs::array::data_type::float32(),
123//! 0.0f32, // fill value
124//! )
125//! .subchunk_shape(vec![2, 1]) // subchunk (inner chunk) shape
126//! .bytes_to_bytes_codecs(vec![
127//! // GzipCodec requires the gzip feature
128//! # #[cfg(feature = "gzip")]
129//! Arc::new(zarrs::array::codec::GzipCodec::new(5)?),
130//! ])
131//! .dimension_names(["y", "x"].into())
132//! .attributes(serde_json::json!({"Zarr V3": "is great"}).as_object().unwrap().clone())
133//! .build(store.clone(), "/array")?; // /path/to/hierarchy.zarr/array
134//!
135//! // Store the array metadata
136//! array.store_metadata()?;
137//! println!("{}", serde_json::to_string_pretty(array.metadata())?);
138//! // {
139//! // "zarr_format": 3,
140//! // "node_type": "array",
141//! // ...
142//! // }
143//!
144//! // Perform some write operations on the chunks
145//! array.store_chunk_elements::<f32>(
146//! &[0, 1], // chunk index
147//! &[0.2, 0.3, 1.2, 1.3]
148//! )?;
149//! array.store_array_subset_ndarray::<f32, _>(
150//! &[1, 1], // array index (start of subset)
151//! &ndarray::array![[-1.1, -1.2], [-2.1, -2.2]]
152//! )?;
153//! array.erase_chunk(&[1, 1])?;
154//!
155//! // Retrieve all array elements as an ndarray
156//! let array_all = array.retrieve_array_subset_ndarray::<f32>(&[0..3, 0..4])?;
157//! println!("{array_all:4}");
158//! // [[ NaN, NaN, 0.2, 0.3],
159//! // [ NaN, -1.1, -1.2, 1.3],
160//! // [ NaN, -2.1, NaN, NaN]]
161//!
162//! // Retrieve a chunk directly
163//! let array_chunk = array.retrieve_chunk_ndarray::<f32>(
164//! &[0, 1], // chunk index
165//! )?;
166//! println!("{array_chunk:4}");
167//! // [[ 0.2, 0.3],
168//! // [ -1.2, 1.3]]
169//!
170//! // Retrieve a subchunk
171//! use zarrs::array::ArrayShardedReadableExt;
172//! let shard_index_cache = zarrs::array::ArrayShardedReadableExtCache::new(&array);
173//! let array_subchunk = array.retrieve_subchunk_ndarray_opt::<f32>(
174//! &shard_index_cache,
175//! &[0, 3], // subchunk index
176//! &zarrs::array::CodecOptions::default(),
177//! )?;
178//! println!("{array_subchunk:4}");
179//! // [[ 0.3],
180//! // [ 1.3]]
181//! # Ok::<(), Box<dyn std::error::Error>>(())
182//! ```
183//!
184//! ### Additional Examples
185//! Various examples can be found in the [`examples/`](https://github.com/zarrs/zarrs/blob/main/zarrs/examples) directory of the `zarrs` repository that demonstrate:
186//! - creating and manipulating zarr hierarchies with various stores (sync and async), codecs, etc,
187//! - converting between Zarr V2 and V3, and
188//! - creating custom data types.
189//!
190//! Examples can be run with `cargo run --example <EXAMPLE_NAME>`.
191//! - Some examples require non-default features, which can be enabled with `--all-features` or `--features <FEATURES>`.
192//! - Some examples support a `-- --usage-log` argument to print storage API calls during execution.
193//!
194//! ## Crate Features
195//! #### Default
196//! - `filesystem`: Re-export [`zarrs_filesystem`] as [`zarrs::filesystem`](crate::filesystem`).
197//! - `ndarray`: [`ndarray`] utility functions for [`Array`](crate::array::Array).
198//! - Codecs: `blosc`, `crc32c`, `gzip`, `sharding`, `transpose`, `zstd`.
199//!
200//! #### Non-Default
201//! - `async`: an **experimental** asynchronous API for [`stores`](storage), [`Array`](crate::array::Array), and [`Group`](group::Group).
202//! - The async API is runtime-agnostic. This has some limitations that are detailed in the [`Array`](crate::array::Array) docs.
203//! - The async API is not as performant as the sync API.
204//! - Codecs: `adler32`, `bitround`, `bz2`, `fletcher32`, `gdeflate`, `pcodec`, `zfp`, `zlib`.
205//! - `dlpack`: adds convenience methods for [`DLPack`](https://arrow.apache.org/docs/python/dlpack.html) tensor interop to [`Array`](crate::array::Array).
206//! - Additional [`Element`](crate::array::Element)/[`ElementOwned`](crate::array::ElementOwned) implementations:
207//! - `float8`: add support for [`float8`] subfloat data types.
208//! - `jiff`: add support for [`jiff`] time data types.
209//! - `chrono`: add support for [`chrono`] time data types.
210//!
211//! ## `zarrs` Ecosystem
212#![doc = include_str!("../doc/ecosystem.md")]
213//!
214//! ## Licence
215//! `zarrs` is licensed under either of
216//! - the Apache License, Version 2.0 [LICENSE-APACHE](https://docs.rs/crate/zarrs/latest/source/LICENCE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0> or
217//! - the MIT license [LICENSE-MIT](https://docs.rs/crate/zarrs/latest/source/LICENCE-MIT) or <http://opensource.org/licenses/MIT>, at your option.
218//!
219//! Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
220//!
221//! [The `zarrs` Book]: https://book.zarrs.dev
222#![cfg_attr(docsrs, feature(doc_cfg))]
223#![doc(html_logo_url = "https://zarrs.dev/zarrs-logo-400x400.png")]
224#![warn(clippy::wildcard_enum_match_arm)]
225#![deny(deprecated)]
226
227pub mod array;
228pub mod config;
229pub mod convert;
230pub mod group;
231pub mod hierarchy;
232pub mod node;
233pub mod version;
234
235#[cfg(feature = "filesystem")]
236pub use zarrs_filesystem as filesystem;
237pub use zarrs_metadata as metadata;
238pub use zarrs_metadata_ext as metadata_ext;
239pub use zarrs_plugin as plugin;
240pub use zarrs_storage as storage;
241
242/// Get a mutable slice of the spare capacity in a vector.
243///
244/// # Safety
245/// The caller must not read from the returned slice before it has been initialised.
246unsafe fn vec_spare_capacity_to_mut_slice<T>(vec: &mut Vec<T>) -> &mut [T] {
247 let spare_capacity = vec.spare_capacity_mut();
248 // SAFETY: `spare_capacity` is valid for both reads and writes for len * size_of::<T>() many bytes, and it is properly aligned
249 unsafe {
250 std::slice::from_raw_parts_mut(
251 spare_capacity.as_mut_ptr().cast::<T>(),
252 spare_capacity.len(),
253 )
254 }
255}
256
257/// Log a warning that an extension is experimental and may be incompatible with other Zarr V3 implementations.
258///
259/// # Arguments
260/// * `name` - The name of the extension (e.g., `vlen`, `regular_bounded`)
261/// * `extension_type` - The type of extension (e.g., `codec`, `chunk grid`, `chunk key encoding`)
262pub(crate) fn warn_experimental_extension(name: &str, extension_type: &str) {
263 log::warn!(
264 "The `{name}` {extension_type} is experimental and may be incompatible with other Zarr V3 implementations.",
265 );
266}
267
268/// Log a warning that a deprecated extension name is being used.
269///
270/// # Arguments
271/// * `deprecated_name` - The deprecated name being used (e.g., `binary`)
272/// * `extension_type` - The type of extension (e.g., `codec`, `chunk grid`, `chunk key encoding`)
273/// * `current_name` - The current/preferred name (e.g., `bytes`)
274pub(crate) fn warn_deprecated_extension(
275 deprecated_name: &str,
276 extension_type: &str,
277 current_name: Option<&str>,
278) {
279 if let Some(current_name) = current_name {
280 log::warn!(
281 "The `{deprecated_name}` {extension_type} alias is deprecated, use `{current_name}` instead.",
282 );
283 } else {
284 log::warn!("The `{deprecated_name}` {extension_type} is deprecated.");
285 }
286}
287
288#[cfg(not(target_arch = "wasm32"))]
289use rayon_iter_concurrent_limit::iter_concurrent_limit;
290
291#[cfg(target_arch = "wasm32")]
292/// A serial equivalent of [`rayon_iter_concurrent_limit::iter_concurrent_limit`] for WASM compatibility.
293#[macro_export]
294macro_rules! iter_concurrent_limit {
295 ( $concurrent_limit:expr, $iterator:expr, $fn:tt, $op:expr ) => {{
296 let _concurrent_limit = $concurrent_limit; // fixes unused lint
297 $iterator.into_iter().$fn($op)
298 }};
299}