BigInt to bytes

A low-level way to do this is to call the low-level mpz_export function of GMP:

function to_bytes(n::BigInt; bigendian::Bool=true)
    bytes = Vector{UInt8}(undef, (Base.GMP.MPZ.sizeinbase(n, 2) + 7) ÷ 8)
    order = bigendian ? Cint(1) : Cint(-1)
    count = Ref{Csize_t}()
    @ccall "libgmp".__gmpz_export(bytes::Ptr{UInt8}, count::Ref{Csize_t}, order::Cint,
                1::Csize_t, 1::Cint, 0::Csize_t, n::Ref{BigInt})::Ptr{UInt8}
    @assert count[] ≤ length(bytes)
    return resize!(bytes, count[])
end

This is by far the fastest of the methods discussed so far:

julia> using BenchmarkTools

julia> n = factorial(big(999));

julia> @btime to_bytes($n, bigendian=true);
  2.120 μs (1 allocation: 1.14 KiB)

julia> @btime hex2bytes(string($n, base=16));
  5.632 μs (3 allocations: 3.39 KiB)

julia> @btime reverse!(digits(UInt8, $n; base=256));
  667.791 μs (10670 allocations: 1.79 MiB)

julia> to_bytes(n, bigendian=true) == reverse!(digits(UInt8, n; base=256))
true

(The digits function seems terribly slow, probably could be improved?)

Note that the hex2bytes method fails if string(n, base=16) has an odd number of digits (in which case you need to pad with a zero).

5 Likes