Skip to content

Commit e658217

Browse files
Initial Zarrs Implementation (#172)
1 parent 66e8559 commit e658217

64 files changed

Lines changed: 5648 additions & 17 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.Rbuildignore‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,11 @@ r6-roxygen-convention.md
1616
cran-comments.md
1717
CONTRIBUTING.md
1818
^CRAN-SUBMISSION$
19+
^dev\.md$
20+
^RUST-STYLE\.md$
21+
^\.vscode$
22+
^src/\.cargo$
23+
^src/rust/vendor$
24+
^src/rust/target$
25+
^src/Makevars$
26+
^src/Makevars\.win$

‎.gitattributes‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
* text=auto
22
*.zip binary
33
tests/fixtures/**/* -diff
4+
configure eol=lf
5+
configure.win eol=crlf

‎.github/workflows/R-CMD-Check.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ jobs:
5050
- uses: r-lib/actions/check-r-package@v2
5151
with:
5252
upload-snapshots: true
53+
error-on: '"error"'
5354

5455
- name: Test coverage
5556
run: covr::codecov()

‎.gitignore‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,7 @@ cran-comments.md
2323
/.quarto/
2424
**/*.quarto_ipynb
2525
CRAN-SUBMISSION
26+
src/rust/vendor
27+
src/Makevars
28+
src/Makevars.win
29+
.vscode

‎CONTRIBUTING.md‎

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,65 @@ devtools::install()
99
devtools::load_all()
1010
```
1111

12-
## Testing
12+
## Build and Test Cycles
1313

14-
Tests run single-threaded (`Config/testthat/parallel: false` in
15-
DESCRIPTION).
14+
pizzarr has two build tiers that affect how you develop and test locally.
15+
16+
### CRAN tier (pure R)
17+
18+
If you are working on R-side logic (indexers, stores, codecs, R6 classes)
19+
and do not need the zarrs backend, this is the faster cycle:
1620

1721
``` r
22+
devtools::load_all()
23+
devtools::test()
1824
devtools::check()
25+
```
26+
27+
Tests run single-threaded (`Config/testthat/parallel: false` in
28+
DESCRIPTION). All zarrs-specific tests skip automatically when the Rust
29+
library is absent — they guard on `.pizzarr_env$zarrs_available`.
30+
31+
To produce a CRAN-ready source tarball that strips `src/`, `configure`,
32+
and `SystemRequirements`:
33+
34+
``` bash
35+
bash tools/cran-build.sh
36+
```
37+
38+
The resulting `.tar.gz` passes `R CMD check` without a Rust toolchain.
39+
40+
### r-universe tier (zarrs backend)
41+
42+
The r-universe build compiles the [zarrs](https://github.com/zarrs/zarrs)
43+
Rust crate via [extendr](https://extendr.github.io/rextendr/). You need
44+
rustc >= 1.91 with the GNU target on Windows:
45+
46+
``` bash
47+
rustup target add x86_64-pc-windows-gnu
48+
```
49+
50+
After modifying any `#[extendr]` function in `src/rust/src/lib.rs`,
51+
regenerate the R wrappers:
52+
53+
``` r
54+
rextendr::document()
55+
```
56+
57+
This rebuilds the Rust library and regenerates `R/extendr-wrappers.R`.
58+
Never edit that file by hand. Then run the standard cycle:
59+
60+
``` r
61+
devtools::load_all()
1962
devtools::test()
63+
devtools::check()
2064
```
2165

66+
`devtools::check()` will emit a NOTE about downloading Rust crates — that
67+
is expected and is not a problem for r-universe builds. See
68+
[RUST-STYLE.md](RUST-STYLE.md) for Rust conventions, module layout, and
69+
build pipeline details.
70+
2271
## Building Documentation
2372

2473
``` r

‎DESCRIPTION‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: pizzarr
22
Type: Package
33
Title: Slice into 'Zarr' Arrays
4-
Version: 0.1.3
4+
Version: 0.2.0
55
Authors@R: c(
66
person(given = "David",
77
family = "Blodgett",
@@ -29,8 +29,10 @@ Description: An implementation of chunked, compressed,
2929
Language: en-US
3030
License: MIT + file LICENSE
3131
BugReports: https://github.com/zarr-developers/pizzarr/issues
32-
URL: https://zarr.dev/pizzarr/
33-
Depends: R (>= 4.1.0)
32+
URL: https://zarr.dev/pizzarr/,
33+
https://github.com/zarr-developers/pizzarr
34+
Depends:
35+
R (>= 4.2)
3436
Imports:
3537
jsonlite,
3638
stats,
@@ -42,6 +44,7 @@ Imports:
4244
Encoding: UTF-8
4345
Roxygen: list(markdown = TRUE, r6 = TRUE)
4446
RoxygenNote: 7.3.3
47+
VignetteBuilder: knitr
4548
Suggests:
4649
testthat,
4750
knitr,
@@ -61,3 +64,5 @@ Suggests:
6164
withr
6265
Config/testthat/parallel: false
6366
Config/testthat/edition: 3
67+
Config/rextendr/version: 0.4.2
68+
SystemRequirements: Cargo (Rust's package manager), rustc >= 1.91

‎NAMESPACE‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ export(is_key_error)
2828
export(is_scalar)
2929
export(is_slice)
3030
export(obj_list)
31+
export(pizzarr_compiled_features)
3132
export(pizzarr_option_defaults)
3233
export(pizzarr_sample)
34+
export(pizzarr_upgrade)
3335
export(slice)
3436
export(zarr_create)
3537
export(zarr_create_array)
@@ -41,10 +43,19 @@ export(zarr_open_array)
4143
export(zarr_open_group)
4244
export(zarr_save_array)
4345
export(zarr_volcano)
46+
export(zarrs_close_store)
47+
export(zarrs_compiled_features)
48+
export(zarrs_get_subset)
49+
export(zarrs_node_exists)
50+
export(zarrs_open_array_metadata)
51+
export(zarrs_runtime_info)
52+
export(zarrs_set_codec_concurrent_target)
53+
export(zarrs_set_subset)
4454
export(zb_int)
4555
export(zb_slice)
4656
importFrom(R6,R6Class)
4757
importFrom(memoise,memoise)
4858
importFrom(memoise,timeout)
4959
importFrom(qs2,zstd_compress_raw)
5060
importFrom(qs2,zstd_decompress_raw)
61+
useDynLib(pizzarr, .registration = TRUE)

‎R/extendr-wrappers.R‎

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Generated by extendr: Do not edit by hand
2+
3+
# nolint start
4+
5+
#
6+
# This file was created with the following call:
7+
# .Call("wrap__make_pizzarr_wrappers", use_symbols = TRUE, package_name = "pizzarr")
8+
9+
#' @usage NULL
10+
#' @useDynLib pizzarr, .registration = TRUE
11+
NULL
12+
13+
#' Return compiled zarrs feature flags as a character vector.
14+
#'
15+
#' Called once at `.onLoad` to populate `.pizzarr_env$zarrs_available`.
16+
#' The feature list is determined at compile time via `cfg!` checks.
17+
#' @export
18+
zarrs_compiled_features <- function() .Call(wrap__zarrs_compiled_features)
19+
20+
#' Check whether a node exists at the given path in a zarrs store.
21+
#'
22+
#' Open (or reuse) the store at `store_url`, then probe for V3 and V2
23+
#' metadata keys at `path`.
24+
#'
25+
#' @param store_url Filesystem path or URL to the store root.
26+
#' @param path Path within the store (e.g. `"group1/array1"` or `""` for root).
27+
#' @export
28+
zarrs_node_exists <- function(store_url, path) .Call(wrap__zarrs_node_exists, store_url, path)
29+
30+
#' Close (remove) a cached store handle.
31+
#'
32+
#' Returns `TRUE` if the store was in the cache and was removed,
33+
#' `FALSE` if it was not cached.
34+
#'
35+
#' @param store_url Filesystem path or URL to the store root.
36+
#' @export
37+
zarrs_close_store <- function(store_url) .Call(wrap__zarrs_close_store, store_url)
38+
39+
#' Open a zarrs array and return its metadata as an R list.
40+
#'
41+
#' Returns a named list with `shape`, `chunks`, `dtype`, `r_type`,
42+
#' `fill_value_json`, `zarr_format`, and `order`.
43+
#'
44+
#' @param store_url Filesystem path or URL to the store root.
45+
#' @param array_path Path to the array within the store.
46+
#' @export
47+
zarrs_open_array_metadata <- function(store_url, array_path) .Call(wrap__zarrs_open_array_metadata, store_url, array_path)
48+
49+
#' Return runtime information about the zarrs backend.
50+
#'
51+
#' Returns a named list with `codec_concurrent_target`,
52+
#' `store_cache_entries`, and `compiled_features`.
53+
#' @export
54+
zarrs_runtime_info <- function() .Call(wrap__zarrs_runtime_info)
55+
56+
#' Set the zarrs codec concurrent target.
57+
#'
58+
#' Controls the number of concurrent codec operations zarrs uses
59+
#' within a single array operation.
60+
#'
61+
#' @param n Positive integer.
62+
#' @export
63+
zarrs_set_codec_concurrent_target <- function(n) .Call(wrap__zarrs_set_codec_concurrent_target, n)
64+
65+
#' Get a contiguous subset of an array.
66+
#'
67+
#' Returns a named list with `data` (numeric, integer, or logical vector)
68+
#' and `shape` (integer vector). Ranges are 0-based, exclusive stop.
69+
#'
70+
#' @param store_url Filesystem path or URL to the store root.
71+
#' @param array_path Path to the array within the store.
72+
#' @param ranges R list of length-2 integer vectors `c(start, stop)`.
73+
#' @param concurrent_target Optional codec concurrency override.
74+
#' @export
75+
zarrs_get_subset <- function(store_url, array_path, ranges, concurrent_target) .Call(wrap__zarrs_get_subset, store_url, array_path, ranges, concurrent_target)
76+
77+
#' Set a contiguous subset of an array from R data.
78+
#'
79+
#' Returns `true` on success. Ranges are 0-based, exclusive stop.
80+
#' Data must be a flat vector in C-order (row-major).
81+
#'
82+
#' @param store_url Filesystem path or URL to the store root.
83+
#' @param array_path Path to the array within the store.
84+
#' @param ranges R list of length-2 integer vectors `c(start, stop)`.
85+
#' @param data R vector (numeric, integer, or logical).
86+
#' @param concurrent_target Optional codec concurrency override.
87+
#' @export
88+
zarrs_set_subset <- function(store_url, array_path, ranges, data, concurrent_target) .Call(wrap__zarrs_set_subset, store_url, array_path, ranges, data, concurrent_target)
89+
90+
91+
# nolint end

‎R/onload.R‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
11
#' @keywords internal
22
.onLoad <- function(libname = NULL, pkgname = NULL) {
3-
init_options()
3+
init_options()
4+
.pizzarr_env$zarrs_available <- is_zarrs_available()
5+
}
6+
7+
#' @keywords internal
8+
.onAttach <- function(libname, pkgname) {
9+
if (!.pizzarr_env$zarrs_available &&
10+
getOption("pizzarr.suggest_runiverse", TRUE)) {
11+
packageStartupMessage(
12+
"pizzarr: running without zarrs backend (pure R).\n",
13+
"For parallel I/O and cloud storage, install from r-universe:\n",
14+
' install.packages("pizzarr", ',
15+
'repos = "https://zarr-developers.r-universe.dev")'
16+
)
17+
}
418
}

‎R/stores.R‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,15 @@ Store <- R6::R6Class("Store",
142142
return(private$zmetadata)
143143
},
144144
#' @description
145+
#' Return a store identifier for zarrs dispatch.
146+
#'
147+
#' Subclasses override this to return a filesystem path or URL.
148+
#' Returns `NULL` by default (signals: use R-native path).
149+
#' @return A character string or `NULL`.
150+
get_store_identifier = function() {
151+
NULL
152+
},
153+
#' @description
145154
#' Print a human-readable summary of the store.
146155
#' @param ... Ignored.
147156
#' @return `self` (invisibly).
@@ -259,6 +268,12 @@ DirectoryStore <- R6::R6Class("DirectoryStore",
259268
},
260269
#' @description
261270
#' Print a human-readable summary of the store.
271+
#' @description
272+
#' Return the absolute filesystem path for zarrs dispatch.
273+
#' @return A character string.
274+
get_store_identifier = function() {
275+
normalizePath(self$root, mustWork = FALSE)
276+
},
262277
#' @param ... Ignored.
263278
#' @return `self` (invisibly).
264279
print = function(...) {
@@ -655,6 +670,12 @@ HttpStore <- R6::R6Class("HttpStore",
655670
},
656671
#' @description
657672
#' Print a human-readable summary of the store.
673+
#' @description
674+
#' Return the store URL for zarrs dispatch.
675+
#' @return A character string.
676+
get_store_identifier = function() {
677+
private$url
678+
},
658679
#' @param ... Ignored.
659680
#' @return `self` (invisibly).
660681
print = function(...) {

0 commit comments

Comments
 (0)