| // Copyright ©2015 The gonum Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package mat |
| |
| import ( |
| "encoding/binary" |
| "errors" |
| "io" |
| "math" |
| ) |
| |
| const ( |
| // maxLen is the biggest slice/array len one can create on a 32/64b platform. |
| maxLen = int64(int(^uint(0) >> 1)) |
| ) |
| |
| var ( |
| sizeInt64 = binary.Size(int64(0)) |
| sizeFloat64 = binary.Size(float64(0)) |
| |
| errTooBig = errors.New("mat: resulting data slice too big") |
| errTooSmall = errors.New("mat: input slice too small") |
| errBadBuffer = errors.New("mat: data buffer size mismatch") |
| errBadSize = errors.New("mat: invalid dimension") |
| ) |
| |
| // MarshalBinary encodes the receiver into a binary form and returns the result. |
| // |
| // Dense is little-endian encoded as follows: |
| // 0 - 7 number of rows (int64) |
| // 8 - 15 number of columns (int64) |
| // 16 - .. matrix data elements (float64) |
| // [0,0] [0,1] ... [0,ncols-1] |
| // [1,0] [1,1] ... [1,ncols-1] |
| // ... |
| // [nrows-1,0] ... [nrows-1,ncols-1] |
| func (m Dense) MarshalBinary() ([]byte, error) { |
| bufLen := int64(m.mat.Rows)*int64(m.mat.Cols)*int64(sizeFloat64) + 2*int64(sizeInt64) |
| if bufLen <= 0 { |
| // bufLen is too big and has wrapped around. |
| return nil, errTooBig |
| } |
| |
| p := 0 |
| buf := make([]byte, bufLen) |
| binary.LittleEndian.PutUint64(buf[p:p+sizeInt64], uint64(m.mat.Rows)) |
| p += sizeInt64 |
| binary.LittleEndian.PutUint64(buf[p:p+sizeInt64], uint64(m.mat.Cols)) |
| p += sizeInt64 |
| |
| r, c := m.Dims() |
| for i := 0; i < r; i++ { |
| for j := 0; j < c; j++ { |
| binary.LittleEndian.PutUint64(buf[p:p+sizeFloat64], math.Float64bits(m.at(i, j))) |
| p += sizeFloat64 |
| } |
| } |
| |
| return buf, nil |
| } |
| |
| // MarshalBinaryTo encodes the receiver into a binary form and writes it into w. |
| // MarshalBinaryTo returns the number of bytes written into w and an error, if any. |
| // |
| // See MarshalBinary for the on-disk layout. |
| func (m Dense) MarshalBinaryTo(w io.Writer) (int, error) { |
| var n int |
| var buf [8]byte |
| binary.LittleEndian.PutUint64(buf[:], uint64(m.mat.Rows)) |
| nn, err := w.Write(buf[:]) |
| n += nn |
| if err != nil { |
| return n, err |
| } |
| binary.LittleEndian.PutUint64(buf[:], uint64(m.mat.Cols)) |
| nn, err = w.Write(buf[:]) |
| n += nn |
| if err != nil { |
| return n, err |
| } |
| |
| r, c := m.Dims() |
| for i := 0; i < r; i++ { |
| for j := 0; j < c; j++ { |
| binary.LittleEndian.PutUint64(buf[:], math.Float64bits(m.at(i, j))) |
| nn, err = w.Write(buf[:]) |
| n += nn |
| if err != nil { |
| return n, err |
| } |
| } |
| } |
| |
| return n, nil |
| } |
| |
| // UnmarshalBinary decodes the binary form into the receiver. |
| // It panics if the receiver is a non-zero Dense matrix. |
| // |
| // See MarshalBinary for the on-disk layout. |
| // |
| // Limited checks on the validity of the binary input are performed: |
| // - matrix.ErrShape is returned if the number of rows or columns is negative, |
| // - an error is returned if the resulting Dense matrix is too |
| // big for the current architecture (e.g. a 16GB matrix written by a |
| // 64b application and read back from a 32b application.) |
| // UnmarshalBinary does not limit the size of the unmarshaled matrix, and so |
| // it should not be used on untrusted data. |
| func (m *Dense) UnmarshalBinary(data []byte) error { |
| if !m.IsZero() { |
| panic("mat: unmarshal into non-zero matrix") |
| } |
| |
| if len(data) < 2*sizeInt64 { |
| return errTooSmall |
| } |
| |
| p := 0 |
| rows := int64(binary.LittleEndian.Uint64(data[p : p+sizeInt64])) |
| p += sizeInt64 |
| cols := int64(binary.LittleEndian.Uint64(data[p : p+sizeInt64])) |
| p += sizeInt64 |
| if rows < 0 || cols < 0 { |
| return errBadSize |
| } |
| |
| size := rows * cols |
| if int(size) < 0 || size > maxLen { |
| return errTooBig |
| } |
| |
| if len(data) != int(size)*sizeFloat64+2*sizeInt64 { |
| return errBadBuffer |
| } |
| |
| m.reuseAs(int(rows), int(cols)) |
| for i := range m.mat.Data { |
| m.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(data[p : p+sizeFloat64])) |
| p += sizeFloat64 |
| } |
| |
| return nil |
| } |
| |
| // UnmarshalBinaryFrom decodes the binary form into the receiver and returns |
| // the number of bytes read and an error if any. |
| // It panics if the receiver is a non-zero Dense matrix. |
| // |
| // See MarshalBinary for the on-disk layout. |
| // |
| // Limited checks on the validity of the binary input are performed: |
| // - matrix.ErrShape is returned if the number of rows or columns is negative, |
| // - an error is returned if the resulting Dense matrix is too |
| // big for the current architecture (e.g. a 16GB matrix written by a |
| // 64b application and read back from a 32b application.) |
| // UnmarshalBinary does not limit the size of the unmarshaled matrix, and so |
| // it should not be used on untrusted data. |
| func (m *Dense) UnmarshalBinaryFrom(r io.Reader) (int, error) { |
| if !m.IsZero() { |
| panic("mat: unmarshal into non-zero matrix") |
| } |
| |
| var ( |
| n int |
| buf [8]byte |
| ) |
| nn, err := readFull(r, buf[:]) |
| n += nn |
| if err != nil { |
| return n, err |
| } |
| rows := int64(binary.LittleEndian.Uint64(buf[:])) |
| |
| nn, err = readFull(r, buf[:]) |
| n += nn |
| if err != nil { |
| return n, err |
| } |
| cols := int64(binary.LittleEndian.Uint64(buf[:])) |
| if rows < 0 || cols < 0 { |
| return n, errBadSize |
| } |
| |
| size := rows * cols |
| if int(size) < 0 || size > maxLen { |
| return n, errTooBig |
| } |
| |
| m.reuseAs(int(rows), int(cols)) |
| for i := range m.mat.Data { |
| nn, err = readFull(r, buf[:]) |
| n += nn |
| if err != nil { |
| return n, err |
| } |
| m.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(buf[:])) |
| } |
| |
| return n, nil |
| } |
| |
| // MarshalBinary encodes the receiver into a binary form and returns the result. |
| // |
| // VecDense is little-endian encoded as follows: |
| // 0 - 7 number of elements (int64) |
| // 8 - .. vector's data elements (float64) |
| func (v VecDense) MarshalBinary() ([]byte, error) { |
| bufLen := int64(sizeInt64) + int64(v.n)*int64(sizeFloat64) |
| if bufLen <= 0 { |
| // bufLen is too big and has wrapped around. |
| return nil, errTooBig |
| } |
| |
| p := 0 |
| buf := make([]byte, bufLen) |
| binary.LittleEndian.PutUint64(buf[p:p+sizeInt64], uint64(v.n)) |
| p += sizeInt64 |
| |
| for i := 0; i < v.n; i++ { |
| binary.LittleEndian.PutUint64(buf[p:p+sizeFloat64], math.Float64bits(v.at(i))) |
| p += sizeFloat64 |
| } |
| |
| return buf, nil |
| } |
| |
| // MarshalBinaryTo encodes the receiver into a binary form, writes it to w and |
| // returns the number of bytes written and an error if any. |
| // |
| // See MarshalBainry for the on-disk format. |
| func (v VecDense) MarshalBinaryTo(w io.Writer) (int, error) { |
| var ( |
| n int |
| buf [8]byte |
| ) |
| |
| binary.LittleEndian.PutUint64(buf[:], uint64(v.n)) |
| nn, err := w.Write(buf[:]) |
| n += nn |
| if err != nil { |
| return n, err |
| } |
| |
| for i := 0; i < v.n; i++ { |
| binary.LittleEndian.PutUint64(buf[:], math.Float64bits(v.at(i))) |
| nn, err = w.Write(buf[:]) |
| n += nn |
| if err != nil { |
| return n, err |
| } |
| } |
| |
| return n, nil |
| } |
| |
| // UnmarshalBinary decodes the binary form into the receiver. |
| // It panics if the receiver is a non-zero VecDense. |
| // |
| // See MarshalBinary for the on-disk layout. |
| // |
| // Limited checks on the validity of the binary input are performed: |
| // - matrix.ErrShape is returned if the number of rows is negative, |
| // - an error is returned if the resulting VecDense is too |
| // big for the current architecture (e.g. a 16GB vector written by a |
| // 64b application and read back from a 32b application.) |
| // UnmarshalBinary does not limit the size of the unmarshaled vector, and so |
| // it should not be used on untrusted data. |
| func (v *VecDense) UnmarshalBinary(data []byte) error { |
| if !v.IsZero() { |
| panic("mat: unmarshal into non-zero vector") |
| } |
| |
| p := 0 |
| n := int64(binary.LittleEndian.Uint64(data[p : p+sizeInt64])) |
| p += sizeInt64 |
| if n < 0 { |
| return errBadSize |
| } |
| if n > maxLen { |
| return errTooBig |
| } |
| if len(data) != int(n)*sizeFloat64+sizeInt64 { |
| return errBadBuffer |
| } |
| |
| v.reuseAs(int(n)) |
| for i := range v.mat.Data { |
| v.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(data[p : p+sizeFloat64])) |
| p += sizeFloat64 |
| } |
| |
| return nil |
| } |
| |
| // UnmarshalBinaryFrom decodes the binary form into the receiver, from the |
| // io.Reader and returns the number of bytes read and an error if any. |
| // It panics if the receiver is a non-zero VecDense. |
| // |
| // See MarshalBinary for the on-disk layout. |
| // See UnmarshalBinary for the list of sanity checks performed on the input. |
| func (v *VecDense) UnmarshalBinaryFrom(r io.Reader) (int, error) { |
| if !v.IsZero() { |
| panic("mat: unmarshal into non-zero vector") |
| } |
| |
| var ( |
| n int |
| buf [8]byte |
| ) |
| nn, err := readFull(r, buf[:]) |
| n += nn |
| if err != nil { |
| return n, err |
| } |
| sz := int64(binary.LittleEndian.Uint64(buf[:])) |
| if sz < 0 { |
| return n, errBadSize |
| } |
| if sz > maxLen { |
| return n, errTooBig |
| } |
| |
| v.reuseAs(int(sz)) |
| for i := range v.mat.Data { |
| nn, err = readFull(r, buf[:]) |
| n += nn |
| if err != nil { |
| return n, err |
| } |
| v.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(buf[:])) |
| } |
| |
| if n != sizeInt64+int(sz)*sizeFloat64 { |
| return n, io.ErrUnexpectedEOF |
| } |
| |
| return n, nil |
| } |
| |
| // readFull reads from r into buf until it has read len(buf). |
| // It returns the number of bytes copied and an error if fewer bytes were read. |
| // If an EOF happens after reading fewer than len(buf) bytes, io.ErrUnexpectedEOF is returned. |
| func readFull(r io.Reader, buf []byte) (int, error) { |
| var n int |
| var err error |
| for n < len(buf) && err == nil { |
| var nn int |
| nn, err = r.Read(buf[n:]) |
| n += nn |
| } |
| if n == len(buf) { |
| return n, nil |
| } |
| if err == io.EOF { |
| return n, io.ErrUnexpectedEOF |
| } |
| return n, err |
| } |