2019-09-16 09:18:13 +00:00
|
|
|
package io
|
2019-02-25 22:44:14 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"io"
|
2019-11-13 07:36:29 +00:00
|
|
|
"reflect"
|
2019-02-25 22:44:14 +00:00
|
|
|
)
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// BinWriter is a convenient wrapper around a io.Writer and err object.
|
2019-03-17 18:26:35 +00:00
|
|
|
// Used to simplify error handling when writing into a 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
|
|
|
|
}
|
|
|
|
|
2019-09-15 11:58:19 +00:00
|
|
|
// NewBinWriterFromIO makes a BinWriter from io.Writer.
|
|
|
|
func NewBinWriterFromIO(iow io.Writer) *BinWriter {
|
|
|
|
return &BinWriter{w: iow}
|
|
|
|
}
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// WriteLE writes into the underlying io.Writer from an object v in little-endian format.
|
2019-08-28 10:24:06 +00:00
|
|
|
func (w *BinWriter) WriteLE(v interface{}) {
|
2019-02-25 22:44:14 +00:00
|
|
|
if w.Err != nil {
|
|
|
|
return
|
|
|
|
}
|
2019-09-15 11:58:19 +00:00
|
|
|
w.Err = binary.Write(w.w, binary.LittleEndian, v)
|
2019-02-25 22:44:14 +00:00
|
|
|
}
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// WriteBE writes into the underlying io.Writer from an object v in big-endian format.
|
2019-08-28 10:24:06 +00:00
|
|
|
func (w *BinWriter) WriteBE(v interface{}) {
|
2019-02-25 22:44:14 +00:00
|
|
|
if w.Err != nil {
|
|
|
|
return
|
|
|
|
}
|
2019-09-15 11:58:19 +00:00
|
|
|
w.Err = binary.Write(w.w, binary.BigEndian, v)
|
2019-02-25 22:44:14 +00:00
|
|
|
}
|
|
|
|
|
2019-11-13 07:36:29 +00:00
|
|
|
// WriteArray writes a slice or an array arr into w.
|
|
|
|
func (w *BinWriter) WriteArray(arr interface{}) {
|
|
|
|
switch val := reflect.ValueOf(arr); val.Kind() {
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
typ := val.Type().Elem()
|
|
|
|
method, ok := typ.MethodByName("EncodeBinary")
|
|
|
|
if !ok || !isEncodeBinaryMethod(method) {
|
|
|
|
panic(typ.String() + " does not have EncodeBinary(*BinWriter)")
|
|
|
|
}
|
|
|
|
|
|
|
|
if w.Err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.WriteVarUint(uint64(val.Len()))
|
|
|
|
for i := 0; i < val.Len(); i++ {
|
|
|
|
method := val.Index(i).MethodByName("EncodeBinary")
|
|
|
|
method.Call([]reflect.Value{reflect.ValueOf(w)})
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
panic("not an array")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func isEncodeBinaryMethod(method reflect.Method) bool {
|
|
|
|
t := method.Type
|
|
|
|
return t != nil &&
|
|
|
|
t.NumIn() == 2 && t.In(1) == reflect.TypeOf((*BinWriter)(nil)) &&
|
|
|
|
t.NumOut() == 0
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-02-25 22:44:14 +00:00
|
|
|
if val < 0xfd {
|
2019-09-15 11:58:19 +00:00
|
|
|
w.Err = binary.Write(w.w, binary.LittleEndian, uint8(val))
|
2019-02-25 22:44:14 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if val < 0xFFFF {
|
2019-09-15 11:58:19 +00:00
|
|
|
w.Err = binary.Write(w.w, binary.LittleEndian, byte(0xfd))
|
|
|
|
w.Err = binary.Write(w.w, binary.LittleEndian, uint16(val))
|
2019-02-25 22:44:14 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if val < 0xFFFFFFFF {
|
2019-09-15 11:58:19 +00:00
|
|
|
w.Err = binary.Write(w.w, binary.LittleEndian, byte(0xfe))
|
|
|
|
w.Err = binary.Write(w.w, binary.LittleEndian, uint32(val))
|
2019-02-25 22:44:14 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-09-15 11:58:19 +00:00
|
|
|
w.Err = binary.Write(w.w, binary.LittleEndian, byte(0xff))
|
|
|
|
w.Err = binary.Write(w.w, binary.LittleEndian, val)
|
2019-02-25 22:44:14 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// WriteBytes writes a variable length byte array into the underlying io.Writer.
|
2019-08-28 10:24:06 +00:00
|
|
|
func (w *BinWriter) WriteBytes(b []byte) {
|
|
|
|
w.WriteVarUint(uint64(len(b)))
|
|
|
|
w.WriteLE(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) {
|
|
|
|
w.WriteBytes([]byte(s))
|
2019-03-17 18:26:35 +00:00
|
|
|
}
|