Documentation
¶
Overview ¶
Package hdf5 provides a pure Go implementation for reading HDF5 files. It supports HDF5 format versions 0, 2, and 3, with capabilities for reading datasets, groups, attributes, and various data layouts.
Index ¶
- Constants
- type ChunkIterator
- func (it *ChunkIterator) Chunk() (interface{}, error)
- func (it *ChunkIterator) ChunkCoords() []uint64
- func (it *ChunkIterator) ChunkDims() []uint64
- func (it *ChunkIterator) DatasetDims() []uint64
- func (it *ChunkIterator) Err() error
- func (it *ChunkIterator) Next() bool
- func (it *ChunkIterator) OnProgress(fn func(current, total int))
- func (it *ChunkIterator) Progress() (current, total int)
- func (it *ChunkIterator) Reset()
- func (it *ChunkIterator) Total() int
- type CreateMode
- type Dataset
- func (d *Dataset) Address() uint64
- func (d *Dataset) Attributes() ([]*core.Attribute, error)
- func (d *Dataset) ChunkIterator() (*ChunkIterator, error)
- func (d *Dataset) ChunkIteratorWithContext(ctx context.Context) (*ChunkIterator, error)
- func (d *Dataset) Info() (string, error)
- func (d *Dataset) ListAttributes() ([]string, error)
- func (d *Dataset) Name() string
- func (d *Dataset) Read() ([]float64, error)
- func (d *Dataset) ReadAttribute(name string) (interface{}, error)
- func (d *Dataset) ReadCompound() ([]core.CompoundValue, error)
- func (d *Dataset) ReadHyperslab(selection *HyperslabSelection) (interface{}, error)
- func (d *Dataset) ReadSlice(start, count []uint64) (interface{}, error)
- func (d *Dataset) ReadStrings() ([]string, error)
- func (d *Dataset) ReadVLenBytes() ([][]byte, error)
- type DatasetOption
- func WithArrayDims(dims []uint64) DatasetOption
- func WithChunkDims(dims []uint64) DatasetOption
- func WithEnumValues(names []string, values []int64) DatasetOption
- func WithFletcher32() DatasetOption
- func WithGZIPCompression(level int) DatasetOption
- func WithMaxDims(maxDims []uint64) DatasetOption
- func WithOpaqueTag(tag string, size uint32) DatasetOption
- func WithShuffle() DatasetOption
- func WithStringSize(size uint32) DatasetOption
- type DatasetWriter
- func (dw *DatasetWriter) Close() error
- func (ds *DatasetWriter) DeleteAttribute(name string) error
- func (ds *DatasetWriter) RebalanceAttributeBTree() error
- func (dw *DatasetWriter) Resize(newDims []uint64) error
- func (dw *DatasetWriter) Write(data interface{}) error
- func (ds *DatasetWriter) WriteAttribute(name string, value interface{}) error
- func (dw *DatasetWriter) WriteRaw(data []byte) error
- type Datatype
- type File
- type FileWriteConfig
- type FileWriter
- func (fw *FileWriter) Close() error
- func (fw *FileWriter) CreateCompoundDataset(name string, compoundType *core.DatatypeMessage, dims []uint64, ...) (*DatasetWriter, error)
- func (fw *FileWriter) CreateDataset(name string, dtype Datatype, dims []uint64, opts ...DatasetOption) (*DatasetWriter, error)
- func (fw *FileWriter) CreateDenseGroup(name string, links map[string]string) error
- func (fw *FileWriter) CreateExternalLink(linkPath, fileName, objectPath string) error
- func (fw *FileWriter) CreateGroup(path string) (*GroupWriter, error)
- func (fw *FileWriter) CreateGroupWithLinks(name string, links map[string]string) error
- func (fw *FileWriter) CreateHardLink(linkPath, targetPath string) error
- func (fw *FileWriter) CreateSoftLink(linkPath, targetPath string) error
- func (fw *FileWriter) Delete(path string) error
- func (fw *FileWriter) DisableLazyRebalancing() error
- func (fw *FileWriter) DisableRebalancing()
- func (fw *FileWriter) EnableIncrementalRebalancing(config structures.IncrementalRebalancingConfig) error
- func (fw *FileWriter) EnableLazyRebalancing(config structures.LazyRebalancingConfig) error
- func (fw *FileWriter) EnableRebalancing()
- func (fw *FileWriter) ForceBatchRebalance() error
- func (fw *FileWriter) GetIncrementalRebalancingProgress() (structures.RebalancingProgress, error)
- func (fw *FileWriter) GetLazyRebalancingStats() (totalUnderflow, totalPending int, oldestRebalance time.Duration)
- func (fw *FileWriter) IsIncrementalRebalancingEnabled() bool
- func (fw *FileWriter) IsLazyRebalancingEnabled() bool
- func (fw *FileWriter) OpenDataset(path string) (*DatasetWriter, error)
- func (fw *FileWriter) RebalanceAllBTrees() error
- func (fw *FileWriter) RebalancingEnabled() bool
- func (fw *FileWriter) StopIncrementalRebalancing() error
- type FileWriterOption
- type Group
- type GroupMetadata
- type GroupWriter
- type HeapID
- type HyperslabSelection
- type IncrementalOption
- type LazyOption
- type ModeDecision
- type NamedDatatype
- type Object
- type OpenMode
- type SmartOption
- type SmartRebalancingConfig
- type WriteOption
Constants ¶
const ( // SuperblockV0 (legacy format) - Maximum compatibility with older HDF5 tools. // Use this if you need files to be readable by h5dump, older Python h5py, or legacy C library. // This format doesn't have checksums but works with all HDF5 tools. SuperblockV0 = core.Version0 // SuperblockV2 (modern format) - Default. Includes checksums for data integrity. // This is the recommended format for new files. Supported by HDF5 1.10+. SuperblockV2 = core.Version2 // SuperblockV3 (latest format) - Future format, not yet implemented for writing. SuperblockV3 = core.Version3 )
Superblock version constants for file creation.
const ( // KB represents kilobyte size for smart configuration. KB = 1024 // MB represents megabyte size for smart configuration. MB = 1024 * KB // GB represents gigabyte size for smart configuration. GB = 1024 * MB )
const ( // MaxCompactAttributes is the threshold for transitioning to dense storage. // When an object has 8+ attributes, dense storage (Fractal Heap + B-tree) // is more efficient than compact storage (object header messages). MaxCompactAttributes = 8 )
Attribute storage threshold.
const (
SignatureSNOD = "SNOD" // Symbol table node signature.
)
HDF5 signature constants.
const Unlimited uint64 = 0xFFFFFFFFFFFFFFFF
Unlimited represents unlimited dimension size for resizable datasets. Use with WithMaxDims option to allow dimension to grow indefinitely.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ChunkIterator ¶
type ChunkIterator struct {
// contains filtered or unexported fields
}
ChunkIterator provides memory-efficient iteration over dataset chunks. It reads one chunk at a time, allowing processing of datasets larger than available memory.
Usage:
iter, err := dataset.ChunkIterator()
if err != nil {
log.Fatal(err)
}
for iter.Next() {
chunk, err := iter.Chunk()
if err != nil {
log.Fatal(err)
}
processChunk(chunk)
}
if err := iter.Err(); err != nil {
log.Fatal(err)
}
The iterator follows the Go scanner pattern (bufio.Scanner). Only chunked datasets are supported; compact and contiguous datasets should use Read() or ReadSlice() directly.
func (*ChunkIterator) Chunk ¶
func (it *ChunkIterator) Chunk() (interface{}, error)
Chunk returns the data for the current chunk. Must be called after Next() returns true. Returns the chunk data as interface{} (typically []float64).
func (*ChunkIterator) ChunkCoords ¶
func (it *ChunkIterator) ChunkCoords() []uint64
ChunkCoords returns the scaled coordinates of the current chunk. These are chunk indices, not element indices. For element indices, multiply by chunk dimensions.
func (*ChunkIterator) ChunkDims ¶
func (it *ChunkIterator) ChunkDims() []uint64
ChunkDims returns the chunk dimensions.
func (*ChunkIterator) DatasetDims ¶
func (it *ChunkIterator) DatasetDims() []uint64
DatasetDims returns the dataset dimensions.
func (*ChunkIterator) Err ¶
func (it *ChunkIterator) Err() error
Err returns any error that occurred during iteration. Should be checked after Next() returns false.
func (*ChunkIterator) Next ¶
func (it *ChunkIterator) Next() bool
Next advances to the next chunk. Returns false when iteration is complete or an error occurred. Check Err() after iteration to distinguish.
func (*ChunkIterator) OnProgress ¶
func (it *ChunkIterator) OnProgress(fn func(current, total int))
OnProgress sets a callback function that is called after each Next(). The callback receives the current chunk index (1-based) and total count.
Example:
iter.OnProgress(func(current, total int) {
fmt.Printf("Processing chunk %d/%d\n", current, total)
})
func (*ChunkIterator) Progress ¶
func (it *ChunkIterator) Progress() (current, total int)
Progress returns the current chunk index and total chunk count. Useful for progress reporting.
func (*ChunkIterator) Reset ¶
func (it *ChunkIterator) Reset()
Reset resets the iterator to the beginning, allowing re-iteration.
func (*ChunkIterator) Total ¶
func (it *ChunkIterator) Total() int
Total returns the total number of chunks in the dataset.
type CreateMode ¶
type CreateMode int
CreateMode specifies how to create a new HDF5 file.
const ( // CreateTruncate creates a new file, overwriting if it exists. // This is the default mode, equivalent to os.Create() behavior. CreateTruncate CreateMode = iota // CreateExclusive creates a new file, failing if it already exists. // Useful when you want to ensure a file doesn't get accidentally overwritten. CreateExclusive )
type Dataset ¶
type Dataset struct {
// contains filtered or unexported fields
}
Dataset represents an HDF5 dataset containing multidimensional array data.
func (*Dataset) Attributes ¶
Attributes returns all attributes attached to this dataset.
func (*Dataset) ChunkIterator ¶
func (d *Dataset) ChunkIterator() (*ChunkIterator, error)
ChunkIterator returns an iterator for reading dataset chunks one at a time. This is memory-efficient for large chunked datasets.
Returns an error if the dataset is not chunked (compact or contiguous layout). For non-chunked datasets, use Read() or ReadSlice() instead.
func (*Dataset) ChunkIteratorWithContext ¶
func (d *Dataset) ChunkIteratorWithContext(ctx context.Context) (*ChunkIterator, error)
ChunkIteratorWithContext returns an iterator with context support for cancellation. The context is checked before each Next() call, allowing graceful cancellation.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
iter, err := dataset.ChunkIteratorWithContext(ctx)
for iter.Next() {
// Process chunk...
}
func (*Dataset) ListAttributes ¶
ListAttributes returns the names of all attributes attached to this dataset.
func (*Dataset) Read ¶
Read reads the dataset values and returns them as float64 array. Currently supports float64, float32, int32, int64 datatypes. All values are converted to float64 for convenience.
func (*Dataset) ReadAttribute ¶
ReadAttribute reads a single attribute by name.
func (*Dataset) ReadCompound ¶
func (d *Dataset) ReadCompound() ([]core.CompoundValue, error)
ReadCompound reads compound dataset values and returns them as array of maps. Each map represents one compound structure instance with field names as keys. Supports nested compound types, numeric types, and fixed-length strings.
func (*Dataset) ReadHyperslab ¶
func (d *Dataset) ReadHyperslab(selection *HyperslabSelection) (interface{}, error)
ReadHyperslab reads data with full hyperslab parameters including stride and block. This provides complete control over the selection pattern, allowing strided and blocked selections.
Parameters:
- selection: The hyperslab selection specification
The selection is validated against the dataset's dimensions before reading.
Example (read every 2nd element in 2D):
sel := &HyperslabSelection{
Start: []uint64{100, 200},
Count: []uint64{25, 25}, // 25 blocks
Stride: []uint64{2, 2}, // Every 2nd element
Block: []uint64{1, 1}, // 1x1 blocks
}
data, err := dataset.ReadHyperslab(sel)
Returns:
- interface{}: The selected data in the dataset's native type
- error: Error if selection is invalid or reading fails
func (*Dataset) ReadSlice ¶
ReadSlice reads a rectangular block from the dataset using simple start/count parameters. This is a convenience method for the common case of reading a contiguous rectangular region.
Parameters:
- start: Starting coordinates in each dimension (0-based)
- count: Number of elements to read in each dimension
The number of dimensions in start and count must match the dataset's dimensionality.
Example (2D dataset):
// Read 50x50 block starting at position (100, 200)
data, err := dataset.ReadSlice([]uint64{100, 200}, []uint64{50, 50})
Returns:
- interface{}: The selected data in the dataset's native type ([]float64, []int32, etc.)
- error: Error if selection is invalid or reading fails
func (*Dataset) ReadStrings ¶
ReadStrings reads string dataset values and returns them as string array. Supports fixed-length strings (null-terminated, null-padded, space-padded). Variable-length strings are not yet supported.
func (*Dataset) ReadVLenBytes ¶
ReadVLenBytes reads a variable-length dataset and returns values as [][]byte. Each element in the outer slice corresponds to one dataset element; each inner slice contains the raw bytes of that variable-length sequence.
This works for any VLen datatype (VLenUint8, VLenInt32, VLenString, etc.). For typed sequences the caller must interpret the returned bytes according to the base element type and byte order.
type DatasetOption ¶
type DatasetOption func(*datasetConfig)
DatasetOption is a functional option for customizing dataset creation.
func WithArrayDims ¶
func WithArrayDims(dims []uint64) DatasetOption
WithArrayDims sets the dimensions for Array datatypes. This is required when creating an Array dataset.
Array datatypes are fixed-size collections of a base type. The dimensions specify the shape of each array element.
Example:
// Dataset of shape [10] where each element is [3]int32
ds, _ := fw.CreateDataset("/vectors", hdf5.ArrayInt32, []uint64{10}, hdf5.WithArrayDims([]uint64{3}))
// Dataset of shape [5] where each element is [2][3]float64 (2D array)
ds, _ := fw.CreateDataset("/matrices", hdf5.ArrayFloat64, []uint64{5}, hdf5.WithArrayDims([]uint64{2, 3}))
func WithChunkDims ¶
func WithChunkDims(dims []uint64) DatasetOption
WithChunkDims enables chunked storage with specified chunk dimensions. When specified, the dataset will use chunked layout instead of contiguous.
Chunk dimensions must match dataset rank and be > 0 in all dimensions. Chunks should be chosen for optimal I/O patterns (typical: 10KB-1MB per chunk).
Example:
// 2D dataset 1000x2000, chunked as 100x200
ds, _ := fw.CreateDataset("/data", hdf5.Float64, []uint64{1000, 2000}, hdf5.WithChunkDims([]uint64{100, 200}))
func WithEnumValues ¶
func WithEnumValues(names []string, values []int64) DatasetOption
WithEnumValues sets the name-value mappings for Enum datatypes. This is required when creating an Enum dataset.
Enum datatypes map integer values to symbolic names. Both names and values slices must have the same length.
Example:
// Create enum for days of week (0=Monday, 1=Tuesday, etc.)
names := []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}
values := []int64{0, 1, 2, 3, 4, 5, 6}
ds, _ := fw.CreateDataset("/days", hdf5.EnumInt8, []uint64{100}, hdf5.WithEnumValues(names, values))
func WithFletcher32 ¶
func WithFletcher32() DatasetOption
WithFletcher32 enables Fletcher32 checksum for data integrity verification. This option is only valid for chunked datasets (requires WithChunkDims).
The Fletcher32 filter adds a 4-byte checksum to each chunk, allowing detection of data corruption during storage or transmission.
Overhead:
- Storage: +4 bytes per chunk (minimal)
- CPU: Low (faster than CRC32)
Use when:
- Data integrity is critical
- Detecting corruption is more important than preventing it
- Working with unreliable storage or network
Example:
// Create dataset with compression and checksum
ds, _ := fw.CreateDataset("/data", hdf5.Int32, []uint64{1000},
hdf5.WithChunkDims([]uint64{100}),
hdf5.WithGZIPCompression(6),
hdf5.WithFletcher32())
func WithGZIPCompression ¶
func WithGZIPCompression(level int) DatasetOption
WithGZIPCompression enables GZIP compression with specified level (1-9). This option is only valid for chunked datasets (requires WithChunkDims).
Compression levels:
1 = fastest compression, larger files 6 = balanced (default if invalid level) 9 = best compression, slower
GZIP compression reduces storage size but adds CPU overhead during read/write. Best used with repetitive or structured data.
Example:
// Create compressed dataset with level 6 compression
ds, _ := fw.CreateDataset("/data", hdf5.Int32, []uint64{1000},
hdf5.WithChunkDims([]uint64{100}),
hdf5.WithGZIPCompression(6))
func WithMaxDims ¶
func WithMaxDims(maxDims []uint64) DatasetOption
WithMaxDims sets maximum dimensions for resizable datasets. Use hdf5.Unlimited (0xFFFFFFFFFFFFFFFF) for unlimited dimensions. Requires chunked layout (use WithChunkDims).
The maxDims slice must have the same length as the dataset dimensions. Each maxDim value must be >= the corresponding dimension, or Unlimited.
Example:
// 1D dataset with unlimited dimension
ds, _ := fw.CreateDataset("/data", hdf5.Float64, []uint64{10},
hdf5.WithChunkDims([]uint64{5}),
hdf5.WithMaxDims([]uint64{hdf5.Unlimited}))
// 2D dataset with one unlimited dimension
ds2, _ := fw.CreateDataset("/matrix", hdf5.Float64, []uint64{10, 20},
hdf5.WithChunkDims([]uint64{5, 10}),
hdf5.WithMaxDims([]uint64{hdf5.Unlimited, 20})) // Rows unlimited, cols fixed
func WithOpaqueTag ¶
func WithOpaqueTag(tag string, size uint32) DatasetOption
WithOpaqueTag sets the tag and size for Opaque datatypes. This is required when creating an Opaque dataset.
Opaque datatypes are uninterpreted byte sequences with a descriptive tag. The tag describes the content (e.g., "JPEG image", "binary blob"). The size specifies the number of bytes per element.
Example:
// Dataset of 10 JPEG images, each 1MB
ds, _ := fw.CreateDataset("/images", hdf5.Opaque, []uint64{10}, hdf5.WithOpaqueTag("JPEG image", 1024*1024))
func WithShuffle ¶
func WithShuffle() DatasetOption
WithShuffle enables byte shuffle filter (improves compression). This option is only valid for chunked datasets (requires WithChunkDims).
The shuffle filter reorders bytes to group similar values, significantly improving compression ratios for numeric data (typically 2-10x better).
Shuffle should be combined with compression (e.g., GZIP) to be effective. It's automatically placed before compression in the filter pipeline.
Best for:
- Integer arrays with slowly changing values
- Floating-point arrays with similar magnitudes
- Multi-dimensional arrays with spatial locality
Example:
// Create dataset with shuffle+compression for best compression
ds, _ := fw.CreateDataset("/data", hdf5.Float64, []uint64{1000},
hdf5.WithChunkDims([]uint64{100}),
hdf5.WithShuffle(),
hdf5.WithGZIPCompression(9))
func WithStringSize ¶
func WithStringSize(size uint32) DatasetOption
WithStringSize sets the fixed string size for String datasets. This is required when creating a String dataset.
Example:
ds, _ := fw.CreateDataset("/names", hdf5.String, []uint64{10}, hdf5.WithStringSize(32))
type DatasetWriter ¶
type DatasetWriter struct {
// contains filtered or unexported fields
}
DatasetWriter provides write access to a dataset.
func (*DatasetWriter) Close ¶
func (dw *DatasetWriter) Close() error
Close closes the dataset writer. For MVP, this is a no-op (no per-dataset resources to release).
func (*DatasetWriter) DeleteAttribute ¶
func (ds *DatasetWriter) DeleteAttribute(name string) error
DeleteAttribute removes an attribute by name from the dataset.
This method supports both compact and dense attribute storage: - Compact storage (0-7 attributes): Removes message from object header - Dense storage (8+ attributes): Removes from B-tree and fractal heap
Parameters:
- name: Attribute name to delete
Returns:
- error: If attribute not found or deletion fails
Reference: H5Adelete.c - H5A__delete(), H5Adense.c - H5A__dense_remove().
func (*DatasetWriter) RebalanceAttributeBTree ¶
func (ds *DatasetWriter) RebalanceAttributeBTree() error
RebalanceAttributeBTree manually triggers B-tree rebalancing for this dataset's dense attribute storage.
Use this when:
- You know this specific dataset needs rebalancing
- More efficient than RebalanceAllBTrees() for targeted optimization
- After batch deletions with rebalancing disabled
Performance (for current MVP with single-leaf B-trees):
- Instant (< 1ms) - no-op for single-leaf trees
Future (when multi-level B-trees implemented):
- Small (<1000 attrs): <10ms
- Medium (1000-10000 attrs): 10-100ms
- Large (10000+ attrs): 100ms-1s
Returns:
- error: if dataset doesn't use dense storage or rebalancing fails
Example:
fw.DisableRebalancing()
for i := 0; i < 1000; i++ {
ds.DeleteAttribute(fmt.Sprintf("temp_%d", i)) // Fast deletions
}
ds.RebalanceAttributeBTree() // Rebalance this dataset only
Reference: Similar to per-object rebalancing in HDF5 (hypothetical - not exposed in C API).
func (*DatasetWriter) Resize ¶
func (dw *DatasetWriter) Resize(newDims []uint64) error
Resize changes the dimensions of a dataset. The dataset must have been created with maxDims (using WithMaxDims option). Requires chunked layout. newDims must be <= maxDims for each dimension.
When extending (growing), new space is initialized with zeros. When shrinking, data beyond new dimensions is lost.
Example:
ds, _ := fw.CreateDataset("/data", hdf5.Float64, []uint64{10},
hdf5.WithChunkDims([]uint64{5}),
hdf5.WithMaxDims([]uint64{hdf5.Unlimited}))
ds.Resize([]uint64{20}) // Extend to 20 elements
func (*DatasetWriter) Write ¶
func (dw *DatasetWriter) Write(data interface{}) error
Write writes data to the dataset. The data must match the dataset's datatype and dimensions.
Parameters:
- data: Data to write (type must match dataset datatype)
Supported types:
- []int8, []int16, []int32, []int64
- []uint8, []uint16, []uint32, []uint64
- []float32, []float64
- []string (for fixed-length string datasets)
For multi-dimensional datasets, data should be flattened in row-major order.
Example:
// 1D dataset
ds, _ := fw.CreateDataset("/data", hdf5.Int32, []uint64{5})
ds.Write([]int32{1, 2, 3, 4, 5})
// 2D dataset (3x4 matrix)
ds2, _ := fw.CreateDataset("/matrix", hdf5.Float64, []uint64{3, 4})
// Flatten row-major: [[1,2,3,4], [5,6,7,8], [9,10,11,12]]
ds2.Write([]float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})
func (*DatasetWriter) WriteAttribute ¶
func (ds *DatasetWriter) WriteAttribute(name string, value interface{}) error
WriteAttribute writes an attribute to a dataset.
Storage strategy (automatic):
- 0-7 attributes: Compact storage (object header messages)
- 8+ attributes: Dense storage (Fractal Heap + B-tree v2)
Supported value types:
- Scalars: int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64
- Arrays: []int32, []float64, etc. (1D arrays only)
- Strings: string (fixed-length, converted to byte array)
- String arrays: []string (variable-length strings via Global Heap)
Parameters:
- name: Attribute name (ASCII, no null bytes)
- value: Attribute value (Go scalar, slice, or string)
Returns:
- error: If attribute cannot be written
Example:
ds, _ := fw.CreateDataset("/temperature", Float64, []uint64{10})
ds.WriteAttribute("units", "Celsius")
ds.WriteAttribute("sensor_id", int32(42))
ds.WriteAttribute("calibration", []float64{1.0, 0.0})
ds.WriteAttribute("topics", []string{"camera", "lidar", "imu"})
Limitations:
- No compound types
- Attributes cannot be modified after creation (write-once)
- No attribute deletion
func (*DatasetWriter) WriteRaw ¶
func (dw *DatasetWriter) WriteRaw(data []byte) error
WriteRaw writes raw bytes directly to the dataset without type conversion. This is useful for advanced use cases like compound datatypes where the user has already prepared the binary representation.
Parameters:
- data: Raw bytes to write (must match dataset size exactly)
Returns:
- error: If write fails or size mismatch
Example for compound datatype:
// Write pre-encoded compound struct data
data := []byte{/* encoded struct bytes */}
err := ds.WriteRaw(data)
type Datatype ¶
type Datatype int
Datatype represents HDF5 datatype for creating datasets.
const ( // Int8 represents 8-bit signed integer type. Int8 Datatype = iota // Int16 represents 16-bit signed integer type. Int16 // Int32 represents 32-bit signed integer type. Int32 // Int64 represents 64-bit signed integer type. Int64 // Uint8 represents 8-bit unsigned integer type. Uint8 // Uint16 represents 16-bit unsigned integer type. Uint16 // Uint32 represents 32-bit unsigned integer type. Uint32 // Uint64 represents 64-bit unsigned integer type. Uint64 // Float32 represents 32-bit floating point type. Float32 // Float64 represents 64-bit floating point type. Float64 // String represents fixed-length string type (use with WithStringSize option). String // ArrayInt8 represents array of 8-bit signed integers. ArrayInt8 Datatype = 100 + iota // ArrayInt16 represents array of 16-bit signed integers. ArrayInt16 // ArrayInt32 represents array of 32-bit signed integers. ArrayInt32 // ArrayInt64 represents array of 64-bit signed integers. ArrayInt64 // ArrayUint8 represents array of 8-bit unsigned integers. ArrayUint8 // ArrayUint16 represents array of 16-bit unsigned integers. ArrayUint16 // ArrayUint32 represents array of 32-bit unsigned integers. ArrayUint32 // ArrayUint64 represents array of 64-bit unsigned integers. ArrayUint64 // ArrayFloat32 represents array of 32-bit floating point values. ArrayFloat32 // ArrayFloat64 represents array of 64-bit floating point values. ArrayFloat64 // EnumInt8 represents enumeration based on 8-bit signed integer. EnumInt8 Datatype = 200 + iota // EnumInt16 represents enumeration based on 16-bit signed integer. EnumInt16 // EnumInt32 represents enumeration based on 32-bit signed integer. EnumInt32 // EnumInt64 represents enumeration based on 64-bit signed integer. EnumInt64 // EnumUint8 represents enumeration based on 8-bit unsigned integer. EnumUint8 // EnumUint16 represents enumeration based on 16-bit unsigned integer. EnumUint16 // EnumUint32 represents enumeration based on 32-bit unsigned integer. EnumUint32 // EnumUint64 represents enumeration based on 64-bit unsigned integer. EnumUint64 // ObjectReference represents reference to an object (group/dataset). // Value type: ObjectRef (uint64 - 8-byte object address). ObjectReference Datatype = 300 // RegionReference represents reference to a dataset region. // Value type: RegionRef ([12]byte - 8-byte object addr + 4-byte region info). RegionReference Datatype = 301 // Opaque represents opaque datatype (uninterpreted bytes with tag). // Example: JPEG image, binary blob, etc. Opaque Datatype = 400 // VLenString represents variable-length string (most common vlen type!). // Each element can have different length. // Go type: []string // Example: []string{"short", "very long string"}. VLenString Datatype = 500 // VLenInt32 represents variable-length int32 sequences (ragged arrays). // Each element can have different number of values. // Go type: [][]int32 // Example: [][]int32{{1,2}, {3,4,5}, {6}}. VLenInt32 Datatype = 501 // VLenInt64 represents variable-length int64 sequences. // Go type: [][]int64. VLenInt64 Datatype = 502 // VLenFloat32 represents variable-length float32 sequences. // Go type: [][]float32. VLenFloat32 Datatype = 503 // VLenFloat64 represents variable-length float64 sequences. // Go type: [][]float64. VLenFloat64 Datatype = 504 // VLenUint32 represents variable-length uint32 sequences. // Go type: [][]uint32. VLenUint32 Datatype = 505 // VLenUint64 represents variable-length uint64 sequences. // Go type: [][]uint64. VLenUint64 Datatype = 506 // VLenUint8 represents variable-length uint8 sequences (byte arrays). // Go type: [][]byte. VLenUint8 Datatype = 507 )
type File ¶
type File struct {
// contains filtered or unexported fields
}
File represents an open HDF5 file with its metadata and root group.
func Create ¶
func Create(filename string, mode CreateMode) (*File, error)
Create creates a new HDF5 file with a minimal structure. The file will contain:
- Superblock v2 (48 bytes at offset 0)
- Minimal root group (empty, with Link Info message)
The created file is a valid, minimal HDF5 file that can be:
- Reopened with Open() for reading
- Validated with h5dump
- Extended with groups, datasets, and attributes (in future versions)
Parameters:
- filename: Path to the file to create
- mode: Creation mode (truncate or exclusive)
Returns:
- *File: Handle to the created file (in read-only mode for MVP)
- error: If file creation or initialization fails
Example:
f, err := hdf5.Create("myfile.h5", hdf5.CreateTruncate)
if err != nil {
return err
}
defer f.Close()
For MVP (v0.11.0-beta):
- File is created but returned in read-only mode
- Write operations (datasets, groups, attributes) are not yet supported
- The returned File can only be used for reading the structure
func Open ¶
Open opens an HDF5 file for reading and returns a File handle. The file must be a valid HDF5 file with a supported format version.
func (*File) Close ¶
Close closes the HDF5 file and releases associated resources. It is safe to call Close multiple times.
func (*File) Superblock ¶
func (f *File) Superblock() *core.Superblock
Superblock returns the file's superblock metadata structure.
func (*File) SuperblockVersion ¶
SuperblockVersion returns the HDF5 superblock format version (0, 2, or 3).
type FileWriteConfig ¶
type FileWriteConfig struct {
SuperblockVersion uint8 // HDF5 superblock version (0, 2, or 3)
BTreeRebalancing bool // Enable B-tree rebalancing after deletions (default: true)
}
FileWriteConfig holds configuration for file creation.
type FileWriter ¶
type FileWriter struct {
// contains filtered or unexported fields
}
FileWriter represents an HDF5 file opened for writing. It wraps a File handle and provides write operations.
func CreateForWrite ¶
func CreateForWrite(filename string, mode CreateMode, opts ...interface{}) (*FileWriter, error)
CreateForWrite creates a new HDF5 file for writing. Unlike Create(), this keeps the file open in write mode.
Parameters:
- filename: Path to the file to create
- mode: Creation mode (truncate or exclusive)
- opts: Optional configuration (WithSuperblockVersion, etc.)
Returns:
- *FileWriter: Handle for writing datasets
- error: If creation fails
Example (default - modern format):
fw, err := hdf5.CreateForWrite("data.h5", hdf5.CreateTruncate)
if err != nil {
return err
}
defer fw.Close()
Example (legacy format for h5dump compatibility):
fw, err := hdf5.CreateForWrite("data.h5", hdf5.CreateTruncate,
hdf5.WithSuperblockVersion(core.Version0))
func OpenForWrite ¶
func OpenForWrite(filename string, mode OpenMode, opts ...WriteOption) (*FileWriter, error)
OpenForWrite opens an existing HDF5 file for modification. This function enables read-modify-write operations on existing files.
Supported operations:
- Adding attributes to datasets with existing dense storage
- Creating new datasets in existing files
- Creating new groups (when group write support is added)
Parameters:
- filename: Path to existing HDF5 file
- mode: Open mode (OpenReadOnly or OpenReadWrite)
Returns:
- *FileWriter: Handle for modifying the file
- error: If file doesn't exist or isn't a valid HDF5 file
Example:
// Reopen file to add more attributes
fw, err := hdf5.OpenForWrite("data.h5", hdf5.OpenReadWrite)
if err != nil {
return err
}
defer fw.Close()
// Open existing dataset
ds, err := fw.OpenDataset("/temperature")
if err != nil {
return err
}
// Add more attributes to existing dense storage
ds.WriteAttribute("calibration_date", "2025-11-01")
ds.WriteAttribute("sensor_location", "Lab A")
func (*FileWriter) Close ¶
func (fw *FileWriter) Close() error
Close closes the file writer and flushes all data to disk.
This method automatically stops any running incremental rebalancing goroutines, preventing goroutine leaks even if user forgets to call StopIncrementalRebalancing().
Best practice: Still call defer fw.StopIncrementalRebalancing() explicitly after EnableIncrementalRebalancing() for clarity, but Close() provides a safety net.
func (*FileWriter) CreateCompoundDataset ¶
func (fw *FileWriter) CreateCompoundDataset(name string, compoundType *core.DatatypeMessage, dims []uint64, opts ...DatasetOption) (*DatasetWriter, error)
CreateCompoundDataset creates a dataset with a compound (struct-like) datatype. This is an advanced method for creating datasets with complex structured data.
Parameters:
- name: Dataset path (e.g., "/data" or "/group/dataset")
- compoundType: Pre-configured compound datatype (use core.CreateCompoundTypeFromFields)
- dims: Dataset dimensions (e.g., []uint64{10} for 1D, []uint64{3, 4} for 2D)
- opts: Optional configuration (chunking, compression, etc.)
Returns:
- *DatasetWriter: Dataset writer for writing data with WriteRaw()
- error: If creation fails
Example:
// Define compound type: struct { int32 id; float32 value }
int32Type, _ := core.CreateBasicDatatypeMessage(core.DatatypeFixed, 4)
float32Type, _ := core.CreateBasicDatatypeMessage(core.DatatypeFloat, 4)
fields := []core.CompoundFieldDef{
{Name: "id", Offset: 0, Type: int32Type},
{Name: "value", Offset: 4, Type: float32Type},
}
compoundType, _ := core.CreateCompoundTypeFromFields(fields)
// Create dataset
fw, _ := hdf5.CreateForWrite("file.h5", hdf5.CreateTruncate)
ds, _ := fw.CreateCompoundDataset("/data", compoundType, []uint64{100})
// Write raw struct data
data := []byte{/* encoded structs */}
ds.WriteRaw(data)
Reference: H5Dcreate2.c - H5D__create(), H5Tcompound.c - compound datatype handling.
func (*FileWriter) CreateDataset ¶
func (fw *FileWriter) CreateDataset(name string, dtype Datatype, dims []uint64, opts ...DatasetOption) (*DatasetWriter, error)
CreateDataset creates a new dataset in the HDF5 file. The dataset will use contiguous storage layout.
Parameters:
- name: Dataset name (must start with "/" for root-level datasets)
- dtype: Data type (Int32, Float64, etc.)
- dims: Dimensions (e.g., []uint64{10} for 1D, []uint64{3,4} for 2D)
Returns:
- *DatasetWriter: Handle for writing data to the dataset
- error: If creation fails
Example:
// Create file
fw, _ := hdf5.CreateForWrite("data.h5", hdf5.CreateTruncate)
defer fw.Close()
// Create 1D dataset
ds, _ := fw.CreateDataset("/temperature", hdf5.Float64, []uint64{100})
// Write data
data := make([]float64, 100)
// ... fill data ...
ds.Write(data)
Limitations for MVP (v0.11.0-beta):
- Only contiguous layout (no chunking)
- No compression
- Dataset must be in root group (no nested groups yet)
- Resizable datasets require chunked layout (use WithMaxDims with WithChunkDims)
func (*FileWriter) CreateDenseGroup ¶
func (fw *FileWriter) CreateDenseGroup(name string, links map[string]string) error
CreateDenseGroup creates new dense group (HDF5 1.8+ format).
Dense groups are more efficient for large numbers of links (>8). They use fractal heap + B-tree v2 instead of symbol table.
Parameters:
- name: Group name (must start with "/")
- links: Map of link_name → target_path
Returns:
- error: Non-nil if creation fails
Example:
err := fw.CreateDenseGroup("/large_group", map[string]string{
"dataset1": "/data/dataset1",
"dataset2": "/data/dataset2",
// ... many links
})
Reference: H5Gcreate.c - H5Gcreate2().
func (*FileWriter) CreateExternalLink ¶
func (fw *FileWriter) CreateExternalLink(linkPath, fileName, objectPath string) error
CreateExternalLink creates a link to an object in another HDF5 file. The link stores the external file path and object path within that file. Both files must exist when the external link is accessed (lazy resolution).
Parameters:
- linkPath: Path where external link will be created (e.g., "/links/external1")
- fileName: External HDF5 file name (absolute or relative path)
- objectPath: Path to object within external file (e.g., "/dataset1")
Returns:
- error: if validation fails or creation fails
Examples:
fw.CreateExternalLink("/links/ext1", "other.h5", "/data/dataset1")
fw.CreateExternalLink("/links/ext2", "/absolute/path/file.h5", "/group1")
Behavior:
- Validates all paths
- Creates link message with external link type
- Stores external file name and object path
- Adds link entry in parent group's symbol table
- No file existence check (lazy resolution)
Security:
- Path traversal prevention (blocks ".." in file names)
- File path stored as-is (absolute or relative)
Limitations:
- Symbol table format only (dense groups not yet supported)
- No external link resolution yet (reading external links not implemented)
- No file caching or performance optimization
HDF5 Spec: Section IV.A.2.f "Link Message" - Type 64 (External Link) Reference: H5Lcreate_external() in H5L.c.
func (*FileWriter) CreateGroup ¶
func (fw *FileWriter) CreateGroup(path string) (*GroupWriter, error)
CreateGroup creates a new empty group in the HDF5 file. Groups organize datasets and other groups in a hierarchical structure.
This method creates an empty group using symbol table format (old HDF5 format). For groups with many links, consider using CreateDenseGroup() or CreateGroupWithLinks().
Parameters:
- path: Group path (must start with "/", e.g., "/data" or "/data/experiments")
Returns:
- *GroupWriter: Handle for writing attributes to the group
- error: If creation fails
Example:
fw, _ := hdf5.CreateForWrite("data.h5", hdf5.CreateTruncate)
defer fw.Close()
// Create root-level group
group, _ := fw.CreateGroup("/data")
group.WriteAttribute("description", "My data group")
// Create nested group
nested, _ := fw.CreateGroup("/data/experiments")
nested.WriteAttribute("MATLAB_class", "double")
Limitations for MVP (v0.11.0-beta):
- Only symbol table structure (no indexed groups)
- No link creation time tracking
- Maximum 32 entries per group (symbol table node capacity)
- Parent group must exist (create parents first)
func (*FileWriter) CreateGroupWithLinks ¶
func (fw *FileWriter) CreateGroupWithLinks(name string, links map[string]string) error
CreateGroupWithLinks creates group with automatic format selection.
This method automatically chooses the most efficient storage format:
- Symbol table (old format) for ≤8 links (compact)
- Dense format (new format) for >8 links (scalable)
This matches HDF5 1.8+ behavior: start compact, use dense when needed.
Parameters:
- name: Group name (must start with "/")
- links: Map of link_name → target_path (can be empty)
Returns:
- error: Non-nil if creation fails
Example:
// Small group (will use symbol table)
fw.CreateGroupWithLinks("/small", map[string]string{
"data1": "/dataset1",
"data2": "/dataset2",
})
// Large group (will use dense format)
largeLinks := make(map[string]string)
for i := 0; i < 100; i++ {
largeLinks[fmt.Sprintf("link%d", i)] = fmt.Sprintf("/dataset%d", i)
}
fw.CreateGroupWithLinks("/large", largeLinks)
Reference: H5Gint.c - H5G_convert_to_dense().
func (*FileWriter) CreateHardLink ¶
func (fw *FileWriter) CreateHardLink(linkPath, targetPath string) error
CreateHardLink creates a hard link to an existing object.
Hard links are additional names for the same object. All hard links point to the same object header address. When one link is modified, changes are visible through all other links because they share the same data.
Parameters:
- linkPath: Path where the new link will be created (e.g., "/group1/link_name")
- targetPath: Path to the existing object to link to (e.g., "/group2/dataset1")
Returns:
- error: Non-nil if link creation fails
Behavior:
- Validates both paths exist and are properly formatted
- Looks up target object's header address
- Increments reference count on target object header
- Creates link entry in parent group pointing to target address
- Supports linking datasets and groups
- Works with both symbol table and dense group formats
Example:
fw, _ := hdf5.CreateForWrite("data.h5", hdf5.CreateTruncate)
defer fw.Close()
// Create dataset
fw.CreateDataset("/data/temperature", []float64{1.0, 2.0, 3.0})
// Create hard link to dataset
err := fw.CreateHardLink("/data/temp_link", "/data/temperature")
if err != nil {
log.Fatal(err)
}
// Now /data/temperature and /data/temp_link point to the same dataset
Limitations (MVP v0.11.5-beta):
- Target must exist before creating link
- Parent group must exist before creating link
- Reference count stored in object header (v1) or RefCount message (v2)
- No link deletion support yet (DeleteLink not implemented)
- No circular link detection
Reference: H5L.c - H5Lcreate_hard().
func (*FileWriter) CreateSoftLink ¶
func (fw *FileWriter) CreateSoftLink(linkPath, targetPath string) error
CreateSoftLink creates a symbolic link to a path within the HDF5 file.
Soft links (symbolic links) store a path string that is resolved when accessed. Unlike hard links, soft links do not increment reference counts and can point to objects that don't exist yet (dangling links are allowed).
Parameters:
- linkPath: Path where the soft link will be created (e.g., "/group1/link_to_dataset")
- targetPath: Target path within file (e.g., "/group2/dataset1")
Returns:
- error: Non-nil if link creation fails
Behavior:
- Validates linkPath format (must be absolute path)
- Target path does NOT need to exist (dangling links allowed)
- Creates link message with soft link type
- Adds link entry in parent group's symbol table
- Link stores target path as string (not object address)
- When accessed, target path is resolved dynamically
Example:
fw, _ := hdf5.CreateForWrite("data.h5", hdf5.CreateTruncate)
defer fw.Close()
// Create dataset
fw.CreateDataset("/data/temperature", []float64{1.0, 2.0, 3.0})
// Create soft link (target exists)
err := fw.CreateSoftLink("/links/temp_link", "/data/temperature")
if err != nil {
log.Fatal(err)
}
// Create dangling link (target doesn't exist yet)
err = fw.CreateSoftLink("/links/future_link", "/data/future_dataset")
// This is allowed - target can be created later
Limitations:
- Symbol table format only (dense groups not yet supported)
- No soft link resolution yet (reading soft links not implemented)
- No circular link detection
HDF5 Spec: Section IV.A.2.f "Link Message" - Type 1 (Soft Link) Reference: H5L.c - H5Lcreate_soft().
func (*FileWriter) Delete ¶
func (fw *FileWriter) Delete(path string) error
Delete removes an object (dataset or empty group) from the HDF5 file.
This performs a full deletion:
- Unlinks the object from its parent group's symbol table
- Decrements the object's reference count (hard link count)
- If refcount reaches 0, performs cascade delete: - Frees contiguous data blocks - Frees chunked data blocks (walks chunk B-tree) - Frees the object header itself
Constraints:
- Cannot delete the root group "/"
- Cannot delete non-empty groups (delete children first)
- Path must start with "/"
- Object must exist
Parameters:
- path: Absolute path to the object (e.g., "/dataset1", "/group1/data")
Returns:
- error: If deletion fails
Example:
fw, _ := hdf5.OpenForWrite("data.h5", hdf5.OpenReadWrite)
defer fw.Close()
fw.Delete("/old_dataset") // Remove a dataset
fw.Delete("/empty_group") // Remove an empty group
Reference: H5Ldelete.c, H5G_obj_remove(), H5O_link(adjust=-1), H5O_delete().
func (*FileWriter) DisableLazyRebalancing ¶
func (fw *FileWriter) DisableLazyRebalancing() error
DisableLazyRebalancing disables lazy rebalancing and triggers final batch rebalancing.
This ensures all pending deletions are properly rebalanced before continuing.
Returns:
- error: if final rebalancing fails
func (*FileWriter) DisableRebalancing ¶
func (fw *FileWriter) DisableRebalancing()
DisableRebalancing temporarily disables B-tree rebalancing.
Use this to improve performance during batch delete operations. The B-tree may become sparse, but deletions will be faster.
Important: Call EnableRebalancing() when done, or RebalanceNow() to manually rebalance the tree.
Example - Batch deletions:
fw.DisableRebalancing()
for i := 0; i < 100; i++ {
ds.DeleteAttribute(fmt.Sprintf("temp_%d", i))
}
fw.EnableRebalancing()
fw.RebalanceNow() // Optional: manually rebalance
func (*FileWriter) EnableIncrementalRebalancing ¶
func (fw *FileWriter) EnableIncrementalRebalancing(config structures.IncrementalRebalancingConfig) error
EnableIncrementalRebalancing enables incremental background rebalancing for all B-trees.
This starts a background goroutine that performs rebalancing in small time slices, ensuring ZERO user-visible pause even for TB-scale datasets.
**CRITICAL: Resource Management**
- Background goroutine runs until StopIncrementalRebalancing() called
- ALWAYS call Stop() or defer it after Enable()
- Failure to stop will leak goroutine!
**Prerequisites**:
- Lazy rebalancing must be enabled first (EnableLazyRebalancing)
- Incremental is built on top of lazy mode
**Use Cases**:
- Files > 10GB
- Real-time scientific data processing
- Interactive applications (no freezing!)
- TB-scale workflows
Parameters:
- config: incremental rebalancing configuration
Returns:
- error: if lazy mode not enabled or already running
Example:
// Enable lazy first (required)
fw.EnableLazyRebalancing(structures.DefaultLazyConfig())
// Then enable incremental (zero-wait!)
config := structures.DefaultIncrementalConfig()
config.ProgressCallback = func(p structures.RebalancingProgress) {
log.Printf("Rebalancing: %d nodes done, %d remaining, ETA: %v",
p.NodesRebalanced, p.NodesRemaining, p.EstimatedRemaining)
}
fw.EnableIncrementalRebalancing(config)
defer fw.StopIncrementalRebalancing() // CRITICAL!
// Delete millions of attributes - no pause!
for i := 0; i < 10000000; i++ {
ds.DeleteAttribute(fmt.Sprintf("data_%d", i))
}
// Rebalancing happens in background, user sees no pause!
func (*FileWriter) EnableLazyRebalancing ¶
func (fw *FileWriter) EnableLazyRebalancing(config structures.LazyRebalancingConfig) error
EnableLazyRebalancing enables lazy rebalancing mode for all B-trees in the file.
Lazy rebalancing accumulates deletions and triggers batch rebalancing only when needed. This provides 10-100x performance improvement for deletion-heavy workloads.
**IMPORTANT: Use at your own risk!**
- This is an advanced performance optimization
- User must understand tradeoffs (temporary suboptimal tree structure)
- Data integrity is always preserved
When to use:
- Deleting thousands of attributes from large files (>1GB)
- Batch deletion workflows
- Scientific data processing pipelines
When NOT to use:
- Small files (<100MB) - immediate rebalancing is fast enough
- Read-heavy workloads - suboptimal tree structure may slow reads
- If unsure - use immediate rebalancing (default)
Parameters:
- config: lazy rebalancing configuration
Returns:
- error: if configuration invalid or not supported
Example:
config := structures.DefaultLazyConfig() config.Threshold = 0.05 // Trigger at 5% underflow fw.EnableLazyRebalancing(config)
See docs/guides/PERFORMANCE.md for tuning guidelines.
func (*FileWriter) EnableRebalancing ¶
func (fw *FileWriter) EnableRebalancing()
EnableRebalancing re-enables B-tree rebalancing after being disabled.
This restores the default behavior where deletions automatically trigger B-tree node merging and redistribution.
Example:
fw.DisableRebalancing() // ... batch operations ... fw.EnableRebalancing()
func (*FileWriter) ForceBatchRebalance ¶
func (fw *FileWriter) ForceBatchRebalance() error
ForceBatchRebalance manually triggers batch rebalancing on all B-trees.
This is useful when:
- User wants to optimize tree structure before critical read operations
- Periodic maintenance (e.g., hourly)
- Before closing file
**Safe to call anytime** - will only rebalance if lazy mode enabled.
Returns:
- error: if rebalancing fails
Example:
// Delete millions of attributes
for i := 0; i < 1000000; i++ {
ds.DeleteAttribute(fmt.Sprintf("data_%d", i))
}
// Optimize tree before reads
fw.ForceBatchRebalance()
func (*FileWriter) GetIncrementalRebalancingProgress ¶
func (fw *FileWriter) GetIncrementalRebalancingProgress() (structures.RebalancingProgress, error)
GetIncrementalRebalancingProgress returns progress information for background rebalancing.
Returns:
- progress: aggregated progress across all B-trees
- error: if incremental rebalancing not enabled
Example:
progress, err := fw.GetIncrementalRebalancingProgress()
if err == nil {
fmt.Printf("Rebalanced: %d, Remaining: %d, ETA: %v\n",
progress.NodesRebalanced, progress.NodesRemaining,
progress.EstimatedRemaining)
}
func (*FileWriter) GetLazyRebalancingStats ¶
func (fw *FileWriter) GetLazyRebalancingStats() (totalUnderflow, totalPending int, oldestRebalance time.Duration)
GetLazyRebalancingStats returns statistics about lazy rebalancing across all B-trees.
Returns:
- totalUnderflow: total number of underflow nodes across all B-trees
- totalPending: total pending deletions across all B-trees
- oldestRebalance: time since oldest rebalancing across all B-trees
func (*FileWriter) IsIncrementalRebalancingEnabled ¶
func (fw *FileWriter) IsIncrementalRebalancingEnabled() bool
IsIncrementalRebalancingEnabled checks if incremental rebalancing is active.
Returns:
- bool: true if any B-tree has incremental rebalancing enabled
func (*FileWriter) IsLazyRebalancingEnabled ¶
func (fw *FileWriter) IsLazyRebalancingEnabled() bool
IsLazyRebalancingEnabled checks if lazy rebalancing is enabled.
Returns:
- bool: true if any B-tree has lazy rebalancing enabled
func (*FileWriter) OpenDataset ¶
func (fw *FileWriter) OpenDataset(path string) (*DatasetWriter, error)
OpenDataset opens an existing dataset for modification. This enables read-modify-write operations on datasets.
Supported operations:
- WriteAttribute(): Add attributes to existing dense storage
- Write(): Overwrite dataset data (for contiguous layout)
Parameters:
- path: Dataset path (e.g., "/temperature")
Returns:
- *DatasetWriter: Handle for modifying the dataset
- error: If dataset doesn't exist
Example:
fw, _ := hdf5.OpenForWrite("data.h5", hdf5.OpenReadWrite)
defer fw.Close()
ds, _ := fw.OpenDataset("/temperature")
ds.WriteAttribute("units", "Celsius") // Works with existing dense storage!
func (*FileWriter) RebalanceAllBTrees ¶
func (fw *FileWriter) RebalanceAllBTrees() error
RebalanceAllBTrees manually triggers B-tree rebalancing for all datasets with dense attribute storage.
Use cases:
- After batch deletions with rebalancing disabled (performance optimization)
- Periodic maintenance to optimize sparse B-trees
- Before closing file to ensure optimal structure
Performance (for current MVP with single-leaf B-trees):
- Small files (<10 datasets): <1ms (instant)
- Medium files (10-100 datasets): 1-10ms
- Large files (100+ datasets): 10-100ms
Future (when multi-level B-trees implemented):
- Small datasets (<1000 attrs): <10ms per dataset
- Medium datasets (1000-10000 attrs): 10-100ms per dataset
- Large datasets (10000+ attrs): 100ms-1s per dataset
Note: This operation is I/O bound (reads/writes B-tree nodes to disk). For gigabyte-scale data, consider running during off-peak hours.
Example:
fw.DisableRebalancing()
for i := 0; i < 10000; i++ {
ds.DeleteAttribute(fmt.Sprintf("attr_%d", i)) // Fast, no rebalancing
}
fw.RebalanceAllBTrees() // Rebalance once at end
Returns:
- error: if rebalancing fails for any dataset
func (*FileWriter) RebalancingEnabled ¶
func (fw *FileWriter) RebalancingEnabled() bool
RebalancingEnabled returns true if B-tree rebalancing is currently enabled.
This can be used to check the current rebalancing state.
Returns:
- bool: true if rebalancing is enabled, false otherwise
func (*FileWriter) StopIncrementalRebalancing ¶
func (fw *FileWriter) StopIncrementalRebalancing() error
StopIncrementalRebalancing stops all background rebalancing goroutines.
This method:
- Stops all background goroutines
- Waits for them to finish current session
- Performs final rebalancing of remaining nodes
- Cleans up resources
**CRITICAL**: Always call this before closing the file!
Returns:
- error: if final rebalancing fails
Example:
fw.EnableIncrementalRebalancing(config) defer fw.StopIncrementalRebalancing() // Ensures cleanup
type FileWriterOption ¶
type FileWriterOption func(*FileWriter) error
FileWriterOption configures a FileWriter during creation. This follows the Functional Options Pattern (Go standard 2025).
Example:
fw := hdf5.CreateForWrite("data.h5", hdf5.CreateTruncate,
hdf5.WithLazyRebalancing(
hdf5.LazyThreshold(0.05),
),
)
func WithIncrementalRebalancing ¶
func WithIncrementalRebalancing(opts ...IncrementalOption) FileWriterOption
WithIncrementalRebalancing enables incremental (background) rebalancing mode.
Incremental rebalancing processes underflow nodes in the background using a goroutine with time budgets. This provides ZERO user-visible pause for TB-scale scientific data.
IMPORTANT: Requires lazy rebalancing to be enabled first (prerequisite).
Default configuration if no options provided:
- Budget: 100ms per session
- Interval: 5 seconds between sessions
- ProgressCallback: nil
Example:
fw := hdf5.CreateForWrite("data.h5", hdf5.CreateTruncate,
hdf5.WithLazyRebalancing(), // Prerequisite!
hdf5.WithIncrementalRebalancing(
hdf5.IncrementalBudget(100*time.Millisecond),
hdf5.IncrementalInterval(5*time.Second),
),
)
defer fw.Close() // Automatically stops background goroutine
Reference: docs/dev/BTREE_PERFORMANCE_ANALYSIS.md lines 397-446.
func WithLazyRebalancing ¶
func WithLazyRebalancing(opts ...LazyOption) FileWriterOption
WithLazyRebalancing enables lazy (batch) rebalancing mode.
Lazy rebalancing accumulates deletions and triggers batch rebalancing when a threshold is reached. This is 10-100x faster than immediate rebalancing for deletion-heavy workloads.
Default configuration if no options provided:
- Threshold: 0.05 (5% underflow)
- MaxDelay: 5 minutes
- BatchSize: 100 nodes
Example:
fw := hdf5.CreateForWrite("data.h5", hdf5.CreateTruncate,
hdf5.WithLazyRebalancing(
hdf5.LazyThreshold(0.05),
hdf5.LazyMaxDelay(5*time.Minute),
),
)
Reference: docs/dev/BTREE_PERFORMANCE_ANALYSIS.md.
func WithSmartRebalancing ¶
func WithSmartRebalancing(opts ...SmartOption) FileWriterOption
WithSmartRebalancing enables smart (auto-tuning) rebalancing mode.
Smart rebalancing automatically detects workload patterns and selects the optimal rebalancing mode (none, lazy, or incremental) based on:
- File size
- Operation patterns (delete ratio, batch size)
- Resource constraints (CPU, memory limits)
This is the "auto-pilot" mode for scientific data workflows.
IMPORTANT: This is OPTIONAL and must be explicitly enabled. By default (no options), NO rebalancing is performed (like C library).
Example:
fw := hdf5.CreateForWrite("data.h5", hdf5.CreateTruncate,
hdf5.WithSmartRebalancing(
hdf5.SmartAutoDetect(true),
hdf5.SmartAutoSwitch(true),
hdf5.SmartAllowedModes("lazy", "incremental"),
),
)
Reference: Phase 3 design (2025 best practices).
type Group ¶
type Group struct {
// contains filtered or unexported fields
}
Group represents an HDF5 group that can contain other groups and datasets.
func (*Group) Attributes ¶
Attributes returns all attributes attached to this group. Note: For groups loaded via traditional format (SNOD), the address may be 0, and attributes cannot be retrieved (traditional format doesn't have attributes).
type GroupMetadata ¶
type GroupMetadata struct {
// contains filtered or unexported fields
}
GroupMetadata stores metadata for a group (symbol table format). Used for tracking non-root groups to enable nested dataset creation.
type GroupWriter ¶
type GroupWriter struct {
// contains filtered or unexported fields
}
GroupWriter represents an HDF5 group opened for writing. Groups organize datasets and other groups in a hierarchical structure.
This type enables writing attributes to groups, similar to datasets. It provides a clean, object-oriented API consistent with DatasetWriter.
Example:
fw, _ := hdf5.CreateForWrite("data.h5", hdf5.CreateTruncate)
defer fw.Close()
// Create group
group, _ := fw.CreateGroup("/mygroup")
// Write attributes to group
group.WriteAttribute("description", "My data group")
group.WriteAttribute("version", int32(1))
Note: This is a write-only handle. For reading group contents, use the file-level Walk() or Group() methods after reopening the file.
func (*GroupWriter) DeleteAttribute ¶
func (g *GroupWriter) DeleteAttribute(name string) error
DeleteAttribute removes an attribute by name from this group.
This method supports both compact and dense attribute storage:
- Compact storage (0-7 attributes): Removes message from object header
- Dense storage (8+ attributes): Removes from B-tree and fractal heap
Parameters:
- name: Attribute name to delete
Returns:
- error: If attribute not found or deletion fails
Example:
group, _ := fw.CreateGroup("/mygroup")
group.WriteAttribute("temp", int32(42))
group.DeleteAttribute("temp") // Remove attribute
Reference: H5Adelete.c - H5A__delete().
func (*GroupWriter) Path ¶
func (g *GroupWriter) Path() string
Path returns the full path of this group.
This can be used to display the group's location in the file hierarchy or for debugging purposes.
Returns:
- string: The group's path (e.g., "/mygroup" or "/data/experiments")
Example:
group, _ := fw.CreateGroup("/mygroup")
fmt.Println(group.Path()) // Output: /mygroup
func (*GroupWriter) WriteAttribute ¶
func (g *GroupWriter) WriteAttribute(name string, value interface{}) error
WriteAttribute writes an attribute to this group.
Storage strategy (automatic):
- 0-7 attributes: Compact storage (object header messages)
- 8+ attributes: Dense storage (Fractal Heap + B-tree v2)
Supported value types:
- Scalars: int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64
- Arrays: []int32, []float64, etc. (1D arrays only)
- Strings: string (fixed-length, converted to byte array)
- String arrays: []string (variable-length strings via Global Heap)
Parameters:
- name: Attribute name (ASCII, no null bytes)
- value: Attribute value (Go scalar, slice, or string)
Returns:
- error: If attribute cannot be written
Example:
group, _ := fw.CreateGroup("/mygroup")
group.WriteAttribute("MATLAB_class", "double")
group.WriteAttribute("MATLAB_complex", uint8(1))
group.WriteAttribute("description", "Temperature measurements")
group.WriteAttribute("topics", []string{"camera", "lidar", "imu"})
Limitations:
- No compound types
- Attributes cannot be modified after creation (write-once)
- No attribute deletion
type HeapID ¶
type HeapID struct {
CollectionAddress uint64
ObjectIndex uint16
SeqLen uint32 // Number of elements in the VLen sequence
}
HeapID identifies a global heap object (collection address + object index). On-disk VLen format (C ref: H5Tvlen.c:300, H5Tvlen.c:876):
seq_len (4 bytes) + heap_address (8 bytes) + object_index (4 bytes) = 16 bytes
SeqLen is the number of elements in the variable-length sequence. For VLen strings, SeqLen = string length in bytes (characters). For VLen sequences (e.g., []int32), SeqLen = number of elements.
func (HeapID) Encode ¶
Encode encodes a heap ID to 16 bytes (HDF5 vlen on-disk format). Format (C ref: H5Tvlen.c:876, H5Tvlen.c:300):
Bytes 0-3: seq_len (uint32 LE) — number of elements in sequence Bytes 4-11: heap_address (uint64 LE) — global heap collection address Bytes 12-15: object_index (uint32 LE) — index within the collection
type HyperslabSelection ¶
type HyperslabSelection struct {
Start []uint64
Count []uint64
Stride []uint64 // nil means all 1s (contiguous selection)
Block []uint64 // nil means all 1s (single element blocks)
}
HyperslabSelection represents a rectangular selection in N-dimensional space. It follows the HDF5 hyperslab specification with start, count, stride, and block parameters.
Parameters:
- Start: Starting coordinates in each dimension (0-based indexing)
- Count: Number of blocks to select in each dimension
- Stride: Step between blocks in each dimension (nil = default to all 1s)
- Block: Size of each block in each dimension (nil = default to all 1s)
The total number of elements selected is: product(Count[i] * Block[i]) for all dimensions.
Example 1 - Simple slice (start=100, count=50 in 1D array):
sel := &HyperslabSelection{
Start: []uint64{100},
Count: []uint64{50},
}
Example 2 - Strided selection (every 2nd element):
sel := &HyperslabSelection{
Start: []uint64{0, 0},
Count: []uint64{25, 25}, // 25 blocks in each dimension
Stride: []uint64{2, 2}, // Every 2nd element
Block: []uint64{1, 1}, // Each block is 1x1
}
type IncrementalOption ¶
type IncrementalOption func(*structures.IncrementalRebalancingConfig)
IncrementalOption configures incremental rebalancing behavior.
func IncrementalBudget ¶
func IncrementalBudget(budget time.Duration) IncrementalOption
IncrementalBudget sets the time budget per rebalancing session.
The background goroutine will rebalance for this duration, then pause.
Smaller = less CPU impact, Larger = faster rebalancing Default: 100ms
Example:
hdf5.IncrementalBudget(200*time.Millisecond) // 200ms per session
func IncrementalInterval ¶
func IncrementalInterval(interval time.Duration) IncrementalOption
IncrementalInterval sets how often to run rebalancing sessions.
Smaller = more frequent rebalancing, Larger = more batching Default: 5 seconds
Example:
hdf5.IncrementalInterval(10*time.Second) // Every 10 seconds
func IncrementalProgressCallback ¶
func IncrementalProgressCallback(callback func(structures.RebalancingProgress)) IncrementalOption
IncrementalProgressCallback sets a callback for progress updates.
The callback is called after each rebalancing session with progress info. Optional: Can be nil for no progress reporting.
Example:
hdf5.IncrementalProgressCallback(func(p structures.RebalancingProgress) {
fmt.Printf("Rebalanced: %d, Remaining: %d, ETA: %v\n",
p.NodesRebalanced, p.NodesRemaining, p.EstimatedRemaining)
})
type LazyOption ¶
type LazyOption func(*structures.LazyRebalancingConfig)
LazyOption configures lazy rebalancing behavior.
func LazyBatchSize ¶
func LazyBatchSize(size int) LazyOption
LazyBatchSize sets the number of nodes to rebalance per batch operation.
Larger batches = more work per rebalancing, but fewer total operations.
Default: 100 nodes
Example:
hdf5.LazyBatchSize(200) // Process 200 nodes per batch
func LazyMaxDelay ¶
func LazyMaxDelay(delay time.Duration) LazyOption
LazyMaxDelay sets the maximum time before forcing batch rebalancing.
Even if the threshold is not reached, rebalancing will trigger after this duration. This prevents indefinite delay in write-only workloads.
Default: 5 minutes
Example:
hdf5.LazyMaxDelay(10*time.Minute) // Force rebalance after 10 min
func LazyThreshold ¶
func LazyThreshold(threshold float64) LazyOption
LazyThreshold sets the underflow threshold for triggering batch rebalancing.
The threshold is a ratio of underflow nodes to total nodes. When (underflow_nodes / total_nodes) >= threshold, batch rebalancing triggers.
Range: 0.01 (1%) to 0.20 (20%) Default: 0.05 (5%)
Example:
hdf5.LazyThreshold(0.10) // Trigger at 10% underflow
type ModeDecision ¶
type ModeDecision struct {
SelectedMode string // Mode selected ("none", "lazy", "incremental")
Reason string // Human-readable reason
Confidence float64 // Confidence level [0, 1]
Factors map[string]float64 // Factors that influenced decision
Timestamp time.Time // When decision was made
}
ModeDecision explains why a rebalancing mode was selected.
This provides explainability for auto-tuning decisions.
type NamedDatatype ¶
type NamedDatatype struct {
// contains filtered or unexported fields
}
NamedDatatype represents an HDF5 committed (named) datatype. A named datatype is a datatype stored as a first-class object in the file, allowing it to be shared by multiple datasets.
func (*NamedDatatype) Datatype ¶
func (n *NamedDatatype) Datatype() *core.DatatypeMessage
Datatype returns the underlying datatype definition.
func (*NamedDatatype) Name ¶
func (n *NamedDatatype) Name() string
Name returns the named datatype's name.
type Object ¶
type Object interface {
Name() string
}
Object represents any HDF5 object (Group or Dataset) that can be accessed in the file structure.
type SmartOption ¶
type SmartOption func(*SmartRebalancingConfig)
SmartOption configures smart rebalancing behavior.
func SmartAllowedModes ¶
func SmartAllowedModes(modes ...string) SmartOption
SmartAllowedModes restricts which rebalancing modes can be auto-selected.
Modes: "none", "lazy", "incremental"
Example:
hdf5.SmartAllowedModes("lazy", "incremental") // Don't use "none"
func SmartAutoDetect ¶
func SmartAutoDetect(enabled bool) SmartOption
SmartAutoDetect enables automatic workload pattern detection.
func SmartAutoSwitch ¶
func SmartAutoSwitch(enabled bool) SmartOption
SmartAutoSwitch enables automatic mode switching based on detected patterns.
func SmartMinFileSize ¶
func SmartMinFileSize(size uint64) SmartOption
SmartMinFileSize sets the minimum file size for enabling auto-rebalancing.
Files smaller than this size will not trigger automatic rebalancing.
func SmartOnModeChange ¶
func SmartOnModeChange(callback func(ModeDecision)) SmartOption
SmartOnModeChange sets a callback for mode change notifications.
The callback receives a ModeDecision explaining the change.
Example:
hdf5.SmartOnModeChange(func(d hdf5.ModeDecision) {
log.Printf("Mode: %s (confidence: %.2f%%)", d.SelectedMode, d.Confidence*100)
log.Printf("Reason: %s", d.Reason)
})
type SmartRebalancingConfig ¶
type SmartRebalancingConfig struct {
// Auto-detection settings
AutoDetect bool // Detect workload patterns automatically
AutoSwitch bool // Automatically switch between modes
// Constraints
MinFileSize uint64 // Minimum file size for auto-rebalancing
AllowedModes []string // Allowed rebalancing modes
MaxCPUPercent int // Maximum CPU usage percentage
// Callbacks
OnModeChange func(decision ModeDecision) // Called when mode changes
}
SmartRebalancingConfig configures smart (auto-tuning) rebalancing.
This will be fully implemented in Phase 3.
type WriteOption ¶
type WriteOption func(*FileWriteConfig)
WriteOption is a functional option for configuring file creation.
func WithBTreeRebalancing ¶
func WithBTreeRebalancing(enable bool) WriteOption
WithBTreeRebalancing enables or disables B-tree rebalancing after deletions.
When enabled (default):
- Deleting attributes triggers B-tree node merging/redistribution
- Maintains optimal B-tree structure (nodes ≥50% full)
- Better performance for repeated deletions
- Prevents tree from becoming sparse over time
When disabled:
- Faster individual deletions (no rebalancing overhead)
- B-tree may become sparse after many deletions
- Useful for batch delete operations
Default: true (matches HDF5 C library behavior)
Example - Disable for batch deletions:
fw, err := hdf5.CreateForWrite("data.h5", hdf5.CreateTruncate,
hdf5.WithBTreeRebalancing(false))
// ... perform many deletions ...
fw.RebalanceNow() // Optional: manually rebalance at end
Example - Default behavior (rebalancing enabled):
fw, err := hdf5.CreateForWrite("data.h5", hdf5.CreateTruncate)
// Deletions automatically rebalance the tree
func WithSuperblockVersion ¶
func WithSuperblockVersion(version uint8) WriteOption
WithSuperblockVersion sets the HDF5 superblock version.
Available versions:
- SuperblockV0: Legacy format, maximum compatibility with older tools (h5dump, etc.)
- SuperblockV2: Modern format with checksums (default)
- SuperblockV3: Latest format (not yet implemented for writing)
Default: SuperblockV2 (modern format)
Example for maximum compatibility:
fw, err := hdf5.CreateForWrite("file.h5", hdf5.CreateTruncate,
hdf5.WithSuperblockVersion(hdf5.SuperblockV0))
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
dump_hdf5
command
Package main provides a command-line utility to dump HDF5 file contents.
|
Package main provides a command-line utility to dump HDF5 file contents. |
|
01-basic
command
|
|
|
02-list-objects
command
|
|
|
03-read-dataset
command
|
|
|
04-vlen-strings
command
|
|
|
05-comprehensive
command
|
|
|
06-write-dataset
command
Package main demonstrates how to create and write datasets to HDF5 files.
|
Package main demonstrates how to create and write datasets to HDF5 files. |
|
07-rebalancing/default
command
|
|
|
07-rebalancing/incremental
command
|
|
|
07-rebalancing/lazy
command
|
|
|
07-rebalancing/smart
command
|
|
|
internal
|
|
|
core
Package core provides HDF5 file format parsing and manipulation functionality.
|
Package core provides HDF5 file format parsing and manipulation functionality. |
|
rebalancing
Package rebalancing provides intelligent B-tree rebalancing strategies for HDF5 files.
|
Package rebalancing provides intelligent B-tree rebalancing strategies for HDF5 files. |
|
structures
Package structures provides parsers for HDF5 internal data structures.
|
Package structures provides parsers for HDF5 internal data structures. |
|
testing
Package testing provides test utilities for HDF5 library testing.
|
Package testing provides test utilities for HDF5 library testing. |
|
utils
Package utils provides utility functions for the HDF5 library.
|
Package utils provides utility functions for the HDF5 library. |
|
writer
Package writer provides HDF5 file writing infrastructure.
|
Package writer provides HDF5 file writing infrastructure. |