├── go.mod ├── .golangci.yml ├── Makefile ├── cmem ├── cmem.go ├── encoder_test.go └── encoder.go ├── hdf5_test.go ├── cmd ├── test-go-hdf5 │ └── main.go ├── test-go-cpxcmpd │ ├── main_test.go │ └── main.go ├── test-go-extend-ds │ └── main.go ├── test-go-table-01 │ ├── main_test.go │ └── main.go └── test-go-table-01-readback │ └── main.go ├── .github ├── PULL_REQUEST_TEMPLATE ├── ISSUE_TEMPLATE └── workflows │ └── ci.yml ├── .travis.yml ├── h5d_public.go ├── cgoflags.go ├── errors.go ├── README.md ├── LICENSE ├── hdf5.go ├── h5a_attribute_test.go ├── h5i_identifier.go ├── h5s_dataspace_test.go ├── h5a_attribute.go ├── h5g_group_test.go ├── h5f_file_test.go ├── h5p_proplist.go ├── h5f_file.go ├── h5s_dataspace.go ├── h5d_dataset_test.go ├── h5d_dataset.go ├── h5t_types_test.go ├── h5pt_table.go ├── h5p_proplist_test.go ├── h5pt_table_test.go ├── h5g_group.go ├── h5t_shim.go └── h5t_types.go /go.mod: -------------------------------------------------------------------------------- 1 | module gonum.org/v1/hdf5 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | service: 2 | prepare: 3 | - apt-get update && apt-get install -y libhdf5-serial-dev 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /cmem/cmem.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2018 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package cmem provides helper functionality for accessing memory in a C 6 | // compatible fashion. 7 | package cmem 8 | 9 | // CMarshaler is an interface for types that can marshal their data into a 10 | // C compatible binary layout. 11 | type CMarshaler interface { 12 | MarshalC() ([]byte, error) 13 | } 14 | -------------------------------------------------------------------------------- /hdf5_test.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | import "testing" 8 | 9 | func TestLibVersion(t *testing.T) { 10 | v, err := LibVersion() 11 | if err != nil { 12 | t.Fatalf("Could not get HDF5 library version: %s", err) 13 | } 14 | if v.Major < 1 || (v.Major == 1 && v.Minor < 8) { 15 | t.Fatalf("go-hdf5 requires HDF5 > 1.8.0, detected %s", v) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /cmd/test-go-hdf5/main.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | 10 | "gonum.org/v1/hdf5" 11 | ) 12 | 13 | func main() { 14 | fmt.Println("=== go-hdf5 ===") 15 | version, err := hdf5.LibVersion() 16 | if err != nil { 17 | fmt.Printf("** error ** %s\n", err) 18 | return 19 | } 20 | fmt.Printf("=== version: %s", version) 21 | fmt.Println("=== bye.") 22 | } 23 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | Please take a look. 2 | 3 | 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.14.x 4 | - 1.13.x 5 | - master 6 | 7 | arch: 8 | - amd64 9 | - arm64 10 | 11 | matrix: 12 | fast_finish: true 13 | allow_failures: 14 | - go: master 15 | 16 | go_import_path: gonum.org/v1/hdf5 17 | 18 | sudo: false 19 | 20 | addons: 21 | apt: 22 | packages: 23 | - libhdf5-serial-dev 24 | 25 | env: 26 | global: 27 | - GODEBUG=cgocheck=0 28 | 29 | notifications: 30 | email: 31 | recipients: 32 | - binet@cern.ch 33 | on_success: change 34 | on_failure: always 35 | 36 | script: 37 | - go get -d -t -v ./... 38 | - go install -v ./... 39 | - go test -v ./... 40 | 41 | -------------------------------------------------------------------------------- /cmd/test-go-cpxcmpd/main_test.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main_test 6 | 7 | import ( 8 | "bytes" 9 | "os" 10 | "os/exec" 11 | "testing" 12 | ) 13 | 14 | func TestCpxCmpd(t *testing.T) { 15 | const fname = "SDScompound.h5" 16 | stdout := new(bytes.Buffer) 17 | cmd := exec.Command("test-go-cpxcmpd") 18 | cmd.Stdout = stdout 19 | cmd.Stderr = stdout 20 | cmd.Stdin = os.Stdin 21 | 22 | err := cmd.Run() 23 | if err != nil { 24 | t.Fatalf("error: %v\n%s\n", err, string(stdout.Bytes())) 25 | } 26 | os.Remove(fname) 27 | } 28 | -------------------------------------------------------------------------------- /h5d_public.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2019 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | // #include "hdf5.h" 8 | import "C" 9 | 10 | // Used to unset chunk cache configuration parameter. 11 | // https://support.hdfgroup.org/HDF5/doc/RM/RM_H5P.html#Property-SetChunkCache 12 | const ( 13 | D_CHUNK_CACHE_NSLOTS_DEFAULT int = -1 // The number of chunk slots in the raw data chunk cache for this dataset 14 | D_CHUNK_CACHE_NBYTES_DEFAULT int = -1 // The total size of the raw data chunk cache for this dataset 15 | D_CHUNK_CACHE_W0_DEFAULT float64 = -1 // The chunk preemption policy for this dataset 16 | ) 17 | -------------------------------------------------------------------------------- /cgoflags.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | // #cgo LDFLAGS: -lhdf5 -lhdf5_hl 8 | // #cgo darwin CFLAGS: -I/usr/local/include 9 | // #cgo darwin LDFLAGS: -L/usr/local/lib 10 | // #cgo linux,!arm64 CFLAGS: -I/usr/local/include, -I/usr/lib/x86_64-linux-gnu/hdf5/serial/include 11 | // #cgo linux,!arm64 LDFLAGS: -L/usr/local/lib, -L/usr/lib/x86_64-linux-gnu/hdf5/serial/ 12 | // #cgo linux,arm64 CFLAGS: -I/usr/local/include, -I/usr/lib/aarch64-linux-gnu/hdf5/serial/include 13 | // #cgo linux,arm64 LDFLAGS: -L/usr/local/lib, -L/usr/lib/aarch64-linux-gnu/hdf5/serial/ 14 | // #include "hdf5.h" 15 | import "C" 16 | -------------------------------------------------------------------------------- /cmd/test-go-extend-ds/main.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | 10 | "gonum.org/v1/hdf5" 11 | ) 12 | 13 | func main() { 14 | 15 | fname := "SDSextendible.h5" 16 | //dsname:= "ExtendibleArray" 17 | //NX := 10 18 | //NY := 5 19 | //RANK:= 2 20 | 21 | //dims := []int{3, 3} // dset dimensions at creation 22 | //maxdims:= []int{hdf5.S_UNLIMITED, hdf5.S_UNLIMITED} 23 | 24 | //mspace := hdf5.CreateDataspace(dims, maxdims) 25 | 26 | // create a new file 27 | f, err := hdf5.CreateFile(fname, hdf5.F_ACC_TRUNC) 28 | if err != nil { 29 | panic(err) 30 | } 31 | 32 | fmt.Printf(":: file [%s] created\n", f.Name()) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /cmd/test-go-table-01/main_test.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main_test 6 | 7 | import ( 8 | "bytes" 9 | "os" 10 | "os/exec" 11 | "testing" 12 | ) 13 | 14 | func TestTableRWCmd(t *testing.T) { 15 | const fname = "ex_table_01.h5" 16 | func() { 17 | stdout := new(bytes.Buffer) 18 | cmd := exec.Command("test-go-table-01") 19 | cmd.Stdout = stdout 20 | cmd.Stderr = stdout 21 | cmd.Stdin = os.Stdin 22 | 23 | err := cmd.Run() 24 | if err != nil { 25 | t.Fatalf("error: %v\n%s\n", err, string(stdout.Bytes())) 26 | } 27 | }() 28 | 29 | func() { 30 | stdout := new(bytes.Buffer) 31 | cmd := exec.Command("test-go-table-01-readback") 32 | cmd.Stdout = stdout 33 | cmd.Stderr = stdout 34 | cmd.Stdin = os.Stdin 35 | 36 | err := cmd.Run() 37 | if err != nil { 38 | t.Fatalf("error: %v\n%s\n", err, string(stdout.Bytes())) 39 | } 40 | }() 41 | os.Remove(fname) 42 | } 43 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | // #include "hdf5.h" 8 | // 9 | // herr_t _go_hdf5_unsilence_errors(void) { 10 | // return H5Eset_auto2(H5E_DEFAULT, (H5E_auto2_t)(H5Eprint), stderr); 11 | // } 12 | // 13 | // herr_t _go_hdf5_silence_errors(void) { 14 | // return H5Eset_auto2(H5E_DEFAULT, NULL, NULL); 15 | // } 16 | import "C" 17 | 18 | import ( 19 | "fmt" 20 | ) 21 | 22 | // DisplayErrors enables/disables HDF5's automatic error printing 23 | func DisplayErrors(on bool) error { 24 | var err error 25 | if on { 26 | err = h5err(C._go_hdf5_unsilence_errors()) 27 | } else { 28 | err = h5err(C._go_hdf5_silence_errors()) 29 | } 30 | if err != nil { 31 | return fmt.Errorf("hdf5: could not call H5E_set_auto(): %s", err) 32 | } 33 | return nil 34 | } 35 | 36 | func init() { 37 | if err := DisplayErrors(false); err != nil { 38 | panic(err) 39 | } 40 | } 41 | 42 | // EOF 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | 10 | ### What are you trying to do? 11 | 12 | 13 | ### What did you do? 14 | 15 | 16 | 17 | ### What did you expect to happen? 18 | 19 | 20 | 21 | ### What actually happened? 22 | 23 | 24 | 25 | ### What version of Go, Gonum, Gonum/netlib and libhdf5 are you using? 26 | 31 | 32 | 33 | ### Does this issue reproduce with the current master? 34 | 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hdf5 2 | 3 | [![Build status](https://github.com/gonum/hdf5/workflows/CI/badge.svg)](https://github.com/gonum/hdf5/actions) 4 | [![Build Status](https://secure.travis-ci.org/gonum/hdf5.png)](http://travis-ci.org/gonum/hdf5) 5 | [![GoDoc](https://godoc.org/gonum.org/v1/hdf5?status.svg)](https://godoc.org/gonum.org/v1/hdf5) 6 | 7 | Naive ``cgo`` bindings for the ``C-API`` of ``hdf5``. 8 | 9 | **WIP: No stable API for this package yet.** 10 | 11 | **NOTE** that starting with Go >= 1.6, one needs to run with `GODEBUG=cgocheck=0` to disable the new stricter `CGo` rules. 12 | 13 | ## Example 14 | 15 | - Hello world example: [cmd/test-go-hdf5/main.go](https://github.com/gonum/hdf5/blob/master/cmd/test-go-hdf5/main.go) 16 | 17 | - Writing/reading an ``hdf5`` with compound data: [cmd/test-go-cpxcmpd/main.go](https://github.com/gonum/hdf5/blob/master/cmd/test-go-cpxcmpd/main.go) 18 | 19 | ## Note 20 | 21 | - *Only* version *1.8.x* of ``HDF5`` is supported. 22 | - 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. 23 | 24 | 25 | ## Known problems 26 | 27 | - the ``h5pt`` packet table interface is broken. 28 | - support for structs with slices and strings as fields is broken 29 | 30 | ## License 31 | 32 | Please see github.com/gonum/gonum for general license information, contributors, authors, etc on the Gonum suite of packages. 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright ©2013 The Gonum 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 Gonum 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 | -------------------------------------------------------------------------------- /cmem/encoder_test.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2018 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmem 6 | 7 | import ( 8 | "bytes" 9 | "encoding/binary" 10 | "strconv" 11 | "testing" 12 | ) 13 | 14 | func TestEncode(t *testing.T) { 15 | for i, tc := range []struct { 16 | v interface{} 17 | want []byte 18 | }{ 19 | { 20 | v: struct { 21 | V1 uint8 22 | V2 uint64 23 | V3 uint8 24 | V4 uint16 25 | }{1, 2, 3, 4}, 26 | want: []byte{ 27 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |........| 28 | 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |........| 29 | 0x03, 0x00, 0x04, 0x00, /* */ // |....| 30 | }, 31 | }, 32 | { 33 | v: []int32{1, 20, 300, 4000, 50000, 600000, 7000000, 8000000}, 34 | want: []byte{ 35 | 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, // |........| 36 | 0x2c, 0x01, 0x00, 0x00, 0xa0, 0x0f, 0x00, 0x00, // |,.......| 37 | 0x50, 0xc3, 0x00, 0x00, 0xc0, 0x27, 0x09, 0x00, // |P....'..| 38 | 0xc0, 0xcf, 0x6a, 0x00, 0x00, 0x12, 0x7a, 0x00, // |..j...z.| 39 | }, 40 | }, 41 | } { 42 | t.Run(strconv.Itoa(i), func(t *testing.T) { 43 | oldEndian := nativeEndian 44 | nativeEndian = binary.LittleEndian 45 | defer func() { nativeEndian = oldEndian }() 46 | var enc Encoder 47 | err := enc.Encode(tc.v) 48 | if err != nil { 49 | t.Fatalf("could not encode: %v", err) 50 | } 51 | if !bytes.Equal(enc.Buf, tc.want) { 52 | t.Fatalf("encoding error:\ngot = %v\nwant= %v", enc.Buf, tc.want) 53 | } 54 | }) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /hdf5.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package hdf5 provides access to the HDF5 C library. 6 | package hdf5 // import "gonum.org/v1/hdf5" 7 | 8 | // #include "hdf5.h" 9 | import "C" 10 | 11 | import ( 12 | "fmt" 13 | ) 14 | 15 | // init initializes the hdf5 library 16 | func init() { 17 | err := h5err(C.H5open()) 18 | if err != nil { 19 | err_str := fmt.Sprintf("pb calling H5open(): %s", err) 20 | panic(err_str) 21 | } 22 | } 23 | 24 | // hdferror wraps hdf5 int-based error codes 25 | type h5error struct { 26 | code int 27 | } 28 | 29 | func (h h5error) Error() string { 30 | return fmt.Sprintf("code %d", h.code) 31 | } 32 | 33 | func h5err(herr C.herr_t) error { 34 | if herr < 0 { 35 | return h5error{code: int(herr)} 36 | } 37 | return nil 38 | } 39 | 40 | func checkID(hid C.hid_t) error { 41 | if hid < 0 { 42 | return h5error{code: int(hid)} 43 | } 44 | return nil 45 | } 46 | 47 | // Close flushes all data to disk, closes all open identifiers, and cleans up memory. 48 | // It should generally be called before your application exits. 49 | func Close() error { 50 | return h5err(C.H5close()) 51 | } 52 | 53 | // Version represents the currently used hdf5 library version 54 | type Version struct { 55 | Major uint 56 | Minor uint 57 | Release uint 58 | } 59 | 60 | func (v Version) String() string { 61 | return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Release) 62 | } 63 | 64 | // LibVersion returns version information for the HDF5 library. 65 | func LibVersion() (Version, error) { 66 | var maj, min, rel C.uint 67 | var v Version 68 | err := h5err(C.H5get_libversion(&maj, &min, &rel)) 69 | if err == nil { 70 | v.Major = uint(maj) 71 | v.Minor = uint(min) 72 | v.Release = uint(rel) 73 | } 74 | return v, err 75 | } 76 | 77 | // GarbageCollect collects garbage on all free-lists of all types. 78 | func GarbageCollect() error { 79 | return h5err(C.H5garbage_collect()) 80 | } 81 | 82 | // Object represents an hdf5 object. 83 | type Object interface { 84 | Name() string 85 | Id() int 86 | File() *File 87 | } 88 | -------------------------------------------------------------------------------- /h5a_attribute_test.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | import ( 8 | "os" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func TestWriteAttribute(t *testing.T) { 14 | DisplayErrors(true) 15 | defer DisplayErrors(false) 16 | defer os.Remove(fname) 17 | 18 | f, err := CreateFile(fname, F_ACC_TRUNC) 19 | if err != nil { 20 | t.Fatalf("CreateFile failed: %s\n", err) 21 | } 22 | defer f.Close() 23 | 24 | scalar, err := CreateDataspace(S_SCALAR) 25 | if err != nil { 26 | t.Fatalf("CreateDataspace failed: %s\n", err) 27 | } 28 | defer scalar.Close() 29 | 30 | dset, err := f.CreateDataset("dset", T_NATIVE_USHORT, scalar) 31 | if err != nil { 32 | t.Fatalf("CreateDataset failed: %s\n", err) 33 | } 34 | defer dset.Close() 35 | 36 | strVal := "I am a string attribute" 37 | intVal := 42 38 | fltVal := 1.234 39 | arrVal := [3]byte{128, 0, 255} 40 | 41 | attrs := map[string]struct { 42 | Value interface{} 43 | Type reflect.Type 44 | }{ 45 | "My string attribute": {&strVal, reflect.TypeOf(strVal)}, 46 | "My int attribute": {&intVal, reflect.TypeOf(intVal)}, 47 | "My float attribute": {&fltVal, reflect.TypeOf(fltVal)}, 48 | "My array attribute": {&arrVal, reflect.TypeOf(arrVal)}, 49 | } 50 | 51 | for name, v := range attrs { 52 | t.Run(name, func(t *testing.T) { 53 | dtype, err := NewDataTypeFromType(v.Type) 54 | if err != nil { 55 | t.Fatalf("NewDatatypeFromValue failed: %v", err) 56 | } 57 | defer dtype.Close() 58 | 59 | attr, err := dset.CreateAttribute(name, dtype, scalar) 60 | if err != nil { 61 | t.Fatalf("CreateAttribute failed: %v", err) 62 | } 63 | defer attr.Close() 64 | 65 | if err := attr.Write(v.Value, dtype); err != nil { 66 | t.Fatalf("Attribute write failed: %v", err) 67 | } 68 | 69 | got := reflect.New(v.Type).Elem() 70 | if err := attr.Read(got.Addr().Interface(), dtype); err != nil { 71 | t.Fatalf("Attribute read failed: %v", err) 72 | } 73 | 74 | want := reflect.ValueOf(v.Value).Elem().Interface() 75 | if !reflect.DeepEqual(want, got.Interface()) { 76 | t.Fatalf("round trip failed:\ngot = %v\nwant= %v\n", got.Interface(), want) 77 | } 78 | }) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | 7 | jobs: 8 | 9 | build: 10 | name: Build 11 | strategy: 12 | matrix: 13 | go-version: [1.16.x, 1.15.x] 14 | platform: [ubuntu-latest, windows-latest] 15 | 16 | runs-on: ${{ matrix.platform }} 17 | env: 18 | GO111MODULE: on 19 | GOPATH: ${{ github.workspace }} 20 | GODEBUG: cgocheck=0 21 | defaults: 22 | run: 23 | working-directory: ${{ env.GOPATH }}/src/gonum.org/v1/hdf5 24 | 25 | steps: 26 | - name: Install Go 27 | uses: actions/setup-go@v2 28 | with: 29 | go-version: ${{ matrix.go-version }} 30 | 31 | - name: Cache-Go 32 | uses: actions/cache@v1 33 | with: 34 | path: | 35 | ~/go/pkg/mod # Module download cache 36 | ~/.cache/go-build # Build cache (Linux) 37 | ~/Library/Caches/go-build # Build cache (Mac) 38 | '%LocalAppData%\go-build' # Build cache (Windows) 39 | 40 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 41 | restore-keys: | 42 | ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 43 | 44 | - name: Checkout code 45 | uses: actions/checkout@v2 46 | with: 47 | path: ${{ env.GOPATH }}/src/gonum.org/v1/hdf5 48 | 49 | - name: Install Windows packages 50 | if: matrix.platform == 'windows-latest' 51 | uses: msys2/setup-msys2@v2 52 | with: 53 | update: true 54 | install: mingw-w64-x86_64-toolchain mingw-w64-x86_64-hdf5 55 | msystem: MINGW64 56 | path-type: inherit 57 | 58 | - name: Install Linux packages 59 | if: matrix.platform == 'ubuntu-latest' 60 | run: | 61 | sudo apt-get update 62 | sudo apt-get install -qq pkg-config libhdf5-serial-dev 63 | 64 | - name: Build Windows 65 | if: matrix.platform == 'windows-latest' 66 | run: | 67 | go install ./... 68 | shell: msys2 {0} 69 | 70 | - name: Build Linux 71 | if: matrix.platform == 'ubuntu-latest' 72 | run: | 73 | go install ./... 74 | 75 | - name: Test Windows 76 | if: matrix.platform == 'windows-latest' 77 | run: | 78 | go test ./... 79 | shell: msys2 {0} 80 | 81 | - name: Test Linux 82 | if: matrix.platform == 'ubuntu-latest' 83 | run: | 84 | go test ./... 85 | -------------------------------------------------------------------------------- /h5i_identifier.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | // #include "hdf5.h" 8 | // #include "hdf5_hl.h" 9 | // #include 10 | // #include 11 | import "C" 12 | 13 | import ( 14 | "fmt" 15 | "unsafe" 16 | ) 17 | 18 | type IType C.H5I_type_t 19 | 20 | const ( 21 | FILE IType = C.H5I_FILE 22 | GROUP IType = C.H5I_GROUP 23 | DATATYPE IType = C.H5I_DATATYPE 24 | DATASPACE IType = C.H5I_DATASPACE 25 | DATASET IType = C.H5I_DATASET 26 | ATTRIBUTE IType = C.H5I_ATTR 27 | BAD_ID IType = C.H5I_BADID 28 | ) 29 | 30 | func (typ IType) String() string { 31 | switch typ { 32 | case FILE: 33 | return "file" 34 | case GROUP: 35 | return "group" 36 | case DATATYPE: 37 | return "datatype" 38 | case DATASPACE: 39 | return "dataspace" 40 | case DATASET: 41 | return "dataset" 42 | case ATTRIBUTE: 43 | return "attribute" 44 | case BAD_ID: 45 | return "bad_id" 46 | default: 47 | return fmt.Sprintf("IType=%d", int(typ)) 48 | } 49 | } 50 | 51 | // Identifier is a simple wrapper around a C hid_t. It has basic methods 52 | // which apply to every type in the go-hdf5 API. 53 | type Identifier struct { 54 | id C.hid_t 55 | } 56 | 57 | // ID returns the integer value of an identifier. 58 | func (i Identifier) ID() int64 { 59 | return int64(i.id) 60 | } 61 | 62 | // Name returns the full name of the Identifier 63 | func (i Identifier) Name() string { 64 | sz := int(C.H5Iget_name(i.id, nil, 0)) + 1 65 | if sz < 0 { 66 | return "" 67 | } 68 | buf := string(make([]byte, sz)) 69 | c_buf := C.CString(buf) 70 | defer C.free(unsafe.Pointer(c_buf)) 71 | sz = int(C.H5Iget_name(i.id, c_buf, C.size_t(sz))) 72 | if sz < 0 { 73 | return "" 74 | } 75 | return C.GoString(c_buf) 76 | } 77 | 78 | // File returns the file associated with this Identifier. 79 | func (i Identifier) File() *File { 80 | fid := C.H5Iget_file_id(i.id) 81 | if fid < 0 { 82 | return nil 83 | } 84 | return &File{CommonFG{Identifier{fid}}} 85 | } 86 | 87 | // Type returns the type of the identifier. 88 | func (i Identifier) Type() IType { 89 | return IType(C.H5Iget_type(i.id)) 90 | } 91 | 92 | func (i *Identifier) closeWith(fn func(C.hid_t) C.herr_t) error { 93 | if i.id == 0 { 94 | return nil 95 | } 96 | err := h5err(fn(i.id)) 97 | i.id = 0 98 | return err 99 | } 100 | -------------------------------------------------------------------------------- /cmd/test-go-table-01-readback/main.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | 10 | "gonum.org/v1/hdf5" 11 | ) 12 | 13 | const ( 14 | fname string = "ex_table_01.h5" 15 | tname string = "table" 16 | nfields int = 5 17 | nrecords int = 8 18 | ) 19 | 20 | type particle struct { 21 | // name string `hdf5:"Name"` // FIXME(sbinet) 22 | Lati int32 `hdf5:"Latitude"` 23 | Longi int64 `hdf5:"Longitude"` 24 | Pressure float32 `hdf5:"Pressure"` 25 | Temperature float64 `hdf5:"Temperature"` 26 | // isthep []int // FIXME(sbinet) 27 | // jmohep [2][2]int64 // FIXME(sbinet) 28 | } 29 | 30 | func (p *particle) Equal(o *particle) bool { 31 | return p.Lati == o.Lati && p.Longi == o.Longi && p.Pressure == o.Pressure && p.Temperature == o.Temperature 32 | } 33 | 34 | func main() { 35 | 36 | // define an array of particles 37 | particles := []particle{ 38 | {0, 0, 0.0, 0.}, 39 | {10, 10, 1.0, 10.}, 40 | {20, 20, 2.0, 20.}, 41 | {30, 30, 3.0, 30.}, 42 | {40, 40, 4.0, 40.}, 43 | {50, 50, 5.0, 50.}, 44 | {60, 60, 6.0, 60.}, 45 | {70, 70, 7.0, 70.}, 46 | } 47 | 48 | // define an array of particles 49 | // p_data := []particle_t{ 50 | // {"zero", 0, 0, 0.0, 0.}, 51 | // {"one", 10, 10, 1.0, 10.}, 52 | // {"two", 20, 20, 2.0, 20.}, 53 | // {"three", 30, 30, 3.0, 30.}, 54 | // {"four", 40, 40, 4.0, 40.}, 55 | // {"five", 50, 50, 5.0, 50.}, 56 | // {"six", 60, 60, 6.0, 60.}, 57 | // {"seven", 70, 70, 7.0, 70.}, 58 | // } 59 | 60 | fmt.Printf(":: reference data: %v\n", particles) 61 | 62 | // open a file 63 | f, err := hdf5.OpenFile(fname, hdf5.F_ACC_RDONLY) 64 | if err != nil { 65 | panic(err) 66 | } 67 | fmt.Printf(":: file [%s] opened (id=%d)\n", f.Name(), f.ID()) 68 | 69 | // create a fixed-length packet table within the file 70 | table, err := f.OpenTable(tname) 71 | if err != nil { 72 | panic(err) 73 | } 74 | fmt.Printf(":: table [%s] opened (id=%d)\n", tname, 3) 75 | 76 | // iterate through packets 77 | for i := 0; i != nrecords; i++ { 78 | //p := []particle_t{{}} 79 | p := make([]particle, 1) 80 | err := table.Next(&p) 81 | if err != nil { 82 | panic(err) 83 | } 84 | fmt.Printf(":: data[%d]: %v -> [%v]\n", i, p, p[0].Equal(&particles[i])) 85 | } 86 | 87 | // reset index 88 | table.CreateIndex() 89 | parts := make([]particle, nrecords) 90 | err = table.ReadPackets(0, nrecords, &parts) 91 | if err != nil { 92 | panic(err) 93 | } 94 | fmt.Printf(":: whole data: %v\n", parts) 95 | 96 | fmt.Printf(":: bye.\n") 97 | } 98 | -------------------------------------------------------------------------------- /cmd/test-go-cpxcmpd/main.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | 10 | "gonum.org/v1/hdf5" 11 | ) 12 | 13 | const ( 14 | fname string = "SDScompound.h5" 15 | dsname string = "ArrayOfStructures" 16 | mbr1 string = "A_name" 17 | mbr2 string = "B_name" 18 | mbr3 string = "C_name" 19 | length uint = 10 20 | rank int = 1 21 | ) 22 | 23 | type s1Type struct { 24 | a int 25 | b float32 26 | c float64 27 | d [3]int 28 | e string 29 | } 30 | 31 | type s2Type struct { 32 | c float64 33 | a int 34 | } 35 | 36 | func main() { 37 | 38 | // initialize data 39 | // s1 := make([]s1_t, LENGTH) 40 | // for i:=0; i 9 | // #include 10 | import "C" 11 | 12 | import ( 13 | "fmt" 14 | "reflect" 15 | "unsafe" 16 | ) 17 | 18 | type Attribute struct { 19 | Identifier 20 | } 21 | 22 | func newAttribute(id C.hid_t) *Attribute { 23 | return &Attribute{Identifier{id}} 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 | // Access the type of an attribute 48 | func (s *Attribute) GetType() Identifier { 49 | ftype := C.H5Aget_type(s.id) 50 | return Identifier{ftype} 51 | } 52 | 53 | // Close releases and terminates access to an attribute. 54 | func (s *Attribute) Close() error { 55 | return s.closeWith(h5aclose) 56 | } 57 | 58 | func h5aclose(id C.hid_t) C.herr_t { 59 | return C.H5Aclose(id) 60 | } 61 | 62 | // Space returns an identifier for a copy of the dataspace for a attribute. 63 | func (s *Attribute) Space() *Dataspace { 64 | hid := C.H5Aget_space(s.id) 65 | if int(hid) > 0 { 66 | return newDataspace(hid) 67 | } 68 | return nil 69 | } 70 | 71 | // Read reads raw data from a attribute into a buffer. 72 | func (s *Attribute) Read(data interface{}, dtype *Datatype) error { 73 | var ( 74 | addr unsafe.Pointer 75 | rv = reflect.ValueOf(data) 76 | ) 77 | if rv.Kind() != reflect.Ptr { 78 | return fmt.Errorf("hdf5: read expects a pointer value") 79 | } 80 | v := reflect.Indirect(rv) 81 | 82 | switch v.Kind() { 83 | 84 | case reflect.Array: 85 | addr = unsafe.Pointer(v.UnsafeAddr()) 86 | 87 | case reflect.String: 88 | dtAttr, err := copyDatatype(s.GetType().id) 89 | if err != nil { 90 | return fmt.Errorf("hdf5: could not access attribute datatype: %v", err) 91 | } 92 | defer dtAttr.Close() 93 | 94 | dtype = dtAttr 95 | dlen := dtype.Size() 96 | cstr := (*C.char)(unsafe.Pointer(C.malloc(C.size_t(uint(unsafe.Sizeof(byte(0))) * (dlen + 1))))) 97 | defer C.free(unsafe.Pointer(cstr)) 98 | switch { 99 | case C.H5Tis_variable_str(dtAttr.Identifier.id) != 0: 100 | addr = unsafe.Pointer(&cstr) 101 | default: 102 | addr = unsafe.Pointer(cstr) 103 | } 104 | defer func() { 105 | v.SetString(C.GoString(cstr)) 106 | }() 107 | 108 | default: 109 | addr = unsafe.Pointer(v.UnsafeAddr()) 110 | } 111 | 112 | rc := C.H5Aread(s.id, dtype.id, addr) 113 | err := h5err(rc) 114 | return err 115 | } 116 | 117 | // Write writes raw data from a buffer to an attribute. 118 | func (s *Attribute) Write(data interface{}, dtype *Datatype) error { 119 | var addr unsafe.Pointer 120 | v := reflect.Indirect(reflect.ValueOf(data)) 121 | switch v.Kind() { 122 | 123 | case reflect.Array: 124 | addr = unsafe.Pointer(v.UnsafeAddr()) 125 | 126 | case reflect.String: 127 | str := C.CString(v.String()) 128 | defer C.free(unsafe.Pointer(str)) 129 | addr = unsafe.Pointer(&str) 130 | 131 | case reflect.Ptr: 132 | addr = unsafe.Pointer(v.Pointer()) 133 | 134 | default: 135 | addr = unsafe.Pointer(v.UnsafeAddr()) 136 | } 137 | 138 | rc := C.H5Awrite(s.id, dtype.id, addr) 139 | err := h5err(rc) 140 | return err 141 | } 142 | -------------------------------------------------------------------------------- /cmd/test-go-table-01/main.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "reflect" 10 | 11 | "gonum.org/v1/hdf5" 12 | ) 13 | 14 | const ( 15 | fname string = "ex_table_01.h5" 16 | tname string = "table" 17 | nrecords int = 8 18 | ) 19 | 20 | type particle struct { 21 | // name string `hdf5:"Name"` // FIXME(sbinet) 22 | Lati int32 `hdf5:"Latitude"` 23 | Longi int64 `hdf5:"Longitude"` 24 | Pressure float32 `hdf5:"Pressure"` 25 | Temperature float64 `hdf5:"Temperature"` 26 | // isthep []int // FIXME(sbinet) 27 | // jmohep [2][2]int64 // FIXME(sbinet) 28 | } 29 | 30 | func main() { 31 | 32 | // define an array of particles 33 | particles := []particle{ 34 | {0, 0, 0.0, 0.}, 35 | {10, 10, 1.0, 10.}, 36 | {20, 20, 2.0, 20.}, 37 | {30, 30, 3.0, 30.}, 38 | {40, 40, 4.0, 40.}, 39 | {50, 50, 5.0, 50.}, 40 | {60, 60, 6.0, 60.}, 41 | {70, 70, 7.0, 70.}, 42 | } 43 | // particles := []particle{ 44 | // {"zero", 0, 0, 0.0, 0., []int{0, 0}, [2][2]int{{0, 0}, {0, 0}}}, 45 | // {"one", 10, 10, 1.0, 10., []int{0, 0}, [2][2]int{{1, 0}, {0, 1}}}, 46 | // {"two", 20, 20, 2.0, 20., []int{0, 0}, [2][2]int{{2, 0}, {0, 2}}}, 47 | // {"three", 30, 30, 3.0, 30., []int{0, 0}, [2][2]int{{3, 0}, {0, 3}}}, 48 | // {"four", 40, 40, 4.0, 40., []int{0, 0}, [2][2]int{{4, 0}, {0, 4}}}, 49 | // {"five", 50, 50, 5.0, 50., []int{0, 0}, [2][2]int{{5, 0}, {0, 5}}}, 50 | // {"six", 60, 60, 6.0, 60., []int{0, 0}, [2][2]int{{6, 0}, {0, 6}}}, 51 | // {"seven", 70, 70, 7.0, 70., []int{0, 0}, [2][2]int{{7, 0}, {0, 7}}}, 52 | // } 53 | 54 | chunkSize := 10 55 | compress := 0 56 | 57 | // create a new file using default properties 58 | f, err := hdf5.CreateFile(fname, hdf5.F_ACC_TRUNC) 59 | if err != nil { 60 | panic(fmt.Errorf("CreateFile failed: %s", err)) 61 | } 62 | defer f.Close() 63 | fmt.Printf(":: file [%s] created (id=%d)\n", fname, f.ID()) 64 | 65 | // create a fixed-length packet table within the file 66 | table, err := f.CreateTableFrom(tname, particle{}, chunkSize, compress) 67 | if err != nil { 68 | panic(fmt.Errorf("CreateTableFrom failed: %s", err)) 69 | } 70 | defer table.Close() 71 | fmt.Printf(":: table [%s] created (id=%d)\n", tname, table.ID()) 72 | 73 | if !table.IsValid() { 74 | panic("table is invalid") 75 | } 76 | 77 | // write one packet to the packet table 78 | if err = table.Append(particles[0]); err != nil { 79 | panic(fmt.Errorf("Append failed with single packet: %s", err)) 80 | } 81 | 82 | // write several packets 83 | if err = table.Append(particles[1], particles[2], particles[3], 84 | particles[4], particles[5], particles[6], particles[7], 85 | ); err != nil { 86 | panic(fmt.Errorf("Append failed with multiple packets: %s", err)) 87 | } 88 | 89 | // get the number of packets 90 | n, err := table.NumPackets() 91 | if err != nil { 92 | panic(fmt.Errorf("NumPackets failed: %s", err)) 93 | } 94 | fmt.Printf(":: nbr entries: %d\n", n) 95 | if n != nrecords { 96 | panic(fmt.Errorf( 97 | "Wrong number of packets reported, expected %d but got %d", 98 | nrecords, n, 99 | )) 100 | } 101 | 102 | // iterate through packets 103 | for i := 0; i != n; i++ { 104 | p := make([]particle, 1) 105 | if err := table.Next(&p); err != nil { 106 | panic(fmt.Errorf("Next failed: %s", err)) 107 | } 108 | fmt.Printf(":: data[%d]: %v\n", i, p) 109 | } 110 | 111 | // reset index 112 | table.CreateIndex() 113 | parts := make([]particle, nrecords) 114 | if err = table.ReadPackets(0, nrecords, &parts); err != nil { 115 | panic(fmt.Errorf("ReadPackets failed: %s", err)) 116 | } 117 | 118 | if !reflect.DeepEqual(parts, particles) { 119 | panic(fmt.Errorf("particles differ.\ngot= %#v\nwant=%#v\n", parts, particles)) 120 | } 121 | 122 | fmt.Printf(":: whole data: %v\n", parts) 123 | fmt.Printf(":: bye.\n") 124 | } 125 | -------------------------------------------------------------------------------- /h5g_group_test.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | import ( 8 | "os" 9 | "testing" 10 | ) 11 | 12 | func TestGroup(t *testing.T) { 13 | f, err := CreateFile(fname, F_ACC_TRUNC) 14 | if err != nil { 15 | t.Fatalf("CreateFile failed: %s", err) 16 | } 17 | defer os.Remove(fname) 18 | defer f.Close() 19 | 20 | if f.LinkExists("/foo") { 21 | t.Error("unexpected /foo link present") 22 | } 23 | 24 | g1, err := f.CreateGroup("foo") 25 | if err != nil { 26 | t.Fatalf("couldn't create group: %s", err) 27 | } 28 | g1f := g1.File() 29 | defer g1f.Close() 30 | if *g1f != *f { 31 | t.Fatal("wrong file for group") 32 | } 33 | if g1.Name() != "/foo" { 34 | t.Errorf(`wrong name for group: want:"/foo", got:%q`, g1.Name()) 35 | } 36 | 37 | if !f.LinkExists("/foo") { 38 | t.Error(`unexpected "/foo" group link not present`) 39 | } 40 | 41 | if g1.LinkExists("bar") { 42 | t.Error(`unexpected "bar" child link for "/foo" group`) 43 | } 44 | 45 | g2, err := g1.CreateGroup("bar") 46 | if err != nil { 47 | t.Fatalf("couldn't create group: %s", err) 48 | } 49 | g2f := g2.File() 50 | defer g2f.Close() 51 | if *g2f != *f { 52 | t.Fatal("wrong file for group") 53 | } 54 | if g2.Name() != "/foo/bar" { 55 | t.Errorf("wrong Name for group: want %q, got %q", "/foo/bar", g1.Name()) 56 | } 57 | if !g1.LinkExists("bar") { 58 | t.Error(`expected "bar" child link for "/foo" group not present`) 59 | } 60 | 61 | g3, err := g2.CreateGroup("baz") 62 | if err != nil { 63 | t.Fatalf("couldn't create group: %s", err) 64 | } 65 | g3f := g3.File() 66 | defer g3f.Close() 67 | if *g3f != *f { 68 | t.Fatal("wrong file for group") 69 | } 70 | if g3.Name() != "/foo/bar/baz" { 71 | t.Errorf("wrong Name for group: want %q, got %q", "/foo/bar/bar", g1.Name()) 72 | } 73 | 74 | if nObjs, err := g2.NumObjects(); err != nil { 75 | t.Fatal(err) 76 | } else if nObjs != 1 { 77 | t.Errorf("wrong number of objects in group: want 1, got %d", nObjs) 78 | } 79 | 80 | if name, err := g2.ObjectNameByIndex(0); err != nil { 81 | t.Fatalf("could not retrieve object name idx=%d: %+v", 0, err) 82 | } else if got, want := name, "baz"; got != want { 83 | t.Errorf("invalid name for object idx=%d: got=%q, want=%q", 0, got, want) 84 | } 85 | 86 | if typ, err := g2.ObjectTypeByIndex(0); err != nil { 87 | t.Fatalf("could not retrieve object type idx=%d: %+v", 0, err) 88 | } else if got, want := typ, H5G_GROUP; got != want { 89 | t.Errorf("invalid type for object idx=%d: got=%v, want=%v", 0, got, want) 90 | } 91 | 92 | err = g1.Close() 93 | if err != nil { 94 | t.Error(err) 95 | } 96 | err = g2.Close() 97 | if err != nil { 98 | t.Error(err) 99 | } 100 | err = g3.Close() 101 | if err != nil { 102 | t.Error(err) 103 | } 104 | 105 | g2, err = f.OpenGroup("/foo/bar") 106 | if err != nil { 107 | t.Fatal(err) 108 | } 109 | defer g2.Close() 110 | 111 | g3, err = g2.OpenGroup("baz") 112 | if err != nil { 113 | t.Fatal(err) 114 | } 115 | defer g3.Close() 116 | 117 | _, err = g3.OpenGroup("bs") 118 | if err == nil { 119 | t.Fatal("expected error on opening invalid group") 120 | } 121 | 122 | data := 5 123 | 124 | dtype, err := NewDatatypeFromValue(data) 125 | if err != nil { 126 | t.Fatal(err) 127 | } 128 | defer dtype.Close() 129 | 130 | dims := []uint{1} 131 | dspace, err := CreateSimpleDataspace(dims, dims) 132 | if err != nil { 133 | t.Fatal(err) 134 | } 135 | defer dspace.Close() 136 | 137 | dset, err := g3.CreateDataset("dset", dtype, dspace) 138 | if err != nil { 139 | t.Fatal(err) 140 | } 141 | defer dset.Close() 142 | 143 | dset2, err := g3.OpenDataset("dset") 144 | if dset.Name() != dset2.Name() { 145 | t.Error("expected dataset names to be equal") 146 | } 147 | defer dset2.Close() 148 | 149 | dset2, err = g3.OpenDataset("bs") 150 | if err == nil { 151 | t.Errorf("opened dataset that was never created: %v", dset2) 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /h5f_file_test.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | import ( 8 | "os" 9 | "testing" 10 | ) 11 | 12 | func TestFile(t *testing.T) { 13 | f, err := CreateFile(fname, F_ACC_TRUNC) 14 | if err != nil { 15 | t.Fatalf("CreateFile failed: %s", err) 16 | } 17 | defer os.Remove(fname) 18 | defer f.Close() 19 | 20 | if fileName := f.FileName(); fileName != fname { 21 | t.Fatalf("FileName() have %v, want %v", fileName, fname) 22 | } 23 | // The file is also the root group 24 | if name := f.Name(); name != "/" { 25 | t.Fatalf("Name() have %v, want %v", name, fname) 26 | } 27 | if err := f.Flush(F_SCOPE_GLOBAL); err != nil { 28 | t.Fatalf("Flush() failed: %s", err) 29 | } 30 | if !IsHDF5(fname) { 31 | t.Fatalf("IsHDF5 returned false") 32 | } 33 | if n, err := f.NumObjects(); err != nil { 34 | t.Fatalf("NumObjects failed: %s", err) 35 | } else if n != 0 { 36 | t.Fatalf("empty file had %d objects", n) 37 | } 38 | 39 | if _, err := f.ObjectNameByIndex(0); err == nil { 40 | t.Fatalf("expected error") 41 | } 42 | 43 | f2 := f.File() 44 | defer f2.Close() 45 | fName := f.FileName() 46 | f2Name := f2.FileName() 47 | if fName != f2Name { 48 | t.Fatalf("f2 FileName() have %v, want %v", f2Name, fName) 49 | } 50 | 51 | // Test a Group 52 | groupName := "test" 53 | g, err := f.CreateGroup(groupName) 54 | if err != nil { 55 | t.Fatalf("CreateGroup() failed: %s", err) 56 | } 57 | defer g.Close() 58 | if name := g.Name(); name != "/"+groupName { 59 | t.Fatalf("Group Name() have %v, want /%v", name, groupName) 60 | } 61 | 62 | g2, err := f.OpenGroup(groupName) 63 | if err != nil { 64 | t.Fatalf("OpenGroup() failed: %s", err) 65 | } 66 | defer g2.Close() 67 | if name := g2.Name(); name != "/"+groupName { 68 | t.Fatalf("Group Name() have %v, want /%v", name, groupName) 69 | } 70 | 71 | if n, err := f.NumObjects(); err != nil { 72 | t.Fatalf("NumObjects failed: %s", err) 73 | } else if n != 1 { 74 | t.Fatalf("NumObjects: got %d, want %d", n, 1) 75 | } 76 | 77 | if name, err := f.ObjectNameByIndex(0); err != nil { 78 | t.Fatalf("ObjectNameByIndex failed: %s", err) 79 | } else if name != groupName { 80 | t.Fatalf("ObjectNameByIndex: got %q, want %q", name, groupName) 81 | } 82 | if _, err := f.ObjectNameByIndex(1); err == nil { 83 | t.Fatalf("expected error") 84 | } 85 | 86 | // Test a Dataset 87 | ds, err := CreateDataspace(S_SCALAR) 88 | if err != nil { 89 | t.Fatalf("CreateDataspace failed: %s", err) 90 | } 91 | defer ds.Close() 92 | dsetName := "test_dataset" 93 | dset, err := f.CreateDataset(dsetName, T_NATIVE_INT, ds) 94 | if err != nil { 95 | t.Fatalf("CreateDataset failed: %s", err) 96 | } 97 | defer dset.Close() 98 | if name := dset.Name(); name != "/"+dsetName { 99 | t.Fatalf("Dataset Name() have %v, want /%v", name, dsetName) 100 | } 101 | dFile := dset.File() 102 | if dFile.Name() != f.Name() { 103 | t.Fatalf("Dataset File() have %v, want %v", dFile.Name(), f.Name()) 104 | } 105 | defer dFile.Close() 106 | 107 | if n, err := f.NumObjects(); err != nil { 108 | t.Fatalf("NumObjects failed: %s", err) 109 | } else if n != 2 { 110 | t.Fatalf("NumObjects: got %d, want %d", n, 1) 111 | } 112 | 113 | for i, n := range []string{groupName, dsetName} { 114 | if name, err := f.ObjectNameByIndex(uint(i)); err != nil { 115 | t.Fatalf("ObjectNameByIndex failed: %s", err) 116 | } else if name != n { 117 | t.Fatalf("ObjectNameByIndex: got %q, want %q", name, groupName) 118 | } 119 | } 120 | } 121 | 122 | func TestClosedFile(t *testing.T) { 123 | f, err := CreateFile(fname, F_ACC_TRUNC) 124 | if err != nil { 125 | t.Fatalf("CreateFile failed: %s", err) 126 | } 127 | fName := f.Name() 128 | f2 := f.File() 129 | f.Close() 130 | 131 | f2Name := f2.FileName() 132 | if f2Name != fname { 133 | t.Fatalf("f2 FileName() have %v, want %v", f2Name, fName) 134 | } 135 | f2.Close() 136 | 137 | os.Remove(fname) 138 | f3 := f.File() 139 | if f3 != nil { 140 | t.Fatalf("expected file to be nil") 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /h5p_proplist.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | // #include "hdf5.h" 8 | // #include 9 | // #include 10 | // static inline hid_t _go_hdf5_H5P_DEFAULT() { return H5P_DEFAULT; } 11 | // static inline hid_t _go_hdf5_H5P_DATASET_CREATE() { return H5P_DATASET_CREATE; } 12 | // static inline hid_t _go_hdf5_H5P_DATASET_ACCESS() { return H5P_DATASET_ACCESS; } 13 | import "C" 14 | 15 | import ( 16 | "compress/zlib" 17 | "fmt" 18 | ) 19 | 20 | const ( 21 | NoCompression = zlib.NoCompression 22 | BestSpeed = zlib.BestSpeed 23 | BestCompression = zlib.BestCompression 24 | DefaultCompression = zlib.DefaultCompression 25 | ) 26 | 27 | type PropType C.hid_t 28 | 29 | type PropList struct { 30 | Identifier 31 | } 32 | 33 | var ( 34 | P_DEFAULT *PropList = newPropList(C._go_hdf5_H5P_DEFAULT()) 35 | P_DATASET_CREATE PropType = PropType(C._go_hdf5_H5P_DATASET_CREATE()) // Properties for dataset creation 36 | P_DATASET_ACCESS PropType = PropType(C._go_hdf5_H5P_DATASET_ACCESS()) // Properties for dataset access 37 | ) 38 | 39 | func newPropList(id C.hid_t) *PropList { 40 | return &PropList{Identifier{id}} 41 | } 42 | 43 | // NewPropList creates a new PropList as an instance of a property list class. 44 | // The returned proplist must be closed by the user when it is no longer needed. 45 | func NewPropList(cls_id PropType) (*PropList, error) { 46 | hid := C.H5Pcreate(C.hid_t(cls_id)) 47 | if err := checkID(hid); err != nil { 48 | return nil, err 49 | } 50 | return newPropList(hid), nil 51 | } 52 | 53 | // Close terminates access to a PropList. 54 | func (p *PropList) Close() error { 55 | return p.closeWith(h5pclose) 56 | } 57 | 58 | // SetChunk sets the size of the chunks used to store a chunked layout dataset. 59 | // https://support.hdfgroup.org/HDF5/doc/RM/RM_H5P.html#Property-SetChunk 60 | func (p *PropList) SetChunk(dims []uint) error { 61 | ndims := len(dims) 62 | if ndims <= 0 { 63 | return fmt.Errorf("number of dimensions must be same size as the rank of the dataset, but zero received") 64 | } 65 | c_dim := make([]C.hsize_t, ndims) 66 | for i := range dims { 67 | c_dim[i] = C.hsize_t(dims[i]) 68 | } 69 | return h5err(C.H5Pset_chunk(C.hid_t(p.id), C.int(ndims), &c_dim[0])) 70 | } 71 | 72 | // GetChunk retrieves the size of chunks for the raw data of a chunked layout dataset. 73 | // https://support.hdfgroup.org/HDF5/doc/RM/RM_H5P.html#Property-GetChunk 74 | func (p *PropList) GetChunk(ndims int) (dims []uint, err error) { 75 | if ndims <= 0 { 76 | err = fmt.Errorf("number of dimensions must be same size as the rank of the dataset, but nonpositive value received") 77 | return 78 | } 79 | c_dims := make([]C.hsize_t, ndims) 80 | if err = h5err(C.H5Pget_chunk(C.hid_t(p.id), C.int(ndims), &c_dims[0])); err != nil { 81 | return 82 | } 83 | dims = make([]uint, ndims) 84 | for i := range dims { 85 | dims[i] = uint(c_dims[i]) 86 | } 87 | return 88 | } 89 | 90 | // SetDeflate sets deflate (GNU gzip) compression method and compression level. 91 | // If level is set as DefaultCompression, 6 will be used. 92 | // https://support.hdfgroup.org/HDF5/doc/RM/RM_H5P.html#Property-SetDeflate 93 | func (p *PropList) SetDeflate(level int) error { 94 | if level == DefaultCompression { 95 | level = 6 96 | } 97 | return h5err(C.H5Pset_deflate(C.hid_t(p.id), C.uint(level))) 98 | } 99 | 100 | // SetChunkCache sets the raw data chunk cache parameters. 101 | // To reset them as default, use `D_CHUNK_CACHE_NSLOTS_DEFAULT`, `D_CHUNK_CACHE_NBYTES_DEFAULT` and `D_CHUNK_CACHE_W0_DEFAULT`. 102 | // https://support.hdfgroup.org/HDF5/doc/RM/RM_H5P.html#Property-SetChunkCache 103 | func (p *PropList) SetChunkCache(nslots, nbytes int, w0 float64) error { 104 | return h5err(C.H5Pset_chunk_cache(C.hid_t(p.id), C.size_t(nslots), C.size_t(nbytes), C.double(w0))) 105 | } 106 | 107 | // GetChunkCache retrieves the number of chunk slots in the raw data chunk cache hash table. 108 | // https://support.hdfgroup.org/HDF5/doc/RM/RM_H5P.html#Property-GetChunkCache 109 | func (p *PropList) GetChunkCache() (nslots, nbytes int, w0 float64, err error) { 110 | var ( 111 | c_nslots C.size_t 112 | c_nbytes C.size_t 113 | c_w0 C.double 114 | ) 115 | err = h5err(C.H5Pget_chunk_cache(C.hid_t(p.id), &c_nslots, &c_nbytes, &c_w0)) 116 | return int(c_nslots), int(c_nbytes), float64(c_w0), err 117 | } 118 | 119 | func h5pclose(id C.hid_t) C.herr_t { 120 | return C.H5Pclose(id) 121 | } 122 | 123 | // Copy copies an existing PropList to create a new PropList. 124 | func (p *PropList) Copy() (*PropList, error) { 125 | hid := C.H5Pcopy(p.id) 126 | if err := checkID(hid); err != nil { 127 | return nil, err 128 | } 129 | return newPropList(hid), nil 130 | } 131 | -------------------------------------------------------------------------------- /cmem/encoder.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2018 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmem 6 | 7 | import ( 8 | "encoding/binary" 9 | "fmt" 10 | "math" 11 | "reflect" 12 | "unsafe" 13 | ) 14 | 15 | var nativeEndian binary.ByteOrder 16 | 17 | func init() { 18 | one := uint16(0x1) 19 | nativeEndian = [...]binary.ByteOrder{ 20 | binary.BigEndian, 21 | binary.LittleEndian, 22 | }[(*[2]byte)(unsafe.Pointer(&one))[0]] 23 | } 24 | 25 | // Encoder is a wrapper type for information necessary to create and 26 | // subsequently write an in memory object to a PacketTable using Append(). 27 | type Encoder struct { 28 | // Buf contains the encoded data. 29 | Buf []byte 30 | offset int 31 | } 32 | 33 | // Encode encodes the value passed as data to binary form stored in []Buf. This 34 | // buffer is a Go representation of a C in-memory object that can be appended 35 | // to e.g. a HDF5 PacketTable. 36 | // 37 | // Struct values must only have exported fields, otherwise Encode will panic. 38 | func (enc *Encoder) Encode(data interface{}) error { 39 | padding := enc.offset - len(enc.Buf) 40 | 41 | if padding > 0 { 42 | enc.Buf = append(enc.Buf, make([]byte, padding)...) 43 | } 44 | 45 | if data, ok := data.(CMarshaler); ok { 46 | raw, err := data.MarshalC() 47 | if err != nil { 48 | return err 49 | } 50 | 51 | enc.Buf = append(enc.Buf, raw...) 52 | 53 | return nil 54 | } 55 | 56 | rv := reflect.Indirect(reflect.ValueOf(data)) 57 | if !rv.IsValid() { 58 | return fmt.Errorf("cmem: reflect.ValueOf returned invalid value for type %T", data) 59 | } 60 | 61 | rt := rv.Type() 62 | 63 | switch rt.Kind() { 64 | case reflect.Slice, reflect.Array: 65 | for i := 0; i < rv.Len(); i++ { 66 | if err := enc.Encode(rv.Index(i).Interface()); err != nil { 67 | return err 68 | } 69 | } 70 | return nil 71 | 72 | case reflect.Struct: 73 | offset := enc.offset 74 | for i := 0; i < rv.NumField(); i++ { 75 | sfv := rv.Field(i).Interface() 76 | // In order to keep the memory offset always correct, we use the 77 | // structs offset. 78 | enc.offset = offset + int(rt.Field(i).Offset) 79 | if err := enc.Encode(sfv); err != nil { 80 | return err 81 | } 82 | // Reset the offset to the correct array size. 83 | enc.offset = offset + int(rt.Size()) 84 | } 85 | return nil 86 | 87 | case reflect.String: 88 | str := append([]byte(rv.String()), 0) 89 | 90 | // This direct machine conversion is only used 91 | // because HDF5 uses machine endianism. 92 | // 93 | // DO NOT DO THIS AT HOME. 94 | // 95 | raw := (*[unsafe.Sizeof(uintptr(0))]byte)(unsafe.Pointer(&str)) 96 | enc.Buf = append(enc.Buf, raw[:]...) 97 | enc.offset += len(raw) 98 | 99 | case reflect.Ptr: 100 | return enc.Encode(rv.Elem()) 101 | 102 | case reflect.Int8: 103 | enc.Buf = append(enc.Buf, byte(rv.Int())) 104 | enc.offset++ 105 | 106 | case reflect.Uint8: 107 | enc.Buf = append(enc.Buf, byte(rv.Uint())) 108 | enc.offset++ 109 | 110 | case reflect.Int16: 111 | var raw [2]byte 112 | nativeEndian.PutUint16(raw[:], uint16(rv.Int())) 113 | enc.Buf = append(enc.Buf, raw[:]...) 114 | enc.offset += 2 115 | 116 | case reflect.Uint16: 117 | var raw [2]byte 118 | nativeEndian.PutUint16(raw[:], uint16(rv.Uint())) 119 | enc.Buf = append(enc.Buf, raw[:]...) 120 | enc.offset += 2 121 | 122 | case reflect.Int32: 123 | var raw [4]byte 124 | nativeEndian.PutUint32(raw[:], uint32(rv.Int())) 125 | enc.Buf = append(enc.Buf, raw[:]...) 126 | enc.offset += 4 127 | 128 | case reflect.Uint32: 129 | var raw [4]byte 130 | nativeEndian.PutUint32(raw[:], uint32(rv.Uint())) 131 | enc.Buf = append(enc.Buf, raw[:]...) 132 | enc.offset += 4 133 | 134 | case reflect.Int64: 135 | var raw [8]byte 136 | nativeEndian.PutUint64(raw[:], uint64(rv.Int())) 137 | enc.Buf = append(enc.Buf, raw[:]...) 138 | enc.offset += 8 139 | 140 | case reflect.Uint64: 141 | var raw [8]byte 142 | nativeEndian.PutUint64(raw[:], uint64(rv.Uint())) 143 | enc.Buf = append(enc.Buf, raw[:]...) 144 | enc.offset += 8 145 | 146 | case reflect.Float32: 147 | var raw [4]byte 148 | nativeEndian.PutUint32(raw[:], math.Float32bits(float32(rv.Float()))) 149 | enc.Buf = append(enc.Buf, raw[:]...) 150 | enc.offset += 4 151 | 152 | case reflect.Float64: 153 | var raw [8]byte 154 | nativeEndian.PutUint64(raw[:], math.Float64bits(rv.Float())) 155 | enc.Buf = append(enc.Buf, raw[:]...) 156 | enc.offset += 8 157 | 158 | case reflect.Bool: 159 | val := byte(0) 160 | if rv.Bool() { 161 | val = 1 162 | } 163 | enc.Buf = append(enc.Buf, val) 164 | enc.offset++ 165 | 166 | default: 167 | return fmt.Errorf("hdf5: PT Append does not support datatype (%s)", rt.Kind()) 168 | } 169 | 170 | return nil 171 | } 172 | -------------------------------------------------------------------------------- /h5f_file.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | // #include "hdf5.h" 8 | // #include "hdf5_hl.h" 9 | // #include 10 | // #include 11 | import "C" 12 | 13 | import ( 14 | "fmt" 15 | "unsafe" 16 | ) 17 | 18 | // File constants 19 | const ( 20 | F_ACC_RDONLY int = 0x0000 // absence of rdwr => rd-only 21 | F_ACC_RDWR int = 0x0001 // open for read and write 22 | F_ACC_TRUNC int = 0x0002 // Truncate file, if it already exists, erasing all data previously stored in the file. 23 | F_ACC_EXCL int = 0x0004 // Fail if file already exists. 24 | F_ACC_DEBUG int = 0x0008 // print debug info 25 | F_ACC_CREAT int = 0x0010 // create non-existing files 26 | F_ACC_DEFAULT int = 0xffff // value passed to set_elink_acc_flags to cause flags to be taken from the parent file 27 | ) 28 | 29 | // The difference between a single file and a set of mounted files. 30 | type Scope C.H5F_scope_t 31 | 32 | const ( 33 | F_SCOPE_LOCAL Scope = 0 // specified file handle only. 34 | F_SCOPE_GLOBAL Scope = 1 // entire virtual file. 35 | ) 36 | 37 | // a HDF5 file 38 | type File struct { 39 | CommonFG 40 | } 41 | 42 | func newFile(id C.hid_t) *File { 43 | return &File{CommonFG{Identifier{id}}} 44 | } 45 | 46 | // Creates an HDF5 file. 47 | func CreateFile(name string, flags int) (*File, error) { 48 | c_name := C.CString(name) 49 | defer C.free(unsafe.Pointer(c_name)) 50 | 51 | // FIXME: file props 52 | hid := C.H5Fcreate(c_name, C.uint(flags), P_DEFAULT.id, P_DEFAULT.id) 53 | if err := checkID(hid); err != nil { 54 | return nil, fmt.Errorf("error creating hdf5 file: %s", err) 55 | } 56 | return newFile(hid), nil 57 | } 58 | 59 | // Open opens and returns an an existing HDF5 file. The returned 60 | // file must be closed by the user when it is no longer needed. 61 | func OpenFile(name string, flags int) (*File, error) { 62 | c_name := C.CString(name) 63 | defer C.free(unsafe.Pointer(c_name)) 64 | 65 | // FIXME: file props 66 | hid := C.H5Fopen(c_name, C.uint(flags), P_DEFAULT.id) 67 | if err := checkID(hid); err != nil { 68 | return nil, fmt.Errorf("error opening hdf5 file: %s", err) 69 | } 70 | return newFile(hid), nil 71 | } 72 | 73 | // ReOpen returns a new identifier for a previously-opened HDF5 file. 74 | // The returned file must be closed by the user when it is no longer needed. 75 | func (f *File) ReOpen() (*File, error) { 76 | hid := C.H5Freopen(f.id) 77 | if err := checkID(hid); err != nil { 78 | return nil, fmt.Errorf("error reopening hdf5 file: %s", err) 79 | } 80 | return newFile(hid), nil 81 | } 82 | 83 | // IsHDF5 Determines whether a file is in the HDF5 format. 84 | func IsHDF5(name string) bool { 85 | c_name := C.CString(name) 86 | defer C.free(unsafe.Pointer(c_name)) 87 | 88 | return C.H5Fis_hdf5(c_name) > 0 89 | } 90 | 91 | // Close closes the file. 92 | func (f *File) Close() error { 93 | return f.closeWith(h5fclose) 94 | } 95 | 96 | func h5fclose(id C.hid_t) C.herr_t { 97 | return C.H5Fclose(id) 98 | } 99 | 100 | // Flushes all buffers associated with a file to disk. 101 | func (f *File) Flush(scope Scope) error { 102 | // herr_t H5Fflush(hid_t object_id, H5F_scope_t scope ) 103 | return h5err(C.H5Fflush(f.id, C.H5F_scope_t(scope))) 104 | } 105 | 106 | // FIXME 107 | // Retrieves name of file to which object belongs. 108 | func (f *File) FileName() string { 109 | // ssize_t H5Fget_name(hid_t obj_id, char *name, size_t size ) 110 | sz := int(C.H5Fget_name(f.id, nil, 0)) + 1 111 | if sz < 0 { 112 | return "" 113 | } 114 | buf := string(make([]byte, sz)) 115 | c_buf := C.CString(buf) 116 | defer C.free(unsafe.Pointer(c_buf)) 117 | sz = int(C.H5Fget_name(f.id, c_buf, C.size_t(sz))) 118 | if sz < 0 { 119 | return "" 120 | } 121 | return C.GoString(c_buf) 122 | 123 | } 124 | 125 | var cdot = C.CString(".") 126 | 127 | // Creates a packet table to store fixed-length packets. The returned 128 | // table must be closed by the user when it is no longer needed. 129 | func (f *File) CreateTable(name string, dtype *Datatype, chunkSize, compression int) (*Table, error) { 130 | // hid_t H5PTcreate_fl( hid_t loc_id, const char * dset_name, hid_t dtype_id, hsize_t chunk_size, int compression ) 131 | return createTable(f.id, name, dtype, chunkSize, compression) 132 | } 133 | 134 | // Creates a packet table to store fixed-length packets. The returned 135 | // table must be closed by the user when it is no longer needed. 136 | func (f *File) CreateTableFrom(name string, dtype interface{}, chunkSize, compression int) (*Table, error) { 137 | // hid_t H5PTcreate_fl( hid_t loc_id, const char * dset_name, hid_t dtype_id, hsize_t chunk_size, int compression ) 138 | return createTableFrom(f.id, name, dtype, chunkSize, compression) 139 | } 140 | 141 | // Opens an existing packet table. The returned table must be closed 142 | // by the user when it is no longer needed. 143 | func (f *File) OpenTable(name string) (*Table, error) { 144 | // hid_t H5PTopen( hid_t loc_id, const char *dset_name ) 145 | return openTable(f.id, name) 146 | } 147 | -------------------------------------------------------------------------------- /h5s_dataspace.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | // #include "hdf5.h" 8 | // #include 9 | // #include 10 | import "C" 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | 16 | "unsafe" 17 | ) 18 | 19 | type Dataspace struct { 20 | Identifier 21 | } 22 | 23 | type SpaceClass C.H5S_class_t 24 | 25 | const ( 26 | S_NO_CLASS SpaceClass = -1 // error 27 | S_SCALAR SpaceClass = 0 // scalar variable 28 | S_SIMPLE SpaceClass = 1 // simple data space 29 | S_NULL SpaceClass = 2 // null data space 30 | ) 31 | 32 | func newDataspace(id C.hid_t) *Dataspace { 33 | return &Dataspace{Identifier{id}} 34 | } 35 | 36 | // CreateDataspace creates a new dataspace of a specified type. The returned 37 | // dataspace must be closed by the user when it is no longer needed. 38 | func CreateDataspace(class SpaceClass) (*Dataspace, error) { 39 | hid := C.H5Screate(C.H5S_class_t(class)) 40 | if err := checkID(hid); err != nil { 41 | return nil, err 42 | } 43 | ds := newDataspace(hid) 44 | return ds, nil 45 | } 46 | 47 | // Copy creates an exact copy of a dataspace. The returned dataspace must 48 | // be closed by the user when it is no longer needed. 49 | func (s *Dataspace) Copy() (*Dataspace, error) { 50 | hid := C.H5Scopy(s.id) 51 | if err := checkID(hid); err != nil { 52 | return nil, err 53 | } 54 | return newDataspace(hid), nil 55 | } 56 | 57 | // Close releases and terminates access to a dataspace. 58 | func (s *Dataspace) Close() error { 59 | return s.closeWith(h5sclose) 60 | } 61 | 62 | func h5sclose(id C.hid_t) C.herr_t { 63 | return C.H5Sclose(id) 64 | } 65 | 66 | // CreateSimpleDataspace creates a new simple dataspace and opens it for access. 67 | // The returned dataspace must be closed by the user when it is no longer needed. 68 | func CreateSimpleDataspace(dims, maxDims []uint) (*Dataspace, error) { 69 | var c_dims, c_maxdims *C.hsize_t 70 | 71 | rank := C.int(0) 72 | if dims != nil { 73 | rank = C.int(len(dims)) 74 | c_dims = (*C.hsize_t)(unsafe.Pointer(&dims[0])) 75 | 76 | } 77 | if maxDims != nil { 78 | rank = C.int(len(maxDims)) 79 | c_maxdims = (*C.hsize_t)(unsafe.Pointer(&maxDims[0])) 80 | 81 | } 82 | if len(dims) != len(maxDims) && (dims != nil && maxDims != nil) { 83 | return nil, errors.New("lengths of dims and maxDims do not match") 84 | } 85 | 86 | hid := C.H5Screate_simple(rank, c_dims, c_maxdims) 87 | if hid < 0 { 88 | return nil, fmt.Errorf("failed to create dataspace") 89 | } 90 | return newDataspace(hid), nil 91 | } 92 | 93 | // IsSimple returns whether a dataspace is a simple dataspace. 94 | func (s *Dataspace) IsSimple() bool { 95 | return int(C.H5Sis_simple(s.id)) > 0 96 | } 97 | 98 | // SetOffset sets the offset of a simple dataspace. 99 | func (s *Dataspace) SetOffset(offset []uint) error { 100 | rank := len(offset) 101 | if rank == 0 { 102 | err := C.H5Soffset_simple(s.id, nil) 103 | return h5err(err) 104 | } 105 | if rank != s.SimpleExtentNDims() { 106 | err := errors.New("size of offset does not match extent") 107 | return err 108 | } 109 | 110 | c_offset := (*C.hssize_t)(unsafe.Pointer(&offset[0])) 111 | err := C.H5Soffset_simple(s.id, c_offset) 112 | return h5err(err) 113 | } 114 | 115 | // SelectHyperslab creates a subset of the data space. 116 | func (s *Dataspace) SelectHyperslab(offset, stride, count, block []uint) error { 117 | rank := len(offset) 118 | if rank == 0 { 119 | err := C.H5Soffset_simple(s.id, nil) 120 | return h5err(err) 121 | } 122 | if rank != s.SimpleExtentNDims() { 123 | err := errors.New("size of offset does not match extent") 124 | return err 125 | } 126 | 127 | c_offset := (*C.hsize_t)(unsafe.Pointer(&offset[0])) 128 | c_count := (*C.hsize_t)(unsafe.Pointer(&count[0])) 129 | var c_stride, c_block *C.hsize_t 130 | if stride != nil { 131 | c_stride = (*C.hsize_t)(unsafe.Pointer(&stride[0])) 132 | } 133 | if block != nil { 134 | c_block = (*C.hsize_t)(unsafe.Pointer(&block[0])) 135 | } 136 | err := C.H5Sselect_hyperslab(s.id, C.H5S_SELECT_SET, c_offset, c_stride, c_count, c_block) 137 | return h5err(err) 138 | } 139 | 140 | // SimpleExtentDims returns dataspace dimension size and maximum size. 141 | func (s *Dataspace) SimpleExtentDims() (dims, maxdims []uint, err error) { 142 | rank := s.SimpleExtentNDims() 143 | dims = make([]uint, rank) 144 | maxdims = make([]uint, rank) 145 | 146 | c_dims := (*C.hsize_t)(unsafe.Pointer(&dims[0])) 147 | c_maxdims := (*C.hsize_t)(unsafe.Pointer(&maxdims[0])) 148 | rc := C.H5Sget_simple_extent_dims(s.id, c_dims, c_maxdims) 149 | err = h5err(C.herr_t(rc)) 150 | return 151 | } 152 | 153 | // SimpleExtentNDims returns the dimensionality of a dataspace. 154 | func (s *Dataspace) SimpleExtentNDims() int { 155 | return int(C.H5Sget_simple_extent_ndims(s.id)) 156 | } 157 | 158 | // SimpleExtentNPoints returns the number of elements in a dataspace. 159 | func (s *Dataspace) SimpleExtentNPoints() int { 160 | return int(C.H5Sget_simple_extent_npoints(s.id)) 161 | } 162 | 163 | // SimpleExtentType returns the current class of a dataspace. 164 | func (s *Dataspace) SimpleExtentType() SpaceClass { 165 | return SpaceClass(C.H5Sget_simple_extent_type(s.id)) 166 | } 167 | -------------------------------------------------------------------------------- /h5d_dataset_test.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | import ( 8 | "os" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func createDataset1(t *testing.T) error { 14 | // create a file with a single 5x20 dataset 15 | f, err := CreateFile(fname, F_ACC_TRUNC) 16 | if err != nil { 17 | t.Fatalf("CreateFile failed: %s", err) 18 | return err 19 | } 20 | defer f.Close() 21 | 22 | var data [100]uint16 23 | for i := range data { 24 | data[i] = uint16(i) 25 | } 26 | 27 | dims := []uint{20, 5} 28 | dspace, err := CreateSimpleDataspace(dims, dims) 29 | if err != nil { 30 | t.Fatal(err) 31 | return err 32 | } 33 | defer dspace.Close() 34 | 35 | dset, err := f.CreateDataset("dset", T_NATIVE_USHORT, dspace) 36 | if err != nil { 37 | t.Fatal(err) 38 | return err 39 | } 40 | defer dset.Close() 41 | 42 | err = dset.Write(&data[0]) 43 | if err != nil { 44 | t.Fatal(err) 45 | return err 46 | } 47 | return err 48 | } 49 | 50 | /** 51 | * TestReadSubset based on the h5_subset.c sample with the HDF5 C library. 52 | * Original copyright notice: 53 | * 54 | * HDF5 (Hierarchical Data Format 5) Software Library and Utilities 55 | * Copyright 2006-2013 by The HDF Group. 56 | * 57 | * NCSA HDF5 (Hierarchical Data Format 5) Software Library and Utilities 58 | * Copyright 1998-2006 by the Board of Trustees of the University of Illinois. 59 | **** 60 | * Write some test data then read back a subset. 61 | */ 62 | func TestReadSubset(t *testing.T) { 63 | DisplayErrors(true) 64 | defer DisplayErrors(false) 65 | defer os.Remove(fname) 66 | err := createDataset1(t) 67 | if err != nil { 68 | return 69 | } 70 | 71 | // load a subset of the data 72 | f, err := OpenFile(fname, F_ACC_RDONLY) 73 | if err != nil { 74 | t.Fatal(err) 75 | } 76 | defer f.Close() 77 | 78 | dset, err := f.OpenDataset("dset") 79 | if err != nil { 80 | t.Fatal(err) 81 | } 82 | defer dset.Close() 83 | 84 | // get the filespace and select the subset 85 | filespace := dset.Space() 86 | defer filespace.Close() 87 | offset, stride, count, block := [2]uint{5, 1}, [2]uint{1, 1}, [2]uint{5, 2}, [2]uint{1, 1} 88 | err = filespace.SelectHyperslab(offset[:], stride[:], count[:], block[:]) 89 | if err != nil { 90 | t.Fatal(err) 91 | } 92 | 93 | // create the memory space for the subset 94 | dims, maxdims := [2]uint{2, 5}, [2]uint{2, 5} 95 | if err != nil { 96 | t.Fatal(err) 97 | } 98 | memspace, err := CreateSimpleDataspace(dims[:], maxdims[:]) 99 | if err != nil { 100 | t.Fatal(err) 101 | } 102 | defer memspace.Close() 103 | 104 | expected := [10]uint16{26, 27, 31, 32, 36, 37, 41, 42, 46, 47} 105 | 106 | // test array 107 | { 108 | // create a buffer for the data 109 | data := [10]uint16{} 110 | 111 | // read the subset 112 | err = dset.ReadSubset(&data, memspace, filespace) 113 | if err != nil { 114 | t.Fatal(err) 115 | } 116 | 117 | if !reflect.DeepEqual(data, expected) { 118 | t.Fatalf("ReadSubset-array error\ngot= %#v\nwant=%#v\n", data, expected) 119 | } 120 | } 121 | 122 | // test slice 123 | { 124 | // create a buffer for the data 125 | data := make([]uint16, 10) 126 | 127 | // read the subset 128 | err = dset.ReadSubset(&data, memspace, filespace) 129 | if err != nil { 130 | t.Fatal(err) 131 | } 132 | 133 | if !reflect.DeepEqual(data, expected[:]) { 134 | t.Fatalf("ReadSubset-slice error\ngot= %#v\nwant=%#v\n", data, expected[:]) 135 | } 136 | } 137 | } 138 | 139 | func TestWriteSubset(t *testing.T) { 140 | DisplayErrors(true) 141 | defer DisplayErrors(false) 142 | defer os.Remove(fname) 143 | 144 | fdims := []uint{12, 4, 6} 145 | fspace, err := CreateSimpleDataspace(fdims, nil) 146 | if err != nil { 147 | t.Fatal(err) 148 | } 149 | defer fspace.Close() 150 | mdims := []uint{2, 6} 151 | mspace, err := CreateSimpleDataspace(mdims, nil) 152 | if err != nil { 153 | t.Fatal(err) 154 | } 155 | defer mspace.Close() 156 | 157 | f, err := CreateFile(fname, F_ACC_TRUNC) 158 | if err != nil { 159 | t.Fatalf("CreateFile failed: %s\n", err) 160 | } 161 | defer f.Close() 162 | 163 | dset, err := f.CreateDataset("dset", T_NATIVE_USHORT, fspace) 164 | if err != nil { 165 | t.Fatal(err) 166 | } 167 | defer dset.Close() 168 | 169 | offset := []uint{6, 0, 0} 170 | stride := []uint{3, 1, 1} 171 | count := []uint{mdims[0], 1, mdims[1]} 172 | if err = fspace.SelectHyperslab(offset, stride, count, nil); err != nil { 173 | t.Fatal(err) 174 | } 175 | 176 | data := make([]uint16, mdims[0]*mdims[1]) 177 | 178 | if err = dset.WriteSubset(&data, mspace, fspace); err != nil { 179 | t.Fatal(err) 180 | } 181 | } 182 | 183 | func TestSelectHyperslab(t *testing.T) { 184 | DisplayErrors(true) 185 | defer DisplayErrors(false) 186 | defer os.Remove(fname) 187 | 188 | dims := []uint{12, 4} 189 | dspace, err := CreateSimpleDataspace(dims, nil) 190 | if err != nil { 191 | t.Fatal(err) 192 | } 193 | defer dspace.Close() 194 | offset, stride, count, block := []uint{1, 2}, []uint{2, 1}, []uint{4, 2}, []uint{1, 1} 195 | if err = dspace.SelectHyperslab(offset, stride, count, block); err != nil { 196 | t.Fatal(err) 197 | } 198 | if err = dspace.SelectHyperslab(offset, nil, count, block); err != nil { 199 | t.Fatal(err) 200 | } 201 | if err = dspace.SelectHyperslab(offset, stride, count, nil); err != nil { 202 | t.Fatal(err) 203 | } 204 | if err = dspace.SelectHyperslab(offset, nil, count, nil); err != nil { 205 | t.Fatal(err) 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /h5d_dataset.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | // #include "hdf5.h" 8 | // #include 9 | // #include 10 | import "C" 11 | 12 | import ( 13 | "fmt" 14 | 15 | "reflect" 16 | "unsafe" 17 | ) 18 | 19 | type Dataset struct { 20 | Identifier 21 | 22 | typ *Datatype 23 | } 24 | 25 | func newDataset(id C.hid_t, typ *Datatype) *Dataset { 26 | return &Dataset{Identifier: Identifier{id}, typ: typ} 27 | } 28 | 29 | func createDataset(id C.hid_t, name string, dtype *Datatype, dspace *Dataspace, dcpl *PropList) (*Dataset, error) { 30 | dtype, err := dtype.Copy() // For safety 31 | if err != nil { 32 | return nil, err 33 | } 34 | c_name := C.CString(name) 35 | defer C.free(unsafe.Pointer(c_name)) 36 | hid := C.H5Dcreate2(id, c_name, dtype.id, dspace.id, P_DEFAULT.id, dcpl.id, P_DEFAULT.id) 37 | if err := checkID(hid); err != nil { 38 | return nil, err 39 | } 40 | return newDataset(hid, dtype), nil 41 | } 42 | 43 | // Close releases and terminates access to a dataset. 44 | func (s *Dataset) Close() error { 45 | return s.closeWith(h5dclose) 46 | } 47 | 48 | func h5dclose(id C.hid_t) C.herr_t { 49 | return C.H5Dclose(id) 50 | } 51 | 52 | // Space returns an identifier for a copy of the dataspace for a dataset. 53 | func (s *Dataset) Space() *Dataspace { 54 | hid := C.H5Dget_space(s.id) 55 | if int(hid) > 0 { 56 | return newDataspace(hid) 57 | } 58 | return nil 59 | } 60 | 61 | // ReadSubset reads a subset of raw data from a dataset into a buffer. 62 | func (s *Dataset) ReadSubset(data interface{}, memspace, filespace *Dataspace) error { 63 | dtype, err := s.Datatype() 64 | defer dtype.Close() 65 | if err != nil { 66 | return err 67 | } 68 | 69 | var addr unsafe.Pointer 70 | v := reflect.Indirect(reflect.ValueOf(data)) 71 | 72 | switch v.Kind() { 73 | 74 | case reflect.Array: 75 | addr = unsafe.Pointer(v.UnsafeAddr()) 76 | 77 | case reflect.Slice: 78 | slice := (*reflect.SliceHeader)(unsafe.Pointer(v.UnsafeAddr())) 79 | addr = unsafe.Pointer(slice.Data) 80 | 81 | case reflect.String: 82 | str := (*reflect.StringHeader)(unsafe.Pointer(v.UnsafeAddr())) 83 | addr = unsafe.Pointer(str.Data) 84 | 85 | case reflect.Ptr: 86 | addr = unsafe.Pointer(v.Pointer()) 87 | 88 | default: 89 | addr = unsafe.Pointer(v.UnsafeAddr()) 90 | } 91 | 92 | var filespace_id, memspace_id C.hid_t = 0, 0 93 | if memspace != nil { 94 | memspace_id = memspace.id 95 | } 96 | if filespace != nil { 97 | filespace_id = filespace.id 98 | } 99 | rc := C.H5Dread(s.id, dtype.id, memspace_id, filespace_id, 0, addr) 100 | err = h5err(rc) 101 | return err 102 | } 103 | 104 | // Read reads raw data from a dataset into a buffer. 105 | func (s *Dataset) Read(data interface{}) error { 106 | return s.ReadSubset(data, nil, nil) 107 | } 108 | 109 | // WriteSubset writes a subset of raw data from a buffer to a dataset. 110 | func (s *Dataset) WriteSubset(data interface{}, memspace, filespace *Dataspace) error { 111 | dtype, err := s.Datatype() 112 | defer dtype.Close() 113 | if err != nil { 114 | return err 115 | } 116 | 117 | addr := unsafe.Pointer(nil) 118 | v := reflect.Indirect(reflect.ValueOf(data)) 119 | 120 | switch v.Kind() { 121 | 122 | case reflect.Array: 123 | addr = unsafe.Pointer(v.UnsafeAddr()) 124 | 125 | case reflect.Slice: 126 | slice := (*reflect.SliceHeader)(unsafe.Pointer(v.UnsafeAddr())) 127 | addr = unsafe.Pointer(slice.Data) 128 | 129 | case reflect.String: 130 | str := (*reflect.StringHeader)(unsafe.Pointer(v.UnsafeAddr())) 131 | addr = unsafe.Pointer(str.Data) 132 | 133 | case reflect.Ptr: 134 | addr = unsafe.Pointer(v.Pointer()) 135 | 136 | default: 137 | addr = unsafe.Pointer(v.UnsafeAddr()) 138 | } 139 | 140 | var filespace_id, memspace_id C.hid_t = 0, 0 141 | if memspace != nil { 142 | memspace_id = memspace.id 143 | } 144 | if filespace != nil { 145 | filespace_id = filespace.id 146 | } 147 | rc := C.H5Dwrite(s.id, dtype.id, memspace_id, filespace_id, 0, addr) 148 | err = h5err(rc) 149 | return err 150 | } 151 | 152 | // Write writes raw data from a buffer to a dataset. 153 | func (s *Dataset) Write(data interface{}) error { 154 | return s.WriteSubset(data, nil, nil) 155 | } 156 | 157 | // Creates a new attribute at this location. The returned attribute 158 | // must be closed by the user when it is no longer needed. 159 | func (s *Dataset) CreateAttribute(name string, dtype *Datatype, dspace *Dataspace) (*Attribute, error) { 160 | return createAttribute(s.id, name, dtype, dspace, P_DEFAULT) 161 | } 162 | 163 | // Creates a new attribute at this location. The returned 164 | // attribute must be closed by the user when it is no longer needed. 165 | func (s *Dataset) CreateAttributeWith(name string, dtype *Datatype, dspace *Dataspace, acpl *PropList) (*Attribute, error) { 166 | return createAttribute(s.id, name, dtype, dspace, acpl) 167 | } 168 | 169 | // Opens an existing attribute. The returned attribute must be closed 170 | // by the user when it is no longer needed. 171 | func (s *Dataset) OpenAttribute(name string) (*Attribute, error) { 172 | return openAttribute(s.id, name) 173 | } 174 | 175 | // Datatype returns the HDF5 Datatype of the Dataset. The returned 176 | // datatype must be closed by the user when it is no longer needed. 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 | 185 | // hasIllegalGoPointer returns whether the Dataset is known to have 186 | // a Go pointer to Go pointer chain. If the Dataset was created by 187 | // a call to OpenDataset without a read operation, it will be false, 188 | // but will not be a valid reflection of the real situation. 189 | func (s *Dataset) hasIllegalGoPointer() bool { 190 | return s.typ.hasIllegalGoPointer() 191 | } 192 | -------------------------------------------------------------------------------- /h5t_types_test.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | import "testing" 8 | 9 | func TestSimpleDatatypes(t *testing.T) { 10 | // Smoke tests for the simple datatypes 11 | tests := []struct { 12 | v interface{} 13 | hasIllegalPtr bool 14 | }{ 15 | {v: int(0), hasIllegalPtr: false}, 16 | {v: int8(0), hasIllegalPtr: false}, 17 | {v: int16(0), hasIllegalPtr: false}, 18 | {v: int32(0), hasIllegalPtr: false}, 19 | {v: int64(0), hasIllegalPtr: false}, 20 | {v: uint(0), hasIllegalPtr: false}, 21 | {v: uint8(0), hasIllegalPtr: false}, 22 | {v: uint16(0), hasIllegalPtr: false}, 23 | {v: uint32(0), hasIllegalPtr: false}, 24 | {v: uint64(0), hasIllegalPtr: false}, 25 | {v: float32(0), hasIllegalPtr: false}, 26 | {v: float64(0), hasIllegalPtr: false}, 27 | {v: string(""), hasIllegalPtr: false}, 28 | {v: ([]int)(nil), hasIllegalPtr: false}, 29 | {v: [1]int{0}, hasIllegalPtr: false}, 30 | {v: bool(true), hasIllegalPtr: false}, 31 | {v: (*int)(nil), hasIllegalPtr: false}, 32 | {v: (*int8)(nil), hasIllegalPtr: false}, 33 | {v: (*int16)(nil), hasIllegalPtr: false}, 34 | {v: (*int32)(nil), hasIllegalPtr: false}, 35 | {v: (*int64)(nil), hasIllegalPtr: false}, 36 | {v: (*uint)(nil), hasIllegalPtr: false}, 37 | {v: (*uint8)(nil), hasIllegalPtr: false}, 38 | {v: (*uint16)(nil), hasIllegalPtr: false}, 39 | {v: (*uint32)(nil), hasIllegalPtr: false}, 40 | {v: (*uint64)(nil), hasIllegalPtr: false}, 41 | {v: (*float32)(nil), hasIllegalPtr: false}, 42 | {v: (*float64)(nil), hasIllegalPtr: false}, 43 | {v: (*string)(nil), hasIllegalPtr: true}, 44 | {v: (*[]int)(nil), hasIllegalPtr: true}, 45 | {v: (*[1]int)(nil), hasIllegalPtr: false}, 46 | {v: (*bool)(nil), hasIllegalPtr: false}, 47 | {v: (**int)(nil), hasIllegalPtr: true}, 48 | {v: (**int8)(nil), hasIllegalPtr: true}, 49 | {v: (**int16)(nil), hasIllegalPtr: true}, 50 | {v: (**int32)(nil), hasIllegalPtr: true}, 51 | {v: (**int64)(nil), hasIllegalPtr: true}, 52 | {v: (**uint)(nil), hasIllegalPtr: true}, 53 | {v: (**uint8)(nil), hasIllegalPtr: true}, 54 | {v: (**uint16)(nil), hasIllegalPtr: true}, 55 | {v: (**uint32)(nil), hasIllegalPtr: true}, 56 | {v: (**uint64)(nil), hasIllegalPtr: true}, 57 | {v: (**float32)(nil), hasIllegalPtr: true}, 58 | {v: (**float64)(nil), hasIllegalPtr: true}, 59 | {v: (**string)(nil), hasIllegalPtr: true}, 60 | {v: (**[]int)(nil), hasIllegalPtr: true}, 61 | {v: (**[1]int)(nil), hasIllegalPtr: true}, 62 | {v: (**bool)(nil), hasIllegalPtr: true}, 63 | } 64 | 65 | for _, test := range tests { 66 | dt, err := NewDatatypeFromValue(test.v) 67 | if err != nil { 68 | t.Errorf("unexpected error: %v", err) 69 | continue 70 | } 71 | gotIllegalPtr := dt.hasIllegalGoPointer() 72 | if gotIllegalPtr != test.hasIllegalPtr { 73 | t.Errorf("unexpected illegal pointer status for %T: got:%t want:%t", test.v, gotIllegalPtr, test.hasIllegalPtr) 74 | } 75 | } 76 | } 77 | 78 | // Test for array datatypes. Checks that the number of dimensions is correct. 79 | func TestArrayDatatype(t *testing.T) { 80 | tests := map[int]interface{}{ 81 | 1: [8]int{1, 1, 2, 3, 5, 8, 13}, 82 | 2: [2][2]int{{1, 2}, {3, 4}}, 83 | 3: [2][2][2]int{{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}}, 84 | } 85 | 86 | for dims, val := range tests { 87 | dt, err := NewDatatypeFromValue(val) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | if dt.hasIllegalGoPointer() { 92 | t.Errorf("unexpected illegal pointer for %T", val) 93 | } 94 | adt := ArrayType{*dt} 95 | if adt.NDims() != dims { 96 | t.Errorf("wrong number of dimensions: got %d, want %d", adt.NDims(), dims) 97 | } 98 | } 99 | } 100 | 101 | func TestStructDatatype(t *testing.T) { 102 | test := struct { 103 | a int32 104 | b string 105 | c struct { 106 | a int32 107 | b string 108 | } 109 | }{} 110 | 111 | // Test that the type can be constructed and that the number of 112 | // members is as expected. 113 | var dtypes []*Datatype 114 | 115 | // "Regular" value 116 | dtype, err := NewDatatypeFromValue(test) 117 | if err != nil { 118 | t.Fatal(err) 119 | } 120 | if dtype.hasIllegalGoPointer() { 121 | t.Errorf("unexpected illegal pointer for %T", test) 122 | } 123 | dtypes = append(dtypes, dtype) 124 | 125 | // pointer to value 126 | dtype, err = NewDatatypeFromValue(&test) 127 | if err != nil { 128 | t.Fatal(err) 129 | } 130 | if !dtype.hasIllegalGoPointer() { 131 | t.Errorf("expected illegal pointer for %T", &test) 132 | } 133 | dtypes = append(dtypes, dtype) 134 | 135 | for _, dtype := range dtypes { 136 | dt := CompoundType{*dtype} 137 | if dt.NMembers() != 3 { 138 | t.Errorf("wrong number of members: got %d, want %d", dt.NMembers(), 3) 139 | } 140 | 141 | memberClasses := []TypeClass{ 142 | T_INTEGER, 143 | T_STRING, 144 | T_COMPOUND, 145 | } 146 | // Test the member classes, and also test that they can be constructed 147 | for idx, class := range memberClasses { 148 | if dt.MemberClass(idx) != class { 149 | t.Errorf("wrong TypeClass: got %v, want %v", dt.MemberClass(idx), class) 150 | } 151 | _, err := dt.MemberType(idx) 152 | if err != nil { 153 | t.Fatal(err) 154 | } 155 | } 156 | 157 | // Test the member names 158 | memberNames := []string{"a", "b", "c"} 159 | for idx, name := range memberNames { 160 | if dt.MemberName(idx) != name { 161 | t.Errorf("wrong name: got %q, want %q", dt.MemberName(idx), name) 162 | } 163 | if dt.MemberIndex(name) != idx { 164 | t.Errorf("wrong index: got %d, want %d", dt.MemberIndex(name), idx) 165 | } 166 | } 167 | 168 | // Pack the datatype, otherwise offsets are implementation defined 169 | dt.Pack() 170 | memberOffsets := []int{0, 4, 12} 171 | for idx, offset := range memberOffsets { 172 | if dt.MemberOffset(idx) != offset { 173 | t.Errorf("wrong offset: got %d, want %d", dt.MemberOffset(idx), offset) 174 | } 175 | } 176 | } 177 | } 178 | 179 | func TestCloseBehavior(t *testing.T) { 180 | var s struct { 181 | a int 182 | b float64 183 | } 184 | dtype, err := NewDatatypeFromValue(s) 185 | if err != nil { 186 | t.Fatal(err) 187 | } 188 | defer dtype.Close() 189 | } 190 | -------------------------------------------------------------------------------- /h5pt_table.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | // #include "hdf5.h" 8 | // #include "hdf5_hl.h" 9 | // #include 10 | // #include 11 | // #include 12 | import "C" 13 | 14 | import ( 15 | "fmt" 16 | "reflect" 17 | "unsafe" 18 | 19 | "gonum.org/v1/hdf5/cmem" 20 | ) 21 | 22 | // Table is an hdf5 packet-table. 23 | type Table struct { 24 | Identifier 25 | } 26 | 27 | func newPacketTable(id C.hid_t) *Table { 28 | return &Table{Identifier{id}} 29 | } 30 | 31 | // Close closes an open packet table. 32 | func (t *Table) Close() error { 33 | return t.closeWith(h5ptclose) 34 | } 35 | 36 | func h5ptclose(id C.hid_t) C.herr_t { 37 | return C.H5PTclose(id) 38 | } 39 | 40 | // IsValid returns whether or not an indentifier points to a packet table. 41 | func (t *Table) IsValid() bool { 42 | return C.H5PTis_valid(t.id) >= 0 43 | } 44 | 45 | // ReadPackets reads a number of packets from a packet table. 46 | func (t *Table) ReadPackets(start, nrecords int, data interface{}) error { 47 | c_start := C.hsize_t(start) 48 | c_nrecords := C.size_t(nrecords) 49 | rv := reflect.Indirect(reflect.ValueOf(data)) 50 | rt := rv.Type() 51 | c_data := unsafe.Pointer(nil) 52 | switch rt.Kind() { 53 | case reflect.Array: 54 | if rv.Len() < nrecords { 55 | panic(fmt.Errorf("not enough capacity in array (cap=%d)", rv.Len())) 56 | } 57 | c_data = unsafe.Pointer(rv.Index(0).UnsafeAddr()) 58 | 59 | case reflect.Slice: 60 | if rv.Len() < nrecords { 61 | panic(fmt.Errorf("not enough capacity in slice (cap=%d)", rv.Len())) 62 | } 63 | slice := (*reflect.SliceHeader)(unsafe.Pointer(rv.UnsafeAddr())) 64 | c_data = unsafe.Pointer(slice.Data) 65 | 66 | default: 67 | panic(fmt.Errorf("unhandled kind (%s), need slice or array", rt.Kind())) 68 | } 69 | err := C.H5PTread_packets(t.id, c_start, c_nrecords, c_data) 70 | return h5err(err) 71 | } 72 | 73 | // Append appends packets to the end of a packet table. 74 | // 75 | // Struct values must only have exported fields, otherwise Append will panic. 76 | func (t *Table) Append(args ...interface{}) error { 77 | if len(args) == 0 { 78 | return fmt.Errorf("hdf5: no arguments passed to packet table append.") 79 | } 80 | 81 | var enc cmem.Encoder 82 | for _, arg := range args { 83 | if err := enc.Encode(arg); err != nil { 84 | return err 85 | } 86 | } 87 | 88 | if len(enc.Buf) <= 0 { 89 | return fmt.Errorf("hdf5: invalid empty buffer") 90 | } 91 | 92 | return h5err(C.H5PTappend(t.id, C.size_t(len(args)), unsafe.Pointer(&enc.Buf[0]))) 93 | } 94 | 95 | // Next reads packets from a packet table starting at the current index into the value pointed at by data. 96 | // i.e. data is a pointer to an array or a slice. 97 | func (t *Table) Next(data interface{}) error { 98 | rt := reflect.TypeOf(data) 99 | if rt.Kind() != reflect.Ptr { 100 | return fmt.Errorf("hdf5: invalid value type. got=%v, want pointer", rt.Kind()) 101 | } 102 | rt = rt.Elem() 103 | rv := reflect.Indirect(reflect.ValueOf(data)) 104 | 105 | n := C.size_t(0) 106 | cdata := unsafe.Pointer(nil) 107 | switch rt.Kind() { 108 | case reflect.Array: 109 | if rv.Cap() <= 0 { 110 | panic(fmt.Errorf("not enough capacity in array (cap=%d)", rv.Cap())) 111 | } 112 | cdata = unsafe.Pointer(rv.UnsafeAddr()) 113 | n = C.size_t(rv.Cap()) 114 | 115 | case reflect.Slice: 116 | if rv.Cap() <= 0 { 117 | panic(fmt.Errorf("not enough capacity in slice (cap=%d)", rv.Cap())) 118 | } 119 | slice := (*reflect.SliceHeader)(unsafe.Pointer(rv.UnsafeAddr())) 120 | cdata = unsafe.Pointer(slice.Data) 121 | n = C.size_t(rv.Cap()) 122 | 123 | default: 124 | panic(fmt.Errorf("unsupported kind (%s), need slice or array", rt.Kind())) 125 | } 126 | err := C.H5PTget_next(t.id, n, cdata) 127 | return h5err(err) 128 | } 129 | 130 | // NumPackets returns the number of packets in a packet table. 131 | func (t *Table) NumPackets() (int, error) { 132 | c_nrecords := C.hsize_t(0) 133 | err := C.H5PTget_num_packets(t.id, &c_nrecords) 134 | return int(c_nrecords), h5err(err) 135 | } 136 | 137 | // CreateIndex resets a packet table's index to the first packet. 138 | func (t *Table) CreateIndex() error { 139 | err := C.H5PTcreate_index(t.id) 140 | return h5err(err) 141 | } 142 | 143 | // SetIndex sets a packet table's index. 144 | func (t *Table) SetIndex(index int) error { 145 | c_idx := C.hsize_t(index) 146 | err := C.H5PTset_index(t.id, c_idx) 147 | return h5err(err) 148 | } 149 | 150 | // Type returns an identifier for a copy of the datatype for a dataset. The returned 151 | // datatype must be closed by the user when it is no longer needed. 152 | func (t *Table) Type() (*Datatype, error) { 153 | hid := C.H5Dget_type(t.id) 154 | if err := checkID(hid); err != nil { 155 | return nil, err 156 | } 157 | return NewDatatype(hid), nil 158 | } 159 | 160 | func createTable(id C.hid_t, name string, dtype *Datatype, chunkSize, compression int) (*Table, error) { 161 | c_name := C.CString(name) 162 | defer C.free(unsafe.Pointer(c_name)) 163 | 164 | chunk := C.hsize_t(chunkSize) 165 | compr := C.int(compression) 166 | hid := C.H5PTcreate_fl(id, c_name, dtype.id, chunk, compr) 167 | if err := checkID(hid); err != nil { 168 | return nil, err 169 | } 170 | return newPacketTable(hid), nil 171 | } 172 | 173 | func createTableFrom(id C.hid_t, name string, dtype interface{}, chunkSize, compression int) (*Table, error) { 174 | var err error 175 | switch dt := dtype.(type) { 176 | case reflect.Type: 177 | if hdfDtype, err := NewDataTypeFromType(dt); err == nil { 178 | return createTable(id, name, hdfDtype, chunkSize, compression) 179 | } 180 | case *Datatype: 181 | return createTable(id, name, dt, chunkSize, compression) 182 | default: 183 | if hdfDtype, err := NewDataTypeFromType(reflect.TypeOf(dtype)); err == nil { 184 | return createTable(id, name, hdfDtype, chunkSize, compression) 185 | } 186 | } 187 | return nil, err 188 | } 189 | 190 | func openTable(id C.hid_t, name string) (*Table, error) { 191 | c_name := C.CString(name) 192 | defer C.free(unsafe.Pointer(c_name)) 193 | 194 | hid := C.H5PTopen(id, c_name) 195 | if err := checkID(hid); err != nil { 196 | return nil, err 197 | } 198 | return newPacketTable(hid), nil 199 | } 200 | -------------------------------------------------------------------------------- /h5p_proplist_test.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2019 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | import ( 8 | "fmt" 9 | "math" 10 | "os" 11 | "testing" 12 | ) 13 | 14 | /** 15 | * These test cases are based on the h5_cmprss.c by The HDF Group. 16 | * https://support.hdfgroup.org/HDF5/examples/intro.html#c 17 | */ 18 | 19 | func TestChunk(t *testing.T) { 20 | DisplayErrors(true) 21 | defer DisplayErrors(false) 22 | var ( 23 | fn = "test_chunk.h5" 24 | dsn = "dset_chunk" 25 | dims = []uint{1000, 1000} 26 | cdims = []uint{100, 100} 27 | ) 28 | defer os.Remove(fn) 29 | 30 | dcpl, err := NewPropList(P_DATASET_CREATE) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | defer dcpl.Close() 35 | err = dcpl.SetChunk(cdims) 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | 40 | cdimsChunk, err := dcpl.GetChunk(len(cdims)) 41 | if err != nil { 42 | t.Fatal(err) 43 | } 44 | for i, cdim := range cdimsChunk { 45 | if cdim != cdims[i] { 46 | t.Fatalf("chunked dimensions mismatch: %d != %d", cdims[i], cdim) 47 | } 48 | } 49 | 50 | data0, err := save(fn, dsn, dims, dcpl) 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | 55 | data1, err := load(fn, dsn, nil) 56 | if err != nil { 57 | t.Fatal(err) 58 | } 59 | 60 | if err := compare(data0, data1); err != nil { 61 | t.Fatal(err) 62 | } 63 | } 64 | 65 | func TestDeflate(t *testing.T) { 66 | DisplayErrors(true) 67 | defer DisplayErrors(false) 68 | var ( 69 | fn = "test_cmprss_deflate.h5" 70 | dsn = "dset_cmpress" 71 | dims = []uint{1000, 1000} 72 | cdims = []uint{100, 100} 73 | ) 74 | defer os.Remove(fn) 75 | 76 | dcpl, err := NewPropList(P_DATASET_CREATE) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | defer dcpl.Close() 81 | err = dcpl.SetChunk(cdims) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | err = dcpl.SetDeflate(DefaultCompression) 86 | if err != nil { 87 | t.Fatal(err) 88 | } 89 | 90 | data0, err := save(fn, dsn, dims, dcpl) 91 | if err != nil { 92 | t.Fatal(err) 93 | } 94 | 95 | data1, err := load(fn, dsn, nil) 96 | if err != nil { 97 | t.Fatal(err) 98 | } 99 | 100 | if err := compare(data0, data1); err != nil { 101 | t.Fatal(err) 102 | } 103 | } 104 | 105 | func TestChunkCache(t *testing.T) { 106 | DisplayErrors(true) 107 | defer DisplayErrors(false) 108 | var ( 109 | fn = "test_chunk_cache.h5" 110 | dsn = "dset_chunk_cache" 111 | dims = []uint{1000, 1000} 112 | cdims = []uint{100, 100} 113 | ) 114 | defer os.Remove(fn) 115 | 116 | dcpl, err := NewPropList(P_DATASET_CREATE) 117 | if err != nil { 118 | t.Fatal(err) 119 | } 120 | defer dcpl.Close() 121 | err = dcpl.SetChunk(cdims) 122 | if err != nil { 123 | t.Fatal(err) 124 | } 125 | 126 | cdimsChunk, err := dcpl.GetChunk(len(cdims)) 127 | if err != nil { 128 | t.Fatal(err) 129 | } 130 | for i, cdim := range cdimsChunk { 131 | if cdim != cdims[i] { 132 | t.Fatalf("chunked dimensions mismatch: %d != %d", cdims[i], cdim) 133 | } 134 | } 135 | 136 | data0, err := save(fn, dsn, dims, dcpl) 137 | if err != nil { 138 | t.Fatal(err) 139 | } 140 | 141 | dapl, err := NewPropList(P_DATASET_ACCESS) 142 | if err != nil { 143 | t.Fatal(err) 144 | } 145 | defer dapl.Close() 146 | 147 | nslots, nbytes, w0, err := dapl.GetChunkCache() 148 | if err != nil { 149 | t.Fatal(err) 150 | } 151 | 152 | nslotsNew, nbytesNew, w0New := nslots*4, nbytes*2, w0/3 153 | if err := dapl.SetChunkCache(nslotsNew, nbytesNew, w0New); err != nil { 154 | t.Fatal(err) 155 | } 156 | if err := checkChunkCache(nslotsNew, nbytesNew, w0New, dapl); err != nil { 157 | t.Fatal(err) 158 | } 159 | 160 | data1, err := load(fn, dsn, dapl) 161 | if err != nil { 162 | t.Fatal(err) 163 | } 164 | if err := compare(data0, data1); err != nil { 165 | t.Fatal(err) 166 | } 167 | 168 | if err := dapl.SetChunkCache(D_CHUNK_CACHE_NSLOTS_DEFAULT, D_CHUNK_CACHE_NBYTES_DEFAULT, D_CHUNK_CACHE_W0_DEFAULT); err != nil { 169 | t.Fatal(err) 170 | } 171 | if err := checkChunkCache(nslots, nbytes, w0, dapl); err != nil { 172 | t.Fatal(err) 173 | } 174 | } 175 | 176 | func save(fn, dsn string, dims []uint, dcpl *PropList) ([]float64, error) { 177 | f, err := CreateFile(fn, F_ACC_TRUNC) 178 | if err != nil { 179 | return nil, err 180 | } 181 | defer f.Close() 182 | 183 | dspace, err := CreateSimpleDataspace(dims, dims) 184 | if err != nil { 185 | return nil, err 186 | } 187 | defer dspace.Close() 188 | 189 | dset, err := f.CreateDatasetWith(dsn, T_NATIVE_DOUBLE, dspace, dcpl) 190 | if err != nil { 191 | return nil, err 192 | } 193 | defer dset.Close() 194 | 195 | n := dims[0] * dims[1] 196 | data := make([]float64, n) 197 | for i := range data { 198 | data[i] = float64((i*i*i + 13) % 8191) 199 | } 200 | err = dset.Write(&data[0]) 201 | if err != nil { 202 | return nil, err 203 | } 204 | return data, nil 205 | } 206 | 207 | func load(fn, dsn string, dapl *PropList) ([]float64, error) { 208 | f, err := OpenFile(fn, F_ACC_RDONLY) 209 | if err != nil { 210 | return nil, err 211 | } 212 | defer f.Close() 213 | 214 | var dset *Dataset 215 | if dapl == nil { 216 | dset, err = f.OpenDataset(dsn) 217 | } else { 218 | dset, err = f.OpenDatasetWith(dsn, dapl) 219 | } 220 | if err != nil { 221 | return nil, err 222 | } 223 | defer dset.Close() 224 | 225 | dims, _, err := dset.Space().SimpleExtentDims() 226 | if err != nil { 227 | return nil, err 228 | } 229 | 230 | data := make([]float64, dims[0]*dims[1]) 231 | dset.Read(&data[0]) 232 | return data, nil 233 | } 234 | 235 | func compare(ds0, ds1 []float64) error { 236 | n0, n1 := len(ds0), len(ds1) 237 | if n0 != n1 { 238 | return fmt.Errorf("dimensions mismatch: %d != %d", n0, n1) 239 | } 240 | for i := 0; i < n0; i++ { 241 | d := math.Abs(ds0[i] - ds1[i]) 242 | if d > 1e-7 { 243 | return fmt.Errorf("values at index %d differ: %f != %f", i, ds0[i], ds1[i]) 244 | } 245 | } 246 | return nil 247 | } 248 | 249 | func checkChunkCache(nslots, nbytes int, w0 float64, dapl *PropList) error { 250 | nslotsCache, nbytesCache, w0Cache, err := dapl.GetChunkCache() 251 | if err != nil { 252 | return err 253 | } 254 | 255 | if nslotsCache != nslots { 256 | return fmt.Errorf("`nslots` mismatch: %d != %d", nslots, nslotsCache) 257 | } 258 | if nbytesCache != nbytes { 259 | return fmt.Errorf("`nbytes` mismatch: %d != %d", nbytes, nbytesCache) 260 | } 261 | if math.Abs(w0Cache-w0) > 1e-5 { 262 | return fmt.Errorf("`w0` mismatch: %.6f != %.6f", w0, w0Cache) 263 | } 264 | return nil 265 | } 266 | -------------------------------------------------------------------------------- /h5pt_table_test.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | import ( 8 | "os" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | const ( 14 | fname string = "ex_table_01.h5" 15 | tname string = "table" 16 | chunkSize int = 10 17 | compress int = 0 18 | ) 19 | 20 | type particle struct { 21 | // Name string `hdf5:"Name"` // FIXME(TacoVox): ReadPackets seems to need an update 22 | Vehicle_no uint8 `hdf5:"Vehicle Number"` 23 | Satellites int8 `hdf5:"Satellites"` 24 | Cars_no int16 `hdf5:"Number of Cars"` 25 | Trucks_no int16 `hdf5:"Number of Trucks"` 26 | Min_speed uint32 `hdf5:"Minimum Speed"` 27 | Lati int32 `hdf5:"Latitude"` 28 | Max_speed uint64 `hdf5:"Maximum Speed"` 29 | Longi int64 `hdf5:"Longitude"` 30 | Pressure float32 `hdf5:"Pressure"` 31 | Temperature float64 `hdf5:"Temperature"` 32 | Accurate bool `hdf5:"Accurate"` 33 | // isthep []int // FIXME(sbinet) 34 | // jmohep [2][2]int64 // FIXME(sbinet) 35 | } 36 | 37 | func testTable(t *testing.T, dType interface{}, data ...interface{}) { 38 | var table *Table 39 | 40 | typeString := reflect.TypeOf(data).String() 41 | 42 | // create a new file using default properties 43 | f, err := CreateFile(fname, F_ACC_TRUNC) 44 | if err != nil { 45 | t.Fatalf("CreateFile failed for %s: %s", typeString, err) 46 | } 47 | defer os.Remove(fname) 48 | defer f.Close() 49 | 50 | table, err = f.CreateTableFrom(tname, dType, chunkSize, compress) 51 | if err != nil { 52 | t.Fatalf("CreateTableFrom struct failed for %s: %s", typeString, err) 53 | } 54 | defer table.Close() 55 | 56 | if !table.IsValid() { 57 | t.Fatalf("PacketTable %q is invalid for %s.", tname, typeString) 58 | } 59 | 60 | // write one packet to the packet table 61 | if err = table.Append(data...); err != nil { 62 | t.Fatalf("Append failed with single packet for %s: %s", typeString, err) 63 | } 64 | 65 | // get the number of packets 66 | n, err := table.NumPackets() 67 | if err != nil { 68 | t.Fatalf("NumPackets failed for %s: %s", typeString, err) 69 | } 70 | if n != len(data) { 71 | t.Fatalf("Wrong number of packets reported for %s, expected %d but got %d", typeString, len(data), n) 72 | } 73 | 74 | // iterate through packets 75 | for i := 0; i != n; i++ { 76 | p := make([]interface{}, 1) 77 | if err = table.Next(&p); err != nil { 78 | t.Fatalf("Next failed for %s: %s", typeString, err) 79 | } 80 | } 81 | 82 | // For now just conduct the "old" test case // FIXME(TacoVox) 83 | if reflect.TypeOf(data).String() != "[]hdf5.particle" { 84 | return 85 | } 86 | 87 | // reset index 88 | table.CreateIndex() 89 | 90 | readdata := make([]particle, len(data)) 91 | 92 | if err = table.ReadPackets(0, len(data), &readdata); err != nil { 93 | t.Fatalf("ReadPackets failed for %s: %s", typeString, err) 94 | } 95 | 96 | if !reflect.DeepEqual(readdata, data) { 97 | t.Fatalf("Data differs for %s.\ngot= %#v\nwant=%#v\n", typeString, readdata, data) 98 | } 99 | } 100 | 101 | func TestPTStruct(t *testing.T) { 102 | // particles := []particle{ 103 | // {"zero", 0, 0, 0.0, 0., []int{0, 0}, [2][2]int{{0, 0}, {0, 0}}}, 104 | // {"one", 10, 10, 1.0, 10., []int{0, 0}, [2][2]int{{1, 0}, {0, 1}}}, 105 | // {"two", 20, 20, 2.0, 20., []int{0, 0}, [2][2]int{{2, 0}, {0, 2}}}, 106 | // {"three", 30, 30, 3.0, 30., []int{0, 0}, [2][2]int{{3, 0}, {0, 3}}}, 107 | // {"four", 40, 40, 4.0, 40., []int{0, 0}, [2][2]int{{4, 0}, {0, 4}}}, 108 | // {"five", 50, 50, 5.0, 50., []int{0, 0}, [2][2]int{{5, 0}, {0, 5}}}, 109 | // {"six", 60, 60, 6.0, 60., []int{0, 0}, [2][2]int{{6, 0}, {0, 6}}}, 110 | // {"seven", 70, 70, 7.0, 70., []int{0, 0}, [2][2]int{{7, 0}, {0, 7}}}, 111 | // } // TODO use array and strings when read is fixed. 112 | 113 | testTable(t, particle{}, particle{0, 0, 0, 0, 0, 0, 0, 0, 0.0, 0.0, false}) 114 | testTable(t, particle{}, 115 | particle{10, 10, 10, 10, 10, 10, 10, 10, 1.0, 10.0, false}, 116 | particle{20, 20, 20, 20, 20, 20, 20, 20, 2.0, 20.0, false}, 117 | particle{30, 30, 30, 30, 30, 30, 30, 30, 3.0, 30.0, false}, 118 | particle{40, 40, 40, 40, 40, 40, 40, 40, 4.0, 40.0, true}, 119 | particle{50, 50, 50, 50, 50, 50, 50, 50, 5.0, 50.0, true}, 120 | particle{60, 60, 60, 60, 60, 60, 60, 60, 6.0, 60.0, true}, 121 | particle{70, 70, 70, 70, 70, 70, 70, 70, 7.0, 70.0, true}, 122 | ) 123 | } 124 | 125 | func TestPTableBasic(t *testing.T) { 126 | // INT8 127 | testTable(t, T_NATIVE_INT8, int8(-3)) 128 | testTable(t, T_NATIVE_INT8, 129 | int8(-2), 130 | int8(-1), 131 | int8(0), 132 | int8(1), 133 | int8(2), 134 | int8(3), 135 | int8(4), 136 | ) 137 | 138 | // INT16 139 | testTable(t, T_NATIVE_INT16, int16(-3)) 140 | testTable(t, T_NATIVE_INT16, 141 | int16(-2), 142 | int16(-1), 143 | int16(0), 144 | int16(1), 145 | int16(2), 146 | int16(3), 147 | int16(4), 148 | ) 149 | 150 | // INT32 151 | testTable(t, T_NATIVE_INT32, int32(-3)) 152 | testTable(t, T_NATIVE_INT32, 153 | int32(-2), 154 | int32(-1), 155 | int32(0), 156 | int32(1), 157 | int32(2), 158 | int32(3), 159 | int32(4), 160 | ) 161 | 162 | // INT64 163 | testTable(t, T_NATIVE_INT64, int64(-3)) 164 | testTable(t, T_NATIVE_INT64, 165 | int64(-2), 166 | int64(-1), 167 | int64(0), 168 | int64(1), 169 | int64(2), 170 | int64(3), 171 | int64(4), 172 | ) 173 | 174 | // UINT8 175 | testTable(t, T_NATIVE_UINT8, uint8(0)) 176 | testTable(t, T_NATIVE_UINT8, 177 | uint8(1), 178 | uint8(2), 179 | uint8(3), 180 | uint8(4), 181 | uint8(5), 182 | uint8(6), 183 | uint8(7), 184 | ) 185 | 186 | // UINT16 187 | testTable(t, T_NATIVE_UINT16, uint16(0)) 188 | testTable(t, T_NATIVE_UINT16, 189 | uint16(1), 190 | uint16(2), 191 | uint16(3), 192 | uint16(4), 193 | uint16(5), 194 | uint16(6), 195 | uint16(7), 196 | ) 197 | 198 | // UINT32 199 | testTable(t, T_NATIVE_UINT32, uint32(0)) 200 | testTable(t, T_NATIVE_UINT32, 201 | uint32(1), 202 | uint32(2), 203 | uint32(3), 204 | uint32(4), 205 | uint32(5), 206 | uint32(6), 207 | uint32(7), 208 | ) 209 | 210 | // UINT64 211 | testTable(t, T_NATIVE_UINT64, uint64(0)) 212 | testTable(t, T_NATIVE_UINT64, 213 | uint64(1), 214 | uint64(2), 215 | uint64(3), 216 | uint64(4), 217 | uint64(5), 218 | uint64(6), 219 | uint64(7), 220 | ) 221 | 222 | // FLOAT32 223 | testTable(t, T_NATIVE_FLOAT, float32(0)) 224 | testTable(t, T_NATIVE_FLOAT, 225 | float32(1), 226 | float32(2), 227 | float32(3), 228 | float32(4), 229 | float32(5), 230 | float32(6), 231 | float32(7), 232 | ) 233 | 234 | // FLOAT64 235 | testTable(t, T_NATIVE_DOUBLE, float64(0)) 236 | testTable(t, T_NATIVE_DOUBLE, 237 | float64(1), 238 | float64(2), 239 | float64(3), 240 | float64(4), 241 | float64(5), 242 | float64(6), 243 | float64(7), 244 | ) 245 | 246 | // BOOL 247 | testTable(t, T_NATIVE_HBOOL, false) 248 | testTable(t, T_NATIVE_HBOOL, 249 | true, 250 | false, 251 | true, 252 | false, 253 | true, 254 | false, 255 | true, 256 | ) 257 | 258 | // STRING 259 | testTable(t, T_GO_STRING, "zero") 260 | testTable(t, T_GO_STRING, 261 | "one", 262 | "two", 263 | "three", 264 | "four", 265 | "five", 266 | "six", 267 | "seven", 268 | ) 269 | } 270 | -------------------------------------------------------------------------------- /h5g_group.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | // #include "hdf5.h" 8 | // #include "hdf5_hl.h" 9 | // #include 10 | // #include 11 | import "C" 12 | 13 | import ( 14 | "fmt" 15 | "unsafe" 16 | ) 17 | 18 | // GType describes the type of an object inside a Group or File. 19 | type GType int 20 | 21 | const ( 22 | H5G_UNKNOWN GType = C.H5G_UNKNOWN // Unknown object type 23 | H5G_GROUP GType = C.H5G_GROUP // Object is a group 24 | H5G_DATASET GType = C.H5G_DATASET // Object is a dataset 25 | H5G_TYPE GType = C.H5G_TYPE // Object is a named data type 26 | H5G_LINK GType = C.H5G_LINK // Object is a symbolic link 27 | H5G_UDLINK GType = C.H5G_UDLINK // Object is a user-defined link 28 | ) 29 | 30 | func (typ GType) String() string { 31 | switch typ { 32 | case H5G_UNKNOWN: 33 | return "unknown" 34 | case H5G_GROUP: 35 | return "group" 36 | case H5G_DATASET: 37 | return "dataset" 38 | case H5G_TYPE: 39 | return "type" 40 | case H5G_LINK: 41 | return "link" 42 | case H5G_UDLINK: 43 | return "udlink" 44 | default: 45 | return fmt.Sprintf("GType(%d)", int(typ)) 46 | } 47 | } 48 | 49 | // CommonFG is for methods common to both File and Group 50 | type CommonFG struct { 51 | Identifier 52 | } 53 | 54 | // Group is an HDF5 container object. It can contain any Location. 55 | type Group struct { 56 | CommonFG 57 | } 58 | 59 | // CreateGroup creates and returns a new empty group and links it to a location 60 | // in the file. The returned group must be closed by the user when it is no 61 | // longer needed. 62 | func (g *CommonFG) CreateGroup(name string) (*Group, error) { 63 | c_name := C.CString(name) 64 | defer C.free(unsafe.Pointer(c_name)) 65 | 66 | link_flags := C.hid_t(C.H5P_DEFAULT) 67 | grp_c_flags := C.hid_t(C.H5P_DEFAULT) 68 | hid := C.H5Gcreate2(g.id, c_name, link_flags, grp_c_flags, P_DEFAULT.id) 69 | if err := checkID(hid); err != nil { 70 | return nil, err 71 | } 72 | group := &Group{CommonFG{Identifier{hid}}} 73 | return group, nil 74 | } 75 | 76 | // CreateDataset creates a new Dataset. The returned dataset must be 77 | // closed by the user when it is no longer needed. 78 | func (g *CommonFG) CreateDataset(name string, dtype *Datatype, dspace *Dataspace) (*Dataset, error) { 79 | return createDataset(g.id, name, dtype, dspace, P_DEFAULT) 80 | } 81 | 82 | // CreateDatasetWith creates a new Dataset with a user-defined PropList. 83 | // The returned dataset must be closed by the user when it is no longer needed. 84 | func (g *CommonFG) CreateDatasetWith(name string, dtype *Datatype, dspace *Dataspace, dcpl *PropList) (*Dataset, error) { 85 | return createDataset(g.id, name, dtype, dspace, dcpl) 86 | } 87 | 88 | // CreateAttribute creates a new attribute at this location. The returned 89 | // attribute must be closed by the user when it is no longer needed. 90 | func (g *Group) CreateAttribute(name string, dtype *Datatype, dspace *Dataspace) (*Attribute, error) { 91 | return createAttribute(g.id, name, dtype, dspace, P_DEFAULT) 92 | } 93 | 94 | // CreateAttributeWith creates a new attribute at this location with a user-defined 95 | // PropList. The returned dataset must be closed by the user when it is no longer needed. 96 | func (g *Group) CreateAttributeWith(name string, dtype *Datatype, dspace *Dataspace, acpl *PropList) (*Attribute, error) { 97 | return createAttribute(g.id, name, dtype, dspace, acpl) 98 | } 99 | 100 | // Opens an existing attribute. The returned attribute must be closed 101 | // by the user when it is no longer needed. 102 | func (g *Group) OpenAttribute(name string) (*Attribute, error) { 103 | return openAttribute(g.id, name) 104 | } 105 | 106 | // Close closes the Group. 107 | func (g *Group) Close() error { 108 | return g.closeWith(h5gclose) 109 | } 110 | 111 | func h5gclose(id C.hid_t) C.herr_t { 112 | return C.H5Gclose(id) 113 | } 114 | 115 | // OpenGroup opens and returns an existing child group from this Group. 116 | // The returned group must be closed by the user when it is no longer needed. 117 | func (g *CommonFG) OpenGroup(name string) (*Group, error) { 118 | c_name := C.CString(name) 119 | defer C.free(unsafe.Pointer(c_name)) 120 | 121 | hid := C.H5Gopen2(g.id, c_name, P_DEFAULT.id) 122 | if err := checkID(hid); err != nil { 123 | return nil, err 124 | } 125 | group := &Group{CommonFG{Identifier{hid}}} 126 | return group, nil 127 | } 128 | 129 | // OpenDataset opens and returns a named Dataset. The returned 130 | // dataset must be closed by the user when it is no longer needed. 131 | func (g *CommonFG) OpenDataset(name string) (*Dataset, error) { 132 | c_name := C.CString(name) 133 | defer C.free(unsafe.Pointer(c_name)) 134 | 135 | hid := C.H5Dopen2(g.id, c_name, P_DEFAULT.id) 136 | if err := checkID(hid); err != nil { 137 | return nil, err 138 | } 139 | return newDataset(hid, nil), nil 140 | } 141 | 142 | // OpenDatasetWith opens and returns a named Dataset with a user-defined PropList. 143 | // The returned dataset must be closed by the user when it is no longer needed. 144 | func (g *CommonFG) OpenDatasetWith(name string, dapl *PropList) (*Dataset, error) { 145 | c_name := C.CString(name) 146 | defer C.free(unsafe.Pointer(c_name)) 147 | 148 | hid := C.H5Dopen2(g.id, c_name, dapl.id) 149 | if err := checkID(hid); err != nil { 150 | return nil, err 151 | } 152 | return newDataset(hid, nil), nil 153 | } 154 | 155 | // NumObjects returns the number of objects in the Group. 156 | func (g *CommonFG) NumObjects() (uint, error) { 157 | var info C.H5G_info_t 158 | err := h5err(C.H5Gget_info(g.id, &info)) 159 | return uint(info.nlinks), err 160 | } 161 | 162 | // ObjectNameByIndex returns the name of the object at idx. 163 | func (g *CommonFG) ObjectNameByIndex(idx uint) (string, error) { 164 | cidx := C.hsize_t(idx) 165 | size := C.H5Lget_name_by_idx(g.id, cdot, C.H5_INDEX_NAME, C.H5_ITER_INC, cidx, nil, 0, C.H5P_DEFAULT) 166 | if size < 0 { 167 | return "", fmt.Errorf("could not get name") 168 | } 169 | 170 | name := make([]C.char, size+1) 171 | 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) 172 | 173 | if size < 0 { 174 | return "", fmt.Errorf("could not get name") 175 | } 176 | return C.GoString(&name[0]), nil 177 | } 178 | 179 | // ObjectTypeByIndex returns the type of the object at idx. 180 | func (g *CommonFG) ObjectTypeByIndex(idx uint) (GType, error) { 181 | cidx := C.hsize_t(idx) 182 | gtyp := GType(C.H5Gget_objtype_by_idx(g.id, cidx)) 183 | if gtyp < H5G_GROUP { 184 | return H5G_UNKNOWN, fmt.Errorf("could not get object type") 185 | } 186 | 187 | return gtyp, nil 188 | } 189 | 190 | // CreateTable creates a packet table to store fixed-length packets. 191 | // The returned table must be closed by the user when it is no longer needed. 192 | func (g *Group) CreateTable(name string, dtype *Datatype, chunkSize, compression int) (*Table, error) { 193 | return createTable(g.id, name, dtype, chunkSize, compression) 194 | } 195 | 196 | // CreateTableFrom creates a packet table to store fixed-length packets. 197 | // The returned table must be closed by the user when it is no longer needed. 198 | func (g *Group) CreateTableFrom(name string, dtype interface{}, chunkSize, compression int) (*Table, error) { 199 | return createTableFrom(g.id, name, dtype, chunkSize, compression) 200 | } 201 | 202 | // OpenTable opens an existing packet table. The returned table must be 203 | // closed by the user when it is no longer needed. 204 | func (g *Group) OpenTable(name string) (*Table, error) { 205 | return openTable(g.id, name) 206 | } 207 | 208 | // LinkExists returns whether a link with the specified name exists in the group. 209 | func (g *CommonFG) LinkExists(name string) bool { 210 | c_name := C.CString(name) 211 | defer C.free(unsafe.Pointer(c_name)) 212 | return C.H5Lexists(g.id, c_name, 0) > 0 213 | } 214 | -------------------------------------------------------------------------------- /h5t_shim.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | /* 8 | #include "hdf5.h" 9 | 10 | #include 11 | #include 12 | 13 | hid_t _H5T_C_S1() { return H5T_C_S1; } 14 | hid_t _H5T_FORTRAN_S1() { return H5T_FORTRAN_S1; } 15 | 16 | hid_t _H5T_STD_I8BE() { return H5T_STD_I8BE; } 17 | hid_t _H5T_STD_I8LE() { return H5T_STD_I8LE; } 18 | hid_t _H5T_STD_I16BE() { return H5T_STD_I16BE; } 19 | hid_t _H5T_STD_I16LE() { return H5T_STD_I16LE; } 20 | hid_t _H5T_STD_I32BE() { return H5T_STD_I32BE; } 21 | hid_t _H5T_STD_I32LE() { return H5T_STD_I32LE; } 22 | hid_t _H5T_STD_I64BE() { return H5T_STD_I64BE; } 23 | hid_t _H5T_STD_I64LE() { return H5T_STD_I64LE; } 24 | hid_t _H5T_STD_U8BE() { return H5T_STD_U8BE; } 25 | hid_t _H5T_STD_U8LE() { return H5T_STD_U8LE; } 26 | hid_t _H5T_STD_U16BE() { return H5T_STD_U16BE; } 27 | hid_t _H5T_STD_U16LE() { return H5T_STD_U16LE; } 28 | hid_t _H5T_STD_U32BE() { return H5T_STD_U32BE; } 29 | hid_t _H5T_STD_U32LE() { return H5T_STD_U32LE; } 30 | hid_t _H5T_STD_U64BE() { return H5T_STD_U64BE; } 31 | hid_t _H5T_STD_U64LE() { return H5T_STD_U64LE; } 32 | hid_t _H5T_STD_B8BE() { return H5T_STD_B8BE; } 33 | hid_t _H5T_STD_B8LE() { return H5T_STD_B8LE; } 34 | 35 | hid_t _H5T_STD_B16BE() { return H5T_STD_B16BE; } 36 | hid_t _H5T_STD_B16LE() { return H5T_STD_B16LE; } 37 | hid_t _H5T_STD_B32BE() { return H5T_STD_B32BE; } 38 | hid_t _H5T_STD_B32LE() { return H5T_STD_B32LE; } 39 | hid_t _H5T_STD_B64BE() { return H5T_STD_B64BE; } 40 | hid_t _H5T_STD_B64LE() { return H5T_STD_B64LE; } 41 | hid_t _H5T_STD_REF_OBJ() { return H5T_STD_REF_OBJ; } 42 | hid_t _H5T_STD_REF_DSETREG() { return H5T_STD_REF_DSETREG; } 43 | 44 | hid_t _H5T_IEEE_F32BE() { return H5T_IEEE_F32BE; } 45 | hid_t _H5T_IEEE_F32LE() { return H5T_IEEE_F32LE; } 46 | hid_t _H5T_IEEE_F64BE() { return H5T_IEEE_F64BE; } 47 | hid_t _H5T_IEEE_F64LE() { return H5T_IEEE_F64LE; } 48 | 49 | hid_t _H5T_UNIX_D32BE() { return H5T_UNIX_D32BE; } 50 | hid_t _H5T_UNIX_D32LE() { return H5T_UNIX_D32LE; } 51 | hid_t _H5T_UNIX_D64BE() { return H5T_UNIX_D64BE; } 52 | hid_t _H5T_UNIX_D64LE() { return H5T_UNIX_D64LE; } 53 | 54 | hid_t _H5T_INTEL_I8() { return H5T_INTEL_I8; } 55 | hid_t _H5T_INTEL_I16() { return H5T_INTEL_I16; } 56 | hid_t _H5T_INTEL_I32() { return H5T_INTEL_I32; } 57 | hid_t _H5T_INTEL_I64() { return H5T_INTEL_I64; } 58 | hid_t _H5T_INTEL_U8() { return H5T_INTEL_U8; } 59 | hid_t _H5T_INTEL_U16() { return H5T_INTEL_U16; } 60 | hid_t _H5T_INTEL_U32() { return H5T_INTEL_U32; } 61 | hid_t _H5T_INTEL_U64() { return H5T_INTEL_U64; } 62 | hid_t _H5T_INTEL_B8() { return H5T_INTEL_B8; } 63 | hid_t _H5T_INTEL_B16() { return H5T_INTEL_B16; } 64 | hid_t _H5T_INTEL_B32() { return H5T_INTEL_B32; } 65 | hid_t _H5T_INTEL_B64() { return H5T_INTEL_B64; } 66 | hid_t _H5T_INTEL_F32() { return H5T_INTEL_F32; } 67 | hid_t _H5T_INTEL_F64() { return H5T_INTEL_F64; } 68 | 69 | hid_t _H5T_ALPHA_I8() { return H5T_ALPHA_I8; } 70 | hid_t _H5T_ALPHA_I16() { return H5T_ALPHA_I16; } 71 | hid_t _H5T_ALPHA_I32() { return H5T_ALPHA_I32; } 72 | hid_t _H5T_ALPHA_I64() { return H5T_ALPHA_I64; } 73 | hid_t _H5T_ALPHA_U8() { return H5T_ALPHA_U8; } 74 | hid_t _H5T_ALPHA_U16() { return H5T_ALPHA_U16; } 75 | hid_t _H5T_ALPHA_U32() { return H5T_ALPHA_U32; } 76 | hid_t _H5T_ALPHA_U64() { return H5T_ALPHA_U64; } 77 | hid_t _H5T_ALPHA_B8() { return H5T_ALPHA_B8; } 78 | hid_t _H5T_ALPHA_B16() { return H5T_ALPHA_B16; } 79 | hid_t _H5T_ALPHA_B32() { return H5T_ALPHA_B32; } 80 | hid_t _H5T_ALPHA_B64() { return H5T_ALPHA_B64; } 81 | hid_t _H5T_ALPHA_F32() { return H5T_ALPHA_F32; } 82 | hid_t _H5T_ALPHA_F64() { return H5T_ALPHA_F64; } 83 | 84 | hid_t _H5T_MIPS_I8() { return H5T_MIPS_I8; } 85 | hid_t _H5T_MIPS_I16() { return H5T_MIPS_I16; } 86 | hid_t _H5T_MIPS_I32() { return H5T_MIPS_I32; } 87 | hid_t _H5T_MIPS_I64() { return H5T_MIPS_I64; } 88 | hid_t _H5T_MIPS_U8() { return H5T_MIPS_U8; } 89 | hid_t _H5T_MIPS_U16() { return H5T_MIPS_U16; } 90 | hid_t _H5T_MIPS_U32() { return H5T_MIPS_U32; } 91 | hid_t _H5T_MIPS_U64() { return H5T_MIPS_U64; } 92 | hid_t _H5T_MIPS_B8() { return H5T_MIPS_B8; } 93 | hid_t _H5T_MIPS_B16() { return H5T_MIPS_B16; } 94 | hid_t _H5T_MIPS_B32() { return H5T_MIPS_B32; } 95 | hid_t _H5T_MIPS_B64() { return H5T_MIPS_B64; } 96 | hid_t _H5T_MIPS_F32() { return H5T_MIPS_F32; } 97 | hid_t _H5T_MIPS_F64() { return H5T_MIPS_F64; } 98 | 99 | hid_t _H5T_NATIVE_CHAR() { return H5T_NATIVE_CHAR; } 100 | hid_t _H5T_NATIVE_INT() { return H5T_NATIVE_INT; } 101 | hid_t _H5T_NATIVE_FLOAT() { return H5T_NATIVE_FLOAT; } 102 | hid_t _H5T_NATIVE_SCHAR() { return H5T_NATIVE_SCHAR; } 103 | hid_t _H5T_NATIVE_UCHAR() { return H5T_NATIVE_UCHAR; } 104 | hid_t _H5T_NATIVE_SHORT() { return H5T_NATIVE_SHORT; } 105 | hid_t _H5T_NATIVE_USHORT() { return H5T_NATIVE_USHORT; } 106 | hid_t _H5T_NATIVE_UINT() { return H5T_NATIVE_UINT; } 107 | hid_t _H5T_NATIVE_LONG() { return H5T_NATIVE_LONG; } 108 | hid_t _H5T_NATIVE_ULONG() { return H5T_NATIVE_ULONG; } 109 | hid_t _H5T_NATIVE_LLONG() { return H5T_NATIVE_LLONG; } 110 | hid_t _H5T_NATIVE_ULLONG() { return H5T_NATIVE_ULLONG; } 111 | hid_t _H5T_NATIVE_DOUBLE() { return H5T_NATIVE_DOUBLE; } 112 | #if H5_SIZEOF_LONG_DOUBLE !=0 113 | hid_t _H5T_NATIVE_LDOUBLE() { return H5T_NATIVE_LDOUBLE; } 114 | #endif 115 | hid_t _H5T_NATIVE_B8() { return H5T_NATIVE_B8; } 116 | hid_t _H5T_NATIVE_B16() { return H5T_NATIVE_B16; } 117 | hid_t _H5T_NATIVE_B32() { return H5T_NATIVE_B32; } 118 | hid_t _H5T_NATIVE_B64() { return H5T_NATIVE_B64; } 119 | hid_t _H5T_NATIVE_OPAQUE() { return H5T_NATIVE_OPAQUE; } 120 | hid_t _H5T_NATIVE_HSIZE() { return H5T_NATIVE_HSIZE; } 121 | hid_t _H5T_NATIVE_HSSIZE() { return H5T_NATIVE_HSSIZE; } 122 | hid_t _H5T_NATIVE_HERR() { return H5T_NATIVE_HERR; } 123 | hid_t _H5T_NATIVE_HBOOL() { return H5T_NATIVE_HBOOL; } 124 | 125 | hid_t _H5T_NATIVE_INT8() { return H5T_NATIVE_INT8; } 126 | hid_t _H5T_NATIVE_UINT8() { return H5T_NATIVE_UINT8; } 127 | hid_t _H5T_NATIVE_INT16() { return H5T_NATIVE_INT16; } 128 | hid_t _H5T_NATIVE_UINT16() { return H5T_NATIVE_UINT16; } 129 | hid_t _H5T_NATIVE_INT32() { return H5T_NATIVE_INT32; } 130 | hid_t _H5T_NATIVE_UINT32() { return H5T_NATIVE_UINT32; } 131 | hid_t _H5T_NATIVE_INT64() { return H5T_NATIVE_INT64; } 132 | hid_t _H5T_NATIVE_UINT64() { return H5T_NATIVE_UINT64; } 133 | 134 | size_t size_t_H5T_VARIABLE() { return H5T_VARIABLE; } 135 | */ 136 | import "C" 137 | 138 | // list of predefined hdf5 data types 139 | var ( 140 | T_C_S1 *Datatype = NewDatatype(C._H5T_C_S1()) 141 | T_FORTRAN_S1 *Datatype = NewDatatype(C._H5T_FORTRAN_S1()) 142 | 143 | T_STD_I8BE *Datatype = NewDatatype(C._H5T_STD_I8BE()) 144 | T_STD_I8LE *Datatype = NewDatatype(C._H5T_STD_I8LE()) 145 | T_STD_I16BE *Datatype = NewDatatype(C._H5T_STD_I16BE()) 146 | T_STD_I16LE *Datatype = NewDatatype(C._H5T_STD_I16LE()) 147 | T_STD_I32BE *Datatype = NewDatatype(C._H5T_STD_I32BE()) 148 | T_STD_I32LE *Datatype = NewDatatype(C._H5T_STD_I32LE()) 149 | T_STD_I64BE *Datatype = NewDatatype(C._H5T_STD_I64BE()) 150 | T_STD_I64LE *Datatype = NewDatatype(C._H5T_STD_I64LE()) 151 | T_STD_U8BE *Datatype = NewDatatype(C._H5T_STD_U8BE()) 152 | T_STD_U8LE *Datatype = NewDatatype(C._H5T_STD_U8LE()) 153 | T_STD_U16BE *Datatype = NewDatatype(C._H5T_STD_U16BE()) 154 | T_STD_U16LE *Datatype = NewDatatype(C._H5T_STD_U16LE()) 155 | T_STD_U32BE *Datatype = NewDatatype(C._H5T_STD_U32BE()) 156 | T_STD_U32LE *Datatype = NewDatatype(C._H5T_STD_U32LE()) 157 | T_STD_U64BE *Datatype = NewDatatype(C._H5T_STD_U64BE()) 158 | T_STD_U64LE *Datatype = NewDatatype(C._H5T_STD_U64LE()) 159 | T_STD_B8BE *Datatype = NewDatatype(C._H5T_STD_B8BE()) 160 | T_STD_B8LE *Datatype = NewDatatype(C._H5T_STD_B8LE()) 161 | 162 | T_STD_B16BE *Datatype = NewDatatype(C._H5T_STD_B16BE()) 163 | T_STD_B16LE *Datatype = NewDatatype(C._H5T_STD_B16LE()) 164 | T_STD_B32BE *Datatype = NewDatatype(C._H5T_STD_B32BE()) 165 | T_STD_B32LE *Datatype = NewDatatype(C._H5T_STD_B32LE()) 166 | T_STD_B64BE *Datatype = NewDatatype(C._H5T_STD_B64BE()) 167 | T_STD_B64LE *Datatype = NewDatatype(C._H5T_STD_B64LE()) 168 | T_STD_REF_OBJ *Datatype = NewDatatype(C._H5T_STD_REF_OBJ()) 169 | T_STD_REF_DSETREG *Datatype = NewDatatype(C._H5T_STD_REF_DSETREG()) 170 | 171 | T_IEEE_F32BE *Datatype = NewDatatype(C._H5T_IEEE_F32BE()) 172 | T_IEEE_F32LE *Datatype = NewDatatype(C._H5T_IEEE_F32LE()) 173 | T_IEEE_F64BE *Datatype = NewDatatype(C._H5T_IEEE_F64BE()) 174 | T_IEEE_F64LE *Datatype = NewDatatype(C._H5T_IEEE_F64LE()) 175 | 176 | T_UNIX_D32BE *Datatype = NewDatatype(C._H5T_UNIX_D32BE()) 177 | T_UNIX_D32LE *Datatype = NewDatatype(C._H5T_UNIX_D32LE()) 178 | T_UNIX_D64BE *Datatype = NewDatatype(C._H5T_UNIX_D64BE()) 179 | T_UNIX_D64LE *Datatype = NewDatatype(C._H5T_UNIX_D64LE()) 180 | 181 | T_INTEL_I8 *Datatype = NewDatatype(C._H5T_INTEL_I8()) 182 | T_INTEL_I16 *Datatype = NewDatatype(C._H5T_INTEL_I16()) 183 | T_INTEL_I32 *Datatype = NewDatatype(C._H5T_INTEL_I32()) 184 | T_INTEL_I64 *Datatype = NewDatatype(C._H5T_INTEL_I64()) 185 | T_INTEL_U8 *Datatype = NewDatatype(C._H5T_INTEL_U8()) 186 | T_INTEL_U16 *Datatype = NewDatatype(C._H5T_INTEL_U16()) 187 | T_INTEL_U32 *Datatype = NewDatatype(C._H5T_INTEL_U32()) 188 | T_INTEL_U64 *Datatype = NewDatatype(C._H5T_INTEL_U64()) 189 | T_INTEL_B8 *Datatype = NewDatatype(C._H5T_INTEL_B8()) 190 | T_INTEL_B16 *Datatype = NewDatatype(C._H5T_INTEL_B16()) 191 | T_INTEL_B32 *Datatype = NewDatatype(C._H5T_INTEL_B32()) 192 | T_INTEL_B64 *Datatype = NewDatatype(C._H5T_INTEL_B64()) 193 | T_INTEL_F32 *Datatype = NewDatatype(C._H5T_INTEL_F32()) 194 | T_INTEL_F64 *Datatype = NewDatatype(C._H5T_INTEL_F64()) 195 | 196 | T_ALPHA_I8 *Datatype = NewDatatype(C._H5T_ALPHA_I8()) 197 | T_ALPHA_I16 *Datatype = NewDatatype(C._H5T_ALPHA_I16()) 198 | T_ALPHA_I32 *Datatype = NewDatatype(C._H5T_ALPHA_I32()) 199 | T_ALPHA_I64 *Datatype = NewDatatype(C._H5T_ALPHA_I64()) 200 | T_ALPHA_U8 *Datatype = NewDatatype(C._H5T_ALPHA_U8()) 201 | T_ALPHA_U16 *Datatype = NewDatatype(C._H5T_ALPHA_U16()) 202 | T_ALPHA_U32 *Datatype = NewDatatype(C._H5T_ALPHA_U32()) 203 | T_ALPHA_U64 *Datatype = NewDatatype(C._H5T_ALPHA_U64()) 204 | T_ALPHA_B8 *Datatype = NewDatatype(C._H5T_ALPHA_B8()) 205 | T_ALPHA_B16 *Datatype = NewDatatype(C._H5T_ALPHA_B16()) 206 | T_ALPHA_B32 *Datatype = NewDatatype(C._H5T_ALPHA_B32()) 207 | T_ALPHA_B64 *Datatype = NewDatatype(C._H5T_ALPHA_B64()) 208 | T_ALPHA_F32 *Datatype = NewDatatype(C._H5T_ALPHA_F32()) 209 | T_ALPHA_F64 *Datatype = NewDatatype(C._H5T_ALPHA_F64()) 210 | 211 | T_MIPS_I8 *Datatype = NewDatatype(C._H5T_MIPS_I8()) 212 | T_MIPS_I16 *Datatype = NewDatatype(C._H5T_MIPS_I16()) 213 | T_MIPS_I32 *Datatype = NewDatatype(C._H5T_MIPS_I32()) 214 | T_MIPS_I64 *Datatype = NewDatatype(C._H5T_MIPS_I64()) 215 | T_MIPS_U8 *Datatype = NewDatatype(C._H5T_MIPS_U8()) 216 | T_MIPS_U16 *Datatype = NewDatatype(C._H5T_MIPS_U16()) 217 | T_MIPS_U32 *Datatype = NewDatatype(C._H5T_MIPS_U32()) 218 | T_MIPS_U64 *Datatype = NewDatatype(C._H5T_MIPS_U64()) 219 | T_MIPS_B8 *Datatype = NewDatatype(C._H5T_MIPS_B8()) 220 | T_MIPS_B16 *Datatype = NewDatatype(C._H5T_MIPS_B16()) 221 | T_MIPS_B32 *Datatype = NewDatatype(C._H5T_MIPS_B32()) 222 | T_MIPS_B64 *Datatype = NewDatatype(C._H5T_MIPS_B64()) 223 | T_MIPS_F32 *Datatype = NewDatatype(C._H5T_MIPS_F32()) 224 | T_MIPS_F64 *Datatype = NewDatatype(C._H5T_MIPS_F64()) 225 | 226 | T_NATIVE_CHAR *Datatype = NewDatatype(C._H5T_NATIVE_CHAR()) 227 | T_NATIVE_INT *Datatype = NewDatatype(C._H5T_NATIVE_INT()) 228 | T_NATIVE_FLOAT *Datatype = NewDatatype(C._H5T_NATIVE_FLOAT()) 229 | T_NATIVE_SCHAR *Datatype = NewDatatype(C._H5T_NATIVE_SCHAR()) 230 | T_NATIVE_UCHAR *Datatype = NewDatatype(C._H5T_NATIVE_UCHAR()) 231 | T_NATIVE_SHORT *Datatype = NewDatatype(C._H5T_NATIVE_SHORT()) 232 | T_NATIVE_USHORT *Datatype = NewDatatype(C._H5T_NATIVE_USHORT()) 233 | T_NATIVE_UINT *Datatype = NewDatatype(C._H5T_NATIVE_UINT()) 234 | T_NATIVE_LONG *Datatype = NewDatatype(C._H5T_NATIVE_LONG()) 235 | T_NATIVE_ULONG *Datatype = NewDatatype(C._H5T_NATIVE_ULONG()) 236 | T_NATIVE_LLONG *Datatype = NewDatatype(C._H5T_NATIVE_LLONG()) 237 | T_NATIVE_ULLONG *Datatype = NewDatatype(C._H5T_NATIVE_ULLONG()) 238 | T_NATIVE_DOUBLE *Datatype = NewDatatype(C._H5T_NATIVE_DOUBLE()) 239 | //#if H5_SIZEOF_LONG_DOUBLE !=0 240 | T_NATIVE_LDOUBLE *Datatype = NewDatatype(C._H5T_NATIVE_LDOUBLE()) 241 | //#endif 242 | T_NATIVE_B8 *Datatype = NewDatatype(C._H5T_NATIVE_B8()) 243 | T_NATIVE_B16 *Datatype = NewDatatype(C._H5T_NATIVE_B16()) 244 | T_NATIVE_B32 *Datatype = NewDatatype(C._H5T_NATIVE_B32()) 245 | T_NATIVE_B64 *Datatype = NewDatatype(C._H5T_NATIVE_B64()) 246 | T_NATIVE_OPAQUE *Datatype = NewDatatype(C._H5T_NATIVE_OPAQUE()) 247 | T_NATIVE_HSIZE *Datatype = NewDatatype(C._H5T_NATIVE_HSIZE()) 248 | T_NATIVE_HSSIZE *Datatype = NewDatatype(C._H5T_NATIVE_HSSIZE()) 249 | T_NATIVE_HERR *Datatype = NewDatatype(C._H5T_NATIVE_HERR()) 250 | T_NATIVE_HBOOL *Datatype = NewDatatype(C._H5T_NATIVE_HBOOL()) 251 | 252 | T_NATIVE_INT8 *Datatype = NewDatatype(C._H5T_NATIVE_INT8()) 253 | T_NATIVE_UINT8 *Datatype = NewDatatype(C._H5T_NATIVE_UINT8()) 254 | T_NATIVE_INT16 *Datatype = NewDatatype(C._H5T_NATIVE_INT16()) 255 | T_NATIVE_UINT16 *Datatype = NewDatatype(C._H5T_NATIVE_UINT16()) 256 | T_NATIVE_INT32 *Datatype = NewDatatype(C._H5T_NATIVE_INT32()) 257 | T_NATIVE_UINT32 *Datatype = NewDatatype(C._H5T_NATIVE_UINT32()) 258 | T_NATIVE_INT64 *Datatype = NewDatatype(C._H5T_NATIVE_INT64()) 259 | T_NATIVE_UINT64 *Datatype = NewDatatype(C._H5T_NATIVE_UINT64()) 260 | 261 | T_GO_STRING *Datatype = makeGoStringDatatype() 262 | ) 263 | 264 | // 265 | var h5t_VARIABLE = int(C.size_t_H5T_VARIABLE()) 266 | 267 | func makeGoStringDatatype() *Datatype { 268 | dt, err := T_C_S1.Copy() 269 | if err != nil { 270 | panic(err) 271 | } 272 | err = dt.SetSize(h5t_VARIABLE) 273 | if err != nil { 274 | panic(err) 275 | } 276 | dt.goPtrPathLen = 1 // This is the first field of the string header. 277 | return dt 278 | } 279 | -------------------------------------------------------------------------------- /h5t_types.go: -------------------------------------------------------------------------------- 1 | // Copyright ©2017 The Gonum Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package hdf5 6 | 7 | // #include "hdf5.h" 8 | // #include 9 | // #include 10 | import "C" 11 | 12 | import ( 13 | "fmt" 14 | "reflect" 15 | "unsafe" 16 | ) 17 | 18 | type Datatype struct { 19 | Identifier 20 | 21 | goPtrPathLen int 22 | } 23 | 24 | type TypeClass C.H5T_class_t 25 | 26 | const ( 27 | T_NO_CLASS TypeClass = -1 // Error 28 | T_INTEGER TypeClass = 0 // integer types 29 | T_FLOAT TypeClass = 1 // floating-point types 30 | T_TIME TypeClass = 2 // date and time types 31 | T_STRING TypeClass = 3 // character string types 32 | T_BITFIELD TypeClass = 4 // bit field types 33 | T_OPAQUE TypeClass = 5 // opaque types 34 | T_COMPOUND TypeClass = 6 // compound types 35 | T_REFERENCE TypeClass = 7 // reference types 36 | T_ENUM TypeClass = 8 // enumeration types 37 | T_VLEN TypeClass = 9 // variable-length types 38 | T_ARRAY TypeClass = 10 // array types 39 | T_NCLASSES TypeClass = 11 // nbr of classes -- MUST BE LAST 40 | ) 41 | 42 | // list of go types 43 | var ( 44 | _go_string_t reflect.Type = reflect.TypeOf(string("")) 45 | 46 | _go_boolean_t reflect.Type = reflect.TypeOf(bool(false)) 47 | 48 | _go_int_t reflect.Type = reflect.TypeOf(int(0)) 49 | _go_int8_t reflect.Type = reflect.TypeOf(int8(0)) 50 | _go_int16_t reflect.Type = reflect.TypeOf(int16(0)) 51 | _go_int32_t reflect.Type = reflect.TypeOf(int32(0)) 52 | _go_int64_t reflect.Type = reflect.TypeOf(int64(0)) 53 | _go_uint_t reflect.Type = reflect.TypeOf(uint(0)) 54 | _go_uint8_t reflect.Type = reflect.TypeOf(uint8(0)) 55 | _go_uint16_t reflect.Type = reflect.TypeOf(uint16(0)) 56 | _go_uint32_t reflect.Type = reflect.TypeOf(uint32(0)) 57 | _go_uint64_t reflect.Type = reflect.TypeOf(uint64(0)) 58 | 59 | _go_float32_t reflect.Type = reflect.TypeOf(float32(0)) 60 | _go_float64_t reflect.Type = reflect.TypeOf(float64(0)) 61 | 62 | _go_array_t reflect.Type = reflect.TypeOf([1]int{0}) 63 | _go_slice_t reflect.Type = reflect.TypeOf([]int{0}) 64 | 65 | _go_struct_t reflect.Type = reflect.TypeOf(struct{}{}) 66 | 67 | _go_ptr_t reflect.Type = reflect.PtrTo(_go_int_t) 68 | ) 69 | 70 | type typeMap map[TypeClass]reflect.Type 71 | 72 | var ( 73 | // Mapping of TypeClass to reflect.Type 74 | typeClassToGoType typeMap = typeMap{ 75 | T_NO_CLASS: nil, 76 | T_INTEGER: _go_int_t, 77 | T_FLOAT: _go_float32_t, 78 | T_TIME: nil, 79 | T_STRING: _go_string_t, 80 | T_BITFIELD: nil, 81 | T_OPAQUE: nil, 82 | T_COMPOUND: _go_struct_t, 83 | T_REFERENCE: _go_ptr_t, 84 | T_ENUM: _go_int_t, 85 | T_VLEN: _go_slice_t, 86 | T_ARRAY: _go_array_t, 87 | } 88 | 89 | parametricTypes typeMap = typeMap{ 90 | // Only these types can be used with CreateDatatype 91 | T_COMPOUND: _go_struct_t, 92 | T_ENUM: _go_int_t, 93 | T_OPAQUE: nil, 94 | T_STRING: _go_string_t, 95 | } 96 | ) 97 | 98 | // OpenDatatype opens a named datatype. The returned datastype must 99 | // be closed by the user when it is no longer needed. 100 | func OpenDatatype(c CommonFG, name string, tapl_id int) (*Datatype, error) { 101 | c_name := C.CString(name) 102 | defer C.free(unsafe.Pointer(c_name)) 103 | 104 | id := C.H5Topen2(C.hid_t(c.id), c_name, C.hid_t(tapl_id)) 105 | if err := checkID(id); err != nil { 106 | return nil, err 107 | } 108 | return NewDatatype(id), nil 109 | } 110 | 111 | // NewDatatype creates a Datatype from an hdf5 id. 112 | func NewDatatype(id C.hid_t) *Datatype { 113 | return &Datatype{Identifier: Identifier{id}} 114 | } 115 | 116 | // CreateDatatype creates a new datatype. The value of class must be T_COMPOUND, 117 | // T_OPAQUE, T_ENUM or T_STRING, and size is the size of the new datatype in bytes. 118 | // The returned datatype must be closed by the user when it is no longer needed. 119 | func CreateDatatype(class TypeClass, size int) (*Datatype, error) { 120 | _, ok := parametricTypes[class] 121 | if !ok { 122 | return nil, 123 | fmt.Errorf( 124 | "invalid TypeClass, want %v, %v, %v or %v, got %v", 125 | T_COMPOUND, T_OPAQUE, T_STRING, T_ENUM, 126 | class, 127 | ) 128 | } 129 | 130 | hid := C.H5Tcreate(C.H5T_class_t(class), C.size_t(size)) 131 | if err := checkID(hid); err != nil { 132 | return nil, err 133 | } 134 | return NewDatatype(hid), nil 135 | } 136 | 137 | // GoType returns the reflect.Type associated with the Datatype's TypeClass 138 | func (t *Datatype) GoType() reflect.Type { 139 | return typeClassToGoType[t.Class()] 140 | } 141 | 142 | // Close releases a datatype. 143 | func (t *Datatype) Close() error { 144 | return t.closeWith(h5tclose) 145 | } 146 | 147 | func h5tclose(id C.hid_t) C.herr_t { 148 | return C.H5Tclose(id) 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 | c, err := copyDatatype(t.id) 159 | if err != nil { 160 | return nil, err 161 | } 162 | c.goPtrPathLen = t.goPtrPathLen 163 | return c, nil 164 | } 165 | 166 | // copyDatatype should be called by any function wishing to return 167 | // an existing Datatype from a Dataset or Attribute. 168 | func copyDatatype(id C.hid_t) (*Datatype, error) { 169 | hid := C.H5Tcopy(id) 170 | if err := checkID(hid); err != nil { 171 | return nil, err 172 | } 173 | return NewDatatype(hid), nil 174 | } 175 | 176 | // Equal determines whether two datatype identifiers refer to the same datatype. 177 | func (t *Datatype) Equal(o *Datatype) bool { 178 | return C.H5Tequal(t.id, o.id) > 0 179 | } 180 | 181 | // Lock locks a datatype. 182 | func (t *Datatype) Lock() error { 183 | return h5err(C.H5Tlock(t.id)) 184 | } 185 | 186 | // Size returns the size of the Datatype. 187 | func (t *Datatype) Size() uint { 188 | return uint(C.H5Tget_size(t.id)) 189 | } 190 | 191 | // SetSize sets the total size of a Datatype. 192 | func (t *Datatype) SetSize(sz int) error { 193 | err := C.H5Tset_size(t.id, C.size_t(sz)) 194 | return h5err(err) 195 | } 196 | 197 | type ArrayType struct { 198 | Datatype 199 | } 200 | 201 | // NewArrayType creates a new ArrayType. The base_type specifies the element type 202 | // of the array and dims specify the dimensions of the array. The returned 203 | // arraytype must be closed by the user when it is no longer needed. 204 | func NewArrayType(base_type *Datatype, dims []int) (*ArrayType, error) { 205 | ndims := C.uint(len(dims)) 206 | c_dims := (*C.hsize_t)(unsafe.Pointer(&dims[0])) 207 | 208 | hid := C.H5Tarray_create2(base_type.id, ndims, c_dims) 209 | if err := checkID(hid); err != nil { 210 | return nil, err 211 | } 212 | t := &ArrayType{Datatype{Identifier: Identifier{hid}}} 213 | return t, nil 214 | } 215 | 216 | // NDims returns the rank of an ArrayType. 217 | func (t *ArrayType) NDims() int { 218 | return int(C.H5Tget_array_ndims(t.id)) 219 | } 220 | 221 | // ArrayDims returns the array dimensions. 222 | func (t *ArrayType) ArrayDims() []int { 223 | rank := t.NDims() 224 | dims := make([]int, rank) 225 | hdims := make([]C.hsize_t, rank) 226 | slice := (*reflect.SliceHeader)(unsafe.Pointer(&hdims)) 227 | c_dims := (*C.hsize_t)(unsafe.Pointer(slice.Data)) 228 | c_rank := int(C.H5Tget_array_dims2(t.id, c_dims)) 229 | if c_rank != rank { 230 | return nil 231 | } 232 | for i, n := range hdims { 233 | dims[i] = int(n) 234 | } 235 | return dims 236 | } 237 | 238 | type VarLenType struct { 239 | Datatype 240 | } 241 | 242 | // NewVarLenType creates a new VarLenType. the base_type specifies the element type 243 | // of the VarLenType. The returned variable length type must be closed by the user 244 | // when it is no longer needed. 245 | func NewVarLenType(base_type *Datatype) (*VarLenType, error) { 246 | id := C.H5Tvlen_create(base_type.id) 247 | if err := checkID(id); err != nil { 248 | return nil, err 249 | } 250 | t := &VarLenType{Datatype{Identifier: Identifier{id}}} 251 | t.goPtrPathLen = 1 // This is the first field of the slice header. 252 | return t, nil 253 | } 254 | 255 | // IsVariableStr determines whether the VarLenType is a string. 256 | func (vl *VarLenType) IsVariableStr() bool { 257 | return C.H5Tis_variable_str(vl.id) > 0 258 | } 259 | 260 | type CompoundType struct { 261 | Datatype 262 | } 263 | 264 | // NewCompoundType creates a new CompoundType. The size is the size in bytes of 265 | // the compound datatype. The returned compound type must be closed by the user 266 | // when it is no longer needed. 267 | func NewCompoundType(size int) (*CompoundType, error) { 268 | id := C.H5Tcreate(C.H5T_class_t(T_COMPOUND), C.size_t(size)) 269 | if err := checkID(id); err != nil { 270 | return nil, err 271 | } 272 | t := &CompoundType{Datatype{Identifier: Identifier{id}}} 273 | return t, nil 274 | } 275 | 276 | // NMembers returns the number of elements in a compound or enumeration datatype. 277 | func (t *CompoundType) NMembers() int { 278 | return int(C.H5Tget_nmembers(t.id)) 279 | } 280 | 281 | // Class returns the TypeClass of the DataType 282 | func (t *Datatype) Class() TypeClass { 283 | return TypeClass(C.H5Tget_class(t.id)) 284 | } 285 | 286 | // MemberClass returns datatype class of compound datatype member. 287 | func (t *CompoundType) MemberClass(mbr_idx int) TypeClass { 288 | return TypeClass(C.H5Tget_member_class(t.id, C.uint(mbr_idx))) 289 | } 290 | 291 | // MemberName returns the name of a compound or enumeration datatype member. 292 | func (t *CompoundType) MemberName(mbr_idx int) string { 293 | c_name := C.H5Tget_member_name(t.id, C.uint(mbr_idx)) 294 | defer C.free(unsafe.Pointer(c_name)) 295 | return C.GoString(c_name) 296 | } 297 | 298 | // MemberIndex returns the index of a compound or enumeration datatype member. 299 | func (t *CompoundType) MemberIndex(name string) int { 300 | c_name := C.CString(name) 301 | defer C.free(unsafe.Pointer(c_name)) 302 | return int(C.H5Tget_member_index(t.id, c_name)) 303 | } 304 | 305 | // MemberOffset returns the offset of a field of a compound datatype. 306 | func (t *CompoundType) MemberOffset(mbr_idx int) int { 307 | return int(C.H5Tget_member_offset(t.id, C.uint(mbr_idx))) 308 | } 309 | 310 | // MemberType returns the datatype of the specified member. The returned 311 | // datatype must be closed by the user when it is no longer needed. 312 | func (t *CompoundType) MemberType(mbr_idx int) (*Datatype, error) { 313 | hid := C.H5Tget_member_type(t.id, C.uint(mbr_idx)) 314 | if err := checkID(hid); err != nil { 315 | return nil, err 316 | } 317 | return NewDatatype(hid), nil 318 | } 319 | 320 | // Insert adds a new member to a compound datatype. 321 | func (t *CompoundType) Insert(name string, offset int, field *Datatype) error { 322 | cname := C.CString(name) 323 | defer C.free(unsafe.Pointer(cname)) 324 | return h5err(C.H5Tinsert(t.id, cname, C.size_t(offset), field.id)) 325 | } 326 | 327 | // Pack recursively removes padding from within a compound datatype. 328 | // This is analogous to C struct packing and will give a space-efficient 329 | // type on the disk. However, using this may require type conversions 330 | // on more machines, so may be a worse option. 331 | func (t *CompoundType) Pack() error { 332 | return h5err(C.H5Tpack(t.id)) 333 | } 334 | 335 | type OpaqueDatatype struct { 336 | Datatype 337 | } 338 | 339 | // SetTag tags an opaque datatype. 340 | func (t *OpaqueDatatype) SetTag(tag string) error { 341 | ctag := C.CString(tag) 342 | defer C.free(unsafe.Pointer(ctag)) 343 | return h5err(C.H5Tset_tag(t.id, ctag)) 344 | } 345 | 346 | // Tag returns the tag associated with an opaque datatype. 347 | func (t *OpaqueDatatype) Tag() string { 348 | cname := C.H5Tget_tag(t.id) 349 | if cname != nil { 350 | defer C.free(unsafe.Pointer(cname)) 351 | return C.GoString(cname) 352 | } 353 | return "" 354 | } 355 | 356 | // NewDatatypeFromValue creates a datatype from a value in an interface. The returned 357 | // datatype must be closed by the user when it is no longer needed. 358 | func NewDatatypeFromValue(v interface{}) (*Datatype, error) { 359 | return NewDataTypeFromType(reflect.TypeOf(v)) 360 | } 361 | 362 | // NewDatatypeFromType creates a new Datatype from a reflect.Type. The returned 363 | // datatype must be closed by the user when it is no longer needed. 364 | func NewDataTypeFromType(t reflect.Type) (*Datatype, error) { 365 | 366 | var dt *Datatype = nil 367 | var err error 368 | 369 | switch t.Kind() { 370 | 371 | case reflect.Int: 372 | dt, err = T_NATIVE_INT.Copy() 373 | 374 | case reflect.Int8: 375 | dt, err = T_NATIVE_INT8.Copy() 376 | 377 | case reflect.Int16: 378 | dt, err = T_NATIVE_INT16.Copy() 379 | 380 | case reflect.Int32: 381 | dt, err = T_NATIVE_INT32.Copy() 382 | 383 | case reflect.Int64: 384 | dt, err = T_NATIVE_INT64.Copy() 385 | 386 | case reflect.Uint: 387 | dt, err = T_NATIVE_UINT.Copy() 388 | 389 | case reflect.Uint8: 390 | dt, err = T_NATIVE_UINT8.Copy() 391 | 392 | case reflect.Uint16: 393 | dt, err = T_NATIVE_UINT16.Copy() 394 | 395 | case reflect.Uint32: 396 | dt, err = T_NATIVE_UINT32.Copy() 397 | 398 | case reflect.Uint64: 399 | dt, err = T_NATIVE_UINT64.Copy() 400 | 401 | case reflect.Float32: 402 | dt, err = T_NATIVE_FLOAT.Copy() 403 | 404 | case reflect.Float64: 405 | dt, err = T_NATIVE_DOUBLE.Copy() 406 | 407 | case reflect.String: 408 | dt, err = T_GO_STRING.Copy() 409 | 410 | case reflect.Bool: 411 | dt, err = T_NATIVE_HBOOL.Copy() 412 | 413 | case reflect.Array: 414 | elem_type, err := NewDataTypeFromType(t.Elem()) 415 | if err != nil { 416 | return nil, err 417 | } 418 | 419 | dims := getArrayDims(t) 420 | 421 | adt, err := NewArrayType(elem_type, dims) 422 | if err != nil { 423 | return nil, err 424 | } 425 | 426 | dt = &adt.Datatype 427 | 428 | case reflect.Slice: 429 | elem_type, err := NewDataTypeFromType(t.Elem()) 430 | if err != nil { 431 | return nil, err 432 | } 433 | 434 | sdt, err := NewVarLenType(elem_type) 435 | if err != nil { 436 | return nil, err 437 | } 438 | 439 | dt = &sdt.Datatype 440 | 441 | case reflect.Struct: 442 | sz := int(t.Size()) 443 | cdt, err := NewCompoundType(sz) 444 | if err != nil { 445 | return nil, err 446 | } 447 | var ptrPathLen int 448 | n := t.NumField() 449 | for i := 0; i < n; i++ { 450 | f := t.Field(i) 451 | var field_dt *Datatype 452 | field_dt, err = NewDataTypeFromType(f.Type) 453 | if err != nil { 454 | return nil, err 455 | } 456 | if field_dt.goPtrPathLen > ptrPathLen { 457 | ptrPathLen = field_dt.goPtrPathLen 458 | } 459 | offset := int(f.Offset + 0) 460 | if field_dt == nil { 461 | return nil, fmt.Errorf("pb with field [%d-%s]", i, f.Name) 462 | } 463 | field_name := string(f.Tag) 464 | if len(field_name) == 0 { 465 | field_name = f.Name 466 | } 467 | err = cdt.Insert(field_name, offset, field_dt) 468 | if err != nil { 469 | return nil, fmt.Errorf("pb with field [%d-%s]: %s", i, f.Name, err) 470 | } 471 | } 472 | dt = &cdt.Datatype 473 | dt.goPtrPathLen += ptrPathLen 474 | 475 | case reflect.Ptr: 476 | dt, err = NewDataTypeFromType(t.Elem()) 477 | dt.goPtrPathLen++ 478 | 479 | default: 480 | // Should never happen. 481 | panic(fmt.Errorf("unhandled kind (%v)", t.Kind())) 482 | } 483 | 484 | return dt, err 485 | } 486 | 487 | // hasIllegalGoPointer returns whether the Datatype is known to have 488 | // a Go pointer to Go pointer chain. 489 | func (t *Datatype) hasIllegalGoPointer() bool { 490 | return t != nil && t.goPtrPathLen > 1 491 | } 492 | 493 | func getArrayDims(dt reflect.Type) []int { 494 | result := []int{} 495 | if dt.Kind() == reflect.Array { 496 | result = append(result, dt.Len()) 497 | for _, dim := range getArrayDims(dt.Elem()) { 498 | result = append(result, dim) 499 | } 500 | } 501 | return result 502 | } 503 | --------------------------------------------------------------------------------