Skip to content

Commit c257073

Browse files
authored
hdf5: mark types with illegal pointer chains
1 parent 21dab89 commit c257073

File tree

5 files changed

+116
-43
lines changed

5 files changed

+116
-43
lines changed

h5d_dataset.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ import (
1818

1919
type Dataset struct {
2020
Identifier
21+
22+
typ *Datatype
2123
}
2224

23-
func newDataset(id C.hid_t) *Dataset {
24-
return &Dataset{Identifier{id}}
25+
func newDataset(id C.hid_t, typ *Datatype) *Dataset {
26+
return &Dataset{Identifier: Identifier{id}, typ: typ}
2527
}
2628

2729
func createDataset(id C.hid_t, name string, dtype *Datatype, dspace *Dataspace, dcpl *PropList) (*Dataset, error) {
@@ -35,7 +37,7 @@ func createDataset(id C.hid_t, name string, dtype *Datatype, dspace *Dataspace,
3537
if err := checkID(hid); err != nil {
3638
return nil, err
3739
}
38-
return newDataset(hid), nil
40+
return newDataset(hid, dtype), nil
3941
}
4042

4143
// Close releases and terminates access to a dataset.
@@ -180,3 +182,11 @@ func (s *Dataset) Datatype() (*Datatype, error) {
180182
}
181183
return NewDatatype(dtype_id), nil
182184
}
185+
186+
// hasIllegalGoPointer returns whether the Dataset is known to have
187+
// a Go pointer to Go pointer chain. If the Dataset was created by
188+
// a call to OpenDataset without a read operation, it will be false,
189+
// but will not be a valid reflection of the real situation.
190+
func (s *Dataset) hasIllegalGoPointer() bool {
191+
return s.typ.hasIllegalGoPointer()
192+
}

h5g_group.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func (g *CommonFG) OpenDataset(name string) (*Dataset, error) {
9999
if err := checkID(hid); err != nil {
100100
return nil, err
101101
}
102-
return newDataset(hid), nil
102+
return newDataset(hid, nil), nil
103103
}
104104

105105
// NumObjects returns the number of objects in the Group.

h5t_shim.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,5 +273,6 @@ func makeGoStringDatatype() *Datatype {
273273
if err != nil {
274274
panic(err)
275275
}
276+
dt.goPtrPathLen = 1 // This is the first field of the string header.
276277
return dt
277278
}

h5t_types.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import (
1717

1818
type Datatype struct {
1919
Identifier
20+
21+
goPtrPathLen int
2022
}
2123

2224
type TypeClass C.H5T_class_t
@@ -108,8 +110,7 @@ func OpenDatatype(c CommonFG, name string, tapl_id int) (*Datatype, error) {
108110

109111
// NewDatatype creates a Datatype from an hdf5 id.
110112
func NewDatatype(id C.hid_t) *Datatype {
111-
t := &Datatype{Identifier{id}}
112-
return t
113+
return &Datatype{Identifier: Identifier{id}}
113114
}
114115

115116
// CreateDatatype creates a new datatype. The value of class must be T_COMPOUND,
@@ -152,10 +153,14 @@ func (t *Datatype) Committed() bool {
152153
return C.H5Tcommitted(t.id) > 0
153154
}
154155

155-
// Copy copies an existing datatype. The returned datatype must be closed by the
156-
// user when it is no longer needed.
156+
// Copy copies an existing datatype.
157157
func (t *Datatype) Copy() (*Datatype, error) {
158-
return copyDatatype(t.id)
158+
c, err := copyDatatype(t.id)
159+
if err != nil {
160+
return nil, err
161+
}
162+
c.goPtrPathLen = t.goPtrPathLen
163+
return c, nil
159164
}
160165

161166
// copyDatatype should be called by any function wishing to return
@@ -204,7 +209,7 @@ func NewArrayType(base_type *Datatype, dims []int) (*ArrayType, error) {
204209
if err := checkID(hid); err != nil {
205210
return nil, err
206211
}
207-
t := &ArrayType{Datatype{Identifier{hid}}}
212+
t := &ArrayType{Datatype{Identifier: Identifier{hid}}}
208213
return t, nil
209214
}
210215

@@ -242,7 +247,8 @@ func NewVarLenType(base_type *Datatype) (*VarLenType, error) {
242247
if err := checkID(id); err != nil {
243248
return nil, err
244249
}
245-
t := &VarLenType{Datatype{Identifier{id}}}
250+
t := &VarLenType{Datatype{Identifier: Identifier{id}}}
251+
t.goPtrPathLen = 1 // This is the first field of the slice header.
246252
return t, nil
247253
}
248254

@@ -263,7 +269,7 @@ func NewCompoundType(size int) (*CompoundType, error) {
263269
if err := checkID(id); err != nil {
264270
return nil, err
265271
}
266-
t := &CompoundType{Datatype{Identifier{id}}}
272+
t := &CompoundType{Datatype{Identifier: Identifier{id}}}
267273
return t, nil
268274
}
269275

@@ -438,14 +444,18 @@ func NewDataTypeFromType(t reflect.Type) (*Datatype, error) {
438444
if err != nil {
439445
return nil, err
440446
}
447+
var ptrPathLen int
441448
n := t.NumField()
442449
for i := 0; i < n; i++ {
443450
f := t.Field(i)
444-
var field_dt *Datatype = nil
451+
var field_dt *Datatype
445452
field_dt, err = NewDataTypeFromType(f.Type)
446453
if err != nil {
447454
return nil, err
448455
}
456+
if field_dt.goPtrPathLen > ptrPathLen {
457+
ptrPathLen = field_dt.goPtrPathLen
458+
}
449459
offset := int(f.Offset + 0)
450460
if field_dt == nil {
451461
return nil, fmt.Errorf("pb with field [%d-%s]", i, f.Name)
@@ -460,9 +470,11 @@ func NewDataTypeFromType(t reflect.Type) (*Datatype, error) {
460470
}
461471
}
462472
dt = &cdt.Datatype
473+
dt.goPtrPathLen += ptrPathLen
463474

464475
case reflect.Ptr:
465-
return NewDataTypeFromType(t.Elem())
476+
dt, err = NewDataTypeFromType(t.Elem())
477+
dt.goPtrPathLen++
466478

467479
default:
468480
// Should never happen.
@@ -472,6 +484,12 @@ func NewDataTypeFromType(t reflect.Type) (*Datatype, error) {
472484
return dt, err
473485
}
474486

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+
475493
func getArrayDims(dt reflect.Type) []int {
476494
result := []int{}
477495
if dt.Kind() == reflect.Array {

h5t_types_test.go

Lines changed: 73 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,74 @@
44

55
package hdf5
66

7-
import (
8-
"runtime"
9-
"testing"
10-
"time"
11-
)
7+
import "testing"
128

139
func TestSimpleDatatypes(t *testing.T) {
1410
// Smoke tests for the simple datatypes
15-
tests := []interface{}{
16-
int(0),
17-
int8(0),
18-
int16(0),
19-
int32(0),
20-
int64(0),
21-
uint(0),
22-
uint8(0),
23-
uint16(0),
24-
uint32(0),
25-
uint64(0),
26-
float32(0),
27-
float64(0),
28-
string(""),
29-
bool(true),
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},
3063
}
3164

32-
for test := range tests {
33-
NewDatatypeFromValue(test)
34-
// Test again for usage with ptrs
35-
NewDatatypeFromValue(&test)
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+
}
3675
}
3776
}
3877

@@ -49,6 +88,9 @@ func TestArrayDatatype(t *testing.T) {
4988
if err != nil {
5089
t.Fatal(err)
5190
}
91+
if dt.hasIllegalGoPointer() {
92+
t.Errorf("unexpected illegal pointer for %T", val)
93+
}
5294
adt := ArrayType{*dt}
5395
if adt.NDims() != dims {
5496
t.Errorf("wrong number of dimensions: got %d, want %d", adt.NDims(), dims)
@@ -75,13 +117,19 @@ func TestStructDatatype(t *testing.T) {
75117
if err != nil {
76118
t.Fatal(err)
77119
}
120+
if dtype.hasIllegalGoPointer() {
121+
t.Errorf("unexpected illegal pointer for %T", test)
122+
}
78123
dtypes = append(dtypes, dtype)
79124

80125
// pointer to value
81-
dtype, err = NewDatatypeFromValue(test)
126+
dtype, err = NewDatatypeFromValue(&test)
82127
if err != nil {
83128
t.Fatal(err)
84129
}
130+
if !dtype.hasIllegalGoPointer() {
131+
t.Errorf("expected illegal pointer for %T", &test)
132+
}
85133
dtypes = append(dtypes, dtype)
86134

87135
for _, dtype := range dtypes {
@@ -138,8 +186,4 @@ func TestCloseBehavior(t *testing.T) {
138186
t.Fatal(err)
139187
}
140188
defer dtype.Close()
141-
142-
// Sleep to ensure GC runs before returning
143-
runtime.GC()
144-
time.Sleep(100 * time.Millisecond)
145189
}

0 commit comments

Comments
 (0)