// Copyright (c) 2013-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package common import ( "encoding/binary" "fmt" "io" "math" ) const ( // MaxVarIntPayload is the maximum payload size for a variable length integer. MaxVarIntPayload = 9 // binaryFreeListMaxItems is the number of buffers to keep in the free // list to use for binary serialization and deserialization. binaryFreeListMaxItems = 1024 ) var ( // littleEndian is a convenience variable since binary.LittleEndian is // quite long. littleEndian = binary.LittleEndian ) // binaryFreeList defines a concurrent safe free list of byte slices (up to the // maximum number defined by the binaryFreeListMaxItems constant) that have a // cap of 8 (thus it supports up to a uint64). It is used to provide temporary // buffers for serializing and deserializing primitive numbers to and from their // binary encoding in order to greatly reduce the number of allocations // required. // // For convenience, functions are provided for each of the primitive unsigned // integers that automatically obtain a buffer from the free list, perform the // necessary binary conversion, read from or write to the given io.Reader or // io.Writer, and return the buffer to the free list. type binaryFreeList chan []byte // Borrow returns a byte slice from the free list with a length of 8. A new // buffer is allocated if there are not any available on the free list. func (l binaryFreeList) Borrow() []byte { var buf []byte select { case buf = <-l: default: buf = make([]byte, 8) } return buf[:8] } // Return puts the provided byte slice back on the free list. The buffer MUST // have been obtained via the Borrow function and therefore have a cap of 8. func (l binaryFreeList) Return(buf []byte) { select { case l <- buf: default: // Let it go to the garbage collector. } } // Uint8 reads a single byte from the provided reader using a buffer from the // free list and returns it as a uint8. func (l binaryFreeList) Uint8(r io.Reader) (uint8, error) { buf := l.Borrow()[:1] if _, err := io.ReadFull(r, buf); err != nil { l.Return(buf) return 0, err } rv := buf[0] l.Return(buf) return rv, nil } // Uint16 reads two bytes from the provided reader using a buffer from the // free list, converts it to a number using the provided byte order, and returns // the resulting uint16. func (l binaryFreeList) Uint16(r io.Reader, byteOrder binary.ByteOrder) (uint16, error) { buf := l.Borrow()[:2] if _, err := io.ReadFull(r, buf); err != nil { l.Return(buf) return 0, err } rv := byteOrder.Uint16(buf) l.Return(buf) return rv, nil } // Uint32 reads four bytes from the provided reader using a buffer from the // free list, converts it to a number using the provided byte order, and returns // the resulting uint32. func (l binaryFreeList) Uint32(r io.Reader, byteOrder binary.ByteOrder) (uint32, error) { buf := l.Borrow()[:4] if _, err := io.ReadFull(r, buf); err != nil { l.Return(buf) return 0, err } rv := byteOrder.Uint32(buf) l.Return(buf) return rv, nil } // Uint64 reads eight bytes from the provided reader using a buffer from the // free list, converts it to a number using the provided byte order, and returns // the resulting uint64. func (l binaryFreeList) Uint64(r io.Reader, byteOrder binary.ByteOrder) (uint64, error) { buf := l.Borrow()[:8] if _, err := io.ReadFull(r, buf); err != nil { l.Return(buf) return 0, err } rv := byteOrder.Uint64(buf) l.Return(buf) return rv, nil } // PutUint8 copies the provided uint8 into a buffer from the free list and // writes the resulting byte to the given writer. func (l binaryFreeList) PutUint8(w io.Writer, val uint8) error { buf := l.Borrow()[:1] buf[0] = val _, err := w.Write(buf) l.Return(buf) return err } // PutUint16 serializes the provided uint16 using the given byte order into a // buffer from the free list and writes the resulting two bytes to the given // writer. func (l binaryFreeList) PutUint16(w io.Writer, byteOrder binary.ByteOrder, val uint16) error { buf := l.Borrow()[:2] byteOrder.PutUint16(buf, val) _, err := w.Write(buf) l.Return(buf) return err } // PutUint32 serializes the provided uint32 using the given byte order into a // buffer from the free list and writes the resulting four bytes to the given // writer. func (l binaryFreeList) PutUint32(w io.Writer, byteOrder binary.ByteOrder, val uint32) error { buf := l.Borrow()[:4] byteOrder.PutUint32(buf, val) _, err := w.Write(buf) l.Return(buf) return err } // PutUint64 serializes the provided uint64 using the given byte order into a // buffer from the free list and writes the resulting eight bytes to the given // writer. func (l binaryFreeList) PutUint64(w io.Writer, byteOrder binary.ByteOrder, val uint64) error { buf := l.Borrow()[:8] byteOrder.PutUint64(buf, val) _, err := w.Write(buf) l.Return(buf) return err } // binarySerializer provides a free list of buffers to use for serializing and // deserializing primitive integer values to and from io.Readers and io.Writers. var binarySerializer binaryFreeList = make(chan []byte, binaryFreeListMaxItems) // errNonCanonicalVarInt is the common format string used for non-canonically // encoded variable length integer errors. var errNonCanonicalVarInt = "non-canonical varint %x - discriminant %x must " + "encode a value greater than %x" // ReadVarInt reads a variable length integer from r and returns it as a uint64. func ReadVarInt(r io.Reader, pver uint32) (uint64, error) { discriminant, err := binarySerializer.Uint8(r) if err != nil { return 0, err } var rv uint64 switch discriminant { case 0xff: sv, err := binarySerializer.Uint64(r, littleEndian) if err != nil { return 0, err } rv = sv // The encoding is not canonical if the value could have been // encoded using fewer bytes. min := uint64(0x100000000) if rv < min { return 0, messageError("ReadVarInt", fmt.Sprintf( errNonCanonicalVarInt, rv, discriminant, min)) } case 0xfe: sv, err := binarySerializer.Uint32(r, littleEndian) if err != nil { return 0, err } rv = uint64(sv) // The encoding is not canonical if the value could have been // encoded using fewer bytes. min := uint64(0x10000) if rv < min { return 0, messageError("ReadVarInt", fmt.Sprintf( errNonCanonicalVarInt, rv, discriminant, min)) } case 0xfd: sv, err := binarySerializer.Uint16(r, littleEndian) if err != nil { return 0, err } rv = uint64(sv) // The encoding is not canonical if the value could have been // encoded using fewer bytes. min := uint64(0xfd) if rv < min { return 0, messageError("ReadVarInt", fmt.Sprintf( errNonCanonicalVarInt, rv, discriminant, min)) } default: rv = uint64(discriminant) } return rv, nil } // WriteVarInt serializes val to w using a variable number of bytes depending // on its value. func WriteVarInt(w io.Writer, pver uint32, val uint64) error { if val < 0xfd { return binarySerializer.PutUint8(w, uint8(val)) } if val <= math.MaxUint16 { err := binarySerializer.PutUint8(w, 0xfd) if err != nil { return err } return binarySerializer.PutUint16(w, littleEndian, uint16(val)) } if val <= math.MaxUint32 { err := binarySerializer.PutUint8(w, 0xfe) if err != nil { return err } return binarySerializer.PutUint32(w, littleEndian, uint32(val)) } err := binarySerializer.PutUint8(w, 0xff) if err != nil { return err } return binarySerializer.PutUint64(w, littleEndian, val) } // VarIntSerializeSize returns the number of bytes it would take to serialize // val as a variable length integer. func VarIntSerializeSize(val uint64) int { // The value is small enough to be represented by itself, so it's // just 1 byte. if val < 0xfd { return 1 } // Discriminant 1 byte plus 2 bytes for the uint16. if val <= math.MaxUint16 { return 3 } // Discriminant 1 byte plus 4 bytes for the uint32. if val <= math.MaxUint32 { return 5 } // Discriminant 1 byte plus 8 bytes for the uint64. return 9 } // ReadVarString reads a variable length string from r and returns it as a Go // string. A variable length string is encoded as a variable length integer // containing the length of the string followed by the bytes that represent the // string itself. func ReadVarString(r io.Reader, pver uint32) (string, error) { count, err := ReadVarInt(r, pver) if err != nil { return "", err } buf := make([]byte, count) _, err = io.ReadFull(r, buf) if err != nil { return "", err } return string(buf), nil } // WriteVarString serializes str to w as a variable length integer containing // the length of the string followed by the bytes that represent the string // itself. func WriteVarString(w io.Writer, pver uint32, str string) error { err := WriteVarInt(w, pver, uint64(len(str))) if err != nil { return err } _, err = w.Write([]byte(str)) return err } // ReadVarBytes reads a variable length byte array. A byte array is encoded // as a varInt containing the length of the array followed by the bytes // themselves. An error is returned if the length is greater than the // passed maxAllowed parameter which helps protect against memory exhaustion // attacks and forced panics through malformed messages. The fieldName // parameter is only used for the error message so it provides more context in // the error. func ReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32, fieldName string) ([]byte, error) { count, err := ReadVarInt(r, pver) if err != nil { return nil, err } // Prevent byte array larger than the max message size. It would // be possible to cause memory exhaustion and panics without a sane // upper bound on this count. if count > uint64(maxAllowed) { str := fmt.Sprintf("%s is larger than the max allowed size "+ "[count %d, max %d]", fieldName, count, maxAllowed) return nil, messageError("ReadVarBytes", str) } b := make([]byte, count) _, err = io.ReadFull(r, b) if err != nil { return nil, err } return b, nil } // WriteVarBytes serializes a variable length byte array to w as a varInt // containing the number of bytes, followed by the bytes themselves. func WriteVarBytes(w io.Writer, pver uint32, bytes []byte) error { slen := uint64(len(bytes)) err := WriteVarInt(w, pver, slen) if err != nil { return err } _, err = w.Write(bytes) return err } // MessageError describes an issue with a message. // An example of some potential issues are messages from the wrong bitcoin // network, invalid commands, mismatched checksums, and exceeding max payloads. // // This provides a mechanism for the caller to type assert the error to // differentiate between general io errors such as io.EOF and issues that // resulted from malformed messages. type MessageError struct { Func string // Function name Description string // Human readable description of the issue } // Error satisfies the error interface and prints human-readable errors. func (e *MessageError) Error() string { if e.Func != "" { return fmt.Sprintf("%v: %v", e.Func, e.Description) } return e.Description } // messageError creates an error for the given function and description. func messageError(f string, desc string) *MessageError { return &MessageError{Func: f, Description: desc} }