2019-09-16 09:18:13 +00:00
|
|
|
package io
|
2019-02-25 22:44:14 +00:00
|
|
|
|
|
|
|
import (
|
2021-08-06 07:52:24 +00:00
|
|
|
"bytes"
|
2019-02-25 22:44:14 +00:00
|
|
|
"encoding/binary"
|
|
|
|
"io"
|
2019-11-13 07:36:29 +00:00
|
|
|
"reflect"
|
2019-02-25 22:44:14 +00:00
|
|
|
)
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// BinWriter is a convenient wrapper around an io.Writer and err object.
|
|
|
|
// Used to simplify error handling when writing into an io.Writer
|
2019-10-22 14:56:03 +00:00
|
|
|
// from a struct with many fields.
|
2019-02-25 22:44:14 +00:00
|
|
|
type BinWriter struct {
|
2019-09-15 11:58:19 +00:00
|
|
|
w io.Writer
|
2019-02-25 22:44:14 +00:00
|
|
|
Err error
|
2021-08-06 07:45:27 +00:00
|
|
|
uv [9]byte
|
2019-02-25 22:44:14 +00:00
|
|
|
}
|
|
|
|
|
2019-09-15 11:58:19 +00:00
|
|
|
// NewBinWriterFromIO makes a BinWriter from io.Writer.
|
|
|
|
func NewBinWriterFromIO(iow io.Writer) *BinWriter {
|
2021-08-06 07:45:27 +00:00
|
|
|
return &BinWriter{w: iow}
|
2019-09-15 11:58:19 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// WriteU64LE writes a uint64 value into the underlying io.Writer in
|
2019-12-12 15:52:23 +00:00
|
|
|
// little-endian format.
|
|
|
|
func (w *BinWriter) WriteU64LE(u64 uint64) {
|
2021-08-06 07:45:27 +00:00
|
|
|
binary.LittleEndian.PutUint64(w.uv[:8], u64)
|
|
|
|
w.WriteBytes(w.uv[:8])
|
2019-12-12 15:52:23 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// WriteU32LE writes a uint32 value into the underlying io.Writer in
|
2019-12-12 15:52:23 +00:00
|
|
|
// little-endian format.
|
|
|
|
func (w *BinWriter) WriteU32LE(u32 uint32) {
|
2021-08-06 07:45:27 +00:00
|
|
|
binary.LittleEndian.PutUint32(w.uv[:4], u32)
|
|
|
|
w.WriteBytes(w.uv[:4])
|
2019-12-12 15:52:23 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// WriteU16LE writes a uint16 value into the underlying io.Writer in
|
2019-12-12 15:52:23 +00:00
|
|
|
// little-endian format.
|
|
|
|
func (w *BinWriter) WriteU16LE(u16 uint16) {
|
2021-08-06 07:45:27 +00:00
|
|
|
binary.LittleEndian.PutUint16(w.uv[:2], u16)
|
|
|
|
w.WriteBytes(w.uv[:2])
|
2019-12-12 15:52:23 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// WriteU16BE writes a uint16 value into the underlying io.Writer in
|
2019-12-12 15:52:23 +00:00
|
|
|
// big-endian format.
|
|
|
|
func (w *BinWriter) WriteU16BE(u16 uint16) {
|
2021-08-06 07:45:27 +00:00
|
|
|
binary.BigEndian.PutUint16(w.uv[:2], u16)
|
|
|
|
w.WriteBytes(w.uv[:2])
|
2019-12-12 15:52:23 +00:00
|
|
|
}
|
|
|
|
|
2019-12-12 17:17:50 +00:00
|
|
|
// WriteB writes a byte into the underlying io.Writer.
|
|
|
|
func (w *BinWriter) WriteB(u8 byte) {
|
2021-08-06 07:45:27 +00:00
|
|
|
w.uv[0] = u8
|
|
|
|
w.WriteBytes(w.uv[:1])
|
2019-12-12 15:52:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// WriteBool writes a boolean value into the underlying io.Writer encoded as
|
|
|
|
// a byte with values of 0 or 1.
|
|
|
|
func (w *BinWriter) WriteBool(b bool) {
|
|
|
|
var i byte
|
|
|
|
if b {
|
|
|
|
i = 1
|
|
|
|
}
|
2019-12-12 17:17:50 +00:00
|
|
|
w.WriteB(i)
|
2019-12-12 15:52:23 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 15:39:30 +00:00
|
|
|
// WriteArray writes a slice or an array arr into w. Note that nil slices and
|
2022-04-20 18:30:09 +00:00
|
|
|
// empty slices are gonna be treated the same resulting in an equal zero-length
|
2019-12-09 15:39:30 +00:00
|
|
|
// array encoded.
|
2023-04-03 10:34:24 +00:00
|
|
|
func (w *BinWriter) WriteArray(arr any) {
|
2019-11-13 07:36:29 +00:00
|
|
|
switch val := reflect.ValueOf(arr); val.Kind() {
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
if w.Err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-12-06 15:15:33 +00:00
|
|
|
typ := val.Type().Elem()
|
|
|
|
|
2019-11-13 07:36:29 +00:00
|
|
|
w.WriteVarUint(uint64(val.Len()))
|
2024-08-30 18:41:02 +00:00
|
|
|
for i := range val.Len() {
|
2019-12-06 15:15:33 +00:00
|
|
|
el, ok := val.Index(i).Interface().(encodable)
|
|
|
|
if !ok {
|
2019-12-09 13:57:25 +00:00
|
|
|
el, ok = val.Index(i).Addr().Interface().(encodable)
|
|
|
|
if !ok {
|
|
|
|
panic(typ.String() + " is not encodable")
|
|
|
|
}
|
2019-12-06 15:15:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
el.EncodeBinary(w)
|
2019-11-13 07:36:29 +00:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
panic("not an array")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// WriteVarUint writes a uint64 into the underlying writer using variable-length encoding.
|
2019-08-28 10:24:06 +00:00
|
|
|
func (w *BinWriter) WriteVarUint(val uint64) {
|
2019-08-29 10:24:03 +00:00
|
|
|
if w.Err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-08-06 07:45:27 +00:00
|
|
|
n := PutVarUint(w.uv[:], val)
|
2021-07-09 12:26:56 +00:00
|
|
|
w.WriteBytes(w.uv[:n])
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// PutVarUint puts a val in the varint form to the pre-allocated buffer.
|
2021-07-09 12:26:56 +00:00
|
|
|
func PutVarUint(data []byte, val uint64) int {
|
|
|
|
_ = data[8]
|
2019-02-25 22:44:14 +00:00
|
|
|
if val < 0xfd {
|
2021-07-09 12:26:56 +00:00
|
|
|
data[0] = byte(val)
|
|
|
|
return 1
|
2019-02-25 22:44:14 +00:00
|
|
|
}
|
|
|
|
if val < 0xFFFF {
|
2021-07-09 12:26:56 +00:00
|
|
|
data[0] = byte(0xfd)
|
|
|
|
binary.LittleEndian.PutUint16(data[1:], uint16(val))
|
|
|
|
return 3
|
2019-02-25 22:44:14 +00:00
|
|
|
}
|
|
|
|
if val < 0xFFFFFFFF {
|
2021-07-09 12:26:56 +00:00
|
|
|
data[0] = byte(0xfe)
|
|
|
|
binary.LittleEndian.PutUint32(data[1:], uint32(val))
|
|
|
|
return 5
|
2019-02-25 22:44:14 +00:00
|
|
|
}
|
|
|
|
|
2021-07-09 12:26:56 +00:00
|
|
|
data[0] = byte(0xff)
|
|
|
|
binary.LittleEndian.PutUint64(data[1:], val)
|
|
|
|
return 9
|
2019-02-25 22:44:14 +00:00
|
|
|
}
|
|
|
|
|
2019-11-22 10:34:06 +00:00
|
|
|
// WriteBytes writes a variable byte into the underlying io.Writer without prefix.
|
|
|
|
func (w *BinWriter) WriteBytes(b []byte) {
|
2019-12-06 14:40:47 +00:00
|
|
|
if w.Err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
_, w.Err = w.w.Write(b)
|
2019-11-22 10:06:32 +00:00
|
|
|
}
|
|
|
|
|
2019-11-22 10:34:06 +00:00
|
|
|
// WriteVarBytes writes a variable length byte array into the underlying io.Writer.
|
|
|
|
func (w *BinWriter) WriteVarBytes(b []byte) {
|
2019-08-28 10:24:06 +00:00
|
|
|
w.WriteVarUint(uint64(len(b)))
|
2019-12-06 14:40:47 +00:00
|
|
|
w.WriteBytes(b)
|
2019-02-25 22:44:14 +00:00
|
|
|
}
|
2019-03-17 18:26:35 +00:00
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// WriteString writes a variable length string into the underlying io.Writer.
|
2019-08-28 10:24:06 +00:00
|
|
|
func (w *BinWriter) WriteString(s string) {
|
2021-11-30 16:07:10 +00:00
|
|
|
w.WriteVarUint(uint64(len(s)))
|
|
|
|
if w.Err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
_, w.Err = io.WriteString(w.w, s)
|
2019-03-17 18:26:35 +00:00
|
|
|
}
|
2021-08-06 07:52:24 +00:00
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Grow tries to increase the underlying buffer capacity so that at least n bytes
|
2021-08-06 07:52:24 +00:00
|
|
|
// can be written without reallocation. If the writer is not a buffer, this is a no-op.
|
|
|
|
func (w *BinWriter) Grow(n int) {
|
|
|
|
if b, ok := w.w.(*bytes.Buffer); ok {
|
|
|
|
b.Grow(n)
|
|
|
|
}
|
|
|
|
}
|