mirror of https://github.com/status-im/nim-eth.git
optimize using two pass
This commit is contained in:
parent
ad04a73c1b
commit
c25c24551c
|
@ -10,6 +10,10 @@ type
|
||||||
RlpWriter* = object
|
RlpWriter* = object
|
||||||
pendingLists: seq[tuple[remainingItems, startPos: int]]
|
pendingLists: seq[tuple[remainingItems, startPos: int]]
|
||||||
output: seq[byte]
|
output: seq[byte]
|
||||||
|
outStreamLen: int
|
||||||
|
listPrefixBytes: seq[int]
|
||||||
|
fillLevel: int
|
||||||
|
dryRun: bool
|
||||||
|
|
||||||
RlpIntBuf* = ArrayBuf[9, byte]
|
RlpIntBuf* = ArrayBuf[9, byte]
|
||||||
## Small buffer for holding a single RLP-encoded integer
|
## Small buffer for holding a single RLP-encoded integer
|
||||||
|
@ -21,44 +25,60 @@ func bytesNeeded(num: SomeUnsignedInt): int =
|
||||||
# Number of non-zero bytes in the big endian encoding
|
# Number of non-zero bytes in the big endian encoding
|
||||||
sizeof(num) - (num.leadingZeros() shr 3)
|
sizeof(num) - (num.leadingZeros() shr 3)
|
||||||
|
|
||||||
func writeBigEndian(outStream: var auto, number: SomeUnsignedInt,
|
func writeBigEndian(writer: var RlpWriter, number: SomeUnsignedInt,
|
||||||
lastByteIdx: int, numberOfBytes: int) =
|
numberOfBytes: int) {.inline.} =
|
||||||
|
if writer.dryRun:
|
||||||
|
writer.outStreamLen += numberOfBytes
|
||||||
|
else:
|
||||||
var n = number
|
var n = number
|
||||||
for i in countdown(lastByteIdx, lastByteIdx - numberOfBytes + 1):
|
for i in countdown(writer.fillLevel + numberOfBytes - 1, writer.fillLevel - 1):
|
||||||
outStream[i] = byte(n and 0xff)
|
writer.output[i] = byte(n and 0xff)
|
||||||
n = n shr 8
|
n = n shr 8
|
||||||
|
|
||||||
func writeBigEndian(outStream: var auto, number: SomeUnsignedInt,
|
writer.fillLevel += numberOfBytes
|
||||||
numberOfBytes: int) {.inline.} =
|
|
||||||
outStream.setLen(outStream.len + numberOfBytes)
|
|
||||||
outStream.writeBigEndian(number, outStream.len - 1, numberOfBytes)
|
|
||||||
|
|
||||||
func writeCount(bytes: var auto, count: int, baseMarker: byte) =
|
func writeCount(writer: var RlpWriter, count: int, baseMarker: byte) =
|
||||||
if count < THRESHOLD_LIST_LEN:
|
if count < THRESHOLD_LIST_LEN:
|
||||||
bytes.add(baseMarker + byte(count))
|
if writer.dryRun:
|
||||||
|
writer.outStreamLen += 1
|
||||||
else:
|
else:
|
||||||
let
|
writer.output[writer.fillLevel] = (baseMarker + byte(count))
|
||||||
origLen = bytes.len
|
writer.fillLevel += 1
|
||||||
lenPrefixBytes = uint64(count).bytesNeeded
|
else:
|
||||||
|
let lenPrefixBytes = uint64(count).bytesNeeded
|
||||||
|
|
||||||
bytes.setLen(origLen + lenPrefixBytes + 1)
|
if writer.dryRun:
|
||||||
bytes[origLen] = baseMarker + (THRESHOLD_LIST_LEN - 1) + byte(lenPrefixBytes)
|
writer.outStreamLen += lenPrefixBytes + 1
|
||||||
bytes.writeBigEndian(uint64(count), bytes.len - 1, lenPrefixBytes)
|
else:
|
||||||
|
writer.output[writer.fillLevel] = baseMarker + (THRESHOLD_LIST_LEN - 1) + byte(lenPrefixBytes)
|
||||||
|
writer.writeBigEndian(uint64(count), lenPrefixBytes)
|
||||||
|
|
||||||
func writeInt(outStream: var auto, i: SomeUnsignedInt) =
|
func writeInt(writer: var RlpWriter, i: SomeUnsignedInt) =
|
||||||
if i == typeof(i)(0):
|
if i == typeof(i)(0):
|
||||||
outStream.add BLOB_START_MARKER
|
if writer.dryRun:
|
||||||
|
writer.outStreamLen += 1
|
||||||
|
else:
|
||||||
|
writer.output[writer.fillLevel] = BLOB_START_MARKER
|
||||||
|
writer.fillLevel += 1
|
||||||
elif i < typeof(i)(BLOB_START_MARKER):
|
elif i < typeof(i)(BLOB_START_MARKER):
|
||||||
outStream.add byte(i)
|
if writer.dryRun:
|
||||||
|
writer.outStreamLen += 1
|
||||||
|
else:
|
||||||
|
writer.output[writer.fillLevel] = byte(i)
|
||||||
|
writer.fillLevel += 1
|
||||||
else:
|
else:
|
||||||
let bytesNeeded = i.bytesNeeded
|
let bytesNeeded = i.bytesNeeded
|
||||||
outStream.writeCount(bytesNeeded, BLOB_START_MARKER)
|
writer.writeCount(bytesNeeded, BLOB_START_MARKER)
|
||||||
outStream.writeBigEndian(i, bytesNeeded)
|
writer.writeBigEndian(i, bytesNeeded)
|
||||||
|
|
||||||
proc initRlpWriter*: RlpWriter =
|
proc initRlpWriter*: RlpWriter =
|
||||||
|
<<<<<<< Updated upstream
|
||||||
# Avoid allocations during initial write of small items - since the writer is
|
# Avoid allocations during initial write of small items - since the writer is
|
||||||
# expected to be short-lived, it doesn't hurt to allocate this buffer
|
# expected to be short-lived, it doesn't hurt to allocate this buffer
|
||||||
result.output = newSeqOfCap[byte](2)
|
result.output = newSeqOfCap[byte](2)
|
||||||
|
=======
|
||||||
|
result.output = newSeqOfCap[byte](2000)
|
||||||
|
>>>>>>> Stashed changes
|
||||||
|
|
||||||
proc maybeClosePendingLists(self: var RlpWriter) =
|
proc maybeClosePendingLists(self: var RlpWriter) =
|
||||||
while self.pendingLists.len > 0:
|
while self.pendingLists.len > 0:
|
||||||
|
@ -73,55 +93,68 @@ proc maybeClosePendingLists(self: var RlpWriter) =
|
||||||
self.pendingLists.setLen lastListIdx
|
self.pendingLists.setLen lastListIdx
|
||||||
|
|
||||||
# How many bytes were written since the start?
|
# How many bytes were written since the start?
|
||||||
let listLen = self.output.len - listStartPos
|
let listLen = self.fillLevel - listStartPos
|
||||||
|
|
||||||
# Compute the number of bytes required to write down the list length
|
# Compute the number of bytes required to write down the list length
|
||||||
let totalPrefixBytes = if listLen < int(THRESHOLD_LIST_LEN): 1
|
let totalPrefixBytes = if listLen < int(THRESHOLD_LIST_LEN): 1
|
||||||
else: int(uint64(listLen).bytesNeeded) + 1
|
else: int(uint64(listLen).bytesNeeded) + 1
|
||||||
|
|
||||||
# Shift the written data to make room for the prefix length
|
if self.dryRun:
|
||||||
self.output.setLen(self.output.len + totalPrefixBytes)
|
self.outStreamLen += totalPrefixBytes
|
||||||
|
self.listPrefixBytes.add(totalPrefixBytes)
|
||||||
moveMem(addr self.output[listStartPos + totalPrefixBytes],
|
else:
|
||||||
unsafeAddr self.output[listStartPos],
|
|
||||||
listLen)
|
|
||||||
|
|
||||||
# Write out the prefix length
|
# Write out the prefix length
|
||||||
if listLen < THRESHOLD_LIST_LEN:
|
if listLen < THRESHOLD_LIST_LEN:
|
||||||
self.output[listStartPos] = LIST_START_MARKER + byte(listLen)
|
self.output[listStartPos] = LIST_START_MARKER + byte(listLen)
|
||||||
else:
|
else:
|
||||||
let listLenBytes = totalPrefixBytes - 1
|
let listLenBytes = totalPrefixBytes - 1
|
||||||
self.output[listStartPos] = LEN_PREFIXED_LIST_MARKER + byte(listLenBytes)
|
self.output[listStartPos] = LEN_PREFIXED_LIST_MARKER + byte(listLenBytes)
|
||||||
self.output.writeBigEndian(uint64(listLen), listStartPos + listLenBytes, listLenBytes)
|
|
||||||
|
self.writeBigEndian(uint64(listLen), listLenBytes)
|
||||||
|
self.fillLevel -= listLenBytes
|
||||||
else:
|
else:
|
||||||
# The currently open list is not finished yet. Nothing to do.
|
# The currently open list is not finished yet. Nothing to do.
|
||||||
return
|
return
|
||||||
|
|
||||||
proc appendRawBytes*(self: var RlpWriter, bytes: openArray[byte]) =
|
proc appendRawBytes*(self: var RlpWriter, bytes: openArray[byte]) =
|
||||||
self.output.setLen(self.output.len + bytes.len)
|
if self.dryRun:
|
||||||
|
self.outStreamLen += bytes.len
|
||||||
|
else:
|
||||||
assign(self.output.toOpenArray(
|
assign(self.output.toOpenArray(
|
||||||
self.output.len - bytes.len, self.output.len - 1), bytes)
|
self.fillLevel, self.fillLevel + bytes.len - 1), bytes)
|
||||||
|
|
||||||
|
# increment the fill level
|
||||||
|
self.fillLevel += bytes.len
|
||||||
self.maybeClosePendingLists()
|
self.maybeClosePendingLists()
|
||||||
|
|
||||||
proc startList*(self: var RlpWriter, listSize: int) =
|
proc startList*(self: var RlpWriter, listSize: int) =
|
||||||
if listSize == 0:
|
if listSize == 0:
|
||||||
self.output.writeCount(0, LIST_START_MARKER)
|
self.writeCount(0, LIST_START_MARKER)
|
||||||
self.appendRawBytes([])
|
self.appendRawBytes([])
|
||||||
else:
|
else:
|
||||||
self.pendingLists.add((listSize, self.output.len))
|
# add a list to the stack with the starting position as the current fill level
|
||||||
|
self.pendingLists.add((listSize, self.fillLevel))
|
||||||
|
|
||||||
|
# if not in dry run mode shift the fill level by prefixBytes (calculated during dry run)
|
||||||
|
if not self.dryRun:
|
||||||
|
self.fillLevel += self.listPrefixBytes[self.pendingLists.len - 1]
|
||||||
|
|
||||||
proc appendBlob(self: var RlpWriter, data: openArray[byte]) =
|
proc appendBlob(self: var RlpWriter, data: openArray[byte]) =
|
||||||
if data.len == 1 and byte(data[0]) < BLOB_START_MARKER:
|
if data.len == 1 and byte(data[0]) < BLOB_START_MARKER:
|
||||||
self.output.add byte(data[0])
|
if self.dryRun:
|
||||||
|
self.outStreamLen += 1
|
||||||
|
else:
|
||||||
|
self.output[self.fillLevel] = byte(data[0])
|
||||||
|
self.fillLevel += 1
|
||||||
self.maybeClosePendingLists()
|
self.maybeClosePendingLists()
|
||||||
else:
|
else:
|
||||||
self.output.writeCount(data.len, BLOB_START_MARKER)
|
self.writeCount(data.len, BLOB_START_MARKER)
|
||||||
self.appendRawBytes(data)
|
self.appendRawBytes(data)
|
||||||
|
|
||||||
proc appendInt(self: var RlpWriter, i: SomeUnsignedInt) =
|
proc appendInt(self: var RlpWriter, i: SomeUnsignedInt) =
|
||||||
# this is created as a separate proc as an extra precaution against
|
# this is created as a separate proc as an extra precaution against
|
||||||
# any overloading resolution problems when matching the IntLike concept.
|
# any overloading resolution problems when matching the IntLike concept.
|
||||||
self.output.writeInt(i)
|
self.writeInt(i)
|
||||||
|
|
||||||
self.maybeClosePendingLists()
|
self.maybeClosePendingLists()
|
||||||
|
|
||||||
|
@ -280,13 +313,32 @@ func clear*(w: var RlpWriter) =
|
||||||
proc encode*[T](v: T): seq[byte] =
|
proc encode*[T](v: T): seq[byte] =
|
||||||
mixin append
|
mixin append
|
||||||
|
|
||||||
var writer = initRlpWriter()
|
var writer: RlpWriter
|
||||||
|
writer.dryRun = true
|
||||||
|
writer.append(v)
|
||||||
|
doAssert writer.pendingLists.len == 0, $writer.pendingLists.len & "Insufficient number of elements written to a started list"
|
||||||
|
writer.dryRun = false
|
||||||
|
writer.output = newSeqOfCap[byte](writer.outStreamLen)
|
||||||
|
writer.output.setLen(writer.outStreamLen)
|
||||||
writer.append(v)
|
writer.append(v)
|
||||||
move(writer.finish)
|
move(writer.finish)
|
||||||
|
|
||||||
func encodeInt*(i: SomeUnsignedInt): RlpIntBuf =
|
func encodeInt*(i: SomeUnsignedInt): RlpIntBuf =
|
||||||
var buf: RlpIntBuf
|
var buf: RlpIntBuf
|
||||||
buf.writeInt(i)
|
|
||||||
|
if i == typeof(i)(0):
|
||||||
|
buf.add BLOB_START_MARKER
|
||||||
|
elif i < typeof(i)(BLOB_START_MARKER):
|
||||||
|
buf.add byte(i)
|
||||||
|
else:
|
||||||
|
let bytesNeeded = i.bytesNeeded
|
||||||
|
|
||||||
|
var n = i
|
||||||
|
buf.add(BLOB_START_MARKER + byte(bytesNeeded))
|
||||||
|
for j in countdown(1, bytesNeeded + 1):
|
||||||
|
buf.add(byte(n and 0xff))
|
||||||
|
n = n shr 8
|
||||||
|
|
||||||
buf
|
buf
|
||||||
|
|
||||||
macro encodeList*(args: varargs[untyped]): seq[byte] =
|
macro encodeList*(args: varargs[untyped]): seq[byte] =
|
||||||
|
|
Loading…
Reference in New Issue