how to convert boost::multiprecision::cpp_int to&f

2019-08-04 18:42发布

问题:

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?

回答1:

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;
}
}


回答2:

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



标签: c++ boost