Skip to content

Commit e5758c6

Browse files
accounts/abi: improve unpack performance (#31387)
Slightly improves performance of abi.Unpack ``` Before BenchmarkUnpack/0-14 5965714 210.9 ns/op 280 B/op 5 allocs/op BenchmarkUnpack/1-14 2148283 569.7 ns/op 688 B/op 16 allocs/op After: BenchmarkUnpack/0-14 7693365 151.2 ns/op 136 B/op 4 allocs/op BenchmarkUnpack/1-14 2261294 508.9 ns/op 544 B/op 15 allocs/op ``` replaces #31292 since I was unable to push to your branch @Exca-DK --------- Co-authored-by: Exca-DK <[email protected]>
1 parent f9f1172 commit e5758c6

File tree

2 files changed

+60
-13
lines changed

2 files changed

+60
-13
lines changed

accounts/abi/argument.go

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,18 @@ func (arguments Arguments) isTuple() bool {
7777
}
7878

7979
// Unpack performs the operation hexdata -> Go format.
80-
func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
80+
func (arguments Arguments) Unpack(data []byte) ([]any, error) {
8181
if len(data) == 0 {
8282
if len(arguments.NonIndexed()) != 0 {
8383
return nil, errors.New("abi: attempting to unmarshal an empty string while arguments are expected")
8484
}
85-
return make([]interface{}, 0), nil
85+
return make([]any, 0), nil
8686
}
8787
return arguments.UnpackValues(data)
8888
}
8989

9090
// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value.
91-
func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
91+
func (arguments Arguments) UnpackIntoMap(v map[string]any, data []byte) error {
9292
// Make sure map is not nil
9393
if v == nil {
9494
return errors.New("abi: cannot unpack into a nil map")
@@ -110,7 +110,7 @@ func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte)
110110
}
111111

112112
// Copy performs the operation go format -> provided struct.
113-
func (arguments Arguments) Copy(v interface{}, values []interface{}) error {
113+
func (arguments Arguments) Copy(v any, values []any) error {
114114
// make sure the passed value is arguments pointer
115115
if reflect.Ptr != reflect.ValueOf(v).Kind() {
116116
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
@@ -128,7 +128,7 @@ func (arguments Arguments) Copy(v interface{}, values []interface{}) error {
128128
}
129129

130130
// copyAtomic copies ( hexdata -> go ) a single value
131-
func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{}) error {
131+
func (arguments Arguments) copyAtomic(v any, marshalledValues any) error {
132132
dst := reflect.ValueOf(v).Elem()
133133
src := reflect.ValueOf(marshalledValues)
134134

@@ -139,7 +139,7 @@ func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{
139139
}
140140

141141
// copyTuple copies a batch of values from marshalledValues to v.
142-
func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface{}) error {
142+
func (arguments Arguments) copyTuple(v any, marshalledValues []any) error {
143143
value := reflect.ValueOf(v).Elem()
144144
nonIndexedArgs := arguments.NonIndexed()
145145

@@ -181,11 +181,17 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
181181
// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
182182
// without supplying a struct to unpack into. Instead, this method returns a list containing the
183183
// values. An atomic argument will be a list with one element.
184-
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
185-
nonIndexedArgs := arguments.NonIndexed()
186-
retval := make([]interface{}, 0, len(nonIndexedArgs))
187-
virtualArgs := 0
188-
for index, arg := range nonIndexedArgs {
184+
func (arguments Arguments) UnpackValues(data []byte) ([]any, error) {
185+
var (
186+
retval = make([]any, 0)
187+
virtualArgs = 0
188+
index = 0
189+
)
190+
191+
for _, arg := range arguments {
192+
if arg.Indexed {
193+
continue
194+
}
189195
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
190196
if err != nil {
191197
return nil, err
@@ -208,18 +214,19 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
208214
virtualArgs += getTypeSize(arg.Type)/32 - 1
209215
}
210216
retval = append(retval, marshalledValue)
217+
index++
211218
}
212219
return retval, nil
213220
}
214221

215222
// PackValues performs the operation Go format -> Hexdata.
216223
// It is the semantic opposite of UnpackValues.
217-
func (arguments Arguments) PackValues(args []interface{}) ([]byte, error) {
224+
func (arguments Arguments) PackValues(args []any) ([]byte, error) {
218225
return arguments.Pack(args...)
219226
}
220227

221228
// Pack performs the operation Go format -> Hexdata.
222-
func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
229+
func (arguments Arguments) Pack(args ...any) ([]byte, error) {
223230
// Make sure arguments match up and pack them
224231
abiArgs := arguments
225232
if len(args) != len(abiArgs) {

accounts/abi/unpack_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,46 @@ import (
3131
"github.com/stretchr/testify/require"
3232
)
3333

34+
func BenchmarkUnpack(b *testing.B) {
35+
testCases := []struct {
36+
def string
37+
packed string
38+
}{
39+
{
40+
def: `[{"type": "uint32"}]`,
41+
packed: "0000000000000000000000000000000000000000000000000000000000000001",
42+
},
43+
{
44+
def: `[{"type": "uint32[]"}]`,
45+
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
46+
"0000000000000000000000000000000000000000000000000000000000000002" +
47+
"0000000000000000000000000000000000000000000000000000000000000001" +
48+
"0000000000000000000000000000000000000000000000000000000000000002",
49+
},
50+
}
51+
for i, test := range testCases {
52+
b.Run(strconv.Itoa(i), func(b *testing.B) {
53+
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
54+
abi, err := JSON(strings.NewReader(def))
55+
if err != nil {
56+
b.Fatalf("invalid ABI definition %s: %v", def, err)
57+
}
58+
encb, err := hex.DecodeString(test.packed)
59+
if err != nil {
60+
b.Fatalf("invalid hex %s: %v", test.packed, err)
61+
}
62+
63+
b.ResetTimer()
64+
65+
var result any
66+
for range b.N {
67+
result, _ = abi.Unpack("method", encb)
68+
}
69+
_ = result
70+
})
71+
}
72+
}
73+
3474
// TestUnpack tests the general pack/unpack tests in packing_test.go
3575
func TestUnpack(t *testing.T) {
3676
t.Parallel()

0 commit comments

Comments
 (0)