Skip to content

Commit 6cc619b

Browse files
committed
feat: v0.3.0 production quality release
Security fixes (TASK-014): - Add tag size validation with 2GB limit - Add dimension overflow checks - Fix v73 complex number reading API improvements (TASK-015, TASK-016, TASK-012): - Add 17 testable godoc examples - Add 7 convenience methods - Add functional options pattern Quality improvements: - Tests: 238 to 298 (100% passing) - Coverage: 78.5% to 85.4% - Grade: B+ to A- - Linter: 0 issues maintained
1 parent 31b6bec commit 6cc619b

18 files changed

+2237
-42
lines changed

example_test.go

Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
// Package matlab_test provides testable examples for the MATLAB file library.
2+
//
3+
// These examples demonstrate common use cases and serve as both documentation
4+
// and verification that the API works as expected.
5+
package matlab_test
6+
7+
import (
8+
"encoding/binary"
9+
"fmt"
10+
"os"
11+
"path/filepath"
12+
13+
"github.com/scigolib/matlab"
14+
"github.com/scigolib/matlab/types"
15+
)
16+
17+
// Example demonstrates basic usage of the MATLAB file library.
18+
func Example() {
19+
tmpfile := filepath.Join(os.TempDir(), "example.mat")
20+
defer os.Remove(tmpfile)
21+
22+
// Create a simple v5 MATLAB file
23+
writer, _ := matlab.Create(tmpfile, matlab.Version5)
24+
defer writer.Close()
25+
26+
// Write a variable
27+
writer.WriteVariable(&types.Variable{
28+
Name: "data",
29+
Dimensions: []int{3},
30+
DataType: types.Double,
31+
Data: []float64{1.0, 2.0, 3.0},
32+
})
33+
34+
fmt.Println("MATLAB file created successfully")
35+
// Output:
36+
// MATLAB file created successfully
37+
}
38+
39+
// ExampleCreate demonstrates creating a MATLAB file.
40+
func ExampleCreate() {
41+
tmpfile := filepath.Join(os.TempDir(), "example_create.mat")
42+
defer os.Remove(tmpfile)
43+
44+
writer, err := matlab.Create(tmpfile, matlab.Version5)
45+
if err != nil {
46+
panic(err)
47+
}
48+
defer writer.Close()
49+
50+
fmt.Println("File created")
51+
// Output:
52+
// File created
53+
}
54+
55+
// ExampleCreate_v5 demonstrates creating a v5 format file.
56+
func ExampleCreate_v5() {
57+
tmpfile := filepath.Join(os.TempDir(), "example_v5.mat")
58+
defer os.Remove(tmpfile)
59+
60+
writer, _ := matlab.Create(tmpfile, matlab.Version5)
61+
defer writer.Close()
62+
63+
writer.WriteVariable(&types.Variable{
64+
Name: "matrix",
65+
Dimensions: []int{2, 3},
66+
DataType: types.Double,
67+
Data: []float64{1, 2, 3, 4, 5, 6},
68+
})
69+
70+
fmt.Println("v5 file created")
71+
// Output:
72+
// v5 file created
73+
}
74+
75+
// ExampleCreate_v73 demonstrates creating a v7.3 HDF5 format file.
76+
func ExampleCreate_v73() {
77+
tmpfile := filepath.Join(os.TempDir(), "example_v73.mat")
78+
defer os.Remove(tmpfile)
79+
80+
writer, _ := matlab.Create(tmpfile, matlab.Version73)
81+
defer writer.Close()
82+
83+
writer.WriteVariable(&types.Variable{
84+
Name: "data",
85+
Dimensions: []int{100},
86+
DataType: types.Double,
87+
Data: make([]float64, 100),
88+
})
89+
90+
fmt.Println("v7.3 file created")
91+
// Output:
92+
// v7.3 file created
93+
}
94+
95+
// ExampleOpen demonstrates reading a MATLAB file.
96+
func ExampleOpen() {
97+
file, _ := os.Open("testdata/generated/simple_double.mat")
98+
defer file.Close()
99+
100+
matFile, _ := matlab.Open(file)
101+
102+
fmt.Printf("Found %d variable(s)\n", len(matFile.Variables))
103+
// Output:
104+
// Found 1 variable(s)
105+
}
106+
107+
// ExampleMatFile_Variables demonstrates iterating over variables.
108+
func ExampleMatFile_Variables() {
109+
file, _ := os.Open("testdata/generated/simple_double.mat")
110+
defer file.Close()
111+
112+
matFile, _ := matlab.Open(file)
113+
114+
for _, v := range matFile.Variables {
115+
fmt.Printf("Variable: %s, Type: %v\n", v.Name, v.DataType)
116+
}
117+
// Output:
118+
// Variable: data, Type: double
119+
}
120+
121+
// ExampleMatFileWriter_WriteVariable demonstrates writing a simple array.
122+
func ExampleMatFileWriter_WriteVariable() {
123+
tmpfile := filepath.Join(os.TempDir(), "example_array.mat")
124+
defer os.Remove(tmpfile)
125+
126+
writer, _ := matlab.Create(tmpfile, matlab.Version5)
127+
defer writer.Close()
128+
129+
err := writer.WriteVariable(&types.Variable{
130+
Name: "mydata",
131+
Dimensions: []int{5},
132+
DataType: types.Double,
133+
Data: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
134+
})
135+
136+
if err == nil {
137+
fmt.Println("Variable written")
138+
}
139+
// Output:
140+
// Variable written
141+
}
142+
143+
// ExampleMatFileWriter_WriteVariable_matrix demonstrates writing a 2D matrix.
144+
func ExampleMatFileWriter_WriteVariable_matrix() {
145+
tmpfile := filepath.Join(os.TempDir(), "example_matrix.mat")
146+
defer os.Remove(tmpfile)
147+
148+
writer, _ := matlab.Create(tmpfile, matlab.Version5)
149+
defer writer.Close()
150+
151+
// 3x4 matrix in column-major order (MATLAB standard)
152+
writer.WriteVariable(&types.Variable{
153+
Name: "A",
154+
Dimensions: []int{3, 4},
155+
DataType: types.Double,
156+
Data: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12},
157+
})
158+
159+
fmt.Println("Matrix written")
160+
// Output:
161+
// Matrix written
162+
}
163+
164+
// ExampleMatFileWriter_WriteVariable_complex demonstrates writing complex numbers.
165+
func ExampleMatFileWriter_WriteVariable_complex() {
166+
tmpfile := filepath.Join(os.TempDir(), "example_complex.mat")
167+
defer os.Remove(tmpfile)
168+
169+
writer, _ := matlab.Create(tmpfile, matlab.Version73)
170+
defer writer.Close()
171+
172+
complexData := &types.NumericArray{
173+
Real: []float64{1.0, 2.0, 3.0},
174+
Imag: []float64{4.0, 5.0, 6.0},
175+
}
176+
177+
writer.WriteVariable(&types.Variable{
178+
Name: "signal",
179+
Dimensions: []int{3},
180+
DataType: types.Double,
181+
Data: complexData,
182+
IsComplex: true,
183+
})
184+
185+
fmt.Println("Complex variable written")
186+
// Output:
187+
// Complex variable written
188+
}
189+
190+
// ExampleMatFileWriter_WriteVariable_int32 demonstrates writing integer data.
191+
func ExampleMatFileWriter_WriteVariable_int32() {
192+
tmpfile := filepath.Join(os.TempDir(), "example_integers.mat")
193+
defer os.Remove(tmpfile)
194+
195+
writer, _ := matlab.Create(tmpfile, matlab.Version5)
196+
defer writer.Close()
197+
198+
writer.WriteVariable(&types.Variable{
199+
Name: "counts",
200+
Dimensions: []int{4},
201+
DataType: types.Int32,
202+
Data: []int32{10, 20, 30, 40},
203+
})
204+
205+
fmt.Println("Integer array written")
206+
// Output:
207+
// Integer array written
208+
}
209+
210+
// ExampleVariable_Data_simple demonstrates accessing simple numeric data.
211+
func ExampleVariable_Data_simple() {
212+
file, _ := os.Open("testdata/generated/simple_double.mat")
213+
defer file.Close()
214+
215+
matFile, _ := matlab.Open(file)
216+
variable := matFile.Variables[0]
217+
218+
// Simple arrays are stored directly
219+
data := variable.Data.([]float64)
220+
fmt.Printf("First value: %.1f\n", data[0])
221+
// Output:
222+
// First value: 1.0
223+
}
224+
225+
// ExampleVariable_Data_complex demonstrates the structure of complex number data.
226+
func ExampleVariable_Data_complex() {
227+
// Create a complex numeric array
228+
complexData := &types.NumericArray{
229+
Real: []float64{1, 2, 3},
230+
Imag: []float64{4, 5, 6},
231+
}
232+
233+
// Demonstrate accessing the parts
234+
fmt.Printf("Real part: %v\n", complexData.Real)
235+
fmt.Printf("Imag part: %v\n", complexData.Imag)
236+
// Output:
237+
// Real part: [1 2 3]
238+
// Imag part: [4 5 6]
239+
}
240+
241+
// ExampleOpen_roundTrip demonstrates writing and reading back data.
242+
func ExampleOpen_roundTrip() {
243+
tmpfile := filepath.Join(os.TempDir(), "example_roundtrip.mat")
244+
defer os.Remove(tmpfile)
245+
246+
// Write
247+
writer, _ := matlab.Create(tmpfile, matlab.Version5)
248+
writer.WriteVariable(&types.Variable{
249+
Name: "test",
250+
Dimensions: []int{2},
251+
DataType: types.Double,
252+
Data: []float64{3.14, 2.71},
253+
})
254+
writer.Close()
255+
256+
// Read
257+
file, _ := os.Open(tmpfile)
258+
defer file.Close()
259+
260+
matFile, _ := matlab.Open(file)
261+
data := matFile.Variables[0].Data.([]float64)
262+
263+
fmt.Printf("Read back: %.2f, %.2f\n", data[0], data[1])
264+
// Output:
265+
// Read back: 3.14, 2.71
266+
}
267+
268+
// ExampleOpen_multipleVariables demonstrates handling multiple variables.
269+
func ExampleOpen_multipleVariables() {
270+
tmpfile := filepath.Join(os.TempDir(), "example_multi.mat")
271+
defer os.Remove(tmpfile)
272+
273+
// Write multiple variables
274+
writer, _ := matlab.Create(tmpfile, matlab.Version5)
275+
writer.WriteVariable(&types.Variable{
276+
Name: "x",
277+
Dimensions: []int{3},
278+
DataType: types.Double,
279+
Data: []float64{1, 2, 3},
280+
})
281+
writer.WriteVariable(&types.Variable{
282+
Name: "y",
283+
Dimensions: []int{3},
284+
DataType: types.Double,
285+
Data: []float64{4, 5, 6},
286+
})
287+
writer.Close()
288+
289+
// Read all variables
290+
file, _ := os.Open(tmpfile)
291+
defer file.Close()
292+
293+
matFile, _ := matlab.Open(file)
294+
fmt.Printf("Total variables: %d\n", len(matFile.Variables))
295+
for _, v := range matFile.Variables {
296+
fmt.Printf("- %s\n", v.Name)
297+
}
298+
// Output:
299+
// Total variables: 2
300+
// - x
301+
// - y
302+
}
303+
304+
// ExampleCreate_withOptions demonstrates using functional options.
305+
func ExampleCreate_withOptions() {
306+
tmpfile := filepath.Join(os.TempDir(), "options.mat")
307+
defer os.Remove(tmpfile)
308+
309+
writer, _ := matlab.Create(tmpfile, matlab.Version5,
310+
matlab.WithEndianness(binary.BigEndian),
311+
matlab.WithDescription("Simulation results"),
312+
)
313+
defer writer.Close()
314+
315+
fmt.Println("File created with custom options")
316+
// Output:
317+
// File created with custom options
318+
}
319+
320+
// ExampleWithEndianness demonstrates setting byte order.
321+
func ExampleWithEndianness() {
322+
tmpfile := filepath.Join(os.TempDir(), "bigendian.mat")
323+
defer os.Remove(tmpfile)
324+
325+
writer, _ := matlab.Create(tmpfile, matlab.Version5,
326+
matlab.WithEndianness(binary.BigEndian),
327+
)
328+
defer writer.Close()
329+
330+
fmt.Println("Big-endian file created")
331+
// Output:
332+
// Big-endian file created
333+
}
334+
335+
// ExampleWithDescription demonstrates custom file description.
336+
func ExampleWithDescription() {
337+
tmpfile := filepath.Join(os.TempDir(), "described.mat")
338+
defer os.Remove(tmpfile)
339+
340+
writer, _ := matlab.Create(tmpfile, matlab.Version5,
341+
matlab.WithDescription("My experimental data from 2025"),
342+
)
343+
defer writer.Close()
344+
345+
fmt.Println("File with custom description created")
346+
// Output:
347+
// File with custom description created
348+
}

go.mod

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,13 @@ module github.com/scigolib/matlab
22

33
go 1.25
44

5-
require github.com/scigolib/hdf5 v0.13.1
5+
require (
6+
github.com/scigolib/hdf5 v0.13.1
7+
github.com/stretchr/testify v1.11.1
8+
)
9+
10+
require (
11+
github.com/davecgh/go-spew v1.1.1 // indirect
12+
github.com/pmezard/go-difflib v1.0.0 // indirect
13+
gopkg.in/yaml.v3 v3.0.1 // indirect
14+
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@ github.com/scigolib/hdf5 v0.13.1 h1:q9rgbEVQWYagWdi+HhqfymMG7URApQULls6VlUL5gQI=
66
github.com/scigolib/hdf5 v0.13.1/go.mod h1:7KLvpsidPPQjmd83dKH8RazoKXdbCO+FItz7ksezhrY=
77
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
88
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
9+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
10+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
911
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
1012
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)