[IO] dumping 2^63 works
This commit is contained in:
parent
df72a0896b
commit
2856378427
|
@ -40,6 +40,8 @@
|
|||
import ./word_types
|
||||
|
||||
type Word* = Ct[uint64]
|
||||
type BaseType* = uint64 # Exported type for conversion in "normal integers"
|
||||
|
||||
const WordBitSize* = sizeof(Word) * 8 - 1
|
||||
## 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:
|
||||
bigEndian64(outp, inp)
|
||||
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:
|
||||
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:
|
||||
littleEndian64(outp, inp)
|
||||
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:
|
||||
littleEndian16(outp, inp)
|
||||
|
||||
func round_step_up(x: Natural, step: static Natural): int {.inline.} =
|
||||
## Round the input to the next multiple of "step"
|
||||
assert (step and (step - 1)) == 0, "Step must be a power of 2"
|
||||
result = (x + step - 1) and not(step - 1)
|
||||
func dumpRawUintLE(
|
||||
dst: var openarray[byte],
|
||||
src: BigInt) {.inline.}=
|
||||
## 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*(
|
||||
dst: var openarray[byte],
|
||||
src: BigInt,
|
||||
endian: static Endianness) =
|
||||
order: static Endianness) =
|
||||
## Serialize a bigint into its canonical big-endian or little endian
|
||||
## representation.
|
||||
## A destination buffer of size "BigInt.bits div 8" at minimum is needed.
|
||||
|
@ -157,36 +197,8 @@ func dumpRawUint*(
|
|||
|
||||
when BigInt.bits == 0:
|
||||
zeroMem(dst, dst.len)
|
||||
|
||||
when order == littleEndian:
|
||||
dumpRawUintLE(dst, src)
|
||||
else:
|
||||
var
|
||||
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
|
||||
{.error: "Not implemented at the moment".}
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
# * 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.
|
||||
|
||||
import unittest,
|
||||
import unittest, random,
|
||||
../constantine/[io, bigints]
|
||||
|
||||
type T = uint64
|
||||
randomize(0xDEADBEEF) # Random seed for reproducibility
|
||||
type T = BaseType
|
||||
|
||||
suite "IO":
|
||||
test "Parsing raw integers":
|
||||
|
@ -29,3 +30,22 @@ suite "IO":
|
|||
check:
|
||||
T(big[0]) == 0
|
||||
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