mat: disallow zero dimension results
MODIFY
mat/dense.go
mat/dense.go
@@ -42,6 +42,9 @@ type Dense struct {
// The data must be arranged in row-major order, i.e. the (i*c + j)-th
// element in the data slice is the {i, j}-th element in the matrix.
func NewDense(r, c int, data []float64) *Dense {
+ if r < 0 || c < 0 {
+ panic("mat: negative dimension")
+ }
if data != nil && r*c != len(data) {
panic(ErrShape)
}
@@ -69,6 +72,9 @@ func (m *Dense) reuseAs(r, c int) {
// Panic as a string, not a mat.Error.
panic("mat: caps not correctly set")
}
+ if r == 0 || c == 0 {
+ panic(ErrZeroLength)
+ }
if m.IsZero() {
m.mat = blas64.General{
Rows: r,
@@ -95,6 +101,9 @@ func (m *Dense) reuseAsZeroed(r, c int) {
// Panic as a string, not a mat.Error.
panic("mat: caps not correctly set")
}
+ if r == 0 || c == 0 {
+ panic(ErrZeroLength)
+ }
if m.IsZero() {
m.mat = blas64.General{
Rows: r,
@@ -129,6 +138,9 @@ func untranspose(a Matrix) (Matrix, bool) {
// This should be used when a method receiver is the same pointer as an input argument.
func (m *Dense) isolatedWorkspace(a Matrix) (w *Dense, restore func()) {
r, c := a.Dims()
+ if r == 0 || c == 0 {
+ panic(ErrZeroLength)
+ }
w = getWorkspace(r, c, false)
return w, func() {
m.Copy(w)

MODIFY
mat/errors.go
mat/errors.go
@@ -118,7 +118,7 @@ var (
ErrRowAccess = Error{"matrix: row index out of range"}
ErrColAccess = Error{"matrix: column index out of range"}
ErrVectorAccess = Error{"matrix: vector index out of range"}
- ErrZeroLength = Error{"matrix: zero length in matrix definition"}
+ ErrZeroLength = Error{"matrix: zero length in matrix dimension"}
ErrRowLength = Error{"matrix: row length mismatch"}
ErrColLength = Error{"matrix: col length mismatch"}
ErrSquare = Error{"matrix: expect square matrix"}

MODIFY
mat/io.go
mat/io.go
@@ -127,6 +127,9 @@ func (m *Dense) UnmarshalBinary(data []byte) error {
}
size := rows * cols
+ if size == 0 {
+ return ErrZeroLength
+ }
if int(size) < 0 || size > maxLen {
return errTooBig
}
@@ -184,6 +187,9 @@ func (m *Dense) UnmarshalBinaryFrom(r io.Reader) (int, error) {
}
size := rows * cols
+ if size == 0 {
+ return n, ErrZeroLength
+ }
if int(size) < 0 || size > maxLen {
return n, errTooBig
}
@@ -274,6 +280,9 @@ func (v *VecDense) UnmarshalBinary(data []byte) error {
p := 0
n := int64(binary.LittleEndian.Uint64(data[p : p+sizeInt64]))
+ if n == 0 {
+ return ErrZeroLength
+ }
p += sizeInt64
if n < 0 {
return errBadSize
@@ -315,6 +324,9 @@ func (v *VecDense) UnmarshalBinaryFrom(r io.Reader) (int, error) {
return n, err
}
sz := int64(binary.LittleEndian.Uint64(buf[:]))
+ if sz == 0 {
+ return n, ErrZeroLength
+ }
if sz < 0 {
return n, errBadSize
}

MODIFY
mat/io_test.go
mat/io_test.go
@@ -25,11 +25,13 @@ var (
var denseData = []struct {
raw []byte
want *Dense
+ err error
eq func(got, want Matrix) bool
}{
{
raw: []byte("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
want: NewDense(0, 0, []float64{}),
+ err: ErrZeroLength,
eq: Equal,
},
{
@@ -142,7 +144,9 @@ func TestDenseUnmarshal(t *testing.T) {
var v Dense
err := v.UnmarshalBinary(test.raw)
if err != nil {
+ if err != test.err {
t.Errorf("error decoding test-%d: %v\n", i, err)
+ }
continue
}
if !test.eq(&v, test.want) {
@@ -161,7 +165,9 @@ func TestDenseUnmarshalFrom(t *testing.T) {
buf := bytes.NewReader(test.raw)
n, err := v.UnmarshalBinaryFrom(buf)
if err != nil {
+ if err != test.err {
t.Errorf("error decoding test-%d: %v\n", i, err)
+ }
continue
}
if n != len(test.raw) {
@@ -257,8 +263,11 @@ func TestDenseIORoundTrip(t *testing.T) {
var got Dense
err = got.UnmarshalBinary(buf)
if err != nil {
+ if err != test.err {
t.Errorf("error decoding test #%d: %v\n", i, err)
}
+ continue
+ }
if !test.eq(&got, test.want) {
t.Errorf("r/w test #%d failed\n got=%#v\nwant=%#v\n", i, &got, test.want)
@@ -291,11 +300,13 @@ func TestDenseIORoundTrip(t *testing.T) {
var vectorData = []struct {
raw []byte
want *VecDense
+ err error
eq func(got, want Matrix) bool
}{
{
raw: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"),
want: NewVecDense(0, []float64{}),
+ err: ErrZeroLength,
eq: Equal,
},
{
@@ -414,7 +425,9 @@ func TestVecDenseUnmarshal(t *testing.T) {
var v VecDense
err := v.UnmarshalBinary(test.raw)
if err != nil {
+ if err != test.err {
t.Errorf("error decoding test-%d: %v\n", i, err)
+ }
continue
}
if !test.eq(&v, test.want) {
@@ -433,7 +446,9 @@ func TestVecDenseUnmarshalFrom(t *testing.T) {
buf := bytes.NewReader(test.raw)
n, err := v.UnmarshalBinaryFrom(buf)
if err != nil {
+ if err != test.err {
t.Errorf("error decoding test-%d: %v\n", i, err)
+ }
continue
}
if n != len(test.raw) {
@@ -519,8 +534,11 @@ func TestVecDenseIORoundTrip(t *testing.T) {
var got VecDense
err = got.UnmarshalBinary(buf)
if err != nil {
+ if err != test.err {
t.Errorf("error decoding test #%d: %v\n", i, err)
}
+ continue
+ }
if !test.eq(&got, test.want) {
t.Errorf("r/w test #%d failed\n got=%#v\nwant=%#v\n", i, &got, test.want)
}

MODIFY
mat/qr_test.go
mat/qr_test.go
@@ -17,7 +17,6 @@ func TestQR(t *testing.T) {
for _, test := range []struct {
m, n int
}{
- {0, 0}, // Check that there is no panic for zero-sized matrix.
{5, 5},
{10, 5},
} {

MODIFY
mat/triangular.go
mat/triangular.go
@@ -235,6 +235,9 @@ func untransposeTri(a Triangular) (Triangular, bool) {
// orientation. If the receiver is non-zero, reuseAs checks that the receiver
// is the correct size and orientation.
func (t *TriDense) reuseAs(n int, kind TriKind) {
+ if n == 0 {
+ panic(ErrZeroLength)
+ }
ul := blas.Lower
if kind == Upper {
ul = blas.Upper
@@ -267,7 +270,7 @@ func (t *TriDense) reuseAs(n int, kind TriKind) {
func (t *TriDense) isolatedWorkspace(a Triangular) (w *TriDense, restore func()) {
n, kind := a.Triangle()
if n == 0 {
- panic("zero size")
+ panic(ErrZeroLength)
}
w = getWorkspaceTri(n, kind, false)
return w, func() {

mat: disallow zero dimension results