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: * if the number is <= 0x7FFFFFFFFFFFFFFF (that is: it can fit in 63 bits), we set the first bit to `0` and "add" the number * if the number is greater than 63bits, then we create a GMP int (mpz_t), we set the first bit to `1` and store the pointer to this GMP int in the remaining 63 bits (obviously, we don't need that many) I'm using: * a slightly customized wrapper for GMP (<https://github.com/arturo-lang/arturo/blob/master/src/extras/gmp.nim>) and another one for MPFR (<https://github.com/arturo-lang/arturo/blob/master/src/extras/mpfr.nim>) * a helper for the two libraries (<https://github.com/arturo-lang/arturo/blob/master/src/helpers/bignums.nim>) 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) Run 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?
