--- /dev/null
+// 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
+}