So, I've already said (https://forum.nim-lang.org/t/9478) I've been experimenting with some like NaN-boxing.
This time the question is about something similar, but simpler.
Let's say we could have simple 64-bit int values that could hold infinitely-large integers:
I'm using:
All of them have been working consistently, so there is no issue whatsoever on that part (at least nothing I have noticed after thousands of tests).
Now, the biggest challenge is getting a pointer to one of this Int objects (which are nothing but ref mpz_t), stuff it in an int64 and hold on to it - until we don't want to anymore...
Here's an experimental module I have created that does precisely this:
#=======================================
# Libraries
#=======================================
import hashes, strutils
import src/helpers/bignums
#=======================================
# Types
#=======================================
type
VInteger* = distinct int64
#=======================================
# Constants
#=======================================
const
IntegerMask* = 0x7FFFFFFFFFFFFFFF
IntegerBit* = 0x8000000000000000
#=======================================
# Helpers
#=======================================
template pack*(x: untyped): VInteger =
VInteger(x)
template packBig*(x: untyped, doCreate: static bool = true): VInteger =
when doCreate:
var r = newInt(x)
VInteger(IntegerBit or cast[int64](
when doCreate : addr r
else : addr x
)
)
func isBig*(x: VInteger): bool {.inline,enforceNoRaises.} =
(x.int64 and IntegerBit) != 0
func getBig*(x: VInteger): Int {.inline.} =
return (cast[ptr Int](x.int64 and IntegerMask))[]
#=======================================
# Methods
#=======================================
proc newVInteger*(i: int64): VInteger {.inline,enforceNoRaises.} =
if i < IntegerMask : pack(i)
else : packBig((int)i)
func newVInteger*(i: var Int): VInteger {.inline,enforceNoRaises.} =
packBig(i, doCreate=false)
proc newVInteger*(s: string): VInteger {.inline.} =
try:
newVInteger(parseInt(s))
except ValueError:
return packBig(s)
#=======================================
# Overloads
#=======================================
func `$`(x: VInteger): string {.inline,enforceNoRaises.} =
if isBig(x) : $(getBig(x))
else : $(x.int64)
func hash*(x: VInteger): Hash {.inline.} =
cast[Hash](x)
#=======================================
# Tests
#=======================================
when isMainModule:
let v1 = newVInteger(1)
let v2 = newVInteger("1231312312312312")
let v3 = newVInteger("234726384762384762384762384723634873264")
echo $(v1)
echo $(v2)
echo $(v3)
I compile with nim r -d:danger --mm:orc.
The problem comes with the v3 value (and its accompanying $(v3) which triggers a "Illegal storage access" error. And quite obviously so, since after stuffing the pointer in packBig our newInt in variable r seems to have been destroyed. If I bring r outside, declared as some global var, the whole thing seems to be working fine. But it's obviously not the point (just a verification that the whole logic is correct).
I have also tried using a GC_ref for this variable, but it's still not working.
So, the question is simple: How do I get this to work as intended?