[IO] dumping 2^63 works

This commit is contained in:
mratsim 2018-12-03 19:56:14 +01:00
parent df72a0896b
commit 2856378427
3 changed files with 75 additions and 41 deletions

View File

@ -40,6 +40,8 @@
import ./word_types import ./word_types
type Word* = Ct[uint64] type Word* = Ct[uint64]
type BaseType* = uint64 # Exported type for conversion in "normal integers"
const WordBitSize* = sizeof(Word) * 8 - 1 const WordBitSize* = sizeof(Word) * 8 - 1
## Limbs are 63-bit by default ## Limbs are 63-bit by default

View File

@ -119,7 +119,7 @@ func parseRawUint*(
# #
# ############################################################ # ############################################################
template bigEndian[T: uint16 or uint32 or uint64](outp: pointer, inp: ptr T) = template bigEndianXX[T: uint16 or uint32 or uint64](outp: pointer, inp: ptr T) =
when T is uint64: when T is uint64:
bigEndian64(outp, inp) bigEndian64(outp, inp)
elif T is uint32: elif T is uint32:
@ -127,7 +127,7 @@ template bigEndian[T: uint16 or uint32 or uint64](outp: pointer, inp: ptr T) =
elif T is uint16: elif T is uint16:
bigEndian16(outp, inp) bigEndian16(outp, inp)
template littleEndian[T: uint16 or uint32 or uint64](outp: pointer, inp: ptr T) = template littleEndianXX[T: uint16 or uint32 or uint64](outp: pointer, inp: ptr T) =
when T is uint64: when T is uint64:
littleEndian64(outp, inp) littleEndian64(outp, inp)
elif T is uint32: elif T is uint32:
@ -135,15 +135,55 @@ template littleEndian[T: uint16 or uint32 or uint64](outp: pointer, inp: ptr T)
elif T is uint16: elif T is uint16:
littleEndian16(outp, inp) littleEndian16(outp, inp)
func round_step_up(x: Natural, step: static Natural): int {.inline.} = func dumpRawUintLE(
## Round the input to the next multiple of "step" dst: var openarray[byte],
assert (step and (step - 1)) == 0, "Step must be a power of 2" src: BigInt) {.inline.}=
result = (x + step - 1) and not(step - 1) ## Serialize a bigint into its canonical big-endian representation
## I.e least significant bit is aligned to buffer boundary
var
src_idx, dst_idx = 0
acc: BaseType = 0
acc_len = 0
var tail = dst.len
while tail > 0:
let w = if src_idx < src.limbs.len: src[src_idx].BaseType
else: 0
inc src_idx
if acc_len == 0:
# Edge case, we need to refill the buffer to output 64-bit
# as we can only read 63-bit per word
acc = w
acc_len = WordBitSize
else:
let lo = (w shl acc_len) or acc
dec acc_len
acc = w shr (WordBitSize - acc_len)
if tail >= sizeof(Word):
# Unrolled copy
# debugecho src.repr
littleEndianXX(dst[dst_idx].addr, lo.unsafeAddr)
dst_idx += sizeof(Word)
tail -= sizeof(Word)
else:
# Process the tail
when cpuEndian == littleEndian:
# When requesting little-endian on little-endian platform
# we can just copy each byte
for i in dst_idx ..< tail:
dst[dst_idx] = byte(lo shr (i-dst_idx))
else:
# We need to copy from the end
for i in 0 ..< tail:
dst[dst_idx] = byte(lo shr (tail-i))
func dumpRawUint*( func dumpRawUint*(
dst: var openarray[byte], dst: var openarray[byte],
src: BigInt, src: BigInt,
endian: static Endianness) = order: static Endianness) =
## Serialize a bigint into its canonical big-endian or little endian ## Serialize a bigint into its canonical big-endian or little endian
## representation. ## representation.
## A destination buffer of size "BigInt.bits div 8" at minimum is needed. ## A destination buffer of size "BigInt.bits div 8" at minimum is needed.
@ -157,36 +197,8 @@ func dumpRawUint*(
when BigInt.bits == 0: when BigInt.bits == 0:
zeroMem(dst, dst.len) zeroMem(dst, dst.len)
else:
var
src_idx = 0
acc = Word(0)
acc_len = 0
template body(){.dirty.} = when order == littleEndian:
let w = if src_idx < src.limbs.len: src[src_idx] dumpRawUintLE(dst, src)
else: Word(0)
inc src_idx
if acc_len == 0:
# Edge case to avoid shifting by 0
acc = w
acc_len = WordBitSize
else: else:
let lo = (w shr acc_len) or acc {.error: "Not implemented at the moment".}
dec acc_len
acc = w shr (WordBitSize - acc_len)
when endian == bigEndian:
# We're counting down
bigEndian(dst[dst_idx - Word.sizeof], w.unsafeAddr)
else:
littleEndian(dst[dst_idx], w.unsafeAddr)
when endian == bigEndian:
discard # TODO
else:
let unroll_stop = round_step_up(dst.len, Word.sizeof)
for dst_idx in countup(0, unroll_stop - 1, Word.sizeof):
body()
# Process the tail - TODO

View File

@ -5,10 +5,11 @@
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). # * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import unittest, import unittest, random,
../constantine/[io, bigints] ../constantine/[io, bigints]
type T = uint64 randomize(0xDEADBEEF) # Random seed for reproducibility
type T = BaseType
suite "IO": suite "IO":
test "Parsing raw integers": test "Parsing raw integers":
@ -29,3 +30,22 @@ suite "IO":
check: check:
T(big[0]) == 0 T(big[0]) == 0
T(big[1]) == 1 T(big[1]) == 1
test "Parsing and dumping round-trip":
block: # "Little-endian"
let x = 1'u64 shl 63
let x_bytes = cast[array[8, byte]](x)
let big = parseRawUint(x_bytes, 64, littleEndian) # It's fine even on big-endian platform. We only want the byte-pattern
var r_bytes: array[8, byte]
dumpRawUint(r_bytes, big, littleEndian)
check: x_bytes == r_bytes
# block: # "Little-endian"
# let x = uint64 rand(0..high(int))
# let x_bytes = cast[array[8, byte]](x)
# let big = parseRawUint(x_bytes, 64, littleEndian) # It's fine even on big-endian platform. We only want the byte-pattern
# var r_bytes: array[8, byte]
# dumpRawUint(r_bytes, big, littleEndian)
# check: x_bytes == r_bytes