123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- // 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 (
- "bytes"
- "encoding/binary"
- "errors"
- "fmt"
- "io"
- "math"
- )
- // version is the current on-disk codec version.
- const version uint32 = 0x1
- // maxLen is the biggest slice/array len one can create on a 32/64b platform.
- const maxLen = int64(int(^uint(0) >> 1))
- var (
- headerSize = binary.Size(storage{})
- sizeFloat64 = binary.Size(float64(0))
- errWrongType = errors.New("mat: wrong data type")
- 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")
- )
- // Type encoding scheme:
- //
- // Type Form Packing Uplo Unit Rows Columns kU kL
- // uint8 [GST] uint8 [BPF] uint8 [AUL] bool int64 int64 int64 int64
- // General 'G' 'F' 'A' false r c 0 0
- // Band 'G' 'B' 'A' false r c kU kL
- // Symmetric 'S' 'F' ul false n n 0 0
- // SymmetricBand 'S' 'B' ul false n n k k
- // SymmetricPacked 'S' 'P' ul false n n 0 0
- // Triangular 'T' 'F' ul Diag==Unit n n 0 0
- // TriangularBand 'T' 'B' ul Diag==Unit n n k k
- // TriangularPacked 'T' 'P' ul Diag==Unit n n 0 0
- //
- // G - general, S - symmetric, T - triangular
- // F - full, B - band, P - packed
- // A - all, U - upper, L - lower
- // MarshalBinary encodes the receiver into a binary form and returns the result.
- //
- // Dense is little-endian encoded as follows:
- // 0 - 3 Version = 1 (uint32)
- // 4 'G' (byte)
- // 5 'F' (byte)
- // 6 'A' (byte)
- // 7 0 (byte)
- // 8 - 15 number of rows (int64)
- // 16 - 23 number of columns (int64)
- // 24 - 31 0 (int64)
- // 32 - 39 0 (int64)
- // 40 - .. 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(headerSize) + int64(m.mat.Rows)*int64(m.mat.Cols)*int64(sizeFloat64)
- if bufLen <= 0 {
- // bufLen is too big and has wrapped around.
- return nil, errTooBig
- }
- header := storage{
- Form: 'G', Packing: 'F', Uplo: 'A',
- Rows: int64(m.mat.Rows), Cols: int64(m.mat.Cols),
- Version: version,
- }
- buf := make([]byte, bufLen)
- n, err := header.marshalBinaryTo(bytes.NewBuffer(buf[:0]))
- if err != nil {
- return buf[:n], err
- }
- p := headerSize
- 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) {
- header := storage{
- Form: 'G', Packing: 'F', Uplo: 'A',
- Rows: int64(m.mat.Rows), Cols: int64(m.mat.Cols),
- Version: version,
- }
- n, err := header.marshalBinaryTo(w)
- if err != nil {
- return n, err
- }
- r, c := m.Dims()
- var b [8]byte
- for i := 0; i < r; i++ {
- for j := 0; j < c; j++ {
- binary.LittleEndian.PutUint64(b[:], math.Float64bits(m.at(i, j)))
- nn, err := w.Write(b[:])
- 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-empty 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.IsEmpty() {
- panic("mat: unmarshal into non-empty matrix")
- }
- if len(data) < headerSize {
- return errTooSmall
- }
- var header storage
- err := header.unmarshalBinary(data[:headerSize])
- if err != nil {
- return err
- }
- rows := header.Rows
- cols := header.Cols
- header.Version = 0
- header.Rows = 0
- header.Cols = 0
- if (header != storage{Form: 'G', Packing: 'F', Uplo: 'A'}) {
- return errWrongType
- }
- if rows < 0 || cols < 0 {
- return errBadSize
- }
- size := rows * cols
- if size == 0 {
- return ErrZeroLength
- }
- if int(size) < 0 || size > maxLen {
- return errTooBig
- }
- if len(data) != headerSize+int(rows*cols)*sizeFloat64 {
- return errBadBuffer
- }
- p := headerSize
- m.reuseAsNonZeroed(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-empty 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.IsEmpty() {
- panic("mat: unmarshal into non-empty matrix")
- }
- var header storage
- n, err := header.unmarshalBinaryFrom(r)
- if err != nil {
- return n, err
- }
- rows := header.Rows
- cols := header.Cols
- header.Version = 0
- header.Rows = 0
- header.Cols = 0
- if (header != storage{Form: 'G', Packing: 'F', Uplo: 'A'}) {
- return n, errWrongType
- }
- if rows < 0 || cols < 0 {
- return n, errBadSize
- }
- size := rows * cols
- if size == 0 {
- return n, ErrZeroLength
- }
- if int(size) < 0 || size > maxLen {
- return n, errTooBig
- }
- m.reuseAsNonZeroed(int(rows), int(cols))
- var b [8]byte
- for i := range m.mat.Data {
- nn, err := readFull(r, b[:])
- n += nn
- if err != nil {
- if err == io.EOF {
- return n, io.ErrUnexpectedEOF
- }
- return n, err
- }
- m.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(b[:]))
- }
- return n, nil
- }
- // MarshalBinary encodes the receiver into a binary form and returns the result.
- //
- // VecDense is little-endian encoded as follows:
- //
- // 0 - 3 Version = 1 (uint32)
- // 4 'G' (byte)
- // 5 'F' (byte)
- // 6 'A' (byte)
- // 7 0 (byte)
- // 8 - 15 number of elements (int64)
- // 16 - 23 1 (int64)
- // 24 - 31 0 (int64)
- // 32 - 39 0 (int64)
- // 40 - .. vector's data elements (float64)
- func (v VecDense) MarshalBinary() ([]byte, error) {
- bufLen := int64(headerSize) + int64(v.mat.N)*int64(sizeFloat64)
- if bufLen <= 0 {
- // bufLen is too big and has wrapped around.
- return nil, errTooBig
- }
- header := storage{
- Form: 'G', Packing: 'F', Uplo: 'A',
- Rows: int64(v.mat.N), Cols: 1,
- Version: version,
- }
- buf := make([]byte, bufLen)
- n, err := header.marshalBinaryTo(bytes.NewBuffer(buf[:0]))
- if err != nil {
- return buf[:n], err
- }
- p := headerSize
- for i := 0; i < v.mat.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) {
- header := storage{
- Form: 'G', Packing: 'F', Uplo: 'A',
- Rows: int64(v.mat.N), Cols: 1,
- Version: version,
- }
- n, err := header.marshalBinaryTo(w)
- if err != nil {
- return n, err
- }
- var buf [8]byte
- for i := 0; i < v.mat.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-empty 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.IsEmpty() {
- panic("mat: unmarshal into non-empty vector")
- }
- if len(data) < headerSize {
- return errTooSmall
- }
- var header storage
- err := header.unmarshalBinary(data[:headerSize])
- if err != nil {
- return err
- }
- if header.Cols != 1 {
- return ErrShape
- }
- n := header.Rows
- header.Version = 0
- header.Rows = 0
- header.Cols = 0
- if (header != storage{Form: 'G', Packing: 'F', Uplo: 'A'}) {
- return errWrongType
- }
- if n == 0 {
- return ErrZeroLength
- }
- if n < 0 {
- return errBadSize
- }
- if int64(maxLen) < n {
- return errTooBig
- }
- if len(data) != headerSize+int(n)*sizeFloat64 {
- return errBadBuffer
- }
- p := headerSize
- v.reuseAsNonZeroed(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-empty 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.IsEmpty() {
- panic("mat: unmarshal into non-empty vector")
- }
- var header storage
- n, err := header.unmarshalBinaryFrom(r)
- if err != nil {
- return n, err
- }
- if header.Cols != 1 {
- return n, ErrShape
- }
- l := header.Rows
- header.Version = 0
- header.Rows = 0
- header.Cols = 0
- if (header != storage{Form: 'G', Packing: 'F', Uplo: 'A'}) {
- return n, errWrongType
- }
- if l == 0 {
- return n, ErrZeroLength
- }
- if l < 0 {
- return n, errBadSize
- }
- if int64(maxLen) < l {
- return n, errTooBig
- }
- v.reuseAsNonZeroed(int(l))
- var b [8]byte
- for i := range v.mat.Data {
- nn, err := readFull(r, b[:])
- n += nn
- if err != nil {
- if err == io.EOF {
- return n, io.ErrUnexpectedEOF
- }
- return n, err
- }
- v.mat.Data[i] = math.Float64frombits(binary.LittleEndian.Uint64(b[:]))
- }
- return n, nil
- }
- // storage is the internal representation of the storage format of a
- // serialised matrix.
- type storage struct {
- Version uint32 // Keep this first.
- Form byte // [GST]
- Packing byte // [BPF]
- Uplo byte // [AUL]
- Unit bool
- Rows int64
- Cols int64
- KU int64
- KL int64
- }
- // TODO(kortschak): Consider replacing these with calls to direct
- // encoding/decoding of fields rather than to binary.Write/binary.Read.
- func (s storage) marshalBinaryTo(w io.Writer) (int, error) {
- buf := bytes.NewBuffer(make([]byte, 0, headerSize))
- err := binary.Write(buf, binary.LittleEndian, s)
- if err != nil {
- return 0, err
- }
- return w.Write(buf.Bytes())
- }
- func (s *storage) unmarshalBinary(buf []byte) error {
- err := binary.Read(bytes.NewReader(buf), binary.LittleEndian, s)
- if err != nil {
- return err
- }
- if s.Version != version {
- return fmt.Errorf("mat: incorrect version: %d", s.Version)
- }
- return nil
- }
- func (s *storage) unmarshalBinaryFrom(r io.Reader) (int, error) {
- buf := make([]byte, headerSize)
- n, err := readFull(r, buf)
- if err != nil {
- return n, err
- }
- return n, s.unmarshalBinary(buf[:n])
- }
- // 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
- }
|