h5wasm

repository·main·Indexed 41 days ago

https://github.com/usnistgov/h5wasm

A zero-dependency WebAssembly library for reading and writing HDF5 files from JavaScript. It leverages a wasm-compiled version of the HDF5 C library (libhdf5-wasm) and interacts with files via the Emscripten filesystem. Version 0.10.3 provides high-level APIs for managing HDF5 Groups, Datasets, and Attributes, supporting various access modes and library version bounds.

Tokens
12K
Snippets
42
Records
46
Agent score
100%

What's inside h5wasm

  1. Use Half-precision (float16) data

    main

    Half-precision floats (dtype: '<e') are supported via the Float16Array in browsers that support it.

    • Reading: If the runtime supports Float16Array, data is loaded into it. If not, it returns a Uint8Array and logs a warning.
    • Writing: You can pass a Float16Array directly, or pass plain numbers with an explicit dtype: '<e', in which case values are rounded to the nearest representable half.

    Note: bfloat16 is also stored as a 2-byte float but currently lacks a native JS container; these will return raw bytes.

    // Writing from Float16Array
    new_file.create_dataset({name: "half", data: new Float16Array([1.5, -2.5])});
    
    // Writing plain numbers with explicit dtype (auto-rounding)
    new_file.create_dataset({name: "rounded", data: [0.1], dtype: '<e'});
  2. Write Compound Datatypes using Maps

    main

    HDF5 Compound Datatypes (H5T_COMPOUND) can be written by passing a JavaScript Map representing a Structure of Arrays (SoA). The keys of the Map become the member names, and the values are arrays/TypedArrays representing the columns. h5wasm automatically interleaves them into the expected Array of Structures (AoS) format.

    Automatic Inference: If dtype is omitted, types are guessed from the provided TypedArrays.

    Explicit Schema: Pass an array of [name, type_string] tuples to dtype to enforce a specific layout.

    Nested Compounds: Nest Map objects within each other to create deeply nested structures.

    // 1. Create SoA using a Map
    const particle_data = new Map([
      ['id', new Int32Array([10, 20, 30])],
      ['velocity', new Float64Array([1.1, 2.2, 3.3])]
    ]);
    
    // 2. Write it
    let new_file = new h5wasm.File("myfile.h5", "w");
    new_file.create_dataset({ name: "particles", data: particle_data });
    
    // 3. Reading it back returns an Array of Structures (AoS) format
    const read_back = new_file.get("particles").value;
    // Result: [[10, 1.1], [20, 2.2], [30, 3.3]]
    
    // Nested Example
    const nested_data = new Map([
      ['id', new Uint8Array([1, 2])],
      ['position', new Map([
        ['x', new Float64Array([10.1, 20.1])],
        ['y', new Float64Array([1.1, 2.2])]
      ])]
    ]);
    new_file.create_dataset({ name: "nested", data: nested_data });
  3. QuickStart: Use h5wasm in the browser (no-build)

    main

    To use h5wasm directly in a browser without a build step, import the ESM module from a CDN. Because the WebAssembly module loads asynchronously, you must await h5wasm.ready to access the Module and its FS (FileSystem) object.

    To work with files, you first write the file data into the Emscripten filesystem using FS.writeFile, then open it using the h5wasm.File constructor with an appropriate access mode (e.g., 'r' for reading).

    import h5wasm from "https://cdn.jsdelivr.net/npm/h5wasm@latest/dist/esm/hdf5_hl.js";
    
    // Wait for WASM to load
    const Module = await h5wasm.ready;
    const { FS } = Module;
    
    // Load a file into the virtual filesystem
    let response = await fetch("https://ncnr.nist.gov/pub/ncnrdata/vsans/202003/24845/data/sans59510.nxs.ngv");
    let ab = await response.arrayBuffer();
    FS.writeFile("sans59510.nxs.ngv", new Uint8Array(ab));
    
    // Open the file for reading
    let f = new h5wasm.File("sans59510.nxs.ngv", "r");
  4. QuickStart: Use h5wasm in a Web Worker

    main

    For environments where ESM is not supported (like some Web Worker contexts in Firefox), use the IIFE build located at ./dist/iife/h5wasm.js.

    In a worker, you can use FS.mount with FS.filesystems.WORKERFS to provide random access to files, such as those received from an <input type="file" /> via onmessage.

    // worker.js
    onmessage = async function(e) {
        const { FS } = await h5wasm.ready;
    
        // e.data[0] is a file object from an <input type="file" />
        const f_in = e.data[0];
    
        FS.mkdir('/work');
        FS.mount(FS.filesystems.WORKERFS, { files: [f_in] }, '/work');
    
        const f = new h5wasm.File(`/work/${f_in.name}`, 'r');
        console.log(f);
    }
    
    self.importScripts('../dist/iife/h5wasm.js');
  5. Persist Files in Browser using IDBFS

    main

    By default, the Emscripten virtual filesystem is in-memory and lost on page refresh. To persist HDF5 files in the browser, use IDBFS to sync the virtual filesystem with IndexedDB.

    Workflow:

    1. Mount: Mount FS.filesystems.IDBFS to a directory in the virtual filesystem (e.g., /home/web_user).
    2. Sync In: Use FS.syncfs(true, callback) to load files from IndexedDB into the virtual filesystem.
    3. Sync Out: Use FS.syncfs(false, callback) to save files from the virtual filesystem back to IndexedDB (e.g., before the user leaves the page).
    // 1. Mount the IndexedDB filesystem
    FS.mount(FS.filesystems.IDBFS, {}, "/home/web_user");
    
    // 2. Load files from IndexedDB into memory
    FS.syncfs(true, (e) => { console.log("Sync in complete", e); });
    
    // 3. Save files from memory to IndexedDB
    FS.syncfs(false, (e) => { console.log("Sync out complete", e); });
  6. Build h5wasm from source

    main

    To build the project, you must be in a Linux environment with Emscripten installed and activated (ensuring emcmake, emmake, and emcc are in your PATH). The build process retrieves libhdf5.a and libhdf5_hl.a from the libhdf5-wasm repository via CMake.

    Run the following command to start the build:

    make
  7. Use Web Helpers for Uploads and Downloads

    main

    For browser-based applications, h5wasm provides optional helpers to handle file I/O via standard HTML elements.

    • uploader: An event handler for <input type="file">. When triggered, it saves the uploaded file to the Emscripten virtual filesystem (Module.FS) using its original filename.
    • download(file): Takes an h5wasm.File instance and triggers a browser download of a snapshot of that file.
    • UPLOADED_FILES: An array containing references to files recently uploaded via the uploader.

    Note: These helpers are available via the file_handlers.js ESM module.

    import {uploader, download, UPLOADED_FILES} from "https://cdn.jsdelivr.net/npm/h5wasm@latest/dist/esm/file_handlers.js";
    
    // 1. Setup Upload
    document.getElementById("upload_selector").onchange = uploader;
    
    // 2. Access uploaded file
    const lastFile = UPLOADED_FILES[UPLOADED_FILES.length - 1];
    let f = new h5wasm.File(lastFile, "r");
    
    // 3. Download a file
    let new_file = new h5wasm.File("myfile.h5", "w");
    // ... perform writes ...
    download(new_file);
  8. Install h5wasm for Browser or Node.js

    main

    To use h5wasm in a web application with a build system, install it via npm or yarn. Note that your build system must target ES2020 or higher to support BigInt.

    For Node.js environments, use the specific /node entry point. If you are using Node.js < 16, you may need to enable BigInt support manually.

    npm i h5wasm
    # or
    yarn add h5wasm
    // Browser usage
    import h5wasm from "h5wasm";
    const { FS } = await h5wasm.ready;
    
    // Node.js usage
    const h5wasm = await import("h5wasm/node");
    await h5wasm.ready;
  9. Implement SWMR (Single Writer Multiple Reader)

    main

    SWMR allows one process to write to a file while multiple others read from it simultaneously.

    Requirements:

    1. The file must be created with at least libver: "v110".
    2. The dataset must be chunked and extensible (maxshape must include null).

    Workflow:

    1. Open file in "w" mode with { libver: "v110" }.
    2. Create a chunked dataset with maxshape: [null].
    3. Call file.start_swmr_write().
    4. Readers open the file using mode "Sr".
    5. When the reader needs to see new data/shapes, call dataset.refresh().

    Note: To append to a closed file, re-open with mode "Sa" or open with "a" and call start_swmr_write().

    // Writer setup
    const f_write = new h5wasm.File(FILEPATH, "w", { libver: "v110" });
    const dset_write = f_write.create_dataset({
      name: "data",
      data: INITIAL_DATA,
      maxshape: [null], // Required for extensibility
      chunks: [10]
    });
    
    f_write.start_swmr_write();
    
    // ... later extend and write
    dset_write.resize([6]);
    dset_write.write_slice([[3, 6]], new Float32Array([4, 5, 6]));
    f_write.flush();
    
    // Reader setup
    const f_read = new h5wasm.File(FILEPATH, "Sr");
    const dset_read = f_read.get("data");
    
    dset_read.refresh(); // Refresh to see new shape/data
    console.log(dset_read.value);
  10. Use h5wasm in a Firefox Web Worker via IIFE

    main

    The build process generates an IIFE version of the library at ./dist/iife/h5wasm.js. This file is specifically designed for use in Firefox web workers using importScripts(). Once imported, the library's exported names are available under the global h5wasm object.

    // Inside a Firefox Web Worker
    importScripts('./dist/iife/h5wasm.js');
    
    // Access the library via the global h5wasm object
    console.log(h5wasm);
  11. Configure Library Version Bounds (libver)

    main

    You can control the minimum and maximum HDF5 library version used when writing objects via the libver option in FileOptions. This ensures compatibility with specific HDF5 versions.

    Valid values: "earliest", "v108", "v110", "v112", "v114", "v200", and "latest".

    Usage Patterns:

    • Single Bound: Pass a string to set both low and high bounds to that version.
    • Asymmetric Bounds: Pass an array [low, high] to allow a range of versions.
    // Require at least v110
    const f = new h5wasm.File("myfile.h5", "w", { libver: "v110" });
    
    // Allow range from v110 to latest
    const f_range = new h5wasm.File("myfile.h5", "w", { libver: ["v110", "latest"] });