-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add toBytes proc #128
base: master
Are you sure you want to change the base?
Add toBytes proc #128
Conversation
Thanks for your contribution! The algorithm looks fine to me. |
src/bigints.nim
Outdated
## Convert a `BigInt` to a byte-sequence. | ||
## The byte-sequence *does not* include a sign-bit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this "limbs-only" bytes serialization?
## Convert a `BigInt` to a byte-sequence. | |
## The byte-sequence *does not* include a sign-bit. | |
## Convert the limbs of a `BigInt` to a byte-sequence. | |
## Sign-bit must be stored separately. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think sign-bits only belong in hardware-registers and it's more common for arbitrary width integers in binary serializations to be tagged some other way if they are negative. If a sign bit really needs to be in there then it can be inserted in the buffer after toBytes
.
I pushed a test for |
Now with |
include tliterals | ||
include ./literals |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why rename this specific file and not the others? Since we overwrite the default nimble test
task, we shouldn't need the t
prefix anymore. Either way, this is unrelated to the rest of the PR, so please make a new PR for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok then.
doAssert buf == @[0x1'u8,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0xA] | ||
if not a.isZero: | ||
result = newSeq[byte](a.limbs.len shl 2) | ||
var i: int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var i: int | |
var i = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nim initializes integers to zero.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know, but imo it's clearer to explicitly initialize them, rather than relying on implicit initialization.
var a, b: Bigint | ||
for x in 0..trials: | ||
a = rand(0.initBigInt..pow(2.initBigInt, x)) | ||
for endian in [bigEndian, littleEndian]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for endian in [bigEndian, littleEndian]: | |
for endian in [bigEndian, littleEndian]: |
bigEndian
and littleEndian
aren't in scope here. I wonder why the CI didn't run...
@@ -880,6 +880,22 @@ proc main() = | |||
doAssert pred(a, 3) == initBigInt(4) | |||
doAssert succ(a, 3) == initBigInt(10) | |||
|
|||
when nimvm: discard |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why doesn't this work in a static
context?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Endianness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean std/endians
is not available? I see.
for s in [24, 16, 8, 0]: | ||
result[i] = uint8(a.limbs[a.limbs.high] shr s) | ||
if result[0] != 0x00: inc(i) | ||
for l in countdown(a.limbs.high.pred, a.limbs.low): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for l in countdown(a.limbs.high.pred, a.limbs.low): | |
for l in countdown(a.limbs.high.pred, 0): |
low
on a seq
is always 0. You also used 0 below, so that's consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A low on an array is always 0, a low on an empty seq can be -1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already checked that a
is not zero (so a.limbs
is not empty), but I don't really care, we can keep it that way.
var | ||
li = result.limbs.low | ||
bi = buf.low | ||
while (bi + 4) < buf.len: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
while (bi + 4) < buf.len: | |
while (bi + 3) < buf.len: |
littleEndian32
uses buf[bi]
, buf[bi + 1]
, buf[bi + 2]
and buf[bi + 3]
, not buf[bi + 4]
.
inc(li) | ||
if bi < buf.len: | ||
var | ||
limb: uint32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
limb: uint32 | |
limb = 0'u32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nim initializes uint32 to zero.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicit is better than implicit. For integers, I may understand since we implicitly rely on the litteral 0 being an integer.
bi = buf.low | ||
block: | ||
var | ||
limb: uint32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
limb: uint32 | |
limb = 0'u32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nim initializes uint32 to zero.
for s in [24, 16, 8, 0]: | ||
result[i] = uint8(a.limbs[a.limbs.high] shr s) | ||
if result[0] != 0x00: inc(i) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for s in [24, 16, 8, 0]: | |
result[i] = uint8(a.limbs[a.limbs.high] shr s) | |
if result[0] != 0x00: inc(i) | |
# convert a.limbs[a.limbs.high] | |
let highLimb = a.limbs[a.limbs.high] | |
for s in [24, 16, 8, 0]: | |
result[i] = uint8(highLimb shr s) | |
if result[0] != 0x00: inc(i) | |
var | ||
limb: uint32 | ||
j = 4 - (buf.len and 3) | ||
while j < 4: | ||
limb = (limb shl 8) or buf[bi].uint32 | ||
inc(bi) | ||
inc(j) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var | |
limb: uint32 | |
j = 4 - (buf.len and 3) | |
while j < 4: | |
limb = (limb shl 8) or buf[bi].uint32 | |
inc(bi) | |
inc(j) | |
var limb = 0'u32 | |
for _ in 1..(buf.len and 0b11): | |
limb = (limb shl 8) or buf[bi].uint32 | |
inc(bi) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(buf.len and 3)
should be (buf.len and 0b11)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, edited.
I'm against exporting |
That would be adding more code to do something that is already implemented. |
Yes, but it's also uglier imo. |
I am for exporting isNegative only and implement equality with integers. |
Note that |
Thanks for the reminder. |
isNegative
andisZero
because it is easier to export here than to reimplement downstream.toBytes
proc for serializing. This is used by the CBOR package. Also easier to implement here than downstream.nimble test
for testing, some rely only on Nimble standards.