|
|
@@ -1,30 +1,36 @@
|
|
|
package util
|
|
|
|
|
|
import (
|
|
|
+ "bufio"
|
|
|
"bytes"
|
|
|
- "encoding/binary"
|
|
|
"errors"
|
|
|
+ "fmt"
|
|
|
"io"
|
|
|
"math"
|
|
|
+ "os"
|
|
|
"unsafe"
|
|
|
|
|
|
"github.com/opencost/opencost/core/pkg/util/stringutil"
|
|
|
)
|
|
|
|
|
|
+var bytePool *bufferPool = newBufferPool()
|
|
|
+
|
|
|
// NonPrimitiveTypeError represents an error where the user provided a non-primitive data type for reading/writing
|
|
|
var NonPrimitiveTypeError error = errors.New("Type provided to read/write does not fit inside 8 bytes.")
|
|
|
|
|
|
// Buffer is a utility type which implements a very basic binary protocol for
|
|
|
// writing core go types.
|
|
|
type Buffer struct {
|
|
|
- b *bytes.Buffer
|
|
|
+ b *bufio.Reader
|
|
|
+ bw *bytes.Buffer
|
|
|
}
|
|
|
|
|
|
// NewBuffer creates a new Buffer instance using LittleEndian ByteOrder.
|
|
|
func NewBuffer() *Buffer {
|
|
|
var b bytes.Buffer
|
|
|
return &Buffer{
|
|
|
- b: &b,
|
|
|
+ b: nil,
|
|
|
+ bw: &b,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -32,7 +38,7 @@ func NewBuffer() *Buffer {
|
|
|
// The new buffer assumes ownership of the byte slice.
|
|
|
func NewBufferFromBytes(b []byte) *Buffer {
|
|
|
return &Buffer{
|
|
|
- b: bytes.NewBuffer(b),
|
|
|
+ bw: bytes.NewBuffer(b),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -41,180 +47,296 @@ func NewBufferFromBytes(b []byte) *Buffer {
|
|
|
func NewBufferFrom(b *Buffer) *Buffer {
|
|
|
bb := b.Bytes()
|
|
|
return &Buffer{
|
|
|
- b: bytes.NewBuffer(bb),
|
|
|
+ bw: bytes.NewBuffer(bb),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// NewBufferFromReader creates a new Buffer instance using the provided io.Reader. This
|
|
|
+// buffer is set to read-only.
|
|
|
+func NewBufferFromReader(reader io.Reader) *Buffer {
|
|
|
+ return &Buffer{
|
|
|
+ b: bufio.NewReader(reader),
|
|
|
+ bw: nil,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// WriteBool writes a bool value to the buffer.
|
|
|
-func (b *Buffer) WriteBool(t bool) {
|
|
|
- write(b.b, t)
|
|
|
+// WriteBool writes a bool value to the buffer
|
|
|
+func (b *Buffer) WriteBool(i bool) {
|
|
|
+ b.checkRO()
|
|
|
+ writeBool(b.bw, i)
|
|
|
}
|
|
|
|
|
|
// WriteInt writes an int value to the buffer.
|
|
|
func (b *Buffer) WriteInt(i int) {
|
|
|
- write(b.b, int32(i))
|
|
|
+ b.checkRO()
|
|
|
+ writeInt(b.bw, i)
|
|
|
}
|
|
|
|
|
|
// WriteInt8 writes an int8 value to the buffer.
|
|
|
func (b *Buffer) WriteInt8(i int8) {
|
|
|
- write(b.b, i)
|
|
|
+ b.checkRO()
|
|
|
+ writeInt8(b.bw, i)
|
|
|
}
|
|
|
|
|
|
// WriteInt16 writes an int16 value to the buffer.
|
|
|
func (b *Buffer) WriteInt16(i int16) {
|
|
|
- write(b.b, i)
|
|
|
+ b.checkRO()
|
|
|
+ writeInt16(b.bw, i)
|
|
|
}
|
|
|
|
|
|
// WriteInt32 writes an int32 value to the buffer.
|
|
|
func (b *Buffer) WriteInt32(i int32) {
|
|
|
- write(b.b, i)
|
|
|
+ b.checkRO()
|
|
|
+ writeInt32(b.bw, i)
|
|
|
}
|
|
|
|
|
|
// WriteInt64 writes an int64 value to the buffer.
|
|
|
func (b *Buffer) WriteInt64(i int64) {
|
|
|
- write(b.b, i)
|
|
|
+ b.checkRO()
|
|
|
+ writeInt64(b.bw, i)
|
|
|
}
|
|
|
|
|
|
// WriteUInt writes a uint value to the buffer.
|
|
|
func (b *Buffer) WriteUInt(i uint) {
|
|
|
- write(b.b, i)
|
|
|
+ b.checkRO()
|
|
|
+ writeUint(b.bw, i)
|
|
|
}
|
|
|
|
|
|
// WriteUInt8 writes a uint8 value to the buffer.
|
|
|
func (b *Buffer) WriteUInt8(i uint8) {
|
|
|
- write(b.b, i)
|
|
|
+ b.checkRO()
|
|
|
+ writeUint8(b.bw, i)
|
|
|
}
|
|
|
|
|
|
// WriteUInt16 writes a uint16 value to the buffer.
|
|
|
func (b *Buffer) WriteUInt16(i uint16) {
|
|
|
- write(b.b, i)
|
|
|
+ b.checkRO()
|
|
|
+ writeUint16(b.bw, i)
|
|
|
}
|
|
|
|
|
|
// WriteUInt32 writes a uint32 value to the buffer.
|
|
|
func (b *Buffer) WriteUInt32(i uint32) {
|
|
|
- write(b.b, i)
|
|
|
+ b.checkRO()
|
|
|
+ writeUint32(b.bw, i)
|
|
|
}
|
|
|
|
|
|
// WriteUInt64 writes a uint64 value to the buffer.
|
|
|
func (b *Buffer) WriteUInt64(i uint64) {
|
|
|
- write(b.b, i)
|
|
|
+ b.checkRO()
|
|
|
+ writeUint64(b.bw, i)
|
|
|
}
|
|
|
|
|
|
// WriteFloat32 writes a float32 value to the buffer.
|
|
|
func (b *Buffer) WriteFloat32(i float32) {
|
|
|
- write(b.b, i)
|
|
|
+ b.checkRO()
|
|
|
+ writeFloat32(b.bw, i)
|
|
|
}
|
|
|
|
|
|
// WriteFloat64 writes a float64 value to the buffer.
|
|
|
func (b *Buffer) WriteFloat64(i float64) {
|
|
|
- write(b.b, i)
|
|
|
+ b.checkRO()
|
|
|
+ writeFloat64(b.bw, i)
|
|
|
}
|
|
|
|
|
|
// WriteString writes the string's length as a uint16 followed by the string contents.
|
|
|
func (b *Buffer) WriteString(i string) {
|
|
|
+ b.checkRO()
|
|
|
s := stringToBytes(i)
|
|
|
|
|
|
// string lengths are limited to uint16 - See ReadString()
|
|
|
if len(s) > math.MaxUint16 {
|
|
|
s = s[:math.MaxUint16]
|
|
|
}
|
|
|
- write(b.b, uint16(len(s)))
|
|
|
- b.b.Write(s)
|
|
|
+ writeUint16(b.bw, uint16(len(s)))
|
|
|
+ b.bw.Write(s)
|
|
|
}
|
|
|
|
|
|
// WriteBytes writes the contents of the byte slice to the buffer.
|
|
|
func (b *Buffer) WriteBytes(bytes []byte) {
|
|
|
- b.b.Write(bytes)
|
|
|
+ b.checkRO()
|
|
|
+ b.bw.Write(bytes)
|
|
|
+}
|
|
|
+
|
|
|
+// Bytes returns the unread portion of the underlying buffer storage.
|
|
|
+func (b *Buffer) Bytes() []byte {
|
|
|
+ if b.bw != nil {
|
|
|
+ return b.bw.Bytes()
|
|
|
+ }
|
|
|
+
|
|
|
+ bytes, err := io.ReadAll(b.b)
|
|
|
+ if err != nil {
|
|
|
+ fmt.Fprintf(os.Stderr, "failed to read remaining bytes from Buffer: %s\n", err)
|
|
|
+ }
|
|
|
+ return bytes
|
|
|
+}
|
|
|
+
|
|
|
+func (b *Buffer) Peek(length int) ([]byte, error) {
|
|
|
+ if b.bw != nil {
|
|
|
+ return nil, fmt.Errorf("unsupported Peek() operation on read/write buffer.")
|
|
|
+ }
|
|
|
+ return b.b.Peek(length)
|
|
|
+}
|
|
|
+
|
|
|
+// this should be inlined
|
|
|
+func (b *Buffer) checkRO() {
|
|
|
+ if b.bw == nil {
|
|
|
+ panic("Buffer is set to read-only")
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// ReadBool reads a bool value from the buffer.
|
|
|
func (b *Buffer) ReadBool() bool {
|
|
|
var i bool
|
|
|
- read(b.b, &i)
|
|
|
+ if b.bw != nil {
|
|
|
+ readBool(b.bw, &i)
|
|
|
+ return i
|
|
|
+ }
|
|
|
+
|
|
|
+ readBuffBool(b.b, &i)
|
|
|
return i
|
|
|
}
|
|
|
|
|
|
// ReadInt reads an int value from the buffer.
|
|
|
func (b *Buffer) ReadInt() int {
|
|
|
- var i int32
|
|
|
- read(b.b, &i)
|
|
|
- return int(i)
|
|
|
+ var i int
|
|
|
+ if b.bw != nil {
|
|
|
+ readInt(b.bw, &i)
|
|
|
+ return i
|
|
|
+ }
|
|
|
+
|
|
|
+ readBuffInt(b.b, &i)
|
|
|
+ return i
|
|
|
}
|
|
|
|
|
|
// ReadInt8 reads an int8 value from the buffer.
|
|
|
func (b *Buffer) ReadInt8() int8 {
|
|
|
var i int8
|
|
|
- read(b.b, &i)
|
|
|
+ if b.bw != nil {
|
|
|
+ readInt8(b.bw, &i)
|
|
|
+ return i
|
|
|
+ }
|
|
|
+
|
|
|
+ readBuffInt8(b.b, &i)
|
|
|
return i
|
|
|
}
|
|
|
|
|
|
// ReadInt16 reads an int16 value from the buffer.
|
|
|
func (b *Buffer) ReadInt16() int16 {
|
|
|
var i int16
|
|
|
- read(b.b, &i)
|
|
|
+ if b.bw != nil {
|
|
|
+ readInt16(b.bw, &i)
|
|
|
+ return i
|
|
|
+ }
|
|
|
+
|
|
|
+ readBuffInt16(b.b, &i)
|
|
|
return i
|
|
|
}
|
|
|
|
|
|
// ReadInt32 reads an int32 value from the buffer.
|
|
|
func (b *Buffer) ReadInt32() int32 {
|
|
|
var i int32
|
|
|
- read(b.b, &i)
|
|
|
+ if b.bw != nil {
|
|
|
+ readInt32(b.bw, &i)
|
|
|
+ return i
|
|
|
+ }
|
|
|
+
|
|
|
+ readBuffInt32(b.b, &i)
|
|
|
return i
|
|
|
}
|
|
|
|
|
|
// ReadInt64 reads an int64 value from the buffer.
|
|
|
func (b *Buffer) ReadInt64() int64 {
|
|
|
var i int64
|
|
|
- read(b.b, &i)
|
|
|
+ if b.bw != nil {
|
|
|
+ readInt64(b.bw, &i)
|
|
|
+ return i
|
|
|
+ }
|
|
|
+
|
|
|
+ readBuffInt64(b.b, &i)
|
|
|
return i
|
|
|
}
|
|
|
|
|
|
// ReadUInt reads a uint value from the buffer.
|
|
|
func (b *Buffer) ReadUInt() uint {
|
|
|
var i uint
|
|
|
- read(b.b, &i)
|
|
|
+ if b.bw != nil {
|
|
|
+ readUint(b.bw, &i)
|
|
|
+ return i
|
|
|
+ }
|
|
|
+
|
|
|
+ readBuffUint(b.b, &i)
|
|
|
return i
|
|
|
}
|
|
|
|
|
|
// ReadUInt8 reads a uint8 value from the buffer.
|
|
|
func (b *Buffer) ReadUInt8() uint8 {
|
|
|
var i uint8
|
|
|
- read(b.b, &i)
|
|
|
+ if b.bw != nil {
|
|
|
+ readUint8(b.bw, &i)
|
|
|
+ return i
|
|
|
+ }
|
|
|
+
|
|
|
+ readBuffUint8(b.b, &i)
|
|
|
return i
|
|
|
}
|
|
|
|
|
|
// ReadUInt16 reads a uint16 value from the buffer.
|
|
|
func (b *Buffer) ReadUInt16() uint16 {
|
|
|
var i uint16
|
|
|
- read(b.b, &i)
|
|
|
+ if b.bw != nil {
|
|
|
+ readUint16(b.bw, &i)
|
|
|
+ return i
|
|
|
+ }
|
|
|
+
|
|
|
+ readBuffUint16(b.b, &i)
|
|
|
return i
|
|
|
}
|
|
|
|
|
|
// ReadUInt32 reads a uint32 value from the buffer.
|
|
|
func (b *Buffer) ReadUInt32() uint32 {
|
|
|
var i uint32
|
|
|
- read(b.b, &i)
|
|
|
+ if b.bw != nil {
|
|
|
+ readUint32(b.bw, &i)
|
|
|
+ return i
|
|
|
+ }
|
|
|
+
|
|
|
+ readBuffUint32(b.b, &i)
|
|
|
return i
|
|
|
}
|
|
|
|
|
|
// ReadUInt64 reads a uint64 value from the buffer.
|
|
|
func (b *Buffer) ReadUInt64() uint64 {
|
|
|
var i uint64
|
|
|
- read(b.b, &i)
|
|
|
+ if b.bw != nil {
|
|
|
+ readUint64(b.bw, &i)
|
|
|
+ return i
|
|
|
+ }
|
|
|
+
|
|
|
+ readBuffUint64(b.b, &i)
|
|
|
return i
|
|
|
}
|
|
|
|
|
|
// ReadFloat32 reads a float32 value from the buffer.
|
|
|
func (b *Buffer) ReadFloat32() float32 {
|
|
|
var i float32
|
|
|
- read(b.b, &i)
|
|
|
+ if b.bw != nil {
|
|
|
+ readFloat32(b.bw, &i)
|
|
|
+ return i
|
|
|
+ }
|
|
|
+
|
|
|
+ readBuffFloat32(b.b, &i)
|
|
|
return i
|
|
|
}
|
|
|
|
|
|
// ReadFloat64 reads a float64 value from the buffer.
|
|
|
func (b *Buffer) ReadFloat64() float64 {
|
|
|
var i float64
|
|
|
- read(b.b, &i)
|
|
|
+ if b.bw != nil {
|
|
|
+ readFloat64(b.bw, &i)
|
|
|
+ return i
|
|
|
+ }
|
|
|
+
|
|
|
+ readBuffFloat64(b.b, &i)
|
|
|
return i
|
|
|
}
|
|
|
|
|
|
@@ -222,70 +344,45 @@ func (b *Buffer) ReadFloat64() float64 {
|
|
|
// then uses the length to extract the exact length []byte representing the string.
|
|
|
func (b *Buffer) ReadString() string {
|
|
|
var l uint16
|
|
|
- read(b.b, &l)
|
|
|
- return bytesToString(b.b.Next(int(l)))
|
|
|
+ if b.bw != nil {
|
|
|
+ readUint16(b.bw, &l)
|
|
|
+ return bytesToString(b.bw.Next(int(l)))
|
|
|
+ }
|
|
|
+
|
|
|
+ readBuffUint16(b.b, &l)
|
|
|
+
|
|
|
+ bytes := bytePool.Get(int(l))
|
|
|
+ defer bytePool.Put(bytes)
|
|
|
+
|
|
|
+ _, err := readFull(b.b, bytes)
|
|
|
+ if err != nil {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+
|
|
|
+ return bytesToString(bytes)
|
|
|
}
|
|
|
|
|
|
// ReadBytes reads the specified length from the buffer and returns the byte slice.
|
|
|
func (b *Buffer) ReadBytes(length int) []byte {
|
|
|
- return b.b.Next(length)
|
|
|
-}
|
|
|
+ if b.bw != nil {
|
|
|
+ return b.bw.Next(length)
|
|
|
+ }
|
|
|
|
|
|
-// Bytes returns the unread portion of the underlying buffer storage.
|
|
|
-func (b *Buffer) Bytes() []byte {
|
|
|
- return b.b.Bytes()
|
|
|
-}
|
|
|
-
|
|
|
-// Read reads structured binary data from r into data.
|
|
|
-func read(r *bytes.Buffer, data interface{}) error {
|
|
|
- order := binary.LittleEndian
|
|
|
-
|
|
|
- var b [8]byte
|
|
|
- if n := intDataSize(data); n != 0 {
|
|
|
- bs := b[:n]
|
|
|
-
|
|
|
- if _, err := readFull(r, bs); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- switch data := data.(type) {
|
|
|
- case *bool:
|
|
|
- *data = bs[0] != 0
|
|
|
- case *int8:
|
|
|
- *data = int8(bs[0])
|
|
|
- case *uint8:
|
|
|
- *data = bs[0]
|
|
|
- case *int16:
|
|
|
- *data = int16(order.Uint16(bs))
|
|
|
- case *uint16:
|
|
|
- *data = order.Uint16(bs)
|
|
|
- case *int32:
|
|
|
- *data = int32(order.Uint32(bs))
|
|
|
- case *uint32:
|
|
|
- *data = order.Uint32(bs)
|
|
|
- case *int64:
|
|
|
- *data = int64(order.Uint64(bs))
|
|
|
- case *uint64:
|
|
|
- *data = order.Uint64(bs)
|
|
|
- case *float32:
|
|
|
- *data = math.Float32frombits(order.Uint32(bs))
|
|
|
- case *float64:
|
|
|
- *data = math.Float64frombits(order.Uint64(bs))
|
|
|
- default:
|
|
|
- n = 0 // fast path doesn't apply
|
|
|
- }
|
|
|
-
|
|
|
- if n != 0 {
|
|
|
- return nil
|
|
|
- }
|
|
|
+ bytes := bytePool.Get(length)
|
|
|
+ defer bytePool.Put(bytes)
|
|
|
+
|
|
|
+ _, err := readFull(b.b, bytes)
|
|
|
+ if err != nil {
|
|
|
+ return bytes
|
|
|
}
|
|
|
|
|
|
- return NonPrimitiveTypeError
|
|
|
+ return bytes
|
|
|
}
|
|
|
|
|
|
-// read full is a bytes.Buffer specific implementation of ioutil.ReadFull() which
|
|
|
+// read full is a bufio.Reader specific implementation of io.ReadFull() which
|
|
|
// avoids escaping our stack allocated scratch bytes
|
|
|
-func readFull(r *bytes.Buffer, buf []byte) (n int, err error) {
|
|
|
+func readFull(r *bufio.Reader, buf []byte) (n int, err error) {
|
|
|
+
|
|
|
min := len(buf)
|
|
|
for n < min && err == nil {
|
|
|
var nn int
|
|
|
@@ -300,96 +397,6 @@ func readFull(r *bytes.Buffer, buf []byte) (n int, err error) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
-// Write writes the binary representation of data into w.
|
|
|
-func write(w *bytes.Buffer, data interface{}) error {
|
|
|
- order := binary.LittleEndian
|
|
|
-
|
|
|
- var b [8]byte
|
|
|
- if n := intDataSize(data); n != 0 {
|
|
|
- bs := b[:n]
|
|
|
-
|
|
|
- switch v := data.(type) {
|
|
|
- case *bool:
|
|
|
- if *v {
|
|
|
- bs[0] = 1
|
|
|
- } else {
|
|
|
- bs[0] = 0
|
|
|
- }
|
|
|
- case bool:
|
|
|
- if v {
|
|
|
- bs[0] = 1
|
|
|
- } else {
|
|
|
- bs[0] = 0
|
|
|
- }
|
|
|
- case *int8:
|
|
|
- bs[0] = byte(*v)
|
|
|
- case int8:
|
|
|
- bs[0] = byte(v)
|
|
|
- case *uint8:
|
|
|
- bs[0] = *v
|
|
|
- case uint8:
|
|
|
- bs[0] = v
|
|
|
- case *int16:
|
|
|
- order.PutUint16(bs, uint16(*v))
|
|
|
- case int16:
|
|
|
- order.PutUint16(bs, uint16(v))
|
|
|
- case *uint16:
|
|
|
- order.PutUint16(bs, *v)
|
|
|
- case uint16:
|
|
|
- order.PutUint16(bs, v)
|
|
|
- case *int32:
|
|
|
- order.PutUint32(bs, uint32(*v))
|
|
|
- case int32:
|
|
|
- order.PutUint32(bs, uint32(v))
|
|
|
- case *uint32:
|
|
|
- order.PutUint32(bs, *v)
|
|
|
- case uint32:
|
|
|
- order.PutUint32(bs, v)
|
|
|
- case *int64:
|
|
|
- order.PutUint64(bs, uint64(*v))
|
|
|
- case int64:
|
|
|
- order.PutUint64(bs, uint64(v))
|
|
|
- case *uint64:
|
|
|
- order.PutUint64(bs, *v)
|
|
|
- case uint64:
|
|
|
- order.PutUint64(bs, v)
|
|
|
- case *float32:
|
|
|
- order.PutUint32(bs, math.Float32bits(*v))
|
|
|
- case float32:
|
|
|
- order.PutUint32(bs, math.Float32bits(v))
|
|
|
- case *float64:
|
|
|
- order.PutUint64(bs, math.Float64bits(*v))
|
|
|
- case float64:
|
|
|
- order.PutUint64(bs, math.Float64bits(v))
|
|
|
- }
|
|
|
-
|
|
|
- _, err := w.Write(bs)
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- return NonPrimitiveTypeError
|
|
|
-}
|
|
|
-
|
|
|
-// intDataSize returns the size of the data required to represent the data when encoded.
|
|
|
-// It returns zero if the type cannot be implemented by the fast path in Read or Write.
|
|
|
-func intDataSize(data interface{}) int {
|
|
|
- switch data.(type) {
|
|
|
- case bool, int8, uint8, *bool, *int8, *uint8:
|
|
|
- return 1
|
|
|
- case int16, uint16, *int16, *uint16:
|
|
|
- return 2
|
|
|
- case int32, uint32, *int32, *uint32:
|
|
|
- return 4
|
|
|
- case int64, uint64, *int64, *uint64:
|
|
|
- return 8
|
|
|
- case float32, *float32:
|
|
|
- return 4
|
|
|
- case float64, *float64:
|
|
|
- return 8
|
|
|
- }
|
|
|
- return 0
|
|
|
-}
|
|
|
-
|
|
|
// Conversion from byte slice to string
|
|
|
func bytesToString(b []byte) string {
|
|
|
// This code will take the passed byte slice and cast it in-place into a string. By doing
|