mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-29 20:51:45 +00:00
Added README docs for account/binary and renamed UInt -> Uint etc.
This commit is contained in:
167
binary/README.md
Normal file
167
binary/README.md
Normal file
@ -0,0 +1,167 @@
|
||||
# `tendermint/binary`
|
||||
|
||||
The `binary` submodule encodes primary types and structs into bytes.
|
||||
|
||||
## Primary types
|
||||
|
||||
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`
|
||||
```go
|
||||
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`
|
||||
```go
|
||||
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:
|
||||
```go
|
||||
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:
|
||||
```go
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
```go
|
||||
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.
|
||||
|
||||
```go
|
||||
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:
|
||||
|
||||
```go
|
||||
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.
|
||||
|
||||
```go
|
||||
|
||||
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,
|
||||
})
|
||||
```
|
Reference in New Issue
Block a user