Skip to content

Commit

Permalink
Add compact-u16 encoder/decoder
Browse files Browse the repository at this point in the history
  • Loading branch information
gagliardetto committed Aug 21, 2021
1 parent 1684ce6 commit 26a59a9
Show file tree
Hide file tree
Showing 9 changed files with 620 additions and 40 deletions.
12 changes: 6 additions & 6 deletions compact-u16.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package bin

import "io"

// EncodeCompact16Length encodes a "Compact-u16" length into the provided slice pointer.
// EncodeCompactU16Length encodes a "Compact-u16" length into the provided slice pointer.
// See https://docs.solana.com/developing/programming-model/transactions#compact-u16-format
// See https://github.com/solana-labs/solana/blob/2ef2b6daa05a7cff057e9d3ef95134cee3e4045d/web3.js/src/util/shortvec-encoding.ts
func EncodeCompact16Length(bytes *[]byte, ln int) {
func EncodeCompactU16Length(bytes *[]byte, ln int) {
rem_len := ln
for {
elem := rem_len & 0x7f
Expand All @@ -20,8 +20,8 @@ func EncodeCompact16Length(bytes *[]byte, ln int) {
}
}

// DecodeCompact16Length decodes a "Compact-u16" length from the provided byte slice.
func DecodeCompact16Length(bytes []byte) int {
// DecodeCompactU16Length decodes a "Compact-u16" length from the provided byte slice.
func DecodeCompactU16Length(bytes []byte) int {
ln := 0
size := 0
for {
Expand All @@ -36,8 +36,8 @@ func DecodeCompact16Length(bytes []byte) int {
return ln
}

// DecodeCompact16LengthFromByteReader decodes a "Compact-u16" length from the provided io.ByteReader.
func DecodeCompact16LengthFromByteReader(reader io.ByteReader) (int, error) {
// DecodeCompactU16LengthFromByteReader decodes a "Compact-u16" length from the provided io.ByteReader.
func DecodeCompactU16LengthFromByteReader(reader io.ByteReader) (int, error) {
ln := 0
size := 0
for {
Expand Down
8 changes: 4 additions & 4 deletions compact-u16_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ func TestCompactU16(t *testing.T) {
candidates := []int{3, 0x7f, 0x7f + 1, 0x3fff, 0x3fff + 1}
for _, val := range candidates {
buf := make([]byte, 0)
EncodeCompact16Length(&buf, val)
EncodeCompactU16Length(&buf, val)

buf = append(buf, []byte("hello world")...)
decoded := DecodeCompact16Length(buf)
decoded := DecodeCompactU16Length(buf)

require.Equal(t, val, decoded)
}
for _, val := range candidates {
buf := make([]byte, 0)
EncodeCompact16Length(&buf, val)
EncodeCompactU16Length(&buf, val)

buf = append(buf, []byte("hello world")...)
decoded, err := DecodeCompact16LengthFromByteReader(bytes.NewReader(buf))
decoded, err := DecodeCompactU16LengthFromByteReader(bytes.NewReader(buf))
if err != nil {
panic(err)
}
Expand Down
22 changes: 16 additions & 6 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ func (dec *Decoder) IsBin() bool {
return dec.encoding.IsBin()
}

func (dec *Decoder) IsCompact16() bool {
return dec.encoding.IsCompact16()
func (dec *Decoder) IsCompactU16() bool {
return dec.encoding.IsCompactU16()
}

func NewDecoderWithEncoding(data []byte, enc Encoding) *Decoder {
Expand All @@ -94,8 +94,8 @@ func NewBorshDecoder(data []byte) *Decoder {
return NewDecoderWithEncoding(data, EncodingBorsh)
}

func NewCompact16Decoder(data []byte) *Decoder {
return NewDecoderWithEncoding(data, EncodingCompact16)
func NewCompactU16Decoder(data []byte) *Decoder {
return NewDecoderWithEncoding(data, EncodingCompactU16)
}

func (dec *Decoder) Decode(v interface{}) (err error) {
Expand All @@ -104,6 +104,8 @@ func (dec *Decoder) Decode(v interface{}) (err error) {
return dec.decodeWithOptionBin(v, nil)
case EncodingBorsh:
return dec.decodeWithOptionBorsh(v, nil)
case EncodingCompactU16:
return dec.decodeWithOptionCompactU16(v, nil)
default:
panic(fmt.Errorf("encoding not implemented: %s", dec.encoding))
}
Expand Down Expand Up @@ -216,8 +218,8 @@ func (dec *Decoder) ReadByteSlice() (out []byte, err error) {
return nil, err
}
length = int(val)
case EncodingCompact16:
val, err := DecodeCompact16LengthFromByteReader(dec)
case EncodingCompactU16:
val, err := DecodeCompactU16LengthFromByteReader(dec)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -481,6 +483,14 @@ func (dec *Decoder) ReadString() (out string, err error) {
return
}

func (dec *Decoder) ReadCompactU16Length() (int, error) {
val, err := DecodeCompactU16LengthFromByteReader(dec)
if traceEnabled {
zlog.Debug("read compact-u16 length", zap.Int("val", val))
}
return val, err
}

func (dec *Decoder) SkipBytes(count uint) error {
if uint(dec.Remaining()) < count {
return fmt.Errorf("request to skip %d but only %d bytes remain", count, dec.Remaining())
Expand Down
29 changes: 27 additions & 2 deletions decoder_bin.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,18 +170,43 @@ func (dec *Decoder) decodeBin(rv reflect.Value, opt *option) (err error) {
}

case reflect.Struct:
if err = dec.decodeStruct(rt, rv); err != nil {
if err = dec.decodeStructBin(rt, rv); err != nil {
return
}

case reflect.Map:
l, err := dec.ReadUvarint64()
if err != nil {
return err
}
if l == 0 {
// If the map has no content, keep it nil.
return nil
}
rv.Set(reflect.MakeMap(rt))
for i := 0; i < int(l); i++ {
key := reflect.New(rt.Key())
err := dec.decodeBin(key.Elem(), nil)
if err != nil {
return err
}
val := reflect.New(rt.Elem())
err = dec.decodeBin(val.Elem(), nil)
if err != nil {
return err
}
rv.SetMapIndex(key.Elem(), val.Elem())
}
return nil

default:
return fmt.Errorf("decode: unsupported type %q", rt)
}

return
}

func (dec *Decoder) decodeStruct(rt reflect.Type, rv reflect.Value) (err error) {
func (dec *Decoder) decodeStructBin(rt reflect.Type, rv reflect.Value) (err error) {
l := rv.NumField()

if traceEnabled {
Expand Down
Loading

0 comments on commit 26a59a9

Please sign in to comment.