mirror of
https://github.com/status-im/nim-rlp.git
synced 2025-02-19 17:34:26 +00:00
Add support for float64
This commit is contained in:
parent
def016260a
commit
2b28b1a236
28
rlp.nim
28
rlp.nim
@ -176,10 +176,10 @@ proc isInt*(self: Rlp): bool =
|
|||||||
return bytes[offset] != 0
|
return bytes[offset] != 0
|
||||||
return false
|
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 =
|
proc toInt*(self: Rlp, IntType: type): IntType =
|
||||||
# XXX: work-around a Nim issue with typedesc parameters
|
# XXX: work-around a Nim issue with type parameters
|
||||||
type OutputType = IntType
|
type OutputType = IntType
|
||||||
mixin maxBytes, to
|
mixin maxBytes, to
|
||||||
|
|
||||||
@ -289,14 +289,22 @@ proc readImpl(rlp: var Rlp, T: type Integer): Integer =
|
|||||||
result = rlp.toInt(T)
|
result = rlp.toInt(T)
|
||||||
rlp.skipElem
|
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))
|
result = type(result)(rlp.toInt(int))
|
||||||
rlp.skipElem
|
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
|
result = rlp.toInt(int) != 0
|
||||||
rlp.skipElem
|
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 =
|
proc readImpl[R, E](rlp: var Rlp, T: type array[R, E]): T =
|
||||||
mixin read
|
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] =
|
proc readImpl[E](rlp: var Rlp, T: type openarray[E]): seq[E] =
|
||||||
result = readImpl(rlp, 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 =
|
wrappedInList = wrapObjectsInList): T =
|
||||||
mixin enumerateRlpFields, read
|
mixin enumerateRlpFields, read
|
||||||
|
|
||||||
@ -380,7 +388,7 @@ proc toNodes*(self: var Rlp): RlpNode =
|
|||||||
|
|
||||||
# We define a single `read` template with a pretty low specifity
|
# We define a single `read` template with a pretty low specifity
|
||||||
# score in order to facilitate easier overloading with user types:
|
# 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)
|
readImpl(rlp, T)
|
||||||
|
|
||||||
proc decode*(bytes: openarray[byte]): RlpNode =
|
proc decode*(bytes: openarray[byte]): RlpNode =
|
||||||
@ -389,16 +397,16 @@ proc decode*(bytes: openarray[byte]): RlpNode =
|
|||||||
rlp = rlpFromBytes(bytesCopy.toRange())
|
rlp = rlpFromBytes(bytesCopy.toRange())
|
||||||
return rlp.toNodes
|
return rlp.toNodes
|
||||||
|
|
||||||
template decode*(bytes: BytesRange, T: typedesc): untyped =
|
template decode*(bytes: BytesRange, T: type): untyped =
|
||||||
mixin read
|
mixin read
|
||||||
var rlp = rlpFromBytes(bytes)
|
var rlp = rlpFromBytes(bytes)
|
||||||
rlp.read(T)
|
rlp.read(T)
|
||||||
|
|
||||||
template decode*(bytes: openarray[byte], T: typedesc): T =
|
template decode*(bytes: openarray[byte], T: type): T =
|
||||||
var bytesCopy = @bytes
|
var bytesCopy = @bytes
|
||||||
decode(bytesCopy.toRange, T)
|
decode(bytesCopy.toRange, T)
|
||||||
|
|
||||||
template decode*(bytes: seq[byte], T: typedesc): untyped =
|
template decode*(bytes: seq[byte], T: type): untyped =
|
||||||
decode(bytes.toRange, T)
|
decode(bytes.toRange, T)
|
||||||
|
|
||||||
proc append*(writer: var RlpWriter; rlp: Rlp) =
|
proc append*(writer: var RlpWriter; rlp: Rlp) =
|
||||||
|
@ -185,6 +185,14 @@ proc appendInt(self; i: Integer) =
|
|||||||
|
|
||||||
self.maybeClosePendingLists()
|
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) =
|
template appendImpl(self; i: Integer) =
|
||||||
appendInt(self, i)
|
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
|
# We define a single `append` template with a pretty low specifity
|
||||||
# score in order to facilitate easier overloading with user types:
|
# 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 =
|
proc initRlpList*(listSize: int): RlpWriter =
|
||||||
result = initRlpWriter()
|
result = initRlpWriter()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import
|
import
|
||||||
unittest, strutils,
|
math, unittest, strutils,
|
||||||
rlp, util/json_testing
|
rlp, util/json_testing
|
||||||
|
|
||||||
proc q(s: string): string = "\"" & s & "\""
|
proc q(s: string): string = "\"" & s & "\""
|
||||||
@ -172,3 +172,21 @@ test "empty byte arrays":
|
|||||||
rlp = rlpFromBytes rlp.encode("").toRange
|
rlp = rlpFromBytes rlp.encode("").toRange
|
||||||
b = rlp.toBytes
|
b = rlp.toBytes
|
||||||
check $b == "R[]"
|
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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user