From 1996043a6a11526f7aad266a72160620a72b1ef2 Mon Sep 17 00:00:00 2001 From: failfmi Date: Mon, 28 Nov 2022 20:00:24 +0200 Subject: [PATCH 1/2] feat: add empty --- empty.go | 20 ++++++++++++++++++++ empty_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 empty.go create mode 100644 empty_test.go diff --git a/empty.go b/empty.go new file mode 100644 index 0000000..6b8e370 --- /dev/null +++ b/empty.go @@ -0,0 +1,20 @@ +package goscale + +import "bytes" + +type Empty struct { +} + +func (e Empty) Encode(buffer *bytes.Buffer) { + encoder := Encoder{Writer: buffer} + + encoder.Write([]byte{}) +} + +func (e Empty) String() string { + return "" +} + +func DecodeEmpty() Empty { + return Empty{} +} diff --git a/empty_test.go b/empty_test.go new file mode 100644 index 0000000..1a7833e --- /dev/null +++ b/empty_test.go @@ -0,0 +1,33 @@ +package goscale + +import ( + "bytes" + "testing" +) + +func Test_EncodeEmpty(t *testing.T) { + var examples = []struct { + label string + input Empty + expect []byte + }{ + { + label: "Empty", + input: Empty{}, + expect: nil, + }, + } + + for _, e := range examples { + t.Run(e.label, func(t *testing.T) { + // given: + buffer := &bytes.Buffer{} + + // when: + e.input.Encode(buffer) + + // then: + assertEqual(t, buffer.Bytes(), e.expect) + }) + } +} From b6b11c63110c487f168a7e8fc15e903f123c7bed Mon Sep 17 00:00:00 2001 From: failfmi Date: Mon, 28 Nov 2022 20:01:11 +0200 Subject: [PATCH 2/2] feat: add result --- codec.go | 34 ++++++++++ option.go | 27 +------- option_test.go | 9 ++- result.go | 27 ++++++++ result_test.go | 173 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 241 insertions(+), 29 deletions(-) create mode 100644 result.go create mode 100644 result_test.go diff --git a/codec.go b/codec.go index 627a98d..f5588bf 100644 --- a/codec.go +++ b/codec.go @@ -8,6 +8,7 @@ Substrate Ref - https://docs.substrate.io/reference/scale-codec/ package goscale import ( + "bytes" "io" "strconv" ) @@ -51,3 +52,36 @@ func (dec Decoder) DecodeByte() byte { dec.Read(buf[:1]) return buf[0] } + +func decodeByType(i interface{}, buffer *bytes.Buffer) Encodable { + switch i.(type) { + case Bool: + return DecodeBool(buffer) + case U8: + return DecodeU8(buffer) + case I8: + return DecodeI8(buffer) + case U16: + return DecodeU16(buffer) + case I16: + return DecodeI16(buffer) + case U32: + return DecodeU32(buffer) + case I32: + return DecodeI32(buffer) + case U64: + return DecodeU64(buffer) + case I64: + return DecodeI64(buffer) + case Compact: + return DecodeCompact(buffer) + case Sequence[U8]: + return DecodeSequenceU8(buffer) + case Empty: + return DecodeEmpty() + //TODO: case Result[Encodable]: + //return DecodeResult() + default: + panic("type not found") + } +} diff --git a/option.go b/option.go index c4aed89..b43932f 100644 --- a/option.go +++ b/option.go @@ -28,32 +28,7 @@ func DecodeOption[T Encodable](dec Encodable, buffer *bytes.Buffer) Option[T] { } if b { - switch dec.(type) { - case Bool: - option.Value = DecodeBool(buffer) - case U8: - option.Value = DecodeU8(buffer) - case I8: - option.Value = DecodeI8(buffer) - case U16: - option.Value = DecodeU16(buffer) - case I16: - option.Value = DecodeI16(buffer) - case U32: - option.Value = DecodeU32(buffer) - case I32: - option.Value = DecodeI32(buffer) - case U64: - option.Value = DecodeU64(buffer) - case I64: - option.Value = DecodeI64(buffer) - case Compact: - option.Value = DecodeCompact(buffer) - case Sequence[U8]: - option.Value = DecodeSequenceU8(buffer) - default: - panic("type not found") - } + option.Value = decodeByType(dec, buffer) } return option diff --git a/option_test.go b/option_test.go index 609c445..827acd7 100644 --- a/option_test.go +++ b/option_test.go @@ -37,10 +37,12 @@ func Test_EncodeOption(t *testing.T) { {label: "Encode Option(true, I64(min))", input: Option[Encodable]{true, I64(math.MinInt64)}, expect: []byte{0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80}}, {label: "Encode Option(false, I64(min))", input: Option[Encodable]{false, I64(math.MinInt64)}, expect: []byte{0x0}}, - {label: "Encode Option(true, Compact(MaxUint64)", input: Option[Encodable]{true, Compact(math.MaxUint64)}, expect: []byte{0x01, 0x13, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {label: "Encode Option(true, Compact(MaxUint64)", input: Option[Encodable]{true, Compact(math.MaxUint64)}, expect: []byte{0x1, 0x13, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, {label: "Encode Option(true, empty Seq[U8])", input: Option[Encodable]{true, Sequence[U8]{}}, expect: []byte{0x1, 0x0}}, {label: "Encode Option(true, Seq[U8])", input: Option[Encodable]{true, Sequence[U8]{[]U8{42}}}, expect: []byte{0x1, 0x4, 0x2a}}, + + {label: "Encode Option(true, Result(true, Seq[U8])", input: Option[Encodable]{true, Result[Encodable]{true, Sequence[U8]{[]U8{42}}}}, expect: []byte{0x1, 0x1, 0x4, 0x2a}}, } for _, e := range examples { @@ -165,18 +167,19 @@ func Test_DecodeOption(t *testing.T) { }, { label: "Decode Compact(maxUint64)", - input: []byte{0x01, 0x13, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + input: []byte{0x1, 0x13, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, encodable: Compact(0), expect: Option[Encodable]{HasValue: true, Value: Compact(math.MaxUint64)}, bufferLenLeft: 0, }, { label: "Decode Seq[U8]", - input: []byte{0x01, 0x4, 0x2a}, + input: []byte{0x1, 0x4, 0x2a}, encodable: Sequence[U8]{}, expect: Option[Encodable]{HasValue: true, Value: Sequence[U8]{[]U8{42}}}, bufferLenLeft: 0, }, + // TODO: Decode Option> } for _, e := range examples { diff --git a/result.go b/result.go new file mode 100644 index 0000000..fb26841 --- /dev/null +++ b/result.go @@ -0,0 +1,27 @@ +package goscale + +import ( + "bytes" + "fmt" +) + +type Result[T Encodable] struct { + ok Bool + value Encodable +} + +func (r Result[T]) Encode(buffer *bytes.Buffer) { + r.ok.Encode(buffer) + r.value.Encode(buffer) +} + +func DecodeResult[T Encodable](dec Encodable, buffer *bytes.Buffer) Result[T] { + return Result[T]{ + ok: DecodeBool(buffer), + value: decodeByType(dec, buffer), + } +} + +func (r Result[T]) String() string { + return fmt.Sprintf(r.ok.String(), r.value.String()) +} diff --git a/result_test.go b/result_test.go new file mode 100644 index 0000000..02661d8 --- /dev/null +++ b/result_test.go @@ -0,0 +1,173 @@ +package goscale + +import ( + "bytes" + "math" + "testing" +) + +func Test_EncodeResult(t *testing.T) { + var examples = []struct { + label string + input Result[Encodable] + expect []byte + }{ + {label: "Encode Result(true, false)", input: Result[Encodable]{true, Bool(false)}, expect: []byte{0x1, 0x0}}, + {label: "Encode Result(true, true)", input: Result[Encodable]{true, Bool(true)}, expect: []byte{0x1, 0x1}}, + {label: "Encode Result(false, empty)", input: Result[Encodable]{false, Empty{}}, expect: []byte{0x0}}, + {label: "Encode Result(false, true)", input: Result[Encodable]{false, Bool(true)}, expect: []byte{0x0, 0x1}}, + + {label: "Encode Result(true, U8(max))", input: Result[Encodable]{true, U8(math.MaxUint8)}, expect: []byte{0x1, 0xff}}, + {label: "Encode Result(true, I8(min))", input: Result[Encodable]{true, I8(math.MinInt8)}, expect: []byte{0x1, 0x80}}, + {label: "Encode Result(true, I8(max))", input: Result[Encodable]{true, I8(math.MaxInt8)}, expect: []byte{0x1, 0x7f}}, + {label: "Encode Result(true, U16(max))", input: Result[Encodable]{true, U16(math.MaxUint16)}, expect: []byte{0x1, 0xff, 0xff}}, + {label: "Encode Result(true, I16(min))", input: Result[Encodable]{true, I16(math.MinInt16)}, expect: []byte{0x1, 0x00, 0x80}}, + {label: "Encode Result(true, U32(max))", input: Result[Encodable]{true, U32(math.MaxUint32)}, expect: []byte{0x1, 0xff, 0xff, 0xff, 0xff}}, + {label: "Encode Result(true, I32(min))", input: Result[Encodable]{true, I32(math.MinInt32)}, expect: []byte{0x1, 0x0, 0x0, 0x0, 0x80}}, + {label: "Encode Result(true, U64(max))", input: Result[Encodable]{true, U64(math.MaxUint64)}, expect: []byte{0x1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {label: "Encode Result(true, I64(min))", input: Result[Encodable]{true, I64(math.MinInt64)}, expect: []byte{0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80}}, + {label: "Encode Result(false, I64(min))", input: Result[Encodable]{false, I64(math.MinInt64)}, expect: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80}}, + + {label: "Encode Result(true, Compact(MaxUint64)", input: Result[Encodable]{true, Compact(math.MaxUint64)}, expect: []byte{0x01, 0x13, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + + {label: "Encode Result(true, empty Seq[U8])", input: Result[Encodable]{true, Sequence[U8]{}}, expect: []byte{0x1, 0x0}}, + {label: "Encode Result(true, Seq[U8])", input: Result[Encodable]{true, Sequence[U8]{[]U8{42}}}, expect: []byte{0x1, 0x4, 0x2a}}, + } + + for _, e := range examples { + t.Run(e.label, func(t *testing.T) { + // given: + buffer := &bytes.Buffer{} + + // when: + e.input.Encode(buffer) + + // then: + assertEqual(t, buffer.Bytes(), e.expect) + }) + } +} + +func Test_DecodeResult(t *testing.T) { + var examples = []struct { + label string + input []byte + encodable Encodable + bufferLenLeft int + expect Result[Encodable] + }{ + { + label: "Decode Result(false, empty)", + input: []byte{0x0}, + encodable: Empty{}, + bufferLenLeft: 0, + expect: Result[Encodable]{false, Empty{}}, + }, + { + label: "Decode Result(false, false)", + input: []byte{0x0, 0x0}, + encodable: Bool(false), + bufferLenLeft: 0, + expect: Result[Encodable]{false, Bool(false)}, + }, + { + label: "Decode Result(true,false)", + input: []byte{0x1, 0x0}, + encodable: Bool(false), + bufferLenLeft: 0, + expect: Result[Encodable]{true, Bool(false)}, + }, + { + label: "Decode Result(true,true)", + input: []byte{0x1, 0x1, 0x3}, + encodable: Bool(false), + bufferLenLeft: 1, + expect: Result[Encodable]{true, Bool(true)}, + }, + { + label: "Decode Result(true, U8(max))", + input: []byte{0x1, 0xff, 0xff}, + encodable: U8(0), + expect: Result[Encodable]{true, U8(math.MaxUint8)}, + bufferLenLeft: 1, + }, + { + label: "Decode Result(true, I8(min))", + input: []byte{0x1, 0x80}, + encodable: I8(0), + expect: Result[Encodable]{true, I8(math.MinInt8)}, + bufferLenLeft: 0, + }, + { + label: "Decode Result(true, U16(max))", + input: []byte{0x1, 0xff, 0xff}, + encodable: U16(0), + expect: Result[Encodable]{true, U16(math.MaxUint16)}, + bufferLenLeft: 0, + }, + { + label: "Decode Result(true, I16(min))", + input: []byte{0x1, 0x0, 0x80}, + encodable: I16(0), + expect: Result[Encodable]{true, I16(math.MinInt16)}, + bufferLenLeft: 0, + }, + { + label: "Decode Result(true, U32(max))", + input: []byte{0x1, 0xff, 0xff, 0xff, 0xff}, + encodable: U32(0), + expect: Result[Encodable]{true, U32(math.MaxUint32)}, + bufferLenLeft: 0, + }, + { + label: "Decode Result(true, I32(min))", + input: []byte{0x1, 0x0, 0x0, 0x0, 0x80}, + encodable: I32(0), + expect: Result[Encodable]{true, I32(math.MinInt32)}, + bufferLenLeft: 0, + }, + { + label: "Decode Result(true, U64(max))", + input: []byte{0x1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + encodable: U64(0), + expect: Result[Encodable]{true, U64(math.MaxUint64)}, + bufferLenLeft: 0, + }, + { + label: "Decode Result(true, I64(min))", + input: []byte{0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80}, + encodable: I64(0), + expect: Result[Encodable]{true, I64(math.MinInt64)}, + bufferLenLeft: 0, + }, + { + label: "Decode Compact(maxUint64)", + input: []byte{0x1, 0x13, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + encodable: Compact(0), + expect: Result[Encodable]{true, Compact(math.MaxUint64)}, + bufferLenLeft: 0, + }, + { + label: "Decode Seq[U8]", + input: []byte{0x1, 0x4, 0x2a}, + encodable: Sequence[U8]{}, + expect: Result[Encodable]{true, Sequence[U8]{[]U8{42}}}, + bufferLenLeft: 0, + }, + } + + for _, e := range examples { + t.Run(e.label, func(t *testing.T) { + // given: + buffer := &bytes.Buffer{} + buffer.Write(e.input) + + // when: + result := DecodeResult[Encodable](e.encodable, buffer) + + // then: + assertEqual(t, result, e.expect) + assertEqual(t, buffer.Len(), e.bufferLenLeft) + }) + } +}