mat: disallow zero dimension results

This commit is contained in:
Dan Kortschak
2018-05-21 10:36:59 +09:30
committed by Dan Kortschak
parent 91face6738
commit d1117c6f27
8 changed files with 68 additions and 9 deletions

View File

@@ -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)

View File

@@ -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"}

View File

@@ -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
}

View File

@@ -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 {
t.Errorf("error decoding test-%d: %v\n", i, err)
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 {
t.Errorf("error decoding test-%d: %v\n", i, err)
if err != test.err {
t.Errorf("error decoding test-%d: %v\n", i, err)
}
continue
}
if n != len(test.raw) {
@@ -257,7 +263,10 @@ func TestDenseIORoundTrip(t *testing.T) {
var got Dense
err = got.UnmarshalBinary(buf)
if err != nil {
t.Errorf("error decoding test #%d: %v\n", i, err)
if err != test.err {
t.Errorf("error decoding test #%d: %v\n", i, err)
}
continue
}
if !test.eq(&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 {
t.Errorf("error decoding test-%d: %v\n", i, err)
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 {
t.Errorf("error decoding test-%d: %v\n", i, err)
if err != test.err {
t.Errorf("error decoding test-%d: %v\n", i, err)
}
continue
}
if n != len(test.raw) {
@@ -519,7 +534,10 @@ func TestVecDenseIORoundTrip(t *testing.T) {
var got VecDense
err = got.UnmarshalBinary(buf)
if err != nil {
t.Errorf("error decoding test #%d: %v\n", i, err)
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)

View File

@@ -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},
} {

View File

@@ -138,6 +138,9 @@ func (s *SymDense) IsZero() bool {
// reuseAs resizes an empty matrix to a n×n matrix,
// or checks that a non-empty matrix is n×n.
func (s *SymDense) reuseAs(n int) {
if n == 0 {
panic(ErrZeroLength)
}
if s.mat.N > s.cap {
panic(badSymCap)
}
@@ -161,6 +164,9 @@ func (s *SymDense) reuseAs(n int) {
func (s *SymDense) isolatedWorkspace(a Symmetric) (w *SymDense, restore func()) {
n := a.Symmetric()
if n == 0 {
panic(ErrZeroLength)
}
w = getWorkspaceSym(n, false)
return w, func() {
s.CopySym(w)

View File

@@ -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() {

View File

@@ -87,6 +87,9 @@ type VecDense struct {
// used as the backing slice, and changes to the elements of the returned VecDense
// will be reflected in data. If neither of these is true, NewVecDense will panic.
func NewVecDense(n int, data []float64) *VecDense {
if n < 0 {
panic("mat: negative dimension")
}
if len(data) != n && data != nil {
panic(ErrShape)
}
@@ -633,6 +636,9 @@ func (v *VecDense) MulVec(a Matrix, b Vector) {
// reuseAs resizes an empty vector to a r×1 vector,
// or checks that a non-empty matrix is r×1.
func (v *VecDense) reuseAs(r int) {
if r == 0 {
panic(ErrZeroLength)
}
if v.IsZero() {
v.mat = blas64.Vector{
Inc: 1,
@@ -656,6 +662,9 @@ func (v *VecDense) IsZero() bool {
func (v *VecDense) isolatedWorkspace(a Vector) (n *VecDense, restore func()) {
l := a.Len()
if l == 0 {
panic(ErrZeroLength)
}
n = getWorkspaceVec(l, false)
return n, func() {
v.CopyVec(n)