├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── cgoflags.go ├── cmd ├── test-go-cpxcmpd │ ├── main.go │ └── main_test.go ├── test-go-extend-ds │ └── main.go ├── test-go-hdf5 │ └── main.go ├── test-go-table-01-readback │ └── main.go └── test-go-table-01 │ ├── main.go │ └── main_test.go ├── errors.go ├── h5a.go ├── h5a_test.go ├── h5d.go ├── h5d_test.go ├── h5f.go ├── h5f_test.go ├── h5g.go ├── h5g_test.go ├── h5i.go ├── h5p.go ├── h5pt.go ├── h5pt_test.go ├── h5s.go ├── h5s_test.go ├── h5t.go ├── h5t_shim.go ├── h5t_test.go ├── hdf5.go └── hdf5_test.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.5 4 | - 1.6 5 | - 1.7 6 | - tip 7 | 8 | sudo: false 9 | 10 | addons: 11 | apt: 12 | packages: 13 | - libhdf5-serial-dev 14 | 15 | env: 16 | global: 17 | - GODEBUG=cgocheck=0 18 | 19 | notifications: 20 | email: 21 | recipients: 22 | - binet@cern.ch 23 | on_success: change 24 | on_failure: always 25 | 26 | script: 27 | - go get -d -t -v ./... 28 | - go install -v ./... 29 | - go test -v ./... 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright ©2013-2014 The go-hdf5 Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | * Redistributions of source code must retain the above copyright 6 | notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright 8 | notice, this list of conditions and the following disclaimer in the 9 | documentation and/or other materials provided with the distribution. 10 | * Neither the name of the go-hdf5 project nor the names of its authors and 11 | contributors may be used to endorse or promote products derived from this 12 | software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## simple makefile to log workflow 2 | .PHONY: all test clean build install 3 | 4 | GOFLAGS ?= $(GOFLAGS:) 5 | 6 | all: install test 7 | 8 | 9 | build: 10 | @go build $(GOFLAGS) ./... 11 | 12 | install: 13 | @go get $(GOFLAGS) ./... 14 | 15 | test: install 16 | @go test $(GOFLAGS) ./... 17 | 18 | bench: install 19 | @go test -bench=. $(GOFLAGS) ./... 20 | 21 | clean: 22 | @go clean $(GOFLAGS) -i ./... 23 | 24 | ## EOF 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go-hdf5 2 | ======= 3 | [![Build Status](https://secure.travis-ci.org/sbinet/go-hdf5.png)](http://travis-ci.org/sbinet/go-hdf5) 4 | [![GoDoc](https://godoc.org/github.com/sbinet/go-hdf5?status.svg)](https://godoc.org/github.com/sbinet/go-hdf5) 5 | 6 | Naive ``cgo`` bindings for the ``C-API`` of ``hdf5``. 7 | 8 | ## Status 9 | 10 | **FROZEN: this package is frozen. New developments should be requested to [gonum/hdf5](https://github.com/gonum/hdf5).** 11 | 12 | This package will stay as is to not break people relying on it. 13 | But *NO* new developments will happen. 14 | Requests for new features, bug fixes, etc... should be directed against [gonum/hdf5](https://github.com/gonum/hdf5) (but, do note that `gonum/hdf5` API is still in flux). 15 | 16 | **NOTE** that starting with Go >= 1.6, one needs to run with `GODEBUG=cgocheck=0` to disable the new stricter `CGo` rules. 17 | 18 | Documentation 19 | ------------- 20 | 21 | http://godoc.org/github.com/sbinet/go-hdf5 22 | 23 | Example 24 | ------- 25 | 26 | - Hello world example: https://github.com/sbinet/go-hdf5/blob/master/cmd/test-go-hdf5/main.go 27 | 28 | - Writing/reading an ``hdf5`` with compound data: https://github.com/sbinet/go-hdf5/blob/master/cmd/test-go-cpxcmpd/main.go 29 | 30 | Note 31 | ---- 32 | 33 | - *Only* version *1.8.x* of ``HDF5`` is supported. 34 | - In order to use ``HDF5`` functions in more than one goroutine simultaneously, you must build the HDF5 library with threading support. Many binary distributions (RHEL/centos/Fedora packages, etc.) do not have this enabled. Therefore, you must build HDF5 yourself on these systems. 35 | 36 | 37 | Known problems 38 | -------------- 39 | 40 | - the ``h5pt`` packet table interface is broken. 41 | - support for structs with slices and strings as fields is broken 42 | -------------------------------------------------------------------------------- /cgoflags.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | // #cgo LDFLAGS: -lhdf5 -lhdf5_hl 4 | // #cgo darwin CFLAGS: -I/usr/local/include 5 | // #cgo darwin LDFLAGS: -L/usr/local/lib 6 | // #cgo linux CFLAGS: -I/usr/local/include, -I/usr/lib/x86_64-linux-gnu/hdf5/serial/include 7 | // #cgo linux LDFLAGS: -L/usr/local/lib, -L/usr/lib/x86_64-linux-gnu/hdf5/serial/ 8 | // #include "hdf5.h" 9 | import "C" 10 | -------------------------------------------------------------------------------- /cmd/test-go-cpxcmpd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/sbinet/go-hdf5" 7 | ) 8 | 9 | const ( 10 | fname string = "SDScompound.h5" 11 | dsname string = "ArrayOfStructures" 12 | mbr1 string = "A_name" 13 | mbr2 string = "B_name" 14 | mbr3 string = "C_name" 15 | length uint = 10 16 | rank int = 1 17 | ) 18 | 19 | type s1Type struct { 20 | a int 21 | b float32 22 | c float64 23 | d [3]int 24 | e string 25 | } 26 | 27 | type s2Type struct { 28 | c float64 29 | a int 30 | } 31 | 32 | func main() { 33 | 34 | // initialize data 35 | // s1 := make([]s1_t, LENGTH) 36 | // for i:=0; i [%v]\n", i, p, p[0].Equal(&particles[i])) 81 | } 82 | 83 | // reset index 84 | table.CreateIndex() 85 | parts := make([]particle, nrecords) 86 | err = table.ReadPackets(0, nrecords, &parts) 87 | if err != nil { 88 | panic(err) 89 | } 90 | fmt.Printf(":: whole data: %v\n", parts) 91 | 92 | fmt.Printf(":: bye.\n") 93 | } 94 | -------------------------------------------------------------------------------- /cmd/test-go-table-01/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | hdf5 "github.com/sbinet/go-hdf5" 8 | ) 9 | 10 | const ( 11 | fname string = "ex_table_01.h5" 12 | tname string = "table" 13 | nrecords int = 8 14 | ) 15 | 16 | type particle struct { 17 | // name string `hdf5:"Name"` // FIXME(sbinet) 18 | lati int32 `hdf5:"Latitude"` 19 | longi int64 `hdf5:"Longitude"` 20 | pressure float32 `hdf5:"Pressure"` 21 | temperature float64 `hdf5:"Temperature"` 22 | // isthep []int // FIXME(sbinet) 23 | // jmohep [2][2]int64 // FIXME(sbinet) 24 | } 25 | 26 | func main() { 27 | 28 | // define an array of particles 29 | particles := []particle{ 30 | {0, 0, 0.0, 0.}, 31 | {10, 10, 1.0, 10.}, 32 | {20, 20, 2.0, 20.}, 33 | {30, 30, 3.0, 30.}, 34 | {40, 40, 4.0, 40.}, 35 | {50, 50, 5.0, 50.}, 36 | {60, 60, 6.0, 60.}, 37 | {70, 70, 7.0, 70.}, 38 | } 39 | // particles := []particle{ 40 | // {"zero", 0, 0, 0.0, 0., []int{0, 0}, [2][2]int{{0, 0}, {0, 0}}}, 41 | // {"one", 10, 10, 1.0, 10., []int{0, 0}, [2][2]int{{1, 0}, {0, 1}}}, 42 | // {"two", 20, 20, 2.0, 20., []int{0, 0}, [2][2]int{{2, 0}, {0, 2}}}, 43 | // {"three", 30, 30, 3.0, 30., []int{0, 0}, [2][2]int{{3, 0}, {0, 3}}}, 44 | // {"four", 40, 40, 4.0, 40., []int{0, 0}, [2][2]int{{4, 0}, {0, 4}}}, 45 | // {"five", 50, 50, 5.0, 50., []int{0, 0}, [2][2]int{{5, 0}, {0, 5}}}, 46 | // {"six", 60, 60, 6.0, 60., []int{0, 0}, [2][2]int{{6, 0}, {0, 6}}}, 47 | // {"seven", 70, 70, 7.0, 70., []int{0, 0}, [2][2]int{{7, 0}, {0, 7}}}, 48 | // } 49 | 50 | chunkSize := 10 51 | compress := 0 52 | 53 | // create a new file using default properties 54 | f, err := hdf5.CreateFile(fname, hdf5.F_ACC_TRUNC) 55 | if err != nil { 56 | panic(fmt.Errorf("CreateFile failed: %s", err)) 57 | } 58 | defer f.Close() 59 | fmt.Printf(":: file [%s] created (id=%d)\n", fname, f.Id()) 60 | 61 | // create a fixed-length packet table within the file 62 | table, err := f.CreateTableFrom(tname, particle{}, chunkSize, compress) 63 | if err != nil { 64 | panic(fmt.Errorf("CreateTableFrom failed: %s", err)) 65 | } 66 | defer table.Close() 67 | fmt.Printf(":: table [%s] created (id=%d)\n", tname, table.Id()) 68 | 69 | if !table.IsValid() { 70 | panic("table is invalid") 71 | } 72 | 73 | // write one packet to the packet table 74 | if err = table.Append(&particles[0]); err != nil { 75 | panic(fmt.Errorf("Append failed with single packet: %s", err)) 76 | } 77 | 78 | // write several packets 79 | parts := particles[1:] 80 | if err = table.Append(&parts); err != nil { 81 | panic(fmt.Errorf("Append failed with multiple packets: %s", err)) 82 | } 83 | 84 | // get the number of packets 85 | n, err := table.NumPackets() 86 | if err != nil { 87 | panic(fmt.Errorf("NumPackets failed: %s", err)) 88 | } 89 | fmt.Printf(":: nbr entries: %d\n", n) 90 | if n != nrecords { 91 | panic(fmt.Errorf( 92 | "Wrong number of packets reported, expected %d but got %d", 93 | nrecords, n, 94 | )) 95 | } 96 | 97 | // iterate through packets 98 | for i := 0; i != n; i++ { 99 | p := make([]particle, 1) 100 | if err := table.Next(&p); err != nil { 101 | panic(fmt.Errorf("Next failed: %s", err)) 102 | } 103 | fmt.Printf(":: data[%d]: %v\n", i, p) 104 | } 105 | 106 | // reset index 107 | table.CreateIndex() 108 | parts = make([]particle, nrecords) 109 | if err = table.ReadPackets(0, nrecords, &parts); err != nil { 110 | panic(fmt.Errorf("ReadPackets failed: %s", err)) 111 | } 112 | 113 | if !reflect.DeepEqual(parts, particles) { 114 | panic(fmt.Errorf("particles differ.\ngot= %#v\nwant=%#v\n", parts, particles)) 115 | } 116 | 117 | fmt.Printf(":: whole data: %v\n", parts) 118 | fmt.Printf(":: bye.\n") 119 | } 120 | -------------------------------------------------------------------------------- /cmd/test-go-table-01/main_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "os/exec" 7 | "testing" 8 | ) 9 | 10 | func TestTableRWCmd(t *testing.T) { 11 | const fname = "ex_table_01.h5" 12 | func() { 13 | stdout := new(bytes.Buffer) 14 | cmd := exec.Command("test-go-table-01") 15 | cmd.Stdout = stdout 16 | cmd.Stderr = stdout 17 | cmd.Stdin = os.Stdin 18 | 19 | err := cmd.Run() 20 | if err != nil { 21 | t.Fatalf("error: %v\n%s\n", err, string(stdout.Bytes())) 22 | } 23 | }() 24 | 25 | func() { 26 | stdout := new(bytes.Buffer) 27 | cmd := exec.Command("test-go-table-01-readback") 28 | cmd.Stdout = stdout 29 | cmd.Stderr = stdout 30 | cmd.Stdin = os.Stdin 31 | 32 | err := cmd.Run() 33 | if err != nil { 34 | t.Fatalf("error: %v\n%s\n", err, string(stdout.Bytes())) 35 | } 36 | }() 37 | os.Remove(fname) 38 | } 39 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | // #include "hdf5.h" 4 | // 5 | // herr_t _go_hdf5_unsilence_errors(void) { 6 | // return H5Eset_auto2(H5E_DEFAULT, (H5E_auto2_t)(H5Eprint), stderr); 7 | // } 8 | // 9 | // herr_t _go_hdf5_silence_errors(void) { 10 | // return H5Eset_auto2(H5E_DEFAULT, NULL, NULL); 11 | // } 12 | import "C" 13 | 14 | import ( 15 | "fmt" 16 | ) 17 | 18 | // DisplayErrors enables/disables HDF5's automatic error printing 19 | func DisplayErrors(on bool) error { 20 | var err error 21 | if on { 22 | err = h5err(C._go_hdf5_unsilence_errors()) 23 | } else { 24 | err = h5err(C._go_hdf5_silence_errors()) 25 | } 26 | if err != nil { 27 | return fmt.Errorf("hdf5: could not call H5E_set_auto(): %s", err) 28 | } 29 | return nil 30 | } 31 | 32 | func init() { 33 | if err := DisplayErrors(false); err != nil { 34 | panic(err) 35 | } 36 | } 37 | 38 | // EOF 39 | -------------------------------------------------------------------------------- /h5a.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | // #include "hdf5.h" 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "fmt" 10 | 11 | "reflect" 12 | "runtime" 13 | "unsafe" 14 | ) 15 | 16 | type Attribute struct { 17 | Location 18 | } 19 | 20 | func newAttribute(id C.hid_t) *Attribute { 21 | d := &Attribute{Location{Identifier{id}}} 22 | runtime.SetFinalizer(d, (*Attribute).finalizer) 23 | return d 24 | } 25 | 26 | func createAttribute(id C.hid_t, name string, dtype *Datatype, dspace *Dataspace, acpl *PropList) (*Attribute, error) { 27 | c_name := C.CString(name) 28 | defer C.free(unsafe.Pointer(c_name)) 29 | hid := C.H5Acreate2(id, c_name, dtype.id, dspace.id, acpl.id, P_DEFAULT.id) 30 | if err := checkID(hid); err != nil { 31 | return nil, err 32 | } 33 | return newAttribute(hid), nil 34 | } 35 | 36 | func openAttribute(id C.hid_t, name string) (*Attribute, error) { 37 | c_name := C.CString(name) 38 | defer C.free(unsafe.Pointer(c_name)) 39 | 40 | hid := C.H5Aopen(id, c_name, P_DEFAULT.id) 41 | if err := checkID(hid); err != nil { 42 | return nil, err 43 | } 44 | return newAttribute(hid), nil 45 | } 46 | 47 | func (s *Attribute) finalizer() { 48 | if err := s.Close(); err != nil { 49 | panic(fmt.Errorf("error closing attr: %s", err)) 50 | } 51 | } 52 | 53 | func (s *Attribute) Id() int { 54 | return int(s.id) 55 | } 56 | 57 | // Access the type of an attribute 58 | func (s *Attribute) GetType() Location { 59 | ftype := C.H5Aget_type(s.id) 60 | return Location{Identifier{ftype}} 61 | } 62 | 63 | // Close releases and terminates access to an attribute. 64 | func (s *Attribute) Close() error { 65 | if s.id == 0 { 66 | return nil 67 | } 68 | err := h5err(C.H5Aclose(s.id)) 69 | s.id = 0 70 | return err 71 | } 72 | 73 | // Space returns an identifier for a copy of the dataspace for a attribute. 74 | func (s *Attribute) Space() *Dataspace { 75 | hid := C.H5Aget_space(s.id) 76 | if int(hid) > 0 { 77 | return newDataspace(hid) 78 | } 79 | return nil 80 | } 81 | 82 | // Read reads raw data from a attribute into a buffer. 83 | func (s *Attribute) Read(data interface{}, dtype *Datatype) error { 84 | var addr unsafe.Pointer 85 | v := reflect.ValueOf(data) 86 | 87 | switch v.Kind() { 88 | 89 | case reflect.Array: 90 | addr = unsafe.Pointer(v.UnsafeAddr()) 91 | 92 | case reflect.String: 93 | str := (*reflect.StringHeader)(unsafe.Pointer(v.UnsafeAddr())) 94 | addr = unsafe.Pointer(str.Data) 95 | 96 | case reflect.Ptr: 97 | addr = unsafe.Pointer(v.Pointer()) 98 | 99 | default: 100 | addr = unsafe.Pointer(v.UnsafeAddr()) 101 | } 102 | 103 | rc := C.H5Aread(s.id, dtype.id, addr) 104 | err := h5err(rc) 105 | return err 106 | } 107 | 108 | // Write writes raw data from a buffer to an attribute. 109 | func (s *Attribute) Write(data interface{}, dtype *Datatype) error { 110 | var addr unsafe.Pointer 111 | v := reflect.Indirect(reflect.ValueOf(data)) 112 | switch v.Kind() { 113 | 114 | case reflect.Array: 115 | addr = unsafe.Pointer(v.UnsafeAddr()) 116 | 117 | case reflect.String: 118 | str := C.CString(v.Interface().(string)) 119 | defer C.free(unsafe.Pointer(str)) 120 | addr = unsafe.Pointer(&str) 121 | 122 | case reflect.Ptr: 123 | addr = unsafe.Pointer(v.Pointer()) 124 | 125 | default: 126 | addr = unsafe.Pointer(v.UnsafeAddr()) 127 | } 128 | 129 | rc := C.H5Awrite(s.id, dtype.id, addr) 130 | err := h5err(rc) 131 | return err 132 | } 133 | -------------------------------------------------------------------------------- /h5a_test.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | import ( 4 | "os" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestWriteAttribute(t *testing.T) { 10 | DisplayErrors(true) 11 | defer DisplayErrors(false) 12 | defer os.Remove(fname) 13 | 14 | f, err := CreateFile(fname, F_ACC_TRUNC) 15 | if err != nil { 16 | t.Fatalf("CreateFile failed: %s\n", err) 17 | } 18 | defer f.Close() 19 | 20 | scalar, err := CreateDataspace(S_SCALAR) 21 | if err != nil { 22 | t.Fatalf("CreateDataspace failed: %s\n", err) 23 | } 24 | defer scalar.Close() 25 | 26 | dset, err := f.CreateDataset("dset", T_NATIVE_USHORT, scalar) 27 | if err != nil { 28 | t.Fatalf("CreateDataset failed: %s\n", err) 29 | } 30 | defer dset.Close() 31 | 32 | strVal := "I am a string attribute" 33 | intVal := 42 34 | fltVal := 1.234 35 | arrVal := [3]byte{128, 0, 255} 36 | 37 | attrs := map[string]struct { 38 | Value interface{} 39 | Type reflect.Type 40 | }{ 41 | "My string attribute": {&strVal, reflect.TypeOf(strVal)}, 42 | "My int attribute": {&intVal, reflect.TypeOf(intVal)}, 43 | "My float attribute": {&fltVal, reflect.TypeOf(fltVal)}, 44 | "My array attribute": {&arrVal, reflect.TypeOf(arrVal)}, 45 | } 46 | 47 | for name, v := range attrs { 48 | dtype, err := NewDataTypeFromType(v.Type) 49 | if err != nil { 50 | t.Fatalf("NewDatatypeFromValue failed: %s\n", err) 51 | } 52 | defer dtype.Close() 53 | 54 | attr, err := dset.CreateAttribute(name, dtype, scalar) 55 | if err != nil { 56 | t.Fatalf("CreateAttribute failed: %s\n", err) 57 | } 58 | defer attr.Close() 59 | 60 | if err := attr.Write(v.Value, dtype); err != nil { 61 | t.Fatalf("Attribute write failed: %s\n", err) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /h5d.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | // #include "hdf5.h" 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "fmt" 10 | 11 | "reflect" 12 | "runtime" 13 | "unsafe" 14 | ) 15 | 16 | type Dataset struct { 17 | Location 18 | } 19 | 20 | func newDataset(id C.hid_t) *Dataset { 21 | d := &Dataset{Location{Identifier{id}}} 22 | runtime.SetFinalizer(d, (*Dataset).finalizer) 23 | return d 24 | } 25 | 26 | func createDataset(id C.hid_t, name string, dtype *Datatype, dspace *Dataspace, dcpl *PropList) (*Dataset, error) { 27 | dtype, err := dtype.Copy() // For safety 28 | if err != nil { 29 | return nil, err 30 | } 31 | c_name := C.CString(name) 32 | defer C.free(unsafe.Pointer(c_name)) 33 | hid := C.H5Dcreate2(id, c_name, dtype.id, dspace.id, P_DEFAULT.id, dcpl.id, P_DEFAULT.id) 34 | if err := checkID(hid); err != nil { 35 | return nil, err 36 | } 37 | return newDataset(hid), nil 38 | } 39 | 40 | func (s *Dataset) finalizer() { 41 | if err := s.Close(); err != nil { 42 | panic(fmt.Errorf("error closing dset: %s", err)) 43 | } 44 | } 45 | 46 | // Close releases and terminates access to a dataset. 47 | func (s *Dataset) Close() error { 48 | if s.id == 0 { 49 | return nil 50 | } 51 | err := h5err(C.H5Dclose(s.id)) 52 | s.id = 0 53 | return err 54 | } 55 | 56 | // Space returns an identifier for a copy of the dataspace for a dataset. 57 | func (s *Dataset) Space() *Dataspace { 58 | hid := C.H5Dget_space(s.id) 59 | if int(hid) > 0 { 60 | return newDataspace(hid) 61 | } 62 | return nil 63 | } 64 | 65 | // ReadSubset reads a subset of raw data from a dataset into a buffer. 66 | func (s *Dataset) ReadSubset(data interface{}, memspace, filespace *Dataspace) error { 67 | dtype, err := s.Datatype() 68 | defer dtype.Close() 69 | if err != nil { 70 | return err 71 | } 72 | 73 | var addr unsafe.Pointer 74 | v := reflect.Indirect(reflect.ValueOf(data)) 75 | 76 | switch v.Kind() { 77 | 78 | case reflect.Array: 79 | addr = unsafe.Pointer(v.UnsafeAddr()) 80 | 81 | case reflect.Slice: 82 | slice := (*reflect.SliceHeader)(unsafe.Pointer(v.UnsafeAddr())) 83 | addr = unsafe.Pointer(slice.Data) 84 | 85 | case reflect.String: 86 | str := (*reflect.StringHeader)(unsafe.Pointer(v.UnsafeAddr())) 87 | addr = unsafe.Pointer(str.Data) 88 | 89 | case reflect.Ptr: 90 | addr = unsafe.Pointer(v.Pointer()) 91 | 92 | default: 93 | addr = unsafe.Pointer(v.UnsafeAddr()) 94 | } 95 | 96 | var filespace_id, memspace_id C.hid_t = 0, 0 97 | if memspace != nil { 98 | memspace_id = memspace.id 99 | } 100 | if filespace != nil { 101 | filespace_id = filespace.id 102 | } 103 | rc := C.H5Dread(s.id, dtype.id, memspace_id, filespace_id, 0, addr) 104 | err = h5err(rc) 105 | return err 106 | } 107 | 108 | // Read reads raw data from a dataset into a buffer. 109 | func (s *Dataset) Read(data interface{}) error { 110 | return s.ReadSubset(data, nil, nil) 111 | } 112 | 113 | // WriteSubset writes a subset of raw data from a buffer to a dataset. 114 | func (s *Dataset) WriteSubset(data interface{}, memspace, filespace *Dataspace) error { 115 | dtype, err := s.Datatype() 116 | defer dtype.Close() 117 | if err != nil { 118 | return err 119 | } 120 | 121 | addr := unsafe.Pointer(nil) 122 | v := reflect.Indirect(reflect.ValueOf(data)) 123 | 124 | switch v.Kind() { 125 | 126 | case reflect.Array: 127 | addr = unsafe.Pointer(v.UnsafeAddr()) 128 | 129 | case reflect.Slice: 130 | slice := (*reflect.SliceHeader)(unsafe.Pointer(v.UnsafeAddr())) 131 | addr = unsafe.Pointer(slice.Data) 132 | 133 | case reflect.String: 134 | str := (*reflect.StringHeader)(unsafe.Pointer(v.UnsafeAddr())) 135 | addr = unsafe.Pointer(str.Data) 136 | 137 | case reflect.Ptr: 138 | addr = unsafe.Pointer(v.Pointer()) 139 | 140 | default: 141 | addr = unsafe.Pointer(v.UnsafeAddr()) 142 | } 143 | 144 | var filespace_id, memspace_id C.hid_t = 0, 0 145 | if memspace != nil { 146 | memspace_id = memspace.id 147 | } 148 | if filespace != nil { 149 | filespace_id = filespace.id 150 | } 151 | rc := C.H5Dwrite(s.id, dtype.id, memspace_id, filespace_id, 0, addr) 152 | err = h5err(rc) 153 | return err 154 | } 155 | 156 | // Write writes raw data from a buffer to a dataset. 157 | func (s *Dataset) Write(data interface{}) error { 158 | return s.WriteSubset(data, nil, nil) 159 | } 160 | 161 | // Creates a new attribute at this location. 162 | func (s *Dataset) CreateAttribute(name string, dtype *Datatype, dspace *Dataspace) (*Attribute, error) { 163 | return createAttribute(s.id, name, dtype, dspace, P_DEFAULT) 164 | } 165 | 166 | // Creates a new attribute at this location. 167 | func (s *Dataset) CreateAttributeWith(name string, dtype *Datatype, dspace *Dataspace, acpl *PropList) (*Attribute, error) { 168 | return createAttribute(s.id, name, dtype, dspace, acpl) 169 | } 170 | 171 | // Opens an existing attribute. 172 | func (s *Dataset) OpenAttribute(name string) (*Attribute, error) { 173 | return openAttribute(s.id, name) 174 | } 175 | 176 | // Datatype returns the HDF5 Datatype of the Dataset 177 | func (s *Dataset) Datatype() (*Datatype, error) { 178 | dtype_id := C.H5Dget_type(s.id) 179 | if dtype_id < 0 { 180 | return nil, fmt.Errorf("couldn't open Datatype from Dataset %q", s.Name()) 181 | } 182 | return NewDatatype(dtype_id), nil 183 | } 184 | -------------------------------------------------------------------------------- /h5d_test.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | import ( 4 | "os" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func createDataset1(t *testing.T) error { 10 | // create a file with a single 5x20 dataset 11 | f, err := CreateFile(fname, F_ACC_TRUNC) 12 | if err != nil { 13 | t.Fatalf("CreateFile failed: %s", err) 14 | return err 15 | } 16 | defer f.Close() 17 | 18 | var data [100]uint16 19 | for i := range data { 20 | data[i] = uint16(i) 21 | } 22 | 23 | dims := []uint{20, 5} 24 | dspace, err := CreateSimpleDataspace(dims, dims) 25 | if err != nil { 26 | t.Fatal(err) 27 | return err 28 | } 29 | 30 | dset, err := f.CreateDataset("dset", T_NATIVE_USHORT, dspace) 31 | if err != nil { 32 | t.Fatal(err) 33 | return err 34 | } 35 | 36 | err = dset.Write(&data[0]) 37 | if err != nil { 38 | t.Fatal(err) 39 | return err 40 | } 41 | return err 42 | } 43 | 44 | /** 45 | * TestReadSubset based on the h5_subset.c sample with the HDF5 C library. 46 | * Original copyright notice: 47 | * 48 | * HDF5 (Hierarchical Data Format 5) Software Library and Utilities 49 | * Copyright 2006-2013 by The HDF Group. 50 | * 51 | * NCSA HDF5 (Hierarchical Data Format 5) Software Library and Utilities 52 | * Copyright 1998-2006 by the Board of Trustees of the University of Illinois. 53 | **** 54 | * Write some test data then read back a subset. 55 | */ 56 | func TestReadSubset(t *testing.T) { 57 | DisplayErrors(true) 58 | defer DisplayErrors(false) 59 | defer os.Remove(fname) 60 | err := createDataset1(t) 61 | if err != nil { 62 | return 63 | } 64 | 65 | // load a subset of the data 66 | f, err := OpenFile(fname, F_ACC_RDONLY) 67 | if err != nil { 68 | t.Fatal(err) 69 | } 70 | defer f.Close() 71 | 72 | dset, err := f.OpenDataset("dset") 73 | if err != nil { 74 | t.Fatal(err) 75 | } 76 | 77 | // get the filespace and select the subset 78 | filespace := dset.Space() 79 | offset, stride, count, block := [2]uint{5, 1}, [2]uint{1, 1}, [2]uint{5, 2}, [2]uint{1, 1} 80 | err = filespace.SelectHyperslab(offset[:], stride[:], count[:], block[:]) 81 | if err != nil { 82 | t.Fatal(err) 83 | } 84 | 85 | // create the memory space for the subset 86 | dims, maxdims := [2]uint{2, 5}, [2]uint{2, 5} 87 | if err != nil { 88 | t.Fatal(err) 89 | } 90 | memspace, err := CreateSimpleDataspace(dims[:], maxdims[:]) 91 | if err != nil { 92 | t.Fatal(err) 93 | } 94 | 95 | expected := [10]uint16{26, 27, 31, 32, 36, 37, 41, 42, 46, 47} 96 | 97 | // test array 98 | { 99 | // create a buffer for the data 100 | data := [10]uint16{} 101 | 102 | // read the subset 103 | err = dset.ReadSubset(&data, memspace, filespace) 104 | if err != nil { 105 | t.Fatal(err) 106 | } 107 | 108 | if !reflect.DeepEqual(data, expected) { 109 | t.Fatalf("ReadSubset-array error\ngot= %#v\nwant=%#v\n", data, expected) 110 | } 111 | } 112 | 113 | // test slice 114 | { 115 | // create a buffer for the data 116 | data := make([]uint16, 10) 117 | 118 | // read the subset 119 | err = dset.ReadSubset(&data, memspace, filespace) 120 | if err != nil { 121 | t.Fatal(err) 122 | } 123 | 124 | if !reflect.DeepEqual(data, expected[:]) { 125 | t.Fatalf("ReadSubset-slice error\ngot= %#v\nwant=%#v\n", data, expected[:]) 126 | } 127 | } 128 | } 129 | 130 | func TestWriteSubset(t *testing.T) { 131 | DisplayErrors(true) 132 | defer DisplayErrors(false) 133 | defer os.Remove(fname) 134 | 135 | fdims := []uint{12, 4, 6} 136 | fspace, err := CreateSimpleDataspace(fdims, nil) 137 | if err != nil { 138 | t.Fatal(err) 139 | } 140 | mdims := []uint{2, 6} 141 | mspace, err := CreateSimpleDataspace(mdims, nil) 142 | if err != nil { 143 | t.Fatal(err) 144 | } 145 | 146 | f, err := CreateFile(fname, F_ACC_TRUNC) 147 | if err != nil { 148 | t.Fatalf("CreateFile failed: %s\n", err) 149 | } 150 | defer f.Close() 151 | 152 | dset, err := f.CreateDataset("dset", T_NATIVE_USHORT, fspace) 153 | if err != nil { 154 | t.Fatal(err) 155 | } 156 | 157 | offset := []uint{6, 0, 0} 158 | stride := []uint{3, 1, 1} 159 | count := []uint{mdims[0], 1, mdims[1]} 160 | if err = fspace.SelectHyperslab(offset, stride, count, nil); err != nil { 161 | t.Fatal(err) 162 | } 163 | 164 | data := make([]uint16, mdims[0]*mdims[1]) 165 | 166 | if err = dset.WriteSubset(&data, mspace, fspace); err != nil { 167 | t.Fatal(err) 168 | } 169 | } 170 | 171 | func TestSelectHyperslab(t *testing.T) { 172 | DisplayErrors(true) 173 | defer DisplayErrors(false) 174 | 175 | dims := []uint{12, 4} 176 | dspace, err := CreateSimpleDataspace(dims, nil) 177 | if err != nil { 178 | t.Fatal(err) 179 | } 180 | offset, stride, count, block := []uint{1, 2}, []uint{2, 1}, []uint{4, 2}, []uint{1, 1} 181 | if err = dspace.SelectHyperslab(offset, stride, count, block); err != nil { 182 | t.Fatal(err) 183 | } 184 | if err = dspace.SelectHyperslab(offset, nil, count, block); err != nil { 185 | t.Fatal(err) 186 | } 187 | if err = dspace.SelectHyperslab(offset, stride, count, nil); err != nil { 188 | t.Fatal(err) 189 | } 190 | if err = dspace.SelectHyperslab(offset, nil, count, nil); err != nil { 191 | t.Fatal(err) 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /h5f.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | // #include "hdf5.h" 4 | // #include "hdf5_hl.h" 5 | // #include 6 | // #include 7 | import "C" 8 | 9 | import ( 10 | "fmt" 11 | "runtime" 12 | "unsafe" 13 | ) 14 | 15 | // File constants 16 | const ( 17 | F_ACC_RDONLY int = 0x0000 // absence of rdwr => rd-only 18 | F_ACC_RDWR int = 0x0001 // open for read and write 19 | F_ACC_TRUNC int = 0x0002 // Truncate file, if it already exists, erasing all data previously stored in the file. 20 | F_ACC_EXCL int = 0x0004 // Fail if file already exists. 21 | F_ACC_DEBUG int = 0x0008 // print debug info 22 | F_ACC_CREAT int = 0x0010 // create non-existing files 23 | F_ACC_DEFAULT int = 0xffff // value passed to set_elink_acc_flags to cause flags to be taken from the parent file 24 | ) 25 | 26 | // The difference between a single file and a set of mounted files. 27 | type Scope C.H5F_scope_t 28 | 29 | const ( 30 | F_SCOPE_LOCAL Scope = 0 // specified file handle only. 31 | F_SCOPE_GLOBAL Scope = 1 // entire virtual file. 32 | ) 33 | 34 | // a HDF5 file 35 | type File struct { 36 | CommonFG 37 | } 38 | 39 | func (f *File) finalizer() { 40 | if err := f.Close(); err != nil { 41 | panic(fmt.Errorf("error closing file: %s", err)) 42 | } 43 | } 44 | 45 | func newFile(id C.hid_t) *File { 46 | f := &File{CommonFG{Location{Identifier{id}}}} 47 | runtime.SetFinalizer(f, (*File).finalizer) 48 | return f 49 | } 50 | 51 | // Creates an HDF5 file. 52 | func CreateFile(name string, flags int) (*File, error) { 53 | c_name := C.CString(name) 54 | defer C.free(unsafe.Pointer(c_name)) 55 | 56 | // FIXME: file props 57 | hid := C.H5Fcreate(c_name, C.uint(flags), P_DEFAULT.id, P_DEFAULT.id) 58 | if err := checkID(hid); err != nil { 59 | return nil, fmt.Errorf("error creating hdf5 file: %s", err) 60 | } 61 | return newFile(hid), nil 62 | } 63 | 64 | // Opens an existing HDF5 file. 65 | func OpenFile(name string, flags int) (*File, error) { 66 | c_name := C.CString(name) 67 | defer C.free(unsafe.Pointer(c_name)) 68 | 69 | // FIXME: file props 70 | hid := C.H5Fopen(c_name, C.uint(flags), P_DEFAULT.id) 71 | if err := checkID(hid); err != nil { 72 | return nil, fmt.Errorf("error opening hdf5 file: %s", err) 73 | } 74 | return newFile(hid), nil 75 | } 76 | 77 | // Returns a new identifier for a previously-opened HDF5 file. 78 | func (f *File) ReOpen() (*File, error) { 79 | hid := C.H5Freopen(f.id) 80 | if err := checkID(hid); err != nil { 81 | return nil, fmt.Errorf("error reopening hdf5 file: %s", err) 82 | } 83 | return newFile(hid), nil 84 | } 85 | 86 | // IsHDF5 Determines whether a file is in the HDF5 format. 87 | func IsHDF5(name string) bool { 88 | c_name := C.CString(name) 89 | defer C.free(unsafe.Pointer(c_name)) 90 | 91 | return C.H5Fis_hdf5(c_name) > 0 92 | } 93 | 94 | // Terminates access to an HDF5 file. 95 | func (f *File) Close() error { 96 | if f.id == 0 { 97 | return nil 98 | } 99 | err := h5err(C.H5Fclose(f.id)) 100 | f.id = 0 101 | return err 102 | } 103 | 104 | // Flushes all buffers associated with a file to disk. 105 | // herr_t H5Fflush(hid_t object_id, H5F_scope_t scope ) 106 | func (f *File) Flush(scope Scope) error { 107 | return h5err(C.H5Fflush(f.id, C.H5F_scope_t(scope))) 108 | } 109 | 110 | // FIXME 111 | // Retrieves name of file to which object belongs. 112 | // ssize_t H5Fget_name(hid_t obj_id, char *name, size_t size ) 113 | func (f *File) FileName() string { 114 | sz := int(C.H5Fget_name(f.id, nil, 0)) + 1 115 | if sz < 0 { 116 | return "" 117 | } 118 | buf := string(make([]byte, sz)) 119 | c_buf := C.CString(buf) 120 | defer C.free(unsafe.Pointer(c_buf)) 121 | sz = int(C.H5Fget_name(f.id, c_buf, C.size_t(sz))) 122 | if sz < 0 { 123 | return "" 124 | } 125 | return C.GoString(c_buf) 126 | 127 | } 128 | 129 | var cdot = C.CString(".") 130 | 131 | // Creates a packet table to store fixed-length packets. 132 | // hid_t H5PTcreate_fl( hid_t loc_id, const char * dset_name, hid_t dtype_id, hsize_t chunk_size, int compression ) 133 | func (f *File) CreateTable(name string, dtype *Datatype, chunkSize, compression int) (*Table, error) { 134 | return createTable(f.id, name, dtype, chunkSize, compression) 135 | } 136 | 137 | // Creates a packet table to store fixed-length packets. 138 | // hid_t H5PTcreate_fl( hid_t loc_id, const char * dset_name, hid_t dtype_id, hsize_t chunk_size, int compression ) 139 | func (f *File) CreateTableFrom(name string, dtype interface{}, chunkSize, compression int) (*Table, error) { 140 | return createTableFrom(f.id, name, dtype, chunkSize, compression) 141 | } 142 | 143 | // Opens an existing packet table. 144 | // hid_t H5PTopen( hid_t loc_id, const char *dset_name ) 145 | func (f *File) OpenTable(name string) (*Table, error) { 146 | return openTable(f.id, name) 147 | } 148 | -------------------------------------------------------------------------------- /h5f_test.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func TestFile(t *testing.T) { 9 | f, err := CreateFile(fname, F_ACC_TRUNC) 10 | if err != nil { 11 | t.Fatalf("CreateFile failed: %s", err) 12 | } 13 | defer os.Remove(fname) 14 | defer f.Close() 15 | 16 | if fileName := f.FileName(); fileName != fname { 17 | t.Fatalf("FileName() have %v, want %v", fileName, fname) 18 | } 19 | // The file is also the root group 20 | if name := f.Name(); name != "/" { 21 | t.Fatalf("Name() have %v, want %v", name, fname) 22 | } 23 | if err := f.Flush(F_SCOPE_GLOBAL); err != nil { 24 | t.Fatalf("Flush() failed: %s", err) 25 | } 26 | if !IsHDF5(fname) { 27 | t.Fatalf("IsHDF5 returned false") 28 | } 29 | if n, err := f.NumObjects(); err != nil { 30 | t.Fatalf("NumObjects failed: %s", err) 31 | } else if n != 0 { 32 | t.Fatalf("empty file had %d objects", n) 33 | } 34 | 35 | if _, err := f.ObjectNameByIndex(0); err == nil { 36 | t.Fatalf("expected error") 37 | } 38 | 39 | f2 := f.File() 40 | fName := f.FileName() 41 | f2Name := f2.FileName() 42 | if fName != f2Name { 43 | t.Fatalf("f2 FileName() have %v, want %v", f2Name, fName) 44 | } 45 | 46 | // Test a Group 47 | groupName := "test" 48 | g, err := f.CreateGroup(groupName) 49 | if err != nil { 50 | t.Fatalf("CreateGroup() failed: %s", err) 51 | } 52 | if name := g.Name(); name != "/"+groupName { 53 | t.Fatalf("Group Name() have %v, want /%v", name, groupName) 54 | } 55 | 56 | g2, err := f.OpenGroup(groupName) 57 | if err != nil { 58 | t.Fatalf("OpenGroup() failed: %s", err) 59 | } 60 | if name := g2.Name(); name != "/"+groupName { 61 | t.Fatalf("Group Name() have %v, want /%v", name, groupName) 62 | } 63 | 64 | if n, err := f.NumObjects(); err != nil { 65 | t.Fatalf("NumObjects failed: %s", err) 66 | } else if n != 1 { 67 | t.Fatalf("NumObjects: got %d, want %d", n, 1) 68 | } 69 | 70 | if name, err := f.ObjectNameByIndex(0); err != nil { 71 | t.Fatalf("ObjectNameByIndex failed: %s", err) 72 | } else if name != groupName { 73 | t.Fatalf("ObjectNameByIndex: got %q, want %q", name, groupName) 74 | } 75 | if _, err := f.ObjectNameByIndex(1); err == nil { 76 | t.Fatalf("expected error") 77 | } 78 | 79 | // Test a Dataset 80 | ds, err := CreateDataspace(S_SCALAR) 81 | if err != nil { 82 | t.Fatalf("CreateDataspace failed: %s", err) 83 | } 84 | dsetName := "test_dataset" 85 | dset, err := f.CreateDataset(dsetName, T_NATIVE_INT, ds) 86 | if err != nil { 87 | t.Fatalf("CreateDataset failed: %s", err) 88 | } 89 | if name := dset.Name(); name != "/"+dsetName { 90 | t.Fatalf("Dataset Name() have %v, want /%v", name, dsetName) 91 | } 92 | dFile := dset.File() 93 | if dFile.Name() != f.Name() { 94 | t.Fatalf("Dataset File() have %v, want %v", dFile.Name(), f.Name()) 95 | } 96 | 97 | if n, err := f.NumObjects(); err != nil { 98 | t.Fatalf("NumObjects failed: %s", err) 99 | } else if n != 2 { 100 | t.Fatalf("NumObjects: got %d, want %d", n, 1) 101 | } 102 | 103 | for i, n := range []string{groupName, dsetName} { 104 | if name, err := f.ObjectNameByIndex(uint(i)); err != nil { 105 | t.Fatalf("ObjectNameByIndex failed: %s", err) 106 | } else if name != n { 107 | t.Fatalf("ObjectNameByIndex: got %q, want %q", name, groupName) 108 | } 109 | } 110 | } 111 | 112 | func TestClosedFile(t *testing.T) { 113 | f, err := CreateFile(fname, F_ACC_TRUNC) 114 | if err != nil { 115 | t.Fatalf("CreateFile failed: %s", err) 116 | } 117 | fName := f.Name() 118 | f2 := f.File() 119 | f.Close() 120 | 121 | f2Name := f2.FileName() 122 | if f2Name != fname { 123 | t.Fatalf("f2 FileName() have %v, want %v", f2Name, fName) 124 | } 125 | f2.Close() 126 | 127 | os.Remove(fname) 128 | f3 := f.File() 129 | if f3 != nil { 130 | t.Fatalf("expected file to be nil") 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /h5g.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | // #include "hdf5.h" 4 | // #include "hdf5_hl.h" 5 | // #include 6 | // #include 7 | import "C" 8 | 9 | import ( 10 | "fmt" 11 | "runtime" 12 | "unsafe" 13 | ) 14 | 15 | // CommonFG is for methods common to both File and Group 16 | type CommonFG struct { 17 | Location 18 | } 19 | 20 | // Group is an HDF5 container object. It can contain any Location. 21 | type Group struct { 22 | CommonFG 23 | } 24 | 25 | // CreateGroup creates a new empty group and links it to a location in the file. 26 | func (g *CommonFG) CreateGroup(name string) (*Group, error) { 27 | c_name := C.CString(name) 28 | defer C.free(unsafe.Pointer(c_name)) 29 | 30 | link_flags := C.hid_t(C.H5P_DEFAULT) 31 | grp_c_flags := C.hid_t(C.H5P_DEFAULT) 32 | hid := C.H5Gcreate2(g.id, c_name, link_flags, grp_c_flags, P_DEFAULT.id) 33 | if err := checkID(hid); err != nil { 34 | return nil, err 35 | } 36 | group := &Group{CommonFG{Location{Identifier{hid}}}} 37 | runtime.SetFinalizer(group, (*Group).finalizer) 38 | return group, nil 39 | } 40 | 41 | // CreateDataset creates a new Dataset. 42 | func (g *CommonFG) CreateDataset(name string, dtype *Datatype, dspace *Dataspace) (*Dataset, error) { 43 | return createDataset(g.id, name, dtype, dspace, P_DEFAULT) 44 | } 45 | 46 | // CreateDatasetWith creates a new Dataset with a user-defined PropList. 47 | func (g *CommonFG) CreateDatasetWith(name string, dtype *Datatype, dspace *Dataspace, dcpl *PropList) (*Dataset, error) { 48 | return createDataset(g.id, name, dtype, dspace, dcpl) 49 | } 50 | 51 | // CreateAttribute creates a new attribute at this location. 52 | func (g *Group) CreateAttribute(name string, dtype *Datatype, dspace *Dataspace) (*Attribute, error) { 53 | return createAttribute(g.id, name, dtype, dspace, P_DEFAULT) 54 | } 55 | 56 | // CreateAttributeWith creates a new attribute at this location with a user-defined PropList. 57 | func (g *Group) CreateAttributeWith(name string, dtype *Datatype, dspace *Dataspace, acpl *PropList) (*Attribute, error) { 58 | return createAttribute(g.id, name, dtype, dspace, acpl) 59 | } 60 | 61 | func (g *Group) finalizer() { 62 | if err := g.Close(); err != nil { 63 | panic(fmt.Errorf("error closing group: %s", err)) 64 | } 65 | } 66 | 67 | // Close closes the Group. 68 | func (g *Group) Close() error { 69 | if g.id == 0 { 70 | return nil 71 | } 72 | err := h5err(C.H5Gclose(g.id)) 73 | g.id = 0 74 | return err 75 | } 76 | 77 | // OpenGroup opens an existing child group from this Group. 78 | func (g *CommonFG) OpenGroup(name string) (*Group, error) { 79 | c_name := C.CString(name) 80 | defer C.free(unsafe.Pointer(c_name)) 81 | 82 | hid := C.H5Gopen2(g.id, c_name, P_DEFAULT.id) 83 | if err := checkID(hid); err != nil { 84 | return nil, err 85 | } 86 | group := &Group{CommonFG{Location{Identifier{hid}}}} 87 | runtime.SetFinalizer(group, (*Group).finalizer) 88 | return group, nil 89 | } 90 | 91 | // OpenDataset opens a named Dataset. 92 | func (g *CommonFG) OpenDataset(name string) (*Dataset, error) { 93 | c_name := C.CString(name) 94 | defer C.free(unsafe.Pointer(c_name)) 95 | 96 | hid := C.H5Dopen2(g.id, c_name, P_DEFAULT.id) 97 | if err := checkID(hid); err != nil { 98 | return nil, err 99 | } 100 | return newDataset(hid), nil 101 | } 102 | 103 | // NumObjects returns the number of objects in the Group. 104 | func (g *CommonFG) NumObjects() (uint, error) { 105 | var info C.H5G_info_t 106 | err := h5err(C.H5Gget_info(g.id, &info)) 107 | return uint(info.nlinks), err 108 | } 109 | 110 | // ObjectNameByIndex returns the name of the object at idx. 111 | func (g *CommonFG) ObjectNameByIndex(idx uint) (string, error) { 112 | cidx := C.hsize_t(idx) 113 | size := C.H5Lget_name_by_idx(g.id, cdot, C.H5_INDEX_NAME, C.H5_ITER_INC, cidx, nil, 0, C.H5P_DEFAULT) 114 | if size < 0 { 115 | return "", fmt.Errorf("could not get name") 116 | } 117 | 118 | name := make([]C.char, size+1) 119 | size = C.H5Lget_name_by_idx(g.id, cdot, C.H5_INDEX_NAME, C.H5_ITER_INC, cidx, &name[0], C.size_t(size)+1, C.H5P_DEFAULT) 120 | 121 | if size < 0 { 122 | return "", fmt.Errorf("could not get name") 123 | } 124 | return C.GoString(&name[0]), nil 125 | } 126 | 127 | // CreateTable creates a packet table to store fixed-length packets. 128 | func (g *Group) CreateTable(name string, dtype *Datatype, chunkSize, compression int) (*Table, error) { 129 | return createTable(g.id, name, dtype, chunkSize, compression) 130 | } 131 | 132 | // CreateTableFrom creates a packet table to store fixed-length packets. 133 | func (g *Group) CreateTableFrom(name string, dtype interface{}, chunkSize, compression int) (*Table, error) { 134 | return createTableFrom(g.id, name, dtype, chunkSize, compression) 135 | } 136 | 137 | // OpenTable opens an existing packet table. 138 | func (g *Group) OpenTable(name string) (*Table, error) { 139 | return openTable(g.id, name) 140 | } 141 | -------------------------------------------------------------------------------- /h5g_test.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func TestGroup(t *testing.T) { 9 | f, err := CreateFile(fname, F_ACC_TRUNC) 10 | if err != nil { 11 | t.Fatalf("CreateFile failed: %s", err) 12 | } 13 | defer os.Remove(fname) 14 | defer f.Close() 15 | 16 | g1, err := f.CreateGroup("foo") 17 | if err != nil { 18 | t.Fatalf("couldn't create group: %s", err) 19 | } 20 | if *g1.File() != *f { 21 | t.Fatal("wrong file for group") 22 | } 23 | if g1.Name() != "/foo" { 24 | t.Errorf("wrong Name for group: want %q, got %q", "/foo", g1.Name()) 25 | } 26 | 27 | g2, err := g1.CreateGroup("bar") 28 | if err != nil { 29 | t.Fatalf("couldn't create group: %s", err) 30 | } 31 | if *g2.File() != *f { 32 | t.Fatal("wrong file for group") 33 | } 34 | if g2.Name() != "/foo/bar" { 35 | t.Errorf("wrong Name for group: want %q, got %q", "/foo/bar", g1.Name()) 36 | } 37 | 38 | g3, err := g2.CreateGroup("baz") 39 | if err != nil { 40 | t.Fatalf("couldn't create group: %s", err) 41 | } 42 | if *g3.File() != *f { 43 | t.Fatal("wrong file for group") 44 | } 45 | if g3.Name() != "/foo/bar/baz" { 46 | t.Errorf("wrong Name for group: want %q, got %q", "/foo/bar/bar", g1.Name()) 47 | } 48 | 49 | if nObjs, err := g2.NumObjects(); err != nil { 50 | t.Fatal(err) 51 | } else if nObjs != 1 { 52 | t.Errorf("wrong number of objects in group: want 1, got %d", nObjs) 53 | } 54 | 55 | err = g1.Close() 56 | if err != nil { 57 | t.Error(err) 58 | } 59 | err = g2.Close() 60 | if err != nil { 61 | t.Error(err) 62 | } 63 | err = g3.Close() 64 | if err != nil { 65 | t.Error(err) 66 | } 67 | 68 | g2, err = f.OpenGroup("/foo/bar") 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | 73 | g3, err = g2.OpenGroup("baz") 74 | if err != nil { 75 | t.Fatal(err) 76 | } 77 | 78 | _, err = g3.OpenGroup("bs") 79 | if err == nil { 80 | t.Fatal("expected error on opening invalid group") 81 | } 82 | 83 | data := 5 84 | 85 | dtype, err := NewDatatypeFromValue(data) 86 | if err != nil { 87 | t.Fatal(err) 88 | } 89 | 90 | dims := []uint{1} 91 | dspace, err := CreateSimpleDataspace(dims, dims) 92 | if err != nil { 93 | t.Fatal(err) 94 | } 95 | 96 | dset, err := g3.CreateDataset("dset", dtype, dspace) 97 | if err != nil { 98 | t.Fatal(err) 99 | } 100 | 101 | dset2, err := g3.OpenDataset("dset") 102 | if dset.Name() != dset2.Name() { 103 | t.Error("expected dataset names to be equal") 104 | } 105 | 106 | dset2, err = g3.OpenDataset("bs") 107 | if err == nil { 108 | t.Errorf("opened dataset that was never created: %v", dset2) 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /h5i.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | // #include "hdf5.h" 4 | // #include "hdf5_hl.h" 5 | // #include 6 | // #include 7 | import "C" 8 | 9 | import ( 10 | "unsafe" 11 | ) 12 | 13 | type IType C.H5I_type_t 14 | 15 | const ( 16 | FILE IType = C.H5I_FILE 17 | GROUP IType = C.H5I_GROUP 18 | DATATYPE IType = C.H5I_DATATYPE 19 | DATASPACE IType = C.H5I_DATASPACE 20 | DATASET IType = C.H5I_DATASET 21 | ATTRIBUTE IType = C.H5I_ATTR 22 | BAD_ID IType = C.H5I_BADID 23 | ) 24 | 25 | // Identifier is a simple wrapper around a C hid_t. It has basic methods 26 | // which apply to every type in the go-hdf5 API. 27 | type Identifier struct { 28 | id C.hid_t 29 | } 30 | 31 | // A Location embeds Identifier. Dataset, Datatype and Group are all Locations. 32 | type Location struct { 33 | Identifier 34 | } 35 | 36 | // Id returns the int value of an identifier. 37 | func (i Identifier) Id() int { 38 | return int(i.id) 39 | } 40 | 41 | // Name returns the full name of the Identifier 42 | func (i Identifier) Name() string { 43 | sz := int(C.H5Iget_name(i.id, nil, 0)) + 1 44 | if sz < 0 { 45 | return "" 46 | } 47 | buf := string(make([]byte, sz)) 48 | c_buf := C.CString(buf) 49 | defer C.free(unsafe.Pointer(c_buf)) 50 | sz = int(C.H5Iget_name(i.id, c_buf, C.size_t(sz))) 51 | if sz < 0 { 52 | return "" 53 | } 54 | return C.GoString(c_buf) 55 | } 56 | 57 | // File returns the file associated with this Identifier. 58 | func (i Identifier) File() *File { 59 | fid := C.H5Iget_file_id(i.id) 60 | if fid < 0 { 61 | return nil 62 | } 63 | return &File{CommonFG{Location{Identifier{fid}}}} 64 | } 65 | 66 | // Type returns the type of the identifier. 67 | func (i Identifier) Type() IType { 68 | return IType(C.H5Iget_type(i.id)) 69 | } 70 | -------------------------------------------------------------------------------- /h5p.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | // #include "hdf5.h" 4 | // #include 5 | // #include 6 | // inline static 7 | // hid_t _go_hdf5_H5P_DEFAULT() { return H5P_DEFAULT; } 8 | import "C" 9 | 10 | import ( 11 | "fmt" 12 | "runtime" 13 | ) 14 | 15 | type PropType C.hid_t 16 | 17 | type PropList struct { 18 | Identifier 19 | } 20 | 21 | var ( 22 | P_DEFAULT *PropList = newPropList(C._go_hdf5_H5P_DEFAULT()) 23 | ) 24 | 25 | func newPropList(id C.hid_t) *PropList { 26 | p := &PropList{Identifier{id}} 27 | runtime.SetFinalizer(p, (*PropList).finalizer) 28 | return p 29 | } 30 | 31 | func (p *PropList) finalizer() { 32 | if err := p.Close(); err != nil { 33 | panic(fmt.Errorf("error closing PropList: %s", err)) 34 | } 35 | } 36 | 37 | // NewPropList creates a new PropList as an instance of a property list class. 38 | func NewPropList(cls_id PropType) (*PropList, error) { 39 | hid := C.H5Pcreate(C.hid_t(cls_id)) 40 | if err := checkID(hid); err != nil { 41 | return nil, err 42 | } 43 | return newPropList(hid), nil 44 | } 45 | 46 | // Close terminates access to a PropList. 47 | func (p *PropList) Close() error { 48 | if p.id == 0 { 49 | return nil 50 | } 51 | err := h5err(C.H5Pclose(p.id)) 52 | p.id = 0 53 | return err 54 | } 55 | 56 | // Copy copies an existing PropList to create a new PropList. 57 | func (p *PropList) Copy() (*PropList, error) { 58 | hid := C.H5Pcopy(p.id) 59 | if err := checkID(hid); err != nil { 60 | return nil, err 61 | } 62 | return newPropList(hid), nil 63 | } 64 | -------------------------------------------------------------------------------- /h5pt.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | // #include "hdf5.h" 4 | // #include "hdf5_hl.h" 5 | // #include 6 | // #include 7 | import "C" 8 | 9 | import ( 10 | "fmt" 11 | "reflect" 12 | "runtime" 13 | "unsafe" 14 | ) 15 | 16 | // Table is an hdf5 packet-table. 17 | type Table struct { 18 | Location 19 | } 20 | 21 | func newPacketTable(id C.hid_t) *Table { 22 | t := &Table{Location{Identifier{id}}} 23 | runtime.SetFinalizer(t, (*Table).finalizer) 24 | return t 25 | } 26 | 27 | func (t *Table) finalizer() { 28 | if err := t.Close(); err != nil { 29 | panic(fmt.Errorf("error closing packet table: %s", err)) 30 | } 31 | } 32 | 33 | // Close closes an open packet table. 34 | func (t *Table) Close() error { 35 | if t.id == 0 { 36 | return nil 37 | } 38 | err := h5err(C.H5PTclose(t.id)) 39 | t.id = 0 40 | return err 41 | } 42 | 43 | // IsValid returns whether or not an indentifier points to a packet table. 44 | func (t *Table) IsValid() bool { 45 | return C.H5PTis_valid(t.id) >= 0 46 | } 47 | 48 | func (t *Table) Id() int { 49 | return int(t.id) 50 | } 51 | 52 | // ReadPackets reads a number of packets from a packet table. 53 | func (t *Table) ReadPackets(start, nrecords int, data interface{}) error { 54 | c_start := C.hsize_t(start) 55 | c_nrecords := C.size_t(nrecords) 56 | rv := reflect.Indirect(reflect.ValueOf(data)) 57 | rt := rv.Type() 58 | c_data := unsafe.Pointer(nil) 59 | switch rt.Kind() { 60 | case reflect.Array: 61 | if rv.Len() < nrecords { 62 | panic(fmt.Errorf("not enough capacity in array (cap=%d)", rv.Len())) 63 | } 64 | c_data = unsafe.Pointer(rv.Index(0).UnsafeAddr()) 65 | 66 | case reflect.Slice: 67 | if rv.Len() < nrecords { 68 | panic(fmt.Errorf("not enough capacity in slice (cap=%d)", rv.Len())) 69 | } 70 | slice := (*reflect.SliceHeader)(unsafe.Pointer(rv.UnsafeAddr())) 71 | c_data = unsafe.Pointer(slice.Data) 72 | 73 | default: 74 | panic(fmt.Errorf("unhandled kind (%s), need slice or array", rt.Kind())) 75 | } 76 | err := C.H5PTread_packets(t.id, c_start, c_nrecords, c_data) 77 | return h5err(err) 78 | } 79 | 80 | // Append appends packets to the end of a packet table. 81 | func (t *Table) Append(data interface{}) error { 82 | rv := reflect.Indirect(reflect.ValueOf(data)) 83 | rt := rv.Type() 84 | c_nrecords := C.size_t(0) 85 | c_data := unsafe.Pointer(nil) 86 | 87 | switch rt.Kind() { 88 | 89 | case reflect.Array: 90 | c_nrecords = C.size_t(rv.Len()) 91 | c_data = unsafe.Pointer(rv.UnsafeAddr()) 92 | 93 | case reflect.Slice: 94 | c_nrecords = C.size_t(rv.Len()) 95 | slice := (*reflect.SliceHeader)(unsafe.Pointer(rv.UnsafeAddr())) 96 | c_data = unsafe.Pointer(slice.Data) 97 | 98 | case reflect.String: 99 | c_nrecords = C.size_t(rv.Len()) 100 | str := (*reflect.StringHeader)(unsafe.Pointer(rv.UnsafeAddr())) 101 | c_data = unsafe.Pointer(str.Data) 102 | 103 | case reflect.Ptr: 104 | c_nrecords = C.size_t(1) 105 | c_data = unsafe.Pointer(rv.Elem().UnsafeAddr()) 106 | 107 | default: 108 | c_nrecords = C.size_t(1) 109 | c_data = unsafe.Pointer(rv.UnsafeAddr()) 110 | } 111 | 112 | err := C.H5PTappend(t.id, c_nrecords, c_data) 113 | return h5err(err) 114 | } 115 | 116 | // Next reads packets from a packet table starting at the current index into the value pointed at by data. 117 | // i.e. data is a pointer to an array or a slice. 118 | func (t *Table) Next(data interface{}) error { 119 | rt := reflect.TypeOf(data) 120 | if rt.Kind() != reflect.Ptr { 121 | return fmt.Errorf("hdf5: invalid value type. got=%v, want pointer", rt.Kind()) 122 | } 123 | rt = rt.Elem() 124 | rv := reflect.Indirect(reflect.ValueOf(data)) 125 | 126 | n := C.size_t(0) 127 | cdata := unsafe.Pointer(nil) 128 | switch rt.Kind() { 129 | case reflect.Array: 130 | if rv.Cap() <= 0 { 131 | panic(fmt.Errorf("not enough capacity in array (cap=%d)", rv.Cap())) 132 | } 133 | cdata = unsafe.Pointer(rv.UnsafeAddr()) 134 | n = C.size_t(rv.Cap()) 135 | 136 | case reflect.Slice: 137 | if rv.Cap() <= 0 { 138 | panic(fmt.Errorf("not enough capacity in slice (cap=%d)", rv.Cap())) 139 | } 140 | slice := (*reflect.SliceHeader)(unsafe.Pointer(rv.UnsafeAddr())) 141 | cdata = unsafe.Pointer(slice.Data) 142 | n = C.size_t(rv.Cap()) 143 | 144 | default: 145 | panic(fmt.Errorf("unsupported kind (%s), need slice or array", rt.Kind())) 146 | } 147 | err := C.H5PTget_next(t.id, n, cdata) 148 | return h5err(err) 149 | } 150 | 151 | // NumPackets returns the number of packets in a packet table. 152 | func (t *Table) NumPackets() (int, error) { 153 | c_nrecords := C.hsize_t(0) 154 | err := C.H5PTget_num_packets(t.id, &c_nrecords) 155 | return int(c_nrecords), h5err(err) 156 | } 157 | 158 | // CreateIndex resets a packet table's index to the first packet. 159 | func (t *Table) CreateIndex() error { 160 | err := C.H5PTcreate_index(t.id) 161 | return h5err(err) 162 | } 163 | 164 | // SetIndex sets a packet table's index. 165 | func (t *Table) SetIndex(index int) error { 166 | c_idx := C.hsize_t(index) 167 | err := C.H5PTset_index(t.id, c_idx) 168 | return h5err(err) 169 | } 170 | 171 | // Type returns an identifier for a copy of the datatype for a dataset. 172 | func (t *Table) Type() (*Datatype, error) { 173 | hid := C.H5Dget_type(t.id) 174 | if err := checkID(hid); err != nil { 175 | return nil, err 176 | } 177 | return NewDatatype(hid), nil 178 | } 179 | 180 | func createTable(id C.hid_t, name string, dtype *Datatype, chunkSize, compression int) (*Table, error) { 181 | c_name := C.CString(name) 182 | defer C.free(unsafe.Pointer(c_name)) 183 | 184 | chunk := C.hsize_t(chunkSize) 185 | compr := C.int(compression) 186 | hid := C.H5PTcreate_fl(id, c_name, dtype.id, chunk, compr) 187 | if err := checkID(hid); err != nil { 188 | return nil, err 189 | } 190 | return newPacketTable(hid), nil 191 | } 192 | 193 | func createTableFrom(id C.hid_t, name string, dtype interface{}, chunkSize, compression int) (*Table, error) { 194 | var err error 195 | switch dt := dtype.(type) { 196 | case reflect.Type: 197 | if hdfDtype, err := NewDataTypeFromType(dt); err == nil { 198 | return createTable(id, name, hdfDtype, chunkSize, compression) 199 | } 200 | case *Datatype: 201 | return createTable(id, name, dt, chunkSize, compression) 202 | default: 203 | if hdfDtype, err := NewDataTypeFromType(reflect.TypeOf(dtype)); err == nil { 204 | return createTable(id, name, hdfDtype, chunkSize, compression) 205 | } 206 | } 207 | return nil, err 208 | } 209 | 210 | func openTable(id C.hid_t, name string) (*Table, error) { 211 | c_name := C.CString(name) 212 | defer C.free(unsafe.Pointer(c_name)) 213 | 214 | hid := C.H5PTopen(id, c_name) 215 | if err := checkID(hid); err != nil { 216 | return nil, err 217 | } 218 | return newPacketTable(hid), nil 219 | } 220 | -------------------------------------------------------------------------------- /h5pt_test.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | import ( 4 | "os" 5 | "reflect" 6 | ) 7 | import "testing" 8 | 9 | const ( 10 | fname string = "ex_table_01.h5" 11 | tname string = "table" 12 | nrecords int = 8 13 | ) 14 | 15 | type particle struct { 16 | // name string `hdf5:"Name"` // FIXME(sbinet) 17 | lati int32 `hdf5:"Latitude"` 18 | longi int64 `hdf5:"Longitude"` 19 | pressure float32 `hdf5:"Pressure"` 20 | temperature float64 `hdf5:"Temperature"` 21 | // isthep []int // FIXME(sbinet) 22 | // jmohep [2][2]int64 // FIXME(sbinet) 23 | } 24 | 25 | func TestTable(t *testing.T) { 26 | 27 | // define an array of particles 28 | particles := []particle{ 29 | {0, 0, 0.0, 0.}, 30 | {10, 10, 1.0, 10.}, 31 | {20, 20, 2.0, 20.}, 32 | {30, 30, 3.0, 30.}, 33 | {40, 40, 4.0, 40.}, 34 | {50, 50, 5.0, 50.}, 35 | {60, 60, 6.0, 60.}, 36 | {70, 70, 7.0, 70.}, 37 | } 38 | // particles := []particle{ 39 | // {"zero", 0, 0, 0.0, 0., []int{0, 0}, [2][2]int{{0, 0}, {0, 0}}}, 40 | // {"one", 10, 10, 1.0, 10., []int{0, 0}, [2][2]int{{1, 0}, {0, 1}}}, 41 | // {"two", 20, 20, 2.0, 20., []int{0, 0}, [2][2]int{{2, 0}, {0, 2}}}, 42 | // {"three", 30, 30, 3.0, 30., []int{0, 0}, [2][2]int{{3, 0}, {0, 3}}}, 43 | // {"four", 40, 40, 4.0, 40., []int{0, 0}, [2][2]int{{4, 0}, {0, 4}}}, 44 | // {"five", 50, 50, 5.0, 50., []int{0, 0}, [2][2]int{{5, 0}, {0, 5}}}, 45 | // {"six", 60, 60, 6.0, 60., []int{0, 0}, [2][2]int{{6, 0}, {0, 6}}}, 46 | // {"seven", 70, 70, 7.0, 70., []int{0, 0}, [2][2]int{{7, 0}, {0, 7}}}, 47 | // } 48 | 49 | chunkSize := 10 50 | compress := 0 51 | 52 | // create a new file using default properties 53 | f, err := CreateFile(fname, F_ACC_TRUNC) 54 | if err != nil { 55 | t.Fatalf("CreateFile failed: %s", err) 56 | } 57 | defer os.Remove(fname) 58 | defer f.Close() 59 | 60 | // create a fixed-length packet table within the file 61 | table, err := f.CreateTableFrom(tname, particle{}, chunkSize, compress) 62 | if err != nil { 63 | t.Fatalf("CreateTableFrom failed: %s", err) 64 | } 65 | defer table.Close() 66 | 67 | if !table.IsValid() { 68 | t.Fatal("table is invalid") 69 | } 70 | 71 | // write one packet to the packet table 72 | if err = table.Append(&particles[0]); err != nil { 73 | t.Fatalf("Append failed with single packet: %s", err) 74 | } 75 | 76 | // write several packets 77 | parts := particles[1:] 78 | if err = table.Append(&parts); err != nil { 79 | t.Fatalf("Append failed with multiple packets: %s", err) 80 | } 81 | 82 | // get the number of packets 83 | n, err := table.NumPackets() 84 | if err != nil { 85 | t.Fatalf("NumPackets failed: %s", err) 86 | } 87 | if n != nrecords { 88 | t.Fatalf("Wrong number of packets reported, expected %d but got %d", nrecords, n) 89 | } 90 | 91 | // iterate through packets 92 | for i := 0; i != n; i++ { 93 | p := make([]particle, 1) 94 | if err := table.Next(&p); err != nil { 95 | t.Fatalf("Next failed: %s", err) 96 | } 97 | } 98 | 99 | // reset index 100 | table.CreateIndex() 101 | parts = make([]particle, nrecords) 102 | if err = table.ReadPackets(0, nrecords, &parts); err != nil { 103 | t.Fatalf("ReadPackets failed: %s", err) 104 | } 105 | 106 | if !reflect.DeepEqual(parts, particles) { 107 | t.Fatalf("particles differ.\ngot= %#v\nwant=%#v\n", parts, particles) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /h5s.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | // #include "hdf5.h" 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | 12 | "runtime" 13 | "unsafe" 14 | ) 15 | 16 | type Dataspace struct { 17 | Identifier 18 | } 19 | 20 | type SpaceClass C.H5S_class_t 21 | 22 | const ( 23 | S_NO_CLASS SpaceClass = -1 // error 24 | S_SCALAR SpaceClass = 0 // scalar variable 25 | S_SIMPLE SpaceClass = 1 // simple data space 26 | S_NULL SpaceClass = 2 // null data space 27 | ) 28 | 29 | func newDataspace(id C.hid_t) *Dataspace { 30 | ds := &Dataspace{Identifier{id}} 31 | runtime.SetFinalizer(ds, (*Dataspace).finalizer) 32 | return ds 33 | } 34 | 35 | // CreateDataspace creates a new dataspace of a specified type. 36 | func CreateDataspace(class SpaceClass) (*Dataspace, error) { 37 | hid := C.H5Screate(C.H5S_class_t(class)) 38 | if err := checkID(hid); err != nil { 39 | return nil, err 40 | } 41 | ds := newDataspace(hid) 42 | return ds, nil 43 | } 44 | 45 | func (s *Dataspace) finalizer() { 46 | if err := s.Close(); err != nil { 47 | panic(fmt.Errorf("error closing dspace: %s", err)) 48 | } 49 | } 50 | 51 | // Copy creates an exact copy of a dataspace. 52 | func (s *Dataspace) Copy() (*Dataspace, error) { 53 | hid := C.H5Scopy(s.id) 54 | if err := checkID(hid); err != nil { 55 | return nil, err 56 | } 57 | return newDataspace(hid), nil 58 | } 59 | 60 | // Close releases and terminates access to a dataspace. 61 | func (s *Dataspace) Close() error { 62 | if s.id == 0 { 63 | return nil 64 | } 65 | err := h5err(C.H5Sclose(s.id)) 66 | s.id = 0 67 | return err 68 | } 69 | 70 | func (s *Dataspace) Id() int { 71 | return int(s.id) 72 | } 73 | 74 | // CreateSimpleDataspace creates a new simple dataspace and opens it for access. 75 | func CreateSimpleDataspace(dims, maxDims []uint) (*Dataspace, error) { 76 | var c_dims, c_maxdims *C.hsize_t 77 | 78 | rank := C.int(0) 79 | if dims != nil { 80 | rank = C.int(len(dims)) 81 | c_dims = (*C.hsize_t)(unsafe.Pointer(&dims[0])) 82 | 83 | } 84 | if maxDims != nil { 85 | rank = C.int(len(maxDims)) 86 | c_maxdims = (*C.hsize_t)(unsafe.Pointer(&maxDims[0])) 87 | 88 | } 89 | if len(dims) != len(maxDims) && (dims != nil && maxDims != nil) { 90 | return nil, errors.New("lengths of dims and maxDims do not match") 91 | } 92 | 93 | hid := C.H5Screate_simple(rank, c_dims, c_maxdims) 94 | if hid < 0 { 95 | return nil, fmt.Errorf("failed to create dataspace") 96 | } 97 | return newDataspace(hid), nil 98 | } 99 | 100 | // IsSimple returns whether a dataspace is a simple dataspace. 101 | func (s *Dataspace) IsSimple() bool { 102 | return int(C.H5Sis_simple(s.id)) > 0 103 | } 104 | 105 | // SetOffset sets the offset of a simple dataspace. 106 | func (s *Dataspace) SetOffset(offset []uint) error { 107 | rank := len(offset) 108 | if rank == 0 { 109 | err := C.H5Soffset_simple(s.id, nil) 110 | return h5err(err) 111 | } 112 | if rank != s.SimpleExtentNDims() { 113 | err := errors.New("size of offset does not match extent") 114 | return err 115 | } 116 | 117 | c_offset := (*C.hssize_t)(unsafe.Pointer(&offset[0])) 118 | err := C.H5Soffset_simple(s.id, c_offset) 119 | return h5err(err) 120 | } 121 | 122 | // SelectHyperslab creates a subset of the data space. 123 | func (s *Dataspace) SelectHyperslab(offset, stride, count, block []uint) error { 124 | rank := len(offset) 125 | if rank == 0 { 126 | err := C.H5Soffset_simple(s.id, nil) 127 | return h5err(err) 128 | } 129 | if rank != s.SimpleExtentNDims() { 130 | err := errors.New("size of offset does not match extent") 131 | return err 132 | } 133 | 134 | c_offset := (*C.hsize_t)(unsafe.Pointer(&offset[0])) 135 | c_count := (*C.hsize_t)(unsafe.Pointer(&count[0])) 136 | var c_stride, c_block *C.hsize_t 137 | if stride != nil { 138 | c_stride = (*C.hsize_t)(unsafe.Pointer(&stride[0])) 139 | } 140 | if block != nil { 141 | c_block = (*C.hsize_t)(unsafe.Pointer(&block[0])) 142 | } 143 | err := C.H5Sselect_hyperslab(s.id, C.H5S_SELECT_SET, c_offset, c_stride, c_count, c_block) 144 | return h5err(err) 145 | } 146 | 147 | // SimpleExtentDims returns dataspace dimension size and maximum size. 148 | func (s *Dataspace) SimpleExtentDims() (dims, maxdims []uint, err error) { 149 | rank := s.SimpleExtentNDims() 150 | dims = make([]uint, rank) 151 | maxdims = make([]uint, rank) 152 | 153 | c_dims := (*C.hsize_t)(unsafe.Pointer(&dims[0])) 154 | c_maxdims := (*C.hsize_t)(unsafe.Pointer(&maxdims[0])) 155 | rc := C.H5Sget_simple_extent_dims(s.id, c_dims, c_maxdims) 156 | err = h5err(C.herr_t(rc)) 157 | return 158 | } 159 | 160 | // SimpleExtentNDims returns the dimensionality of a dataspace. 161 | func (s *Dataspace) SimpleExtentNDims() int { 162 | return int(C.H5Sget_simple_extent_ndims(s.id)) 163 | } 164 | 165 | // SimpleExtentNPoints returns the number of elements in a dataspace. 166 | func (s *Dataspace) SimpleExtentNPoints() int { 167 | return int(C.H5Sget_simple_extent_npoints(s.id)) 168 | } 169 | 170 | // SimpleExtentType returns the current class of a dataspace. 171 | func (s *Dataspace) SimpleExtentType() SpaceClass { 172 | return SpaceClass(C.H5Sget_simple_extent_type(s.id)) 173 | } 174 | -------------------------------------------------------------------------------- /h5s_test.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestDataspace(t *testing.T) { 8 | // Creating this dataspace results in an error. 9 | if _, err := CreateDataspace(S_NO_CLASS); err == nil { 10 | t.Errorf("expected an error, but got nil") 11 | } 12 | 13 | // These dataspaces are legitimate. 14 | classes := []SpaceClass{S_SCALAR, S_SIMPLE, S_NULL} 15 | for _, class := range classes { 16 | // Create a new Dataspace 17 | ds, err := CreateDataspace(class) 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | 22 | if ds.SimpleExtentType() != class { 23 | t.Errorf("Dataspace class mismatch: %q != %q", ds.SimpleExtentType(), class) 24 | } 25 | 26 | // Copy the Dataspace 27 | clone, err := ds.Copy() 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | 32 | if ds.Name() != clone.Name() { 33 | t.Errorf("original dataspace name %q != clone name %q", ds.Name(), clone.Name()) 34 | } 35 | if ds.IsSimple() != clone.IsSimple() { 36 | t.Errorf("original dataspace simplicity %v != clone simplicity: %v", ds.IsSimple(), clone.IsSimple()) 37 | } 38 | // Close the Dataspace 39 | if err = ds.Close(); err != nil { 40 | t.Fatal(err) 41 | } 42 | } 43 | } 44 | 45 | func TestSimpleDataspace(t *testing.T) { 46 | dims := []uint{3, 3, 3} 47 | maxdims := []uint{9, 9, 9} 48 | ds, err := CreateSimpleDataspace(dims, maxdims) 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | 53 | dsDims, dsMaxdims, err := ds.SimpleExtentDims() 54 | 55 | if err != nil { 56 | t.Fatal(err) 57 | } 58 | 59 | if !arrayEq(dims, dsDims) { 60 | t.Errorf("retrieved dims not equal: %v != %v", dims, dsDims) 61 | } 62 | 63 | if !arrayEq(maxdims, dsMaxdims) { 64 | t.Errorf("retrieved maxdims not equal: %v != %v", maxdims, dsMaxdims) 65 | } 66 | 67 | if ds.SimpleExtentNDims() != 3 { 68 | t.Errorf("wrong number of dimensions: got %d, want %d", ds.SimpleExtentNDims(), 3) 69 | } 70 | 71 | if ds.SimpleExtentType() != S_SIMPLE { 72 | t.Errorf("wrong extent type: got %d, want %d", ds.SimpleExtentType(), S_SIMPLE) 73 | } 74 | 75 | // npoints should be 3 * 3 * 3 76 | npoints := ds.SimpleExtentNPoints() 77 | if npoints != 27 { 78 | t.Errorf("wrong number of npoints: got %d, want %d", npoints, 27) 79 | } 80 | 81 | // SetOffset should only work for array a where len(a) == len(dims) 82 | if err = ds.SetOffset([]uint{1, 1, 1}); err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | if err = ds.SetOffset([]uint{1}); err == nil { 87 | t.Error("expected a non-nil error") 88 | } 89 | } 90 | 91 | func arrayEq(a, b []uint) bool { 92 | if len(a) != len(b) { 93 | return false 94 | } 95 | for i := range a { 96 | if a[i] != b[i] { 97 | return false 98 | } 99 | } 100 | return true 101 | } 102 | -------------------------------------------------------------------------------- /h5t.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | // #include "hdf5.h" 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "fmt" 10 | "reflect" 11 | "runtime" 12 | "unsafe" 13 | ) 14 | 15 | type Datatype struct { 16 | Location 17 | } 18 | 19 | type TypeClass C.H5T_class_t 20 | 21 | const ( 22 | T_NO_CLASS TypeClass = -1 // Error 23 | T_INTEGER TypeClass = 0 // integer types 24 | T_FLOAT TypeClass = 1 // floating-point types 25 | T_TIME TypeClass = 2 // date and time types 26 | T_STRING TypeClass = 3 // character string types 27 | T_BITFIELD TypeClass = 4 // bit field types 28 | T_OPAQUE TypeClass = 5 // opaque types 29 | T_COMPOUND TypeClass = 6 // compound types 30 | T_REFERENCE TypeClass = 7 // reference types 31 | T_ENUM TypeClass = 8 // enumeration types 32 | T_VLEN TypeClass = 9 // variable-length types 33 | T_ARRAY TypeClass = 10 // array types 34 | T_NCLASSES TypeClass = 11 // nbr of classes -- MUST BE LAST 35 | ) 36 | 37 | // list of go types 38 | var ( 39 | _go_string_t reflect.Type = reflect.TypeOf(string("")) 40 | _go_int_t reflect.Type = reflect.TypeOf(int(0)) 41 | _go_int8_t reflect.Type = reflect.TypeOf(int8(0)) 42 | _go_int16_t reflect.Type = reflect.TypeOf(int16(0)) 43 | _go_int32_t reflect.Type = reflect.TypeOf(int32(0)) 44 | _go_int64_t reflect.Type = reflect.TypeOf(int64(0)) 45 | _go_uint_t reflect.Type = reflect.TypeOf(uint(0)) 46 | _go_uint8_t reflect.Type = reflect.TypeOf(uint8(0)) 47 | _go_uint16_t reflect.Type = reflect.TypeOf(uint16(0)) 48 | _go_uint32_t reflect.Type = reflect.TypeOf(uint32(0)) 49 | _go_uint64_t reflect.Type = reflect.TypeOf(uint64(0)) 50 | 51 | _go_float32_t reflect.Type = reflect.TypeOf(float32(0)) 52 | _go_float64_t reflect.Type = reflect.TypeOf(float64(0)) 53 | 54 | _go_array_t reflect.Type = reflect.TypeOf([1]int{0}) 55 | _go_slice_t reflect.Type = reflect.TypeOf([]int{0}) 56 | 57 | _go_struct_t reflect.Type = reflect.TypeOf(struct{}{}) 58 | 59 | _go_ptr_t reflect.Type = reflect.PtrTo(_go_int_t) 60 | ) 61 | 62 | type typeMap map[TypeClass]reflect.Type 63 | 64 | var ( 65 | // Mapping of TypeClass to reflect.Type 66 | typeClassToGoType typeMap = typeMap{ 67 | T_NO_CLASS: nil, 68 | T_INTEGER: _go_int_t, 69 | T_FLOAT: _go_float32_t, 70 | T_TIME: nil, 71 | T_STRING: _go_string_t, 72 | T_BITFIELD: nil, 73 | T_OPAQUE: nil, 74 | T_COMPOUND: _go_struct_t, 75 | T_REFERENCE: _go_ptr_t, 76 | T_ENUM: _go_int_t, 77 | T_VLEN: _go_slice_t, 78 | T_ARRAY: _go_array_t, 79 | } 80 | 81 | parametricTypes typeMap = typeMap{ 82 | // Only these types can be used with CreateDatatype 83 | T_COMPOUND: _go_struct_t, 84 | T_ENUM: _go_int_t, 85 | T_OPAQUE: nil, 86 | T_STRING: _go_string_t, 87 | } 88 | ) 89 | 90 | // OpenDatatype opens a named datatype. 91 | func OpenDatatype(c CommonFG, name string, tapl_id int) (*Datatype, error) { 92 | c_name := C.CString(name) 93 | defer C.free(unsafe.Pointer(c_name)) 94 | 95 | id := C.H5Topen2(C.hid_t(c.id), c_name, C.hid_t(tapl_id)) 96 | if err := checkID(id); err != nil { 97 | return nil, err 98 | } 99 | return NewDatatype(id), nil 100 | } 101 | 102 | // NewDatatype creates a Datatype from an hdf5 id. 103 | func NewDatatype(id C.hid_t) *Datatype { 104 | t := &Datatype{Location{Identifier{id}}} 105 | runtime.SetFinalizer(t, (*Datatype).finalizer) 106 | return t 107 | } 108 | 109 | // CreateDatatype creates a new datatype. 110 | // class must be T_COMPUND, T_OPAQUE, T_ENUM or T_STRING. 111 | // size is the size of the new datatype in bytes. 112 | func CreateDatatype(class TypeClass, size int) (*Datatype, error) { 113 | _, ok := parametricTypes[class] 114 | if !ok { 115 | return nil, 116 | fmt.Errorf( 117 | "invalid TypeClass, want %v, %v, %v or %v, got %v", 118 | T_COMPOUND, T_OPAQUE, T_STRING, T_ENUM, 119 | class, 120 | ) 121 | } 122 | 123 | hid := C.H5Tcreate(C.H5T_class_t(class), C.size_t(size)) 124 | if err := checkID(hid); err != nil { 125 | return nil, err 126 | } 127 | return NewDatatype(hid), nil 128 | } 129 | 130 | func (t *Datatype) finalizer() { 131 | if err := t.Close(); err != nil { 132 | panic(fmt.Errorf("error closing datatype: %s", err)) 133 | } 134 | } 135 | 136 | // GoType returns the reflect.Type associated with the Datatype's TypeClass 137 | func (t *Datatype) GoType() reflect.Type { 138 | return typeClassToGoType[t.Class()] 139 | } 140 | 141 | // Close releases a datatype. 142 | func (t *Datatype) Close() error { 143 | if t.id == 0 { 144 | return nil 145 | } 146 | err := h5err(C.H5Tclose(t.id)) 147 | t.id = 0 148 | return err 149 | } 150 | 151 | // Committed determines whether a datatype is a named type or a transient type. 152 | func (t *Datatype) Committed() bool { 153 | return C.H5Tcommitted(t.id) > 0 154 | } 155 | 156 | // Copy copies an existing datatype. 157 | func (t *Datatype) Copy() (*Datatype, error) { 158 | return copyDatatype(t.id) 159 | } 160 | 161 | // copyDatatype should be called by any function wishing to return 162 | // an existing Datatype from a Dataset or Attribute. 163 | func copyDatatype(id C.hid_t) (*Datatype, error) { 164 | hid := C.H5Tcopy(id) 165 | if err := checkID(hid); err != nil { 166 | return nil, err 167 | } 168 | return NewDatatype(hid), nil 169 | } 170 | 171 | // Equal determines whether two datatype identifiers refer to the same datatype. 172 | func (t *Datatype) Equal(o *Datatype) bool { 173 | return C.H5Tequal(t.id, o.id) > 0 174 | } 175 | 176 | // Lock locks a datatype. 177 | func (t *Datatype) Lock() error { 178 | return h5err(C.H5Tlock(t.id)) 179 | } 180 | 181 | // Size returns the size of the Datatype. 182 | func (t *Datatype) Size() uint { 183 | return uint(C.H5Tget_size(t.id)) 184 | } 185 | 186 | // SetSize sets the total size of a Datatype. 187 | func (t *Datatype) SetSize(sz uint) error { 188 | err := C.H5Tset_size(t.id, C.size_t(sz)) 189 | return h5err(err) 190 | } 191 | 192 | type ArrayType struct { 193 | Datatype 194 | } 195 | 196 | // NewArrayType creates a new ArrayType. 197 | // base_type specifies the element type of the array. 198 | // dims specify the dimensions of the array. 199 | func NewArrayType(base_type *Datatype, dims []int) (*ArrayType, error) { 200 | ndims := C.uint(len(dims)) 201 | c_dims := (*C.hsize_t)(unsafe.Pointer(&dims[0])) 202 | 203 | hid := C.H5Tarray_create2(base_type.id, ndims, c_dims) 204 | if err := checkID(hid); err != nil { 205 | return nil, err 206 | } 207 | t := &ArrayType{Datatype{Location{Identifier{hid}}}} 208 | runtime.SetFinalizer(t, (*ArrayType).finalizer) 209 | return t, nil 210 | } 211 | 212 | // NDims returns the rank of an ArrayType. 213 | func (t *ArrayType) NDims() int { 214 | return int(C.H5Tget_array_ndims(t.id)) 215 | } 216 | 217 | // ArrayDims returns the array dimensions. 218 | func (t *ArrayType) ArrayDims() []int { 219 | rank := t.NDims() 220 | dims := make([]int, rank) 221 | hdims := make([]C.hsize_t, rank) 222 | slice := (*reflect.SliceHeader)(unsafe.Pointer(&hdims)) 223 | c_dims := (*C.hsize_t)(unsafe.Pointer(slice.Data)) 224 | c_rank := int(C.H5Tget_array_dims2(t.id, c_dims)) 225 | if c_rank != rank { 226 | return nil 227 | } 228 | for i, n := range hdims { 229 | dims[i] = int(n) 230 | } 231 | return dims 232 | } 233 | 234 | type VarLenType struct { 235 | Datatype 236 | } 237 | 238 | // NewVarLenType creates a new VarLenType. 239 | // base_type specifies the element type of the VarLenType. 240 | func NewVarLenType(base_type *Datatype) (*VarLenType, error) { 241 | id := C.H5Tvlen_create(base_type.id) 242 | if err := checkID(id); err != nil { 243 | return nil, err 244 | } 245 | t := &VarLenType{Datatype{Location{Identifier{id}}}} 246 | runtime.SetFinalizer(t, (*VarLenType).finalizer) 247 | return t, nil 248 | } 249 | 250 | // IsVariableStr determines whether the VarLenType is a string. 251 | func (vl *VarLenType) IsVariableStr() bool { 252 | return C.H5Tis_variable_str(vl.id) > 0 253 | } 254 | 255 | type CompoundType struct { 256 | Datatype 257 | } 258 | 259 | // NewCompoundType creates a new CompoundType. 260 | // size is the size in bytes of the compound datatype. 261 | func NewCompoundType(size int) (*CompoundType, error) { 262 | id := C.H5Tcreate(C.H5T_class_t(T_COMPOUND), C.size_t(size)) 263 | if err := checkID(id); err != nil { 264 | return nil, err 265 | } 266 | t := &CompoundType{Datatype{Location{Identifier{id}}}} 267 | runtime.SetFinalizer(t, (*CompoundType).finalizer) 268 | return t, nil 269 | } 270 | 271 | // NMembers returns the number of elements in a compound or enumeration datatype. 272 | func (t *CompoundType) NMembers() int { 273 | return int(C.H5Tget_nmembers(t.id)) 274 | } 275 | 276 | // Class returns the TypeClass of the DataType 277 | func (t *Datatype) Class() TypeClass { 278 | return TypeClass(C.H5Tget_class(t.id)) 279 | } 280 | 281 | // MemberClass returns datatype class of compound datatype member. 282 | func (t *CompoundType) MemberClass(mbr_idx int) TypeClass { 283 | return TypeClass(C.H5Tget_member_class(t.id, C.uint(mbr_idx))) 284 | } 285 | 286 | // MemberName returns the name of a compound or enumeration datatype member. 287 | func (t *CompoundType) MemberName(mbr_idx int) string { 288 | c_name := C.H5Tget_member_name(t.id, C.uint(mbr_idx)) 289 | defer C.free(unsafe.Pointer(c_name)) 290 | return C.GoString(c_name) 291 | } 292 | 293 | // MemberIndex returns the index of a compound or enumeration datatype member. 294 | func (t *CompoundType) MemberIndex(name string) int { 295 | c_name := C.CString(name) 296 | defer C.free(unsafe.Pointer(c_name)) 297 | return int(C.H5Tget_member_index(t.id, c_name)) 298 | } 299 | 300 | // MemberOffset returns the offset of a field of a compound datatype. 301 | func (t *CompoundType) MemberOffset(mbr_idx int) int { 302 | return int(C.H5Tget_member_offset(t.id, C.uint(mbr_idx))) 303 | } 304 | 305 | // MemberType returns the datatype of the specified member. 306 | func (t *CompoundType) MemberType(mbr_idx int) (*Datatype, error) { 307 | hid := C.H5Tget_member_type(t.id, C.uint(mbr_idx)) 308 | if err := checkID(hid); err != nil { 309 | return nil, err 310 | } 311 | return NewDatatype(hid), nil 312 | } 313 | 314 | // Insert adds a new member to a compound datatype. 315 | func (t *CompoundType) Insert(name string, offset int, field *Datatype) error { 316 | cname := C.CString(name) 317 | defer C.free(unsafe.Pointer(cname)) 318 | return h5err(C.H5Tinsert(t.id, cname, C.size_t(offset), field.id)) 319 | } 320 | 321 | // Pack recursively removes padding from within a compound datatype. 322 | // This is analogous to C struct packing and will give a space-efficient 323 | // type on the disk. However, using this may require type conversions 324 | // on more machines, so may be a worse option. 325 | func (t *CompoundType) Pack() error { 326 | return h5err(C.H5Tpack(t.id)) 327 | } 328 | 329 | type OpaqueDatatype struct { 330 | Datatype 331 | } 332 | 333 | // SetTag tags an opaque datatype. 334 | func (t *OpaqueDatatype) SetTag(tag string) error { 335 | ctag := C.CString(tag) 336 | defer C.free(unsafe.Pointer(ctag)) 337 | return h5err(C.H5Tset_tag(t.id, ctag)) 338 | } 339 | 340 | // Tag returns the tag associated with an opaque datatype. 341 | func (t *OpaqueDatatype) Tag() string { 342 | cname := C.H5Tget_tag(t.id) 343 | if cname != nil { 344 | defer C.free(unsafe.Pointer(cname)) 345 | return C.GoString(cname) 346 | } 347 | return "" 348 | } 349 | 350 | // NewDatatypeFromValue creates a datatype from a value in an interface. 351 | func NewDatatypeFromValue(v interface{}) (*Datatype, error) { 352 | return NewDataTypeFromType(reflect.TypeOf(v)) 353 | } 354 | 355 | // NewDatatypeFromType creates a new Datatype from a reflect.Type. 356 | func NewDataTypeFromType(t reflect.Type) (*Datatype, error) { 357 | 358 | var dt *Datatype = nil 359 | var err error 360 | 361 | switch t.Kind() { 362 | 363 | case reflect.Int: 364 | dt, err = T_NATIVE_INT.Copy() 365 | 366 | case reflect.Int8: 367 | dt, err = T_NATIVE_INT8.Copy() 368 | 369 | case reflect.Int16: 370 | dt, err = T_NATIVE_INT16.Copy() 371 | 372 | case reflect.Int32: 373 | dt, err = T_NATIVE_INT32.Copy() 374 | 375 | case reflect.Int64: 376 | dt, err = T_NATIVE_INT64.Copy() 377 | 378 | case reflect.Uint: 379 | dt, err = T_NATIVE_UINT.Copy() 380 | 381 | case reflect.Uint8: 382 | dt, err = T_NATIVE_UINT8.Copy() 383 | 384 | case reflect.Uint16: 385 | dt, err = T_NATIVE_UINT16.Copy() 386 | 387 | case reflect.Uint32: 388 | dt, err = T_NATIVE_UINT32.Copy() 389 | 390 | case reflect.Uint64: 391 | dt, err = T_NATIVE_UINT64.Copy() 392 | 393 | case reflect.Float32: 394 | dt, err = T_NATIVE_FLOAT.Copy() 395 | 396 | case reflect.Float64: 397 | dt, err = T_NATIVE_DOUBLE.Copy() 398 | 399 | case reflect.String: 400 | dt, err = T_GO_STRING.Copy() 401 | 402 | case reflect.Array: 403 | elem_type, err := NewDataTypeFromType(t.Elem()) 404 | if err != nil { 405 | return nil, err 406 | } 407 | 408 | dims := getArrayDims(t) 409 | 410 | adt, err := NewArrayType(elem_type, dims) 411 | if err != nil { 412 | return nil, err 413 | } 414 | 415 | dt = &adt.Datatype 416 | 417 | case reflect.Slice: 418 | elem_type, err := NewDataTypeFromType(t.Elem()) 419 | if err != nil { 420 | return nil, err 421 | } 422 | 423 | sdt, err := NewVarLenType(elem_type) 424 | if err != nil { 425 | return nil, err 426 | } 427 | 428 | dt = &sdt.Datatype 429 | 430 | case reflect.Struct: 431 | sz := int(t.Size()) 432 | cdt, err := NewCompoundType(sz) 433 | if err != nil { 434 | return nil, err 435 | } 436 | n := t.NumField() 437 | for i := 0; i < n; i++ { 438 | f := t.Field(i) 439 | var field_dt *Datatype = nil 440 | field_dt, err = NewDataTypeFromType(f.Type) 441 | if err != nil { 442 | return nil, err 443 | } 444 | offset := int(f.Offset + 0) 445 | if field_dt == nil { 446 | return nil, fmt.Errorf("pb with field [%d-%s]", i, f.Name) 447 | } 448 | field_name := string(f.Tag) 449 | if len(field_name) == 0 { 450 | field_name = f.Name 451 | } 452 | err = cdt.Insert(field_name, offset, field_dt) 453 | if err != nil { 454 | return nil, fmt.Errorf("pb with field [%d-%s]: %s", i, f.Name, err) 455 | } 456 | } 457 | dt = &cdt.Datatype 458 | 459 | default: 460 | // Should never happen. 461 | panic(fmt.Errorf("unhandled kind (%v)", t.Kind())) 462 | } 463 | 464 | return dt, err 465 | } 466 | 467 | func getArrayDims(dt reflect.Type) []int { 468 | result := []int{} 469 | if dt.Kind() == reflect.Array { 470 | result = append(result, dt.Len()) 471 | for _, dim := range getArrayDims(dt.Elem()) { 472 | result = append(result, dim) 473 | } 474 | } 475 | return result 476 | } 477 | -------------------------------------------------------------------------------- /h5t_shim.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | /* 4 | #include "hdf5.h" 5 | 6 | #include 7 | #include 8 | 9 | hid_t _go_hdf5_H5T_C_S1() { return H5T_C_S1; } 10 | hid_t _go_hdf5_H5T_FORTRAN_S1() { return H5T_FORTRAN_S1; } 11 | 12 | hid_t _go_hdf5_H5T_STD_I8BE() { return H5T_STD_I8BE; } 13 | hid_t _go_hdf5_H5T_STD_I8LE() { return H5T_STD_I8LE; } 14 | hid_t _go_hdf5_H5T_STD_I16BE() { return H5T_STD_I16BE; } 15 | hid_t _go_hdf5_H5T_STD_I16LE() { return H5T_STD_I16LE; } 16 | hid_t _go_hdf5_H5T_STD_I32BE() { return H5T_STD_I32BE; } 17 | hid_t _go_hdf5_H5T_STD_I32LE() { return H5T_STD_I32LE; } 18 | hid_t _go_hdf5_H5T_STD_I64BE() { return H5T_STD_I64BE; } 19 | hid_t _go_hdf5_H5T_STD_I64LE() { return H5T_STD_I64LE; } 20 | hid_t _go_hdf5_H5T_STD_U8BE() { return H5T_STD_U8BE; } 21 | hid_t _go_hdf5_H5T_STD_U8LE() { return H5T_STD_U8LE; } 22 | hid_t _go_hdf5_H5T_STD_U16BE() { return H5T_STD_U16BE; } 23 | hid_t _go_hdf5_H5T_STD_U16LE() { return H5T_STD_U16LE; } 24 | hid_t _go_hdf5_H5T_STD_U32BE() { return H5T_STD_U32BE; } 25 | hid_t _go_hdf5_H5T_STD_U32LE() { return H5T_STD_U32LE; } 26 | hid_t _go_hdf5_H5T_STD_U64BE() { return H5T_STD_U64BE; } 27 | hid_t _go_hdf5_H5T_STD_U64LE() { return H5T_STD_U64LE; } 28 | hid_t _go_hdf5_H5T_STD_B8BE() { return H5T_STD_B8BE; } 29 | hid_t _go_hdf5_H5T_STD_B8LE() { return H5T_STD_B8LE; } 30 | 31 | hid_t _go_hdf5_H5T_STD_B16BE() { return H5T_STD_B16BE; } 32 | hid_t _go_hdf5_H5T_STD_B16LE() { return H5T_STD_B16LE; } 33 | hid_t _go_hdf5_H5T_STD_B32BE() { return H5T_STD_B32BE; } 34 | hid_t _go_hdf5_H5T_STD_B32LE() { return H5T_STD_B32LE; } 35 | hid_t _go_hdf5_H5T_STD_B64BE() { return H5T_STD_B64BE; } 36 | hid_t _go_hdf5_H5T_STD_B64LE() { return H5T_STD_B64LE; } 37 | hid_t _go_hdf5_H5T_STD_REF_OBJ() { return H5T_STD_REF_OBJ; } 38 | hid_t _go_hdf5_H5T_STD_REF_DSETREG() { return H5T_STD_REF_DSETREG; } 39 | 40 | hid_t _go_hdf5_H5T_IEEE_F32BE() { return H5T_IEEE_F32BE; } 41 | hid_t _go_hdf5_H5T_IEEE_F32LE() { return H5T_IEEE_F32LE; } 42 | hid_t _go_hdf5_H5T_IEEE_F64BE() { return H5T_IEEE_F64BE; } 43 | hid_t _go_hdf5_H5T_IEEE_F64LE() { return H5T_IEEE_F64LE; } 44 | 45 | hid_t _go_hdf5_H5T_UNIX_D32BE() { return H5T_UNIX_D32BE; } 46 | hid_t _go_hdf5_H5T_UNIX_D32LE() { return H5T_UNIX_D32LE; } 47 | hid_t _go_hdf5_H5T_UNIX_D64BE() { return H5T_UNIX_D64BE; } 48 | hid_t _go_hdf5_H5T_UNIX_D64LE() { return H5T_UNIX_D64LE; } 49 | 50 | hid_t _go_hdf5_H5T_INTEL_I8() { return H5T_INTEL_I8; } 51 | hid_t _go_hdf5_H5T_INTEL_I16() { return H5T_INTEL_I16; } 52 | hid_t _go_hdf5_H5T_INTEL_I32() { return H5T_INTEL_I32; } 53 | hid_t _go_hdf5_H5T_INTEL_I64() { return H5T_INTEL_I64; } 54 | hid_t _go_hdf5_H5T_INTEL_U8() { return H5T_INTEL_U8; } 55 | hid_t _go_hdf5_H5T_INTEL_U16() { return H5T_INTEL_U16; } 56 | hid_t _go_hdf5_H5T_INTEL_U32() { return H5T_INTEL_U32; } 57 | hid_t _go_hdf5_H5T_INTEL_U64() { return H5T_INTEL_U64; } 58 | hid_t _go_hdf5_H5T_INTEL_B8() { return H5T_INTEL_B8; } 59 | hid_t _go_hdf5_H5T_INTEL_B16() { return H5T_INTEL_B16; } 60 | hid_t _go_hdf5_H5T_INTEL_B32() { return H5T_INTEL_B32; } 61 | hid_t _go_hdf5_H5T_INTEL_B64() { return H5T_INTEL_B64; } 62 | hid_t _go_hdf5_H5T_INTEL_F32() { return H5T_INTEL_F32; } 63 | hid_t _go_hdf5_H5T_INTEL_F64() { return H5T_INTEL_F64; } 64 | 65 | hid_t _go_hdf5_H5T_ALPHA_I8() { return H5T_ALPHA_I8; } 66 | hid_t _go_hdf5_H5T_ALPHA_I16() { return H5T_ALPHA_I16; } 67 | hid_t _go_hdf5_H5T_ALPHA_I32() { return H5T_ALPHA_I32; } 68 | hid_t _go_hdf5_H5T_ALPHA_I64() { return H5T_ALPHA_I64; } 69 | hid_t _go_hdf5_H5T_ALPHA_U8() { return H5T_ALPHA_U8; } 70 | hid_t _go_hdf5_H5T_ALPHA_U16() { return H5T_ALPHA_U16; } 71 | hid_t _go_hdf5_H5T_ALPHA_U32() { return H5T_ALPHA_U32; } 72 | hid_t _go_hdf5_H5T_ALPHA_U64() { return H5T_ALPHA_U64; } 73 | hid_t _go_hdf5_H5T_ALPHA_B8() { return H5T_ALPHA_B8; } 74 | hid_t _go_hdf5_H5T_ALPHA_B16() { return H5T_ALPHA_B16; } 75 | hid_t _go_hdf5_H5T_ALPHA_B32() { return H5T_ALPHA_B32; } 76 | hid_t _go_hdf5_H5T_ALPHA_B64() { return H5T_ALPHA_B64; } 77 | hid_t _go_hdf5_H5T_ALPHA_F32() { return H5T_ALPHA_F32; } 78 | hid_t _go_hdf5_H5T_ALPHA_F64() { return H5T_ALPHA_F64; } 79 | 80 | hid_t _go_hdf5_H5T_MIPS_I8() { return H5T_MIPS_I8; } 81 | hid_t _go_hdf5_H5T_MIPS_I16() { return H5T_MIPS_I16; } 82 | hid_t _go_hdf5_H5T_MIPS_I32() { return H5T_MIPS_I32; } 83 | hid_t _go_hdf5_H5T_MIPS_I64() { return H5T_MIPS_I64; } 84 | hid_t _go_hdf5_H5T_MIPS_U8() { return H5T_MIPS_U8; } 85 | hid_t _go_hdf5_H5T_MIPS_U16() { return H5T_MIPS_U16; } 86 | hid_t _go_hdf5_H5T_MIPS_U32() { return H5T_MIPS_U32; } 87 | hid_t _go_hdf5_H5T_MIPS_U64() { return H5T_MIPS_U64; } 88 | hid_t _go_hdf5_H5T_MIPS_B8() { return H5T_MIPS_B8; } 89 | hid_t _go_hdf5_H5T_MIPS_B16() { return H5T_MIPS_B16; } 90 | hid_t _go_hdf5_H5T_MIPS_B32() { return H5T_MIPS_B32; } 91 | hid_t _go_hdf5_H5T_MIPS_B64() { return H5T_MIPS_B64; } 92 | hid_t _go_hdf5_H5T_MIPS_F32() { return H5T_MIPS_F32; } 93 | hid_t _go_hdf5_H5T_MIPS_F64() { return H5T_MIPS_F64; } 94 | 95 | hid_t _go_hdf5_H5T_NATIVE_CHAR() { return H5T_NATIVE_CHAR; } 96 | hid_t _go_hdf5_H5T_NATIVE_INT() { return H5T_NATIVE_INT; } 97 | hid_t _go_hdf5_H5T_NATIVE_FLOAT() { return H5T_NATIVE_FLOAT; } 98 | hid_t _go_hdf5_H5T_NATIVE_SCHAR() { return H5T_NATIVE_SCHAR; } 99 | hid_t _go_hdf5_H5T_NATIVE_UCHAR() { return H5T_NATIVE_UCHAR; } 100 | hid_t _go_hdf5_H5T_NATIVE_SHORT() { return H5T_NATIVE_SHORT; } 101 | hid_t _go_hdf5_H5T_NATIVE_USHORT() { return H5T_NATIVE_USHORT; } 102 | hid_t _go_hdf5_H5T_NATIVE_UINT() { return H5T_NATIVE_UINT; } 103 | hid_t _go_hdf5_H5T_NATIVE_LONG() { return H5T_NATIVE_LONG; } 104 | hid_t _go_hdf5_H5T_NATIVE_ULONG() { return H5T_NATIVE_ULONG; } 105 | hid_t _go_hdf5_H5T_NATIVE_LLONG() { return H5T_NATIVE_LLONG; } 106 | hid_t _go_hdf5_H5T_NATIVE_ULLONG() { return H5T_NATIVE_ULLONG; } 107 | hid_t _go_hdf5_H5T_NATIVE_DOUBLE() { return H5T_NATIVE_DOUBLE; } 108 | #if H5_SIZEOF_LONG_DOUBLE !=0 109 | hid_t _go_hdf5_H5T_NATIVE_LDOUBLE() { return H5T_NATIVE_LDOUBLE; } 110 | #endif 111 | hid_t _go_hdf5_H5T_NATIVE_B8() { return H5T_NATIVE_B8; } 112 | hid_t _go_hdf5_H5T_NATIVE_B16() { return H5T_NATIVE_B16; } 113 | hid_t _go_hdf5_H5T_NATIVE_B32() { return H5T_NATIVE_B32; } 114 | hid_t _go_hdf5_H5T_NATIVE_B64() { return H5T_NATIVE_B64; } 115 | hid_t _go_hdf5_H5T_NATIVE_OPAQUE() { return H5T_NATIVE_OPAQUE; } 116 | hid_t _go_hdf5_H5T_NATIVE_HSIZE() { return H5T_NATIVE_HSIZE; } 117 | hid_t _go_hdf5_H5T_NATIVE_HSSIZE() { return H5T_NATIVE_HSSIZE; } 118 | hid_t _go_hdf5_H5T_NATIVE_HERR() { return H5T_NATIVE_HERR; } 119 | hid_t _go_hdf5_H5T_NATIVE_HBOOL() { return H5T_NATIVE_HBOOL; } 120 | 121 | hid_t _go_hdf5_H5T_NATIVE_INT8() { return H5T_NATIVE_INT8; } 122 | hid_t _go_hdf5_H5T_NATIVE_UINT8() { return H5T_NATIVE_UINT8; } 123 | hid_t _go_hdf5_H5T_NATIVE_INT16() { return H5T_NATIVE_INT16; } 124 | hid_t _go_hdf5_H5T_NATIVE_UINT16() { return H5T_NATIVE_UINT16; } 125 | hid_t _go_hdf5_H5T_NATIVE_INT32() { return H5T_NATIVE_INT32; } 126 | hid_t _go_hdf5_H5T_NATIVE_UINT32() { return H5T_NATIVE_UINT32; } 127 | hid_t _go_hdf5_H5T_NATIVE_INT64() { return H5T_NATIVE_INT64; } 128 | hid_t _go_hdf5_H5T_NATIVE_UINT64() { return H5T_NATIVE_UINT64; } 129 | */ 130 | import "C" 131 | 132 | // list of predefined hdf5 data types 133 | var ( 134 | T_C_S1 *Datatype = NewDatatype(C._go_hdf5_H5T_C_S1()) 135 | T_FORTRAN_S1 *Datatype = NewDatatype(C._go_hdf5_H5T_FORTRAN_S1()) 136 | 137 | T_STD_I8BE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_I8BE()) 138 | T_STD_I8LE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_I8LE()) 139 | T_STD_I16BE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_I16BE()) 140 | T_STD_I16LE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_I16LE()) 141 | T_STD_I32BE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_I32BE()) 142 | T_STD_I32LE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_I32LE()) 143 | T_STD_I64BE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_I64BE()) 144 | T_STD_I64LE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_I64LE()) 145 | T_STD_U8BE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_U8BE()) 146 | T_STD_U8LE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_U8LE()) 147 | T_STD_U16BE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_U16BE()) 148 | T_STD_U16LE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_U16LE()) 149 | T_STD_U32BE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_U32BE()) 150 | T_STD_U32LE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_U32LE()) 151 | T_STD_U64BE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_U64BE()) 152 | T_STD_U64LE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_U64LE()) 153 | T_STD_B8BE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_B8BE()) 154 | T_STD_B8LE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_B8LE()) 155 | 156 | T_STD_B16BE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_B16BE()) 157 | T_STD_B16LE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_B16LE()) 158 | T_STD_B32BE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_B32BE()) 159 | T_STD_B32LE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_B32LE()) 160 | T_STD_B64BE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_B64BE()) 161 | T_STD_B64LE *Datatype = NewDatatype(C._go_hdf5_H5T_STD_B64LE()) 162 | T_STD_REF_OBJ *Datatype = NewDatatype(C._go_hdf5_H5T_STD_REF_OBJ()) 163 | T_STD_REF_DSETREG *Datatype = NewDatatype(C._go_hdf5_H5T_STD_REF_DSETREG()) 164 | 165 | T_IEEE_F32BE *Datatype = NewDatatype(C._go_hdf5_H5T_IEEE_F32BE()) 166 | T_IEEE_F32LE *Datatype = NewDatatype(C._go_hdf5_H5T_IEEE_F32LE()) 167 | T_IEEE_F64BE *Datatype = NewDatatype(C._go_hdf5_H5T_IEEE_F64BE()) 168 | T_IEEE_F64LE *Datatype = NewDatatype(C._go_hdf5_H5T_IEEE_F64LE()) 169 | 170 | T_UNIX_D32BE *Datatype = NewDatatype(C._go_hdf5_H5T_UNIX_D32BE()) 171 | T_UNIX_D32LE *Datatype = NewDatatype(C._go_hdf5_H5T_UNIX_D32LE()) 172 | T_UNIX_D64BE *Datatype = NewDatatype(C._go_hdf5_H5T_UNIX_D64BE()) 173 | T_UNIX_D64LE *Datatype = NewDatatype(C._go_hdf5_H5T_UNIX_D64LE()) 174 | 175 | T_INTEL_I8 *Datatype = NewDatatype(C._go_hdf5_H5T_INTEL_I8()) 176 | T_INTEL_I16 *Datatype = NewDatatype(C._go_hdf5_H5T_INTEL_I16()) 177 | T_INTEL_I32 *Datatype = NewDatatype(C._go_hdf5_H5T_INTEL_I32()) 178 | T_INTEL_I64 *Datatype = NewDatatype(C._go_hdf5_H5T_INTEL_I64()) 179 | T_INTEL_U8 *Datatype = NewDatatype(C._go_hdf5_H5T_INTEL_U8()) 180 | T_INTEL_U16 *Datatype = NewDatatype(C._go_hdf5_H5T_INTEL_U16()) 181 | T_INTEL_U32 *Datatype = NewDatatype(C._go_hdf5_H5T_INTEL_U32()) 182 | T_INTEL_U64 *Datatype = NewDatatype(C._go_hdf5_H5T_INTEL_U64()) 183 | T_INTEL_B8 *Datatype = NewDatatype(C._go_hdf5_H5T_INTEL_B8()) 184 | T_INTEL_B16 *Datatype = NewDatatype(C._go_hdf5_H5T_INTEL_B16()) 185 | T_INTEL_B32 *Datatype = NewDatatype(C._go_hdf5_H5T_INTEL_B32()) 186 | T_INTEL_B64 *Datatype = NewDatatype(C._go_hdf5_H5T_INTEL_B64()) 187 | T_INTEL_F32 *Datatype = NewDatatype(C._go_hdf5_H5T_INTEL_F32()) 188 | T_INTEL_F64 *Datatype = NewDatatype(C._go_hdf5_H5T_INTEL_F64()) 189 | 190 | T_ALPHA_I8 *Datatype = NewDatatype(C._go_hdf5_H5T_ALPHA_I8()) 191 | T_ALPHA_I16 *Datatype = NewDatatype(C._go_hdf5_H5T_ALPHA_I16()) 192 | T_ALPHA_I32 *Datatype = NewDatatype(C._go_hdf5_H5T_ALPHA_I32()) 193 | T_ALPHA_I64 *Datatype = NewDatatype(C._go_hdf5_H5T_ALPHA_I64()) 194 | T_ALPHA_U8 *Datatype = NewDatatype(C._go_hdf5_H5T_ALPHA_U8()) 195 | T_ALPHA_U16 *Datatype = NewDatatype(C._go_hdf5_H5T_ALPHA_U16()) 196 | T_ALPHA_U32 *Datatype = NewDatatype(C._go_hdf5_H5T_ALPHA_U32()) 197 | T_ALPHA_U64 *Datatype = NewDatatype(C._go_hdf5_H5T_ALPHA_U64()) 198 | T_ALPHA_B8 *Datatype = NewDatatype(C._go_hdf5_H5T_ALPHA_B8()) 199 | T_ALPHA_B16 *Datatype = NewDatatype(C._go_hdf5_H5T_ALPHA_B16()) 200 | T_ALPHA_B32 *Datatype = NewDatatype(C._go_hdf5_H5T_ALPHA_B32()) 201 | T_ALPHA_B64 *Datatype = NewDatatype(C._go_hdf5_H5T_ALPHA_B64()) 202 | T_ALPHA_F32 *Datatype = NewDatatype(C._go_hdf5_H5T_ALPHA_F32()) 203 | T_ALPHA_F64 *Datatype = NewDatatype(C._go_hdf5_H5T_ALPHA_F64()) 204 | 205 | T_MIPS_I8 *Datatype = NewDatatype(C._go_hdf5_H5T_MIPS_I8()) 206 | T_MIPS_I16 *Datatype = NewDatatype(C._go_hdf5_H5T_MIPS_I16()) 207 | T_MIPS_I32 *Datatype = NewDatatype(C._go_hdf5_H5T_MIPS_I32()) 208 | T_MIPS_I64 *Datatype = NewDatatype(C._go_hdf5_H5T_MIPS_I64()) 209 | T_MIPS_U8 *Datatype = NewDatatype(C._go_hdf5_H5T_MIPS_U8()) 210 | T_MIPS_U16 *Datatype = NewDatatype(C._go_hdf5_H5T_MIPS_U16()) 211 | T_MIPS_U32 *Datatype = NewDatatype(C._go_hdf5_H5T_MIPS_U32()) 212 | T_MIPS_U64 *Datatype = NewDatatype(C._go_hdf5_H5T_MIPS_U64()) 213 | T_MIPS_B8 *Datatype = NewDatatype(C._go_hdf5_H5T_MIPS_B8()) 214 | T_MIPS_B16 *Datatype = NewDatatype(C._go_hdf5_H5T_MIPS_B16()) 215 | T_MIPS_B32 *Datatype = NewDatatype(C._go_hdf5_H5T_MIPS_B32()) 216 | T_MIPS_B64 *Datatype = NewDatatype(C._go_hdf5_H5T_MIPS_B64()) 217 | T_MIPS_F32 *Datatype = NewDatatype(C._go_hdf5_H5T_MIPS_F32()) 218 | T_MIPS_F64 *Datatype = NewDatatype(C._go_hdf5_H5T_MIPS_F64()) 219 | 220 | T_NATIVE_CHAR *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_CHAR()) 221 | T_NATIVE_INT *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_INT()) 222 | T_NATIVE_FLOAT *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_FLOAT()) 223 | T_NATIVE_SCHAR *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_SCHAR()) 224 | T_NATIVE_UCHAR *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_UCHAR()) 225 | T_NATIVE_SHORT *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_SHORT()) 226 | T_NATIVE_USHORT *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_USHORT()) 227 | T_NATIVE_UINT *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_UINT()) 228 | T_NATIVE_LONG *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_LONG()) 229 | T_NATIVE_ULONG *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_ULONG()) 230 | T_NATIVE_LLONG *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_LLONG()) 231 | T_NATIVE_ULLONG *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_ULLONG()) 232 | T_NATIVE_DOUBLE *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_DOUBLE()) 233 | //#if H5_SIZEOF_LONG_DOUBLE !=0 234 | T_NATIVE_LDOUBLE *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_LDOUBLE()) 235 | //#endif 236 | T_NATIVE_B8 *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_B8()) 237 | T_NATIVE_B16 *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_B16()) 238 | T_NATIVE_B32 *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_B32()) 239 | T_NATIVE_B64 *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_B64()) 240 | T_NATIVE_OPAQUE *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_OPAQUE()) 241 | T_NATIVE_HSIZE *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_HSIZE()) 242 | T_NATIVE_HSSIZE *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_HSSIZE()) 243 | T_NATIVE_HERR *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_HERR()) 244 | T_NATIVE_HBOOL *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_HBOOL()) 245 | 246 | T_NATIVE_INT8 *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_INT8()) 247 | T_NATIVE_UINT8 *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_UINT8()) 248 | T_NATIVE_INT16 *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_INT16()) 249 | T_NATIVE_UINT16 *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_UINT16()) 250 | T_NATIVE_INT32 *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_INT32()) 251 | T_NATIVE_UINT32 *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_UINT32()) 252 | T_NATIVE_INT64 *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_INT64()) 253 | T_NATIVE_UINT64 *Datatype = NewDatatype(C._go_hdf5_H5T_NATIVE_UINT64()) 254 | 255 | T_GO_STRING *Datatype = makeGoStringDatatype() 256 | ) 257 | 258 | // 259 | var h5t_VARIABLE int64 = C.H5T_VARIABLE 260 | 261 | func makeGoStringDatatype() *Datatype { 262 | dt, err := T_C_S1.Copy() 263 | if err != nil { 264 | panic(err) 265 | } 266 | err = dt.SetSize(uint(h5t_VARIABLE)) 267 | if err != nil { 268 | panic(err) 269 | } 270 | return dt 271 | } 272 | -------------------------------------------------------------------------------- /h5t_test.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestSimpleDatatypes(t *testing.T) { 10 | // Smoke tests for the simple datatypes 11 | tests := []interface{}{ 12 | int(0), 13 | int8(0), 14 | int16(0), 15 | int32(0), 16 | int64(0), 17 | uint(0), 18 | uint8(0), 19 | uint16(0), 20 | uint32(0), 21 | uint64(0), 22 | float32(0), 23 | float64(0), 24 | string(""), 25 | } 26 | 27 | for test := range tests { 28 | NewDatatypeFromValue(test) 29 | } 30 | } 31 | 32 | // Test for array datatypes. Checks that the number of dimensions is correct. 33 | func TestArrayDatatype(t *testing.T) { 34 | tests := map[int]interface{}{ 35 | 1: [8]int{1, 1, 2, 3, 5, 8, 13}, 36 | 2: [2][2]int{{1, 2}, {3, 4}}, 37 | 3: [2][2][2]int{{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}}, 38 | } 39 | 40 | for dims, val := range tests { 41 | dt, err := NewDatatypeFromValue(val) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | adt := ArrayType{*dt} 46 | if adt.NDims() != dims { 47 | t.Errorf("wrong number of dimensions: got %d, want %d", adt.NDims(), dims) 48 | } 49 | } 50 | } 51 | 52 | func TestStructDatatype(t *testing.T) { 53 | test := struct { 54 | a int32 55 | b string 56 | c struct { 57 | a int32 58 | b string 59 | } 60 | }{} 61 | 62 | // Test that the type can be constructed and that the number of 63 | // members is as expected. 64 | dtype, err := NewDatatypeFromValue(test) 65 | if err != nil { 66 | t.Fatal(err) 67 | } 68 | dt := CompoundType{*dtype} 69 | if dt.NMembers() != 3 { 70 | t.Errorf("wrong number of members: got %d, want %d", dt.NMembers(), 3) 71 | } 72 | 73 | memberClasses := []TypeClass{ 74 | T_INTEGER, 75 | T_STRING, 76 | T_COMPOUND, 77 | } 78 | // Test the member classes, and also test that they can be constructed 79 | for idx, class := range memberClasses { 80 | if dt.MemberClass(idx) != class { 81 | t.Errorf("wrong TypeClass: got %v, want %v", dt.MemberClass(idx), class) 82 | } 83 | _, err := dt.MemberType(idx) 84 | if err != nil { 85 | t.Fatal(err) 86 | } 87 | } 88 | 89 | // Test the member names 90 | memberNames := []string{"a", "b", "c"} 91 | for idx, name := range memberNames { 92 | if dt.MemberName(idx) != name { 93 | t.Errorf("wrong name: got %q, want %q", dt.MemberName(idx), name) 94 | } 95 | if dt.MemberIndex(name) != idx { 96 | t.Errorf("wrong index: got %d, want %d", dt.MemberIndex(name), idx) 97 | } 98 | } 99 | 100 | // Pack the datatype, otherwise offsets are implementation defined 101 | dt.Pack() 102 | memberOffsets := []int{0, 4, 12} 103 | for idx, offset := range memberOffsets { 104 | if dt.MemberOffset(idx) != offset { 105 | t.Errorf("wrong offset: got %d, want %d", dt.MemberOffset(idx), offset) 106 | } 107 | } 108 | } 109 | 110 | func TestCloseBehavior(t *testing.T) { 111 | var s struct { 112 | a int 113 | b float64 114 | } 115 | dtype, err := NewDatatypeFromValue(s) 116 | if err != nil { 117 | t.Fatal(err) 118 | } 119 | defer dtype.Close() 120 | 121 | // Sleep to ensure GC runs before returning 122 | runtime.GC() 123 | time.Sleep(100 * time.Millisecond) 124 | } 125 | -------------------------------------------------------------------------------- /hdf5.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | // #include "hdf5.h" 4 | import "C" 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | // init initializes the hdf5 library 11 | func init() { 12 | err := h5err(C.H5open()) 13 | if err != nil { 14 | err_str := fmt.Sprintf("pb calling H5open(): %s", err) 15 | panic(err_str) 16 | } 17 | } 18 | 19 | // hdferror wraps hdf5 int-based error codes 20 | type h5error struct { 21 | code int 22 | } 23 | 24 | func (h h5error) Error() string { 25 | return fmt.Sprintf("code %d", h.code) 26 | } 27 | 28 | func h5err(herr C.herr_t) error { 29 | if herr < 0 { 30 | return h5error{code: int(herr)} 31 | } 32 | return nil 33 | } 34 | 35 | func checkID(hid C.hid_t) error { 36 | if hid < 0 { 37 | return h5error{code: int(hid)} 38 | } 39 | return nil 40 | } 41 | 42 | // Close flushes all data to disk, closes all open identifiers, and cleans up memory. 43 | // It should generally be called before your application exits. 44 | func Close() error { 45 | return h5err(C.H5close()) 46 | } 47 | 48 | // Version represents the currently used hdf5 library version 49 | type Version struct { 50 | Major uint 51 | Minor uint 52 | Release uint 53 | } 54 | 55 | func (v Version) String() string { 56 | return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Release) 57 | } 58 | 59 | // LibVersion returns version information for the HDF5 library. 60 | func LibVersion() (Version, error) { 61 | var maj, min, rel C.uint 62 | var v Version 63 | err := h5err(C.H5get_libversion(&maj, &min, &rel)) 64 | if err == nil { 65 | v.Major = uint(maj) 66 | v.Minor = uint(min) 67 | v.Release = uint(rel) 68 | } 69 | return v, err 70 | } 71 | 72 | // GarbageCollect collects garbage on all free-lists of all types. 73 | func GarbageCollect() error { 74 | return h5err(C.H5garbage_collect()) 75 | } 76 | 77 | // Object represents an hdf5 object. 78 | type Object interface { 79 | Name() string 80 | Id() int 81 | File() *File 82 | } 83 | -------------------------------------------------------------------------------- /hdf5_test.go: -------------------------------------------------------------------------------- 1 | package hdf5 2 | 3 | import "testing" 4 | 5 | func TestLibVersion(t *testing.T) { 6 | v, err := LibVersion() 7 | if err != nil { 8 | t.Fatalf("Could not get HDF5 library version: %s", err) 9 | } 10 | if v.Major < 1 || (v.Major == 1 && v.Minor < 8) { 11 | t.Fatalf("go-hdf5 requires HDF5 > 1.8.0, detected %s", v) 12 | } 13 | } 14 | --------------------------------------------------------------------------------