[IO] dumping 2^63 works
This commit is contained in:
parent
df72a0896b
commit
2856378427
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
when order == littleEndian:
|
||||||
|
dumpRawUintLE(dst, src)
|
||||||
else:
|
else:
|
||||||
var
|
{.error: "Not implemented at the moment".}
|
||||||
src_idx = 0
|
|
||||||
acc = Word(0)
|
|
||||||
acc_len = 0
|
|
||||||
|
|
||||||
template body(){.dirty.} =
|
|
||||||
let w = if src_idx < src.limbs.len: src[src_idx]
|
|
||||||
else: Word(0)
|
|
||||||
inc src_idx
|
|
||||||
|
|
||||||
if acc_len == 0:
|
|
||||||
# Edge case to avoid shifting by 0
|
|
||||||
acc = w
|
|
||||||
acc_len = WordBitSize
|
|
||||||
else:
|
|
||||||
let lo = (w shr acc_len) or acc
|
|
||||||
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
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue