-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- no behavior changes - move encbuffer and encreader to `encbuffer.go` - copy-paste rlpgen package
- Loading branch information
1 parent
ad150b8
commit 459b8b5
Showing
26 changed files
with
2,421 additions
and
248 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
// Copyright 2014 The go-ethereum Authors | ||
// (original work) | ||
// Copyright 2024 The Erigon Authors | ||
// (modifications) | ||
// This file is part of Erigon. | ||
// | ||
// Erigon is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU Lesser General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// Erigon is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU Lesser General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU Lesser General Public License | ||
// along with Erigon. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
package rlp | ||
|
||
import ( | ||
"io" | ||
"reflect" | ||
"sync" | ||
) | ||
|
||
type encBuffer struct { | ||
str []byte // string data, contains everything except list headers | ||
lheads []listhead // all list headers | ||
lhsize int // sum of sizes of all encoded list headers | ||
sizebuf [9]byte // auxiliary buffer for uint encoding | ||
bufvalue reflect.Value // used in writeByteArrayCopy | ||
} | ||
|
||
// encbufs are pooled. | ||
var encBufferPool = sync.Pool{ | ||
New: func() interface{} { | ||
var bytes []byte | ||
return &encBuffer{bufvalue: reflect.ValueOf(&bytes).Elem()} | ||
}, | ||
} | ||
|
||
func (w *encBuffer) reset() { | ||
w.lhsize = 0 | ||
w.str = w.str[:0] | ||
w.lheads = w.lheads[:0] | ||
} | ||
|
||
// encBuffer implements io.Writer so it can be passed it into EncodeRLP. | ||
func (w *encBuffer) Write(b []byte) (int, error) { | ||
w.str = append(w.str, b...) | ||
return len(b), nil | ||
} | ||
|
||
func (w *encBuffer) encode(val interface{}) error { | ||
rval := reflect.ValueOf(val) | ||
writer, err := cachedWriter(rval.Type()) | ||
if err != nil { | ||
return err | ||
} | ||
return writer(rval, w) | ||
} | ||
|
||
func (w *encBuffer) encodeStringHeader(size int) { | ||
if size < 56 { | ||
w.str = append(w.str, EmptyStringCode+byte(size)) | ||
} else { | ||
sizesize := putint(w.sizebuf[1:], uint64(size)) | ||
w.sizebuf[0] = 0xB7 + byte(sizesize) | ||
w.str = append(w.str, w.sizebuf[:sizesize+1]...) | ||
} | ||
} | ||
|
||
func (w *encBuffer) encodeString(b []byte) { | ||
if len(b) == 1 && b[0] <= 0x7F { | ||
// fits single byte, no string header | ||
w.str = append(w.str, b[0]) | ||
} else { | ||
w.encodeStringHeader(len(b)) | ||
w.str = append(w.str, b...) | ||
} | ||
} | ||
|
||
func (w *encBuffer) encodeUint(i uint64) { | ||
if i == 0 { | ||
w.str = append(w.str, 0x80) | ||
} else if i < 128 { | ||
// fits single byte | ||
w.str = append(w.str, byte(i)) | ||
} else { | ||
s := putint(w.sizebuf[1:], i) | ||
w.sizebuf[0] = 0x80 + byte(s) | ||
w.str = append(w.str, w.sizebuf[:s+1]...) | ||
} | ||
} | ||
|
||
// list adds a new list header to the header stack. It returns the index | ||
// of the header. The caller must call listEnd with this index after encoding | ||
// the content of the list. | ||
func (w *encBuffer) list() int { | ||
w.lheads = append(w.lheads, listhead{offset: len(w.str), size: w.lhsize}) | ||
return len(w.lheads) - 1 | ||
} | ||
|
||
func (w *encBuffer) listEnd(index int) { | ||
lh := &w.lheads[index] | ||
lh.size = w.size() - lh.offset - lh.size | ||
if lh.size < 56 { | ||
w.lhsize++ // length encoded into kind tag | ||
} else { | ||
w.lhsize += 1 + intsize(uint64(lh.size)) | ||
} | ||
} | ||
|
||
func (w *encBuffer) size() int { | ||
return len(w.str) + w.lhsize | ||
} | ||
|
||
func (w *encBuffer) toBytes() []byte { | ||
out := make([]byte, w.size()) | ||
strpos := 0 | ||
pos := 0 | ||
for _, head := range w.lheads { | ||
// write string data before header | ||
n := copy(out[pos:], w.str[strpos:head.offset]) | ||
pos += n | ||
strpos += n | ||
// write the header | ||
enc := head.encode(out[pos:]) | ||
pos += len(enc) | ||
} | ||
// copy string data after the last list header | ||
copy(out[pos:], w.str[strpos:]) | ||
return out | ||
} | ||
|
||
func (w *encBuffer) toWriter(out io.Writer) (err error) { | ||
strpos := 0 | ||
for _, head := range w.lheads { | ||
// write string data before header | ||
if head.offset-strpos > 0 { | ||
n, nErr := out.Write(w.str[strpos:head.offset]) | ||
strpos += n | ||
if nErr != nil { | ||
return nErr | ||
} | ||
} | ||
// write the header | ||
enc := head.encode(w.sizebuf[:]) | ||
if _, wErr := out.Write(enc); wErr != nil { | ||
return wErr | ||
} | ||
} | ||
if strpos < len(w.str) { | ||
// write string data after the last list header | ||
_, err = out.Write(w.str[strpos:]) | ||
} | ||
return err | ||
} | ||
|
||
// encReader is the io.Reader returned by EncodeToReader. | ||
// It releases its encbuf at EOF. | ||
type encReader struct { | ||
buf *encBuffer // the buffer we're reading from. this is nil when we're at EOF. | ||
lhpos int // index of list header that we're reading | ||
strpos int // current position in string buffer | ||
piece []byte // next piece to be read | ||
} | ||
|
||
func (r *encReader) Read(b []byte) (n int, err error) { | ||
for { | ||
if r.piece = r.next(); r.piece == nil { | ||
// Put the encode buffer back into the pool at EOF when it | ||
// is first encountered. Subsequent calls still return EOF | ||
// as the error but the buffer is no longer valid. | ||
if r.buf != nil { | ||
encBufferPool.Put(r.buf) | ||
r.buf = nil | ||
} | ||
return n, io.EOF | ||
} | ||
nn := copy(b[n:], r.piece) | ||
n += nn | ||
if nn < len(r.piece) { | ||
// piece didn't fit, see you next time. | ||
r.piece = r.piece[nn:] | ||
return n, nil | ||
} | ||
r.piece = nil | ||
} | ||
} | ||
|
||
// next returns the next piece of data to be read. | ||
// it returns nil at EOF. | ||
func (r *encReader) next() []byte { | ||
switch { | ||
case r.buf == nil: | ||
return nil | ||
|
||
case r.piece != nil: | ||
// There is still data available for reading. | ||
return r.piece | ||
|
||
case r.lhpos < len(r.buf.lheads): | ||
// We're before the last list header. | ||
head := r.buf.lheads[r.lhpos] | ||
sizebefore := head.offset - r.strpos | ||
if sizebefore > 0 { | ||
// String data before header. | ||
p := r.buf.str[r.strpos:head.offset] | ||
r.strpos += sizebefore | ||
return p | ||
} | ||
r.lhpos++ | ||
return head.encode(r.buf.sizebuf[:]) | ||
|
||
case r.strpos < len(r.buf.str): | ||
// String data at the end, after all list headers. | ||
p := r.buf.str[r.strpos:] | ||
r.strpos = len(r.buf.str) | ||
return p | ||
|
||
default: | ||
return nil | ||
} | ||
} |
Oops, something went wrong.