I need two functions:
std::vector<uint8_t> bigint_to_bytes(cpp_int a);
cpp_int bytes_to_bigint(std::vector<uint8_t> const& a);
I googled and found the bigint_to_bytes can implement by backend().limbs()
, and do not know how to implement the bytes_to_bigint
.
How can I construct a cpp_int
by an array of byte?
The serialize code in boost is as following:
template <class Archive, class Int>
void do_serialize(Archive& ar, Int& val, mpl::false_ const&, mpl::false_ const&, mpl::true_ const&)
{
// Load.
// Non-trivial.
// Binary.
bool s;
std::size_t c;
ar & s;
ar & c;
val.resize(c, c);
ar.load_binary(val.limbs(), c * sizeof(limb_type));
if(s != val.sign())
val.negate();
val.normalize();
}
template <class Archive, class Int>
void do_serialize(Archive& ar, Int& val, mpl::true_ const&, mpl::false_ const&, mpl::true_ const&)
{
// Store.
// Non-trivial.
// Binary.
bool s = val.sign();
std::size_t c = val.size();
ar & s;
ar & c;
ar.save_binary(val.limbs(), c * sizeof(limb_type));
}
cpp_int_detail::do_serialize(ar, val, save_tag(), trivial_tag(), binary_tag());
Here has a detail about number sign.
I did some tests and found the cpp_int always use big endian, so even if on some platform, for example the sizeof(limb_type)==64 or ==32, I can use memcpy directly as following:
namespace {
using BigInt = mp::uint256_t;// boost::multiprecision::cpp_int;
// ignore sign, only support unsigned big integer
// it seems that cpp_int always use big endian
void BigIntToBytes(BigInt const& i, uint8_t* output, size_t len) {
auto count = i.backend().size();
auto tsize = sizeof(mp::limb_type);
auto copy_count = count * tsize;
if (len < count * tsize)
throw std::runtime_error("len < count * tsize");
memcpy(output, i.backend().limbs(), copy_count);
if (len > copy_count) {
memset(output + copy_count, 0, len - copy_count);
}
}
BigInt BytesToBigInt(uint8_t const* output, size_t len) {
if (len % sizeof(mp::limb_type))
throw std::runtime_error("len % sizeof(mp::limb_type)");
BigInt i;
uint32_t size = (uint32_t)len / sizeof(mp::limb_type);
i.backend().resize(size, size);
memcpy(i.backend().limbs(), output, len);
i.backend().normalize();
return i;
}
}
I'd suggest going the easy way and using the builtin backend serialization:
Bytes to_bytes(Bigint const& i) {
namespace io = boost::iostreams;
namespace ba = boost::archive;
std::vector<char> chars;
{
io::stream_buffer<io::back_insert_device<Chars> > bb(chars);
ba::binary_oarchive oa(bb, ba::no_header | ba::no_tracking | ba::no_codecvt);
oa << i;
}
return {chars.begin(), chars.end()};
}
Then the inverse is
Bigint to_bigint(Bytes const& v) {
namespace io = boost::iostreams;
namespace ba = boost::archive;
Bigint i;
{
std::vector<char> chars { v.begin(), v.end() };
io::stream_buffer<io::array_source> bb(chars.data(), chars.size());
ba::binary_iarchive ia(bb, ba::no_header | ba::no_tracking | ba::no_codecvt);
ia >> i;
}
return i;
}
The only "bummer" here is that Boost binary archives do not support streams having char_type uint8_t
which means you're stuck doing a copy.
You can get around this by introducing some ugly reinterpret_casts
(and requiring a limit on the effective serialized size). I'll leave that as the proverbial exercise to the reader.¹
Demo
Live On Coliru
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/cpp_int/serialize.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
#include <iostream>
namespace {
using Bigint = boost::multiprecision::cpp_int;
using Chars = std::vector<char>;
using Bytes = std::vector<uint8_t>;
Bytes to_bytes(Bigint const& i) {
namespace io = boost::iostreams;
namespace ba = boost::archive;
std::vector<char> chars;
{
io::stream_buffer<io::back_insert_device<Chars> > bb(chars);
ba::binary_oarchive oa(bb, ba::no_header | ba::no_tracking | ba::no_codecvt);
oa << i;
}
return {chars.begin(), chars.end()};
}
Bigint to_bigint(Bytes const& v) {
namespace io = boost::iostreams;
namespace ba = boost::archive;
Bigint i;
{
std::vector<char> chars { v.begin(), v.end() };
io::stream_buffer<io::array_source> bb(chars.data(), chars.size());
ba::binary_iarchive ia(bb, ba::no_header | ba::no_tracking | ba::no_codecvt);
ia >> i;
}
return i;
}
}
int main() {
Bigint i = 77;
i <<= 33;
return i == to_bigint(to_bytes(i))? 0 : 1;
}
Exits with exitcode 0.
¹ the other way around is easier: change to vector<int8_t>
instead: http://coliru.stacked-crooked.com/a/3dbff52c2daab58e