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).