Compiled companion to h5coro: hidefix-backed HDF5 chunk indexing, index save/load, chunk enumeration, and fast hyperslab reads
Project description
h5coro-hidefix
Compiled companion to h5coro: a tiny pyo3 binding over the hidefix crate (consumed from crates.io — no hidefix source lives in this tree). hidefix reads chunked HDF5 files through a pre-built chunk index instead of the HDF5 library's B-tree walk, decoding chunks in parallel (gzip + shuffle), which benchmarked ~6× faster than pure-Python h5coro on ICESat-2 ATL03 workloads — and ~15–20× once the index is built ahead of time and reloaded.
This package exists because the upstream hidefix Python binding cannot
save, load, or enumerate its index, and squeezes length-1 dimensions on read.
This binding fixes all three while keeping the surface minimal:
Index(path)— build an index from a local HDF5 file (~0.2 s / ~0.6 MB for a ~2 GB ATL03 granule).Index.save(path)/Index.load(path, source=None)— persist / restore the index (bincode via hidefix's public serde impls) so reads need zero HDF5 metadata I/O.sourcepoints reads at the data file when it lives somewhere else than at index-build time.Index.datasets()— full dataset paths, recursing groups.Index.chunks(dataset)— the chunk table as numpy arrays(addrs, sizes, offsets): file byte offset, stored (compressed) byte count, and dataspace coordinates per chunk.Index.read(dataset, start=None, end=None)— rows[start, end)along dim 0 (remaining dims in full) as a numpy array with native dtype and the exact request shape. Never squeezes: a(1, 5)request returns(1, 5), byte-identical withh5py_dataset[start:end]. The GIL is released for the duration of the read.Index.read_plan(dataset, start=None, end=None)/Index.read_from_buffers(dataset, buffers, start=None, end=None)— the object-store read path: the plan lists the chunks a read needs (the same(addrs, sizes, offsets)triple aschunks(), restricted to the row range, ascending dataspace offset); the caller fetches those byte ranges itself and hands the raw stored bytes back for decode + assembly, byte-identical toread(). No file or network access happens inside the binding.Index.from_chunks(source, datasets)— construct a decode-capable index from an external chunk manifest (e.g. rows from a parquet sidecar store): no granule file access, no stored bincode.Index.filters(dataset)({"gzip": int|None, "shuffle": bool, "byte_order": ...}) completes the extraction side, soIndex(path)→ getters →from_chunksround-trips byte-identically.
Design discussion: englacial/zagg#155, englacial/zagg#160 (parquet-primary sidecar store).
Install
pip install h5coro-hidefix # once published; not yet registered on PyPI
Or from source (needs a Rust toolchain and cmake — the bundled static
libhdf5 is built at compile time; no system HDF5 required at build or run
time):
pip install .
Quickstart
import h5coro_hidefix as hx
idx = hx.Index("ATL03_20190105163308_01260202_007_01.h5") # ~0.2 s
idx.datasets() # ['/gt1l/heights/h_ph', ...]
idx.shape("/gt1l/heights/signal_conf_ph") # (n_photons, 5)
idx.dtype("/gt1l/heights/h_ph") # 'float64'
# persist the index; later reads skip all HDF5 metadata I/O
idx.save("granule.idx")
idx = hx.Index.load("granule.idx", source="ATL03_...h5")
arr = idx.read("/gt1l/heights/h_ph", 1_000_000, 2_000_000) # float64 (1000000,)
one = idx.read("/gt1l/heights/signal_conf_ph", 7, 8) # int8 (1, 5) -- not (5,)
addrs, sizes, offsets = idx.chunks("/gt1l/heights/h_ph") # uint64 arrays
Obstore-fed reads (the Lambda worker flow)
Workers never open the HDF5 file: indices are built once at catalog time, and at read time the worker loads the index, asks which byte ranges a row-slice needs, fetches those ranges itself (obstore/boto3 ranged GETs), and hands the raw bytes back for decode:
import obstore
import h5coro_hidefix as hx
idx = hx.Index.load("granule.idx") # ~1 ms; zero HDF5 metadata I/O
name = "/gt1l/heights/h_ph"
addrs, sizes, _ = idx.read_plan(name, row0, row1)
buffers = obstore.get_ranges( # caller owns the fetch policy:
store, "ATL03_...h5", # coalescing, concurrency, retries
starts=addrs.tolist(),
ends=(addrs + sizes).tolist(),
)
arr = idx.read_from_buffers(name, buffers, row0, row1)
# arr is byte-identical to idx.read(name, row0, row1) on a local copy
buffers must line up 1:1, in order, with read_plan's chunks; any
bytes-like objects work (bytes is zero-copy, others are copied once).
ValueError is raised on a wrong buffer count or a buffer whose length
differs from the chunk's stored size; the GIL is released during decode.
This path removes any dependency on hidefix-side S3 support.
Manifest-built indices: from_chunks (parquet-primary store)
When the chunk manifest lives in a durable store (parquet sidecars — see englacial/zagg#160), the index is reconstructed from it on demand; the granule file is never opened and no bincode is ever stored:
# caller side: read the manifest (pyarrow / obstore) -- this package stays
# numpy-only at runtime, so parquet decoding happens outside it
idx = hx.Index.from_chunks("s3-key-or-local-path.h5", {
"/gt1l/heights/h_ph": dict(
dtype="<f8", # numpy dtype str; byte order honored
shape=(23_692_855,),
chunk_shape=(100_000,),
gzip=6, # deflate level or None
shuffle=True,
addrs=addrs, # u64[k] chunk byte offsets in the file
sizes=sizes, # u64[k] stored (compressed) byte counts
offsets=offsets, # u64[k, ndim] dataspace chunk coordinates
filter_mask=masks, # optional; must be all zero (see below)
),
...
})
arr = idx.read_from_buffers(name, buffers, row0, row1) # as above
Chunk rows need not be pre-sorted. The result is a real index — read,
read_plan, read_from_buffers, save, chunks all behave exactly as if
it had been built from the file.
Metadata contract for extractors (what a manifest must carry, per
dataset): dtype, shape, chunk_shape, gzip, shuffle, plus the chunk
table (addrs, sizes, and per-chunk dataspace offsets — for 1-D data
the offset equals the element start index; for N-D it must be stored
explicitly). All of it is available from a real index via datasets(),
shape(), chunk_shape(), dtype(), filters() and chunks().
Per-chunk filter masks are unrepresentable: hidefix models filters at
dataset level (shuffle, gzip), and its chunk records carry only
(addr, size, offset). A nonzero HDF5 filter_mask (bit i = filter i
skipped for that chunk) therefore raises ValueError in from_chunks.
ATL03 masks are all zero (verified in englacial/zagg PR #159).
Scope and limitations
- The binding itself performs only local-file I/O (
read()); object-store reads are the caller's job viaread_plan/read_from_buffersabove. - Filters: gzip (deflate) + shuffle + byte-order — full coverage for ATL03 and most NASA Earthdata HDF5. Other filters (szip, lzf, scaleoffset) fail at index time.
- Hyperslabs are row ranges on dimension 0; remaining dimensions are read in full.
- The serialized index format is hidefix's bincode encoding of its Rust types — treat it as an opaque cache keyed by (file, hidefix version), not as a stable interchange format.
Licensing note
The binding code in this repository is MIT. Published wheels, however,
statically embed compiled hidefix, whose upstream license metadata is
currently ambiguous: the repository's LICENSE file is MIT (added 2023),
while its Cargo.toml / crates.io / PyPI metadata still declare
LGPL-3.0-or-later on every release including 0.12.0. Clarification is
pending upstream (gauteh/hidefix#48);
the first PyPI release of this package is gated on that resolution (see
the workflow note below).
Private test deployments
Wheels may be staged at a non-public S3 path for internal Lambda testing: internal use is not conveyance, so the license gate above does not apply to it. Such wheels must not be attached to public release artifacts — GitHub release zips, mirrors, or PyPI — until the upstream clarification resolves.
Development
python -m venv .venv && . .venv/bin/activate
pip install maturin pytest h5py numpy
maturin develop --release
pytest -v # hermetic tests generate their own HDF5 files
A second, local-only test tier runs automatically when real ATL03 granules
are present (default ~/ignore/zagg_neon_atl03_test_shard/granules, override
with H5CORO_HIDEFIX_GRANULE_DIR).
CI builds wheels for manylinux x86_64 + aarch64 (static libhdf5, cmake
installed in the manylinux image) and macOS arm64, abi3 (>=3.9), one wheel
per platform. The publish job triggers on version tags and uses PyPI Trusted
Publishing — it will fail harmlessly until the h5coro-hidefix name is
registered on PyPI and the trusted publisher is configured (and stays gated
on the licensing note above).
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file h5coro_hidefix-0.1.0.tar.gz.
File metadata
- Download URL: h5coro_hidefix-0.1.0.tar.gz
- Upload date:
- Size: 34.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17ceebfdf6724bb18314e1af721721f711b30e887367b3d273446793162199ee
|
|
| MD5 |
97987282f743eb5a3ef36549c21df3f2
|
|
| BLAKE2b-256 |
f5f6fb7fabc608e2143cc7d5027257ad3286c9a77ee79a06d75d078365eac6aa
|
Provenance
The following attestation bundles were made for h5coro_hidefix-0.1.0.tar.gz:
Publisher:
wheels.yml on espg/h5coro-hidefix
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
h5coro_hidefix-0.1.0.tar.gz -
Subject digest:
17ceebfdf6724bb18314e1af721721f711b30e887367b3d273446793162199ee - Sigstore transparency entry: 2068451874
- Sigstore integration time:
-
Permalink:
espg/h5coro-hidefix@004fabc8a27fc65c9ba1cd5599e4159a4f059a5e -
Branch / Tag:
refs/tags/0.0.1 - Owner: https://github.com/espg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@004fabc8a27fc65c9ba1cd5599e4159a4f059a5e -
Trigger Event:
push
-
Statement type:
File details
Details for the file h5coro_hidefix-0.1.0-cp39-abi3-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: h5coro_hidefix-0.1.0-cp39-abi3-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 2.2 MB
- Tags: CPython 3.9+, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
05b51d3e9913918b78f196cbd53249a82e6688dfbbf65f9be38c03dbe5d5578a
|
|
| MD5 |
5b9cdfeb29a142c1cafffabd722be959
|
|
| BLAKE2b-256 |
ef6b9e3d54b7753635de0c395e7cecf06cc9518144357690dfaade97a8844285
|
Provenance
The following attestation bundles were made for h5coro_hidefix-0.1.0-cp39-abi3-manylinux_2_28_x86_64.whl:
Publisher:
wheels.yml on espg/h5coro-hidefix
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
h5coro_hidefix-0.1.0-cp39-abi3-manylinux_2_28_x86_64.whl -
Subject digest:
05b51d3e9913918b78f196cbd53249a82e6688dfbbf65f9be38c03dbe5d5578a - Sigstore transparency entry: 2068453531
- Sigstore integration time:
-
Permalink:
espg/h5coro-hidefix@004fabc8a27fc65c9ba1cd5599e4159a4f059a5e -
Branch / Tag:
refs/tags/0.0.1 - Owner: https://github.com/espg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@004fabc8a27fc65c9ba1cd5599e4159a4f059a5e -
Trigger Event:
push
-
Statement type:
File details
Details for the file h5coro_hidefix-0.1.0-cp39-abi3-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: h5coro_hidefix-0.1.0-cp39-abi3-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 2.2 MB
- Tags: CPython 3.9+, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6c47ddf1f9040f21a2b7ec302a9730ead3993d6f5ce0097762db349472b38c51
|
|
| MD5 |
e7fa6606385e052c85f5a136f7d4be18
|
|
| BLAKE2b-256 |
da67da15c95e0dcdd825660bc02c5298e88de393bc3c8466d593bb05623cece5
|
Provenance
The following attestation bundles were made for h5coro_hidefix-0.1.0-cp39-abi3-manylinux_2_28_aarch64.whl:
Publisher:
wheels.yml on espg/h5coro-hidefix
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
h5coro_hidefix-0.1.0-cp39-abi3-manylinux_2_28_aarch64.whl -
Subject digest:
6c47ddf1f9040f21a2b7ec302a9730ead3993d6f5ce0097762db349472b38c51 - Sigstore transparency entry: 2068453834
- Sigstore integration time:
-
Permalink:
espg/h5coro-hidefix@004fabc8a27fc65c9ba1cd5599e4159a4f059a5e -
Branch / Tag:
refs/tags/0.0.1 - Owner: https://github.com/espg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@004fabc8a27fc65c9ba1cd5599e4159a4f059a5e -
Trigger Event:
push
-
Statement type:
File details
Details for the file h5coro_hidefix-0.1.0-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: h5coro_hidefix-0.1.0-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 1.6 MB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60507d7e7acfab2e9a6f78d81974b7845658b6a5e54fefdc65e83384c8a4af69
|
|
| MD5 |
230e967cfe5618806605815c71448dd3
|
|
| BLAKE2b-256 |
e902083e256f849a689768884f4903f0ce60c9b11f967219cf4523d4d3d9b8c9
|
Provenance
The following attestation bundles were made for h5coro_hidefix-0.1.0-cp39-abi3-macosx_11_0_arm64.whl:
Publisher:
wheels.yml on espg/h5coro-hidefix
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
h5coro_hidefix-0.1.0-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
60507d7e7acfab2e9a6f78d81974b7845658b6a5e54fefdc65e83384c8a4af69 - Sigstore transparency entry: 2068452856
- Sigstore integration time:
-
Permalink:
espg/h5coro-hidefix@004fabc8a27fc65c9ba1cd5599e4159a4f059a5e -
Branch / Tag:
refs/tags/0.0.1 - Owner: https://github.com/espg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@004fabc8a27fc65c9ba1cd5599e4159a4f059a5e -
Trigger Event:
push
-
Statement type: