Skip to content

Commit 175506e

Browse files
holimanfjl
andauthored
core/types, rlp: optimize derivesha (#21728)
This PR contains a minor optimization in derivesha, by exposing the RLP int-encoding and making use of it to write integers directly to a buffer (an RLP integer is known to never require more than 9 bytes total). rlp.AppendUint64 might be useful in other places too. The code assumes, just as before, that the hasher (a trie) will copy the key internally, which it does when doing keybytesToHex(key). Co-authored-by: Felix Lange <[email protected]>
1 parent 36bb7ac commit 175506e

File tree

3 files changed

+117
-12
lines changed

3 files changed

+117
-12
lines changed

core/types/derive_sha.go

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
package types
1818

1919
import (
20-
"bytes"
21-
2220
"github.com/ethereum/go-ethereum/common"
2321
"github.com/ethereum/go-ethereum/rlp"
2422
)
@@ -37,26 +35,24 @@ type Hasher interface {
3735

3836
func DeriveSha(list DerivableList, hasher Hasher) common.Hash {
3937
hasher.Reset()
40-
keybuf := new(bytes.Buffer)
4138

4239
// StackTrie requires values to be inserted in increasing
4340
// hash order, which is not the order that `list` provides
4441
// hashes in. This insertion sequence ensures that the
4542
// order is correct.
43+
44+
var buf []byte
4645
for i := 1; i < list.Len() && i <= 0x7f; i++ {
47-
keybuf.Reset()
48-
rlp.Encode(keybuf, uint(i))
49-
hasher.Update(keybuf.Bytes(), list.GetRlp(i))
46+
buf = rlp.AppendUint64(buf[:0], uint64(i))
47+
hasher.Update(buf, list.GetRlp(i))
5048
}
5149
if list.Len() > 0 {
52-
keybuf.Reset()
53-
rlp.Encode(keybuf, uint(0))
54-
hasher.Update(keybuf.Bytes(), list.GetRlp(0))
50+
buf = rlp.AppendUint64(buf[:0], 0)
51+
hasher.Update(buf, list.GetRlp(0))
5552
}
5653
for i := 0x80; i < list.Len(); i++ {
57-
keybuf.Reset()
58-
rlp.Encode(keybuf, uint(i))
59-
hasher.Update(keybuf.Bytes(), list.GetRlp(i))
54+
buf = rlp.AppendUint64(buf[:0], uint64(i))
55+
hasher.Update(buf, list.GetRlp(i))
6056
}
6157
return hasher.Hash()
6258
}

rlp/raw.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,74 @@ func readSize(b []byte, slen byte) (uint64, error) {
180180
}
181181
return s, nil
182182
}
183+
184+
// AppendUint64 appends the RLP encoding of i to b, and returns the resulting slice.
185+
func AppendUint64(b []byte, i uint64) []byte {
186+
if i == 0 {
187+
return append(b, 0x80)
188+
} else if i < 128 {
189+
return append(b, byte(i))
190+
}
191+
switch {
192+
case i < (1 << 8):
193+
return append(b, 0x81, byte(i))
194+
case i < (1 << 16):
195+
return append(b, 0x82,
196+
byte(i>>8),
197+
byte(i),
198+
)
199+
case i < (1 << 24):
200+
return append(b, 0x83,
201+
byte(i>>16),
202+
byte(i>>8),
203+
byte(i),
204+
)
205+
case i < (1 << 32):
206+
return append(b, 0x84,
207+
byte(i>>24),
208+
byte(i>>16),
209+
byte(i>>8),
210+
byte(i),
211+
)
212+
case i < (1 << 40):
213+
return append(b, 0x85,
214+
byte(i>>32),
215+
byte(i>>24),
216+
byte(i>>16),
217+
byte(i>>8),
218+
byte(i),
219+
)
220+
221+
case i < (1 << 48):
222+
return append(b, 0x86,
223+
byte(i>>40),
224+
byte(i>>32),
225+
byte(i>>24),
226+
byte(i>>16),
227+
byte(i>>8),
228+
byte(i),
229+
)
230+
case i < (1 << 56):
231+
return append(b, 0x87,
232+
byte(i>>48),
233+
byte(i>>40),
234+
byte(i>>32),
235+
byte(i>>24),
236+
byte(i>>16),
237+
byte(i>>8),
238+
byte(i),
239+
)
240+
241+
default:
242+
return append(b, 0x88,
243+
byte(i>>56),
244+
byte(i>>48),
245+
byte(i>>40),
246+
byte(i>>32),
247+
byte(i>>24),
248+
byte(i>>16),
249+
byte(i>>8),
250+
byte(i),
251+
)
252+
}
253+
}

rlp/raw_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"io"
2222
"reflect"
2323
"testing"
24+
"testing/quick"
2425
)
2526

2627
func TestCountValues(t *testing.T) {
@@ -239,3 +240,40 @@ func TestReadSize(t *testing.T) {
239240
}
240241
}
241242
}
243+
244+
func TestAppendUint64(t *testing.T) {
245+
tests := []struct {
246+
input uint64
247+
slice []byte
248+
output string
249+
}{
250+
{0, nil, "80"},
251+
{1, nil, "01"},
252+
{2, nil, "02"},
253+
{127, nil, "7F"},
254+
{128, nil, "8180"},
255+
{129, nil, "8181"},
256+
{0xFFFFFF, nil, "83FFFFFF"},
257+
{127, []byte{1, 2, 3}, "0102037F"},
258+
{0xFFFFFF, []byte{1, 2, 3}, "01020383FFFFFF"},
259+
}
260+
261+
for _, test := range tests {
262+
x := AppendUint64(test.slice, test.input)
263+
if !bytes.Equal(x, unhex(test.output)) {
264+
t.Errorf("AppendUint64(%v, %d): got %x, want %s", test.slice, test.input, x, test.output)
265+
}
266+
}
267+
}
268+
269+
func TestAppendUint64Random(t *testing.T) {
270+
fn := func(i uint64) bool {
271+
enc, _ := EncodeToBytes(i)
272+
encAppend := AppendUint64(nil, i)
273+
return bytes.Equal(enc, encAppend)
274+
}
275+
config := quick.Config{MaxCountScale: 50}
276+
if err := quick.Check(fn, &config); err != nil {
277+
t.Fatal(err)
278+
}
279+
}

0 commit comments

Comments
 (0)