Add support for float64

This commit is contained in:
Zahary Karadjov 2018-09-27 14:01:15 +03:00
parent def016260a
commit 2b28b1a236
3 changed files with 53 additions and 12 deletions

28
rlp.nim
View File

@ -176,10 +176,10 @@ proc isInt*(self: Rlp): bool =
return bytes[offset] != 0
return false
template maxBytes*(o: typedesc[Ordinal | uint64 | uint]): int = sizeof(o)
template maxBytes*(o: type[Ordinal | uint64 | uint]): int = sizeof(o)
proc toInt*(self: Rlp, IntType: typedesc): IntType =
# XXX: work-around a Nim issue with typedesc parameters
proc toInt*(self: Rlp, IntType: type): IntType =
# XXX: work-around a Nim issue with type parameters
type OutputType = IntType
mixin maxBytes, to
@ -289,14 +289,22 @@ proc readImpl(rlp: var Rlp, T: type Integer): Integer =
result = rlp.toInt(T)
rlp.skipElem
proc readImpl(rlp: var Rlp, T: typedesc[enum]): T =
proc readImpl(rlp: var Rlp, T: type[enum]): T =
result = type(result)(rlp.toInt(int))
rlp.skipElem
proc readImpl(rlp: var Rlp, T: typedesc[bool]): T =
proc readImpl(rlp: var Rlp, T: type bool): T =
result = rlp.toInt(int) != 0
rlp.skipElem
proc readImpl(rlp: var Rlp, T: type float64): T =
# This is not covered in the RLP spec, but Geth uses Go's
# `math.Float64bits`, which is defined here:
# https://github.com/gopherjs/gopherjs/blob/master/compiler/natives/src/math/math.go
let uint64bits = rlp.toInt(uint64)
var uint32parts = [uint32(uint64bits), uint32(uint64bits shr 32)]
return cast[ptr float64](unsafeAddr uint32parts)[]
proc readImpl[R, E](rlp: var Rlp, T: type array[R, E]): T =
mixin read
@ -345,7 +353,7 @@ proc readImpl[E](rlp: var Rlp, T: type seq[E]): T =
proc readImpl[E](rlp: var Rlp, T: type openarray[E]): seq[E] =
result = readImpl(rlp, seq[E])
proc readImpl(rlp: var Rlp, T: typedesc[object|tuple],
proc readImpl(rlp: var Rlp, T: type[object|tuple],
wrappedInList = wrapObjectsInList): T =
mixin enumerateRlpFields, read
@ -380,7 +388,7 @@ proc toNodes*(self: var Rlp): RlpNode =
# We define a single `read` template with a pretty low specifity
# score in order to facilitate easier overloading with user types:
template read*(rlp: var Rlp, T: typedesc): auto =
template read*(rlp: var Rlp, T: type): auto =
readImpl(rlp, T)
proc decode*(bytes: openarray[byte]): RlpNode =
@ -389,16 +397,16 @@ proc decode*(bytes: openarray[byte]): RlpNode =
rlp = rlpFromBytes(bytesCopy.toRange())
return rlp.toNodes
template decode*(bytes: BytesRange, T: typedesc): untyped =
template decode*(bytes: BytesRange, T: type): untyped =
mixin read
var rlp = rlpFromBytes(bytes)
rlp.read(T)
template decode*(bytes: openarray[byte], T: typedesc): T =
template decode*(bytes: openarray[byte], T: type): T =
var bytesCopy = @bytes
decode(bytesCopy.toRange, T)
template decode*(bytes: seq[byte], T: typedesc): untyped =
template decode*(bytes: seq[byte], T: type): untyped =
decode(bytes.toRange, T)
proc append*(writer: var RlpWriter; rlp: Rlp) =

View File

@ -185,6 +185,14 @@ proc appendInt(self; i: Integer) =
self.maybeClosePendingLists()
proc appendFloat(self; data: float64) =
# This is not covered in the RLP spec, but Geth uses Go's
# `math.Float64bits`, which is defined here:
# https://github.com/gopherjs/gopherjs/blob/master/compiler/natives/src/math/math.go
let uintWords = cast[ptr UncheckedArray[uint32]](unsafeAddr data)
let uint64bits = (uint64(uintWords[1]) shl 32) or uint64(uintWords[0])
self.appendInt(uint64bits)
template appendImpl(self; i: Integer) =
appendInt(self, i)
@ -238,7 +246,14 @@ proc appendImpl(self; data: tuple, wrapInList = wrapObjectsInList) {.inline.} =
# We define a single `append` template with a pretty low specifity
# score in order to facilitate easier overloading with user types:
template append*[T](self; data: T) = appendImpl(self, data)
template append*[T](self; data: T) =
when data is float64:
# XXX: This works around an overloading bug.
# Apparently, integer literals will be converted to `float64`
# values with higher precedence than the generic match to Integer
appendFloat(self, data)
else:
appendImpl(self, data)
proc initRlpList*(listSize: int): RlpWriter =
result = initRlpWriter()

View File

@ -1,5 +1,5 @@
import
unittest, strutils,
math, unittest, strutils,
rlp, util/json_testing
proc q(s: string): string = "\"" & s & "\""
@ -172,3 +172,21 @@ test "empty byte arrays":
rlp = rlpFromBytes rlp.encode("").toRange
b = rlp.toBytes
check $b == "R[]"
test "encode/decode floats":
for f in [high(float64), low(float64), 0.1, 122.23,
103487315.128934,
1943935743563457201.391754032785692,
0, -0,
Inf, NegInf, NaN]:
template isNaN(n): bool =
classify(n) == fcNaN
template chk(input) =
let restored = decode(encode(input), float64)
check restored == input or (input.isNaN and restored.isNaN)
chk f
chk -f