Jae Kwon 946fa21dc7 | 10 years ago | |
---|---|---|
.. | ||
README.md | 10 years ago | |
binary.go | 10 years ago | |
byteslice.go | 10 years ago | |
codec.go | 10 years ago | |
int.go | 10 years ago | |
reflect.go | 10 years ago | |
string.go | 10 years ago | |
time.go | 10 years ago | |
util.go | 10 years ago |
tendermint/binary
The binary
submodule encodes primary types and structs into bytes.
uint*, int*, string, time, byteslice and byteslice-slice types can be encoded and decoded with the following methods:
The following writes o uint64
to w io.Writer
, and increments n
and/or sets err
WriteUint64(o uint64, w io.Writer, n *int64, err *error)
// Typical usage:
buf, n, err := new(bytes.Buffer), new(int64), new(error)
WriteUint64(uint64(x), buf, n, err)
if *err != nil {
panic(err)
}
The following reads a uint64
from r io.Reader
, and increments n
and/or sets err
var o = ReadUint64(r io.Reader, n *int64, err *error)
Similar methods for uint32
, uint16
, uint8
, int64
, int32
, int16
, int8
exist.
Protobuf variable length encoding is done with uint
and int
types:
WriteUvarint(o uint, w io.Writer, n *int64, err *error)
var o = ReadUvarint(r io.Reader, n *int64, err *error)
Byteslices can be written with:
WriteByteSlice(bz []byte, w io.Writer, n *int64, err *error)
Byteslices (and all slices such as byteslice-slices) are prepended with
uvarint
encoded length, so ReadByteSlice()
knows how many bytes to read.
Note that there is no type information encoded -- the caller is assumed to know what types to decode.
Struct types can be automatically encoded with reflection. Unlike json-encoding, no field name or type information is encoded. Field values are simply encoded in order.
type Foo struct {
MyString string
MyUint32 uint32
myPrivateBytes []byte
}
foo := Foo{"my string", math.MaxUint32, []byte("my private bytes")}
buf, n, err := new(bytes.Buffer), new(int64), new(error)
WriteBinary(foo, buf, n, err)
// fmt.Printf("%X", buf.Bytes()) gives:
// 096D7920737472696E67FFFFFFFF
// 09: uvarint encoded length of string "my string"
// 6D7920737472696E67: bytes of string "my string"
// FFFFFFFF: bytes for MaxUint32
// Note that the unexported "myPrivateBytes" isn't encoded.
foo2 := ReadBinary(Foo{}, buf, n, err).(Foo)
// Or, to decode onto a pointer:
foo2 := ReadBinary(&Foo{}, buf, n, err).(*Foo)
WriteBinary and ReadBinary can encode/decode structs recursively. However, interface field values are a bit more complicated.
type Greeter interface {
Greet() string
}
type Dog struct{}
func (d Dog) Greet() string { return "Woof!" }
type Cat struct{}
func (c Cat) Greet() string { return "Meow!" }
type Foo struct {
Greeter
}
foo := Foo{Dog{}}
buf, n, err := new(bytes.Buffer), new(int64), new(error)
WriteBinary(foo, buf, n, err)
// This errors with "Unknown field type interface"
foo2 := ReadBinary(Foo{}, buf, n, err)
In the above example, ReadBinary()
fails because the Greeter
field for Foo{}
is ambiguous -- it could be either a Dog{}
or a Cat{}
, like a union structure.
In this case, the user must define a custom encoder/decoder as follows:
const (
GreeterTypeDog = byte(0x01)
GreeterTypeCat = byte(0x02)
)
func GreeterEncoder(o interface{}, w io.Writer, n *int64, err *error) {
switch o.(type) {
case Dog:
WriteByte(GreeterTypeDog, w, n, err)
WriteBinary(o, w, n, err)
case Cat:
WriteByte(GreeterTypeCat, w, n, err)
WriteBinary(o, w, n, err)
default:
*err = errors.New(fmt.Sprintf("Unknown greeter type %v", o))
}
}
func GreeterDecoder(r io.Reader, n *int64, err *error) interface{} {
switch t := ReadByte(r, n, err); t {
case GreeterTypeDog:
return ReadBinary(Dog{}, r, n, err)
case GreeterTypeCat:
return ReadBinary(Cat{}, r, n, err)
default:
*err = errors.New(fmt.Sprintf("Unknown greeter type byte %X", t))
return nil
}
}
// This tells the reflection system to use the custom encoder/decoder functions
// for encoding/decoding interface struct-field types.
var _ = RegisterType(&TypeInfo{
Type: reflect.TypeOf((*Greeter)(nil)).Elem(),
Encoder: GreeterEncoder,
Decoder: GreeterDecoder,
})
Sometimes you want to always prefix a globally unique type byte while encoding, whether or not the declared type is an interface or concrete type. In this case, you can declare a "TypeByte() byte" function on the struct (as a value receiver, not a pointer receiver!), and you can skip the declaration of a custom decoder.
type Dog struct{}
func (d Dog) TypeByte() byte { return GreeterTypeDog }
func (d Dog) Greet() string { return "Woof!" }
type Cat struct{}
func (c Cat) TypeByte() byte { return GreeterTypeCat }
func (c Cat) Greet() string { return "Meow!" }
var _ = RegisterType(&TypeInfo{
Type: reflect.TypeOf((*Greeter)(nil)).Elem(),
Decoder: GreeterDecoder,
})