Replace fc::uint128 with boost::multiprecision::uint128_t

This commit is contained in:
Peter Conrad 2019-05-07 08:23:47 +02:00
parent 648d969fb9
commit a3e2410091
16 changed files with 207 additions and 607 deletions

View file

@ -194,7 +194,7 @@ set( CMAKE_FIND_LIBRARY_SUFFIXES ${ORIGINAL_LIB_SUFFIXES} )
option( UNITY_BUILD OFF )
set( fc_sources
src/uint128.cpp
src/popcount.cpp
src/variant.cpp
src/exception.cpp
src/variant_object.cpp

View file

@ -1,9 +1,10 @@
#pragma once
#include <fc/io/raw_fwd.hpp>
#include <fc/crypto/sha512.hpp>
#include <fc/crypto/sha256.hpp>
#include <fc/uint128.hpp>
#include <fc/fwd.hpp>
#include <fc/uint128.hpp>
#include <vector>
namespace fc {
@ -15,9 +16,8 @@ namespace fc {
aes_encoder();
~aes_encoder();
void init( const fc::sha256& key, const fc::uint128& init_value );
void init( const fc::sha256& key, const uint128_t& init_value );
uint32_t encode( const char* plaintxt, uint32_t len, char* ciphertxt );
// uint32_t final_encode( char* ciphertxt );
private:
struct impl;
@ -29,9 +29,8 @@ namespace fc {
aes_decoder();
~aes_decoder();
void init( const fc::sha256& key, const fc::uint128& init_value );
void init( const fc::sha256& key, const uint128_t& init_value );
uint32_t decode( const char* ciphertxt, uint32_t len, char* plaintext );
// uint32_t final_decode( char* plaintext );
private:
struct impl;

View file

@ -41,36 +41,25 @@
// doesn't hold for any hash functions in this file.
#pragma once
#include <fc/uint128.hpp>
#include <stdlib.h> // for size_t.
#include <stdint.h>
#include <utility>
namespace fc {
template<typename T, size_t N>
class array;
class uint128;
// Hash function for a byte array.
uint64_t city_hash64(const char *buf, size_t len);
uint32_t city_hash32(const char *buf, size_t len);
#if SIZE_MAX > UINT32_MAX
inline size_t city_hash_size_t(const char *buf, size_t len) { return city_hash64(buf, len); }
#else
uint32_t city_hash32(const char *buf, size_t len);
inline size_t city_hash_size_t(const char *buf, size_t len) { return city_hash32(buf, len); }
#endif
// Hash function for a byte array.
uint128 city_hash128(const char *s, size_t len);
// Hash function for a byte array.
uint64_t city_hash_crc_64(const char *buf, size_t len);
// Hash function for a byte array.
uint128 city_hash_crc_128(const char *s, size_t len);
array<uint64_t,4> city_hash_crc_256(const char *s, size_t len);
uint128_t city_hash_crc_128(const char *s, size_t len);
} // namespace fc

View file

@ -1,5 +1,6 @@
#pragma once
#include <boost/endian/buffers.hpp>
#include <fc/io/raw_fwd.hpp>
#include <fc/fwd.hpp>
namespace fc
@ -9,16 +10,16 @@ class sha512
{
public:
sha512();
explicit sha512( const string& hex_str );
explicit sha512( const std::string& hex_str );
string str()const;
operator string()const;
std::string str()const;
operator std::string()const;
char* data()const;
size_t data_size()const { return 512 / 8; }
static sha512 hash( const char* d, uint32_t dlen );
static sha512 hash( const string& );
static sha512 hash( const std::string& );
template<typename T>
static sha512 hash( const T& t )

View file

@ -1,5 +1,6 @@
#pragma once
#include <boost/endian/buffers.hpp>
#include <fc/io/raw_variant.hpp>
#include <fc/reflect/reflect.hpp>
#include <fc/io/datastream.hpp>
@ -24,6 +25,24 @@ namespace fc {
pack( s, args..., _max_depth );
}
template<typename Stream>
inline void pack( Stream& s, const uint128_t& v, uint32_t _max_depth )
{
boost::endian::little_uint64_buf_at hilo[2];
hilo[0] = static_cast<uint64_t>(v >> 64);
hilo[1] = static_cast<uint64_t>(v & 0xffffffffffffffffULL);
s.write( hilo[0].data(), sizeof(hilo) );
}
template<typename Stream>
inline void unpack( Stream& s, uint128_t& v, uint32_t _max_depth )
{
boost::endian::little_uint64_buf_at hilo[2];
s.read( (char*) hilo, sizeof(hilo) );
v = hilo[0].value();
v <<= 64;
v += hilo[1].value();
}
template<typename Stream>
inline void pack( Stream& s, const fc::exception& e, uint32_t _max_depth )
{

View file

@ -1,10 +1,12 @@
#pragma once
#include <boost/endian/buffers.hpp>
#include <fc/config.hpp>
#include <fc/container/flat_fwd.hpp>
#include <fc/io/varint.hpp>
#include <fc/array.hpp>
#include <fc/safe.hpp>
#include <fc/uint128.hpp>
#include <deque>
#include <memory>
#include <vector>
@ -42,7 +44,10 @@ namespace fc {
template<typename Stream, typename IntType, typename EnumType>
inline void unpack( Stream& s, fc::enum_type<IntType,EnumType>& tp, uint32_t _max_depth=FC_PACK_MAX_DEPTH );
template<typename Stream>
inline void pack( Stream& s, const uint128_t& v, uint32_t _max_depth=FC_PACK_MAX_DEPTH );
template<typename Stream>
inline void unpack( Stream& s, uint128_t& v, uint32_t _max_depth=FC_PACK_MAX_DEPTH );
template<typename Stream, typename T> inline void pack( Stream& s, const std::set<T>& value, uint32_t _max_depth=FC_PACK_MAX_DEPTH );
template<typename Stream, typename T> inline void unpack( Stream& s, std::set<T>& value, uint32_t _max_depth=FC_PACK_MAX_DEPTH );

40
include/fc/popcount.hpp Normal file
View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2019 BitShares Blockchain Foundation, and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <fc/platform_independence.hpp>
#include <fc/uint128.hpp>
namespace fc {
#if _MSC_VER || __GNUC__ || __clang__
inline uint8_t popcount( uint64_t v ) { return __builtin_popcountll(v); }
#else
uint8_t popcount( uint64_t v );
#endif
uint8_t popcount( const fc::uint128_t& v );
} // namespace fc

View file

@ -11,8 +11,7 @@ namespace fc {
* integer overflow and default initialization. It will
* throw an exception on overflow conditions.
*
* It can only be used on built-in types. In particular,
* safe<uint128_t> is buggy and should not be used.
* It can only be used on built-in types.
*
* Implemented using spec from:
* https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow

View file

@ -1,158 +1,33 @@
/*
* Copyright (c) 2019 BitShares Blockchain Foundation, and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <limits>
#include <stdint.h>
#include <string>
#include <fc/config.hpp>
#include <fc/exception/exception.hpp>
#include <fc/crypto/city.hpp>
#include <fc/io/raw.hpp>
#include <boost/multiprecision/integer.hpp>
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable : 4244)
#endif //// _MSC_VER
namespace fc {
namespace fc
{
class bigint;
/**
* @brief an implementation of 128 bit unsigned integer
*
*/
class uint128
{
public:
uint128():hi(0),lo(0){}
uint128( uint32_t l ):hi(0),lo(l){}
uint128( int32_t l ):hi( -(l<0) ),lo(l){}
uint128( int64_t l ):hi( -(l<0) ),lo(l){}
uint128( uint64_t l ):hi(0),lo(l){}
uint128( const std::string& s );
uint128( uint64_t _h, uint64_t _l )
:hi(_h),lo(_l){}
uint128( const fc::bigint& bi );
operator std::string()const;
operator fc::bigint()const;
bool operator == ( const uint128& o )const{ return hi == o.hi && lo == o.lo; }
bool operator != ( const uint128& o )const{ return hi != o.hi || lo != o.lo; }
bool operator < ( const uint128& o )const { return (hi == o.hi) ? lo < o.lo : hi < o.hi; }
bool operator < ( const int64_t& o )const { return *this < uint128(o); }
bool operator !()const { return !(hi !=0 || lo != 0); }
uint128 operator -()const { return ++uint128( ~hi, ~lo ); }
uint128 operator ~()const { return uint128( ~hi, ~lo ); }
uint128& operator++() { hi += (++lo == 0); return *this; }
uint128& operator--() { hi -= (lo-- == 0); return *this; }
uint128 operator++(int) { auto tmp = *this; ++(*this); return tmp; }
uint128 operator--(int) { auto tmp = *this; --(*this); return tmp; }
uint128& operator |= ( const uint128& u ) { hi |= u.hi; lo |= u.lo; return *this; }
uint128& operator &= ( const uint128& u ) { hi &= u.hi; lo &= u.lo; return *this; }
uint128& operator ^= ( const uint128& u ) { hi ^= u.hi; lo ^= u.lo; return *this; }
uint128& operator <<= ( const uint128& u );
uint128& operator >>= ( const uint128& u );
uint128& operator += ( const uint128& u ) { const uint64_t old = lo; lo += u.lo; hi += u.hi + (lo < old); return *this; }
uint128& operator -= ( const uint128& u ) { return *this += -u; }
uint128& operator *= ( const uint128& u );
uint128& operator /= ( const uint128& u );
uint128& operator %= ( const uint128& u );
friend uint128 operator + ( const uint128& l, const uint128& r ) { return uint128(l)+=r; }
friend uint128 operator - ( const uint128& l, const uint128& r ) { return uint128(l)-=r; }
friend uint128 operator * ( const uint128& l, const uint128& r ) { return uint128(l)*=r; }
friend uint128 operator / ( const uint128& l, const uint128& r ) { return uint128(l)/=r; }
friend uint128 operator % ( const uint128& l, const uint128& r ) { return uint128(l)%=r; }
friend uint128 operator | ( const uint128& l, const uint128& r ) { return uint128(l)=(r); }
friend uint128 operator & ( const uint128& l, const uint128& r ) { return uint128(l)&=r; }
friend uint128 operator ^ ( const uint128& l, const uint128& r ) { return uint128(l)^=r; }
friend uint128 operator << ( const uint128& l, const uint128& r ) { return uint128(l)<<=r; }
friend uint128 operator >> ( const uint128& l, const uint128& r ) { return uint128(l)>>=r; }
friend bool operator > ( const uint128& l, const uint128& r ) { return r < l; }
friend bool operator > ( const uint128& l, const int64_t& r ) { return uint128(r) < l; }
friend bool operator > ( const int64_t& l, const uint128& r ) { return r < uint128(l); }
friend bool operator >= ( const uint128& l, const uint128& r ) { return l == r || l > r; }
friend bool operator >= ( const uint128& l, const int64_t& r ) { return l >= uint128(r); }
friend bool operator >= ( const int64_t& l, const uint128& r ) { return uint128(l) >= r; }
friend bool operator <= ( const uint128& l, const uint128& r ) { return l == r || l < r; }
friend bool operator <= ( const uint128& l, const int64_t& r ) { return l <= uint128(r); }
friend bool operator <= ( const int64_t& l, const uint128& r ) { return uint128(l) <= r; }
friend std::size_t hash_value( const uint128& v ) { return city_hash_size_t((const char*)&v, sizeof(v)); }
uint32_t to_integer()const
{
FC_ASSERT( hi == 0 );
uint32_t lo32 = (uint32_t) lo;
FC_ASSERT( lo == lo32 );
return lo32;
}
uint64_t to_uint64()const
{
FC_ASSERT( hi == 0 );
return lo;
}
uint32_t low_32_bits()const { return (uint32_t) lo; }
uint64_t low_bits()const { return lo; }
uint64_t high_bits()const { return hi; }
static uint128 max_value() {
const uint64_t max64 = std::numeric_limits<uint64_t>::max();
return uint128( max64, max64 );
}
static void full_product( const uint128& a, const uint128& b, uint128& result_hi, uint128& result_lo );
uint8_t popcount() const;
// fields must be public for serialization
uint64_t hi;
uint64_t lo;
};
static_assert( sizeof(uint128) == 2*sizeof(uint64_t), "validate packing assumptions" );
typedef uint128 uint128_t;
class variant;
void to_variant( const uint128& var, variant& vo, uint32_t max_depth = 1 );
void from_variant( const variant& var, uint128& vo, uint32_t max_depth = 1 );
namespace raw
{
template<typename Stream>
inline void pack( Stream& s, const uint128& u, uint32_t _max_depth=FC_PACK_MAX_DEPTH ) {
pack( s, u.hi, _max_depth );
pack( s, u.lo, _max_depth );
}
template<typename Stream>
inline void unpack( Stream& s, uint128& u, uint32_t _max_depth=FC_PACK_MAX_DEPTH ) {
unpack( s, u.hi, _max_depth );
unpack( s, u.lo, _max_depth );
}
}
size_t city_hash_size_t(const char *buf, size_t len);
using boost::multiprecision::uint128_t;
} // namespace fc
namespace std
{
template<>
struct hash<fc::uint128>
{
size_t operator()( const fc::uint128& s )const;
};
}
FC_REFLECT( fc::uint128_t, (hi)(lo) )
#ifdef _MSC_VER
#pragma warning (pop)
#endif ///_MSC_VER

View file

@ -1,5 +1,6 @@
#pragma once
#include <stdint.h>
#include <cstdlib>
#ifdef _MSC_VER
#pragma warning(disable: 4482) // nonstandard extension used enum Name::Val, standard in C++11

View file

@ -11,6 +11,7 @@
#include <string.h> // memset
#include <fc/optional.hpp>
#include <fc/uint128.hpp>
#include <fc/container/flat_fwd.hpp>
#include <boost/endian/buffers.hpp>
#include <boost/multi_index_container_fwd.hpp>
@ -146,6 +147,9 @@ namespace fc
void to_variant( const microseconds& input_microseconds, variant& output_variant, uint32_t max_depth );
void from_variant( const variant& input_variant, microseconds& output_microseconds, uint32_t max_depth );
void to_variant( const uint128_t& var, variant& vo, uint32_t max_depth = 1 );
void from_variant( const variant& var, uint128_t& vo, uint32_t max_depth = 1 );
#ifdef __APPLE__
void to_variant( size_t s, variant& v, uint32_t max_depth = 1 );
#elif !defined(_MSC_VER)

View file

@ -4,11 +4,11 @@
#include <fc/fwd_impl.hpp>
#include <fc/io/fstream.hpp>
#include <fc/io/raw.hpp>
#include <fc/log/logger.hpp>
#include <fc/thread/thread.hpp>
#include <fc/io/raw.hpp>
#include <boost/endian/buffers.hpp>
#include <boost/thread/mutex.hpp>
#include <openssl/opensslconf.h>
@ -38,7 +38,7 @@ aes_encoder::~aes_encoder()
{
}
void aes_encoder::init( const fc::sha256& key, const fc::uint128& init_value )
void aes_encoder::init( const fc::sha256& key, const uint128_t& init_value )
{
my->ctx.obj = EVP_CIPHER_CTX_new();
/* Create and initialise the context */
@ -54,8 +54,8 @@ void aes_encoder::init( const fc::sha256& key, const fc::uint128& init_value )
* IV size for *most* modes is the same as the block size. For AES this
* is 128 bits */
boost::endian::little_uint64_buf_t iv[2];
iv[0] = init_value.hi;
iv[1] = init_value.lo;
iv[0] = static_cast<uint64_t>( init_value >> 64 );
iv[1] = static_cast<uint64_t>( init_value & 0xffffffffffffffffULL );
if(1 != EVP_EncryptInit_ex(my->ctx, EVP_aes_256_cbc(), NULL, (unsigned char*)&key, (const unsigned char*)iv[0].data()))
{
FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc encryption init",
@ -106,7 +106,7 @@ aes_decoder::aes_decoder()
(void)init;
}
void aes_decoder::init( const fc::sha256& key, const fc::uint128& init_value )
void aes_decoder::init( const fc::sha256& key, const uint128_t& init_value )
{
my->ctx.obj = EVP_CIPHER_CTX_new();
/* Create and initialise the context */
@ -122,8 +122,8 @@ void aes_decoder::init( const fc::sha256& key, const fc::uint128& init_value )
* IV size for *most* modes is the same as the block size. For AES this
* is 128 bits */
boost::endian::little_uint64_buf_t iv[2];
iv[0] = init_value.hi;
iv[1] = init_value.lo;
iv[0] = static_cast<uint64_t>( init_value >> 64 );
iv[1] = static_cast<uint64_t>( init_value & 0xffffffffffffffffULL );
if(1 != EVP_DecryptInit_ex(my->ctx, EVP_aes_256_cbc(), NULL, (unsigned char*)&key, (const unsigned char*)iv[0].data()))
{
FC_THROW_EXCEPTION( aes_exception, "error during aes 256 cbc encryption init",

View file

@ -33,8 +33,6 @@
#include <algorithm>
#include <string.h> // for memcpy and memset
#include <fc/crypto/city.hpp>
#include <fc/uint128.hpp>
#include <fc/array.hpp>
#include <boost/endian/buffers.hpp>
#if defined(__SSE4_2__) && defined(__x86_64__)
@ -47,13 +45,16 @@ namespace fc {
using namespace std;
inline uint64_t Uint128Low64(const uint128& x) { return x.low_bits(); }
inline uint64_t Uint128High64(const uint128& x) { return x.high_bits(); }
inline uint64_t Uint128Low64(const uint128_t& x) {
return static_cast<uint64_t>(x & 0xffffffffffffffffULL);
}
inline uint64_t Uint128High64(const uint128_t& x) {
return static_cast<uint64_t>( x >> 64 );
}
// Hash 128 input bits down to 64 bits of output.
// This is intended to be a reasonably good hash function.
inline uint64_t Hash128to64(const uint128& x) {
inline uint64_t Hash128to64(const uint128_t& x) {
// Murmur-inspired hashing.
const uint64_t kMul = 0x9ddfea08eb382d69ULL;
uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
@ -280,7 +281,7 @@ static uint64_t ShiftMix(uint64_t val) {
}
static uint64_t HashLen16(uint64_t u, uint64_t v) {
return Hash128to64(uint128(u, v));
return Hash128to64( ( uint128_t(u) << 64 ) + v);
}
static uint64_t HashLen16(uint64_t u, uint64_t v, uint64_t mul) {
@ -425,7 +426,7 @@ uint64_t CityHash64WithSeed(const char *s, size_t len, uint64_t seed) {
// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
// of any length representable in signed long. Based on City and Murmur.
static uint128 CityMurmur(const char *s, size_t len, uint128 seed) {
uint128_t CityMurmur(const char *s, size_t len, uint128_t seed) {
uint64_t a = Uint128Low64(seed);
uint64_t b = Uint128High64(seed);
uint64_t c = 0;
@ -452,10 +453,10 @@ static uint128 CityMurmur(const char *s, size_t len, uint128 seed) {
}
a = HashLen16(a, c);
b = HashLen16(d, b);
return uint128(a ^ b, HashLen16(b, a));
return ( uint128_t(a ^ b) << 64 ) + HashLen16(b, a);
}
uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed) {
uint128_t CityHash128WithSeed(const char *s, size_t len, uint128_t seed) {
if (len < 128) {
return CityMurmur(s, len, seed);
}
@ -514,15 +515,15 @@ uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed) {
// different 56-byte-to-8-byte hashes to get a 16-byte final result.
x = HashLen16(x, v.first);
y = HashLen16(y + z, w.first);
return uint128(HashLen16(x + v.second, w.second) + y,
HashLen16(x + w.second, y + v.second));
return ( uint128_t( HashLen16(x + v.second, w.second) + y ) << 64 )
+ HashLen16(x + w.second, y + v.second);
}
uint128 city_hash128(const char *s, size_t len) {
uint128_t city_hash128(const char *s, size_t len) {
return len >= 16 ?
CityHash128WithSeed(s + 16, len - 16,
uint128(Fetch64(s), Fetch64(s + 8) + k0)) :
CityHash128WithSeed(s, len, uint128(k0, k1));
( uint128_t( Fetch64(s) ) << 64 ) + Fetch64(s + 8) + k0) :
CityHash128WithSeed(s, len, ( uint128_t(k0) << 64 ) + k1);
}
//#ifdef __SSE4_2__
@ -640,7 +641,7 @@ array<uint64_t,4> city_hash_crc_256(const char *s, size_t len)
return buf;
}
uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed) {
uint128_t CityHashCrc128WithSeed(const char *s, size_t len, uint128_t seed) {
if (len <= 900) {
return CityHash128WithSeed(s, len, seed);
} else {
@ -648,18 +649,18 @@ uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed) {
CityHashCrc256(s, len, result);
uint64_t u = Uint128High64(seed) + result[0];
uint64_t v = Uint128Low64(seed) + result[1];
return uint128(HashLen16(u, v + result[2]),
HashLen16(Rotate(v, 32), u * k0 + result[3]));
return ( uint128_t( HashLen16(u, v + result[2]) ) << 64 )
+ HashLen16(Rotate(v, 32), u * k0 + result[3]);
}
}
uint128 city_hash_crc_128(const char *s, size_t len) {
uint128_t city_hash_crc_128(const char *s, size_t len) {
if (len <= 900) {
return city_hash128(s, len);
} else {
uint64_t result[4];
CityHashCrc256(s, len, result);
return uint128(result[2], result[3]);
return ( uint128_t(result[2]) << 64 ) + result[3];
}
}

54
src/popcount.cpp Normal file
View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2019 BitShares Blockchain Foundation, and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <fc/popcount.hpp>
namespace fc {
#if _MSC_VER || __GNUC__ || __clang__
#else
uint8_t popcount( uint64_t v )
{
// Public Domain code taken from http://graphics.stanford.edu/~seander/bithacks.html
uint8_t c;
static const int S[] = {1, 2, 4, 8, 16, 32};
static const uint64_t B[] = {0x5555555555555555, 0x3333333333333333, 0x0F0F0F0F0F0F0F0F,
0x00FF00FF00FF00FF, 0x0000FFFF0000FFFF, 0x00000000FFFFFFFF };
c = v - ((v >> 1) & B[0]);
c = ((c >> S[1]) & B[1]) + (c & B[1]);
c = ((c >> S[2]) + c) & B[2];
c = ((c >> S[3]) + c) & B[3];
c = ((c >> S[4]) + c) & B[4];
return ((c >> S[5]) + c) & B[5];
}
#endif
uint8_t popcount( const uint128_t& v )
{
return popcount( static_cast<uint64_t>(v >> 64) )
+ popcount( static_cast<uint64_t>(v & 0xffffffffffffffffULL) );
}
} // namespace fc

View file

@ -1,398 +0,0 @@
#include <fc/uint128.hpp>
#include <fc/variant.hpp>
#include <fc/crypto/bigint.hpp>
#include <boost/endian/buffers.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <stdexcept>
namespace fc
{
typedef boost::multiprecision::uint128_t m128;
template <typename T>
static void divide(const T &numerator, const T &denominator, T &quotient, T &remainder)
{
static const int bits = sizeof(T) * 8;
if(denominator == 0) {
throw std::domain_error("divide by zero");
} else {
T n = numerator;
T d = denominator;
T x = 1;
T answer = 0;
while((n >= d) && (((d >> (bits - 1)) & 1) == 0)) {
x <<= 1;
d <<= 1;
}
while(x != 0) {
if(n >= d) {
n -= d;
answer |= x;
}
x >>= 1;
d >>= 1;
}
quotient = answer;
remainder = n;
}
}
uint128::uint128(const std::string &sz)
:hi(0), lo(0)
{
// do we have at least one character?
if(!sz.empty()) {
// make some reasonable assumptions
int radix = 10;
bool minus = false;
std::string::const_iterator i = sz.begin();
// check for minus sign, i suppose technically this should only apply
// to base 10, but who says that -0x1 should be invalid?
if(*i == '-') {
++i;
minus = true;
}
// check if there is radix changing prefix (0 or 0x)
if(i != sz.end()) {
if(*i == '0') {
radix = 8;
++i;
if(i != sz.end()) {
if(*i == 'x') {
radix = 16;
++i;
}
}
}
while(i != sz.end()) {
unsigned int n = 0;
const char ch = *i;
if(ch >= 'A' && ch <= 'Z') {
if(((ch - 'A') + 10) < radix) {
n = (ch - 'A') + 10;
} else {
break;
}
} else if(ch >= 'a' && ch <= 'z') {
if(((ch - 'a') + 10) < radix) {
n = (ch - 'a') + 10;
} else {
break;
}
} else if(ch >= '0' && ch <= '9') {
if((ch - '0') < radix) {
n = (ch - '0');
} else {
break;
}
} else {
/* completely invalid character */
break;
}
(*this) *= radix;
(*this) += n;
++i;
}
}
// if this was a negative number, do that two's compliment madness :-P
if(minus) {
*this = -*this;
}
}
}
uint128::operator bigint()const
{
boost::endian::big_uint64_buf_t tmp[2];
tmp[0] = hi;
tmp[1] = lo;
bigint bi( (char*)&tmp, sizeof(tmp) );
return bi;
}
uint128::uint128( const fc::bigint& bi )
{
*this = uint128( std::string(bi) ); // TODO: optimize this...
}
uint128::operator std::string ()const
{
if(*this == 0) { return "0"; }
// at worst it will be size digits (base 2) so make our buffer
// that plus room for null terminator
static char sz [128 + 1];
sz[sizeof(sz) - 1] = '\0';
uint128 ii(*this);
int i = 128 - 1;
while (ii != 0 && i) {
uint128 remainder;
divide(ii, uint128(10), ii, remainder);
sz [--i] = "0123456789abcdefghijklmnopqrstuvwxyz"[remainder.to_integer()];
}
return &sz[i];
}
uint128& uint128::operator<<=(const uint128& rhs)
{
if(rhs >= 128)
{
hi = 0;
lo = 0;
}
else
{
unsigned int n = rhs.to_integer();
const unsigned int halfsize = 128 / 2;
if(n >= halfsize){
n -= halfsize;
hi = lo;
lo = 0;
}
if(n != 0) {
// shift high half
hi <<= n;
const uint64_t mask(~(uint64_t(-1) >> n));
// and add them to high half
hi |= (lo & mask) >> (halfsize - n);
// and finally shift also low half
lo <<= n;
}
}
return *this;
}
uint128 & uint128::operator>>=(const uint128& rhs)
{
if(rhs >= 128)
{
hi = 0;
lo = 0;
}
else
{
unsigned int n = rhs.to_integer();
const unsigned int halfsize = 128 / 2;
if(n >= halfsize) {
n -= halfsize;
lo = hi;
hi = 0;
}
if(n != 0) {
// shift low half
lo >>= n;
// get lower N bits of high half
const uint64_t mask(~(uint64_t(-1) << n));
// and add them to low qword
lo |= (hi & mask) << (halfsize - n);
// and finally shift also high half
hi >>= n;
}
}
return *this;
}
uint128& uint128::operator/=(const uint128 &b)
{
auto self = (m128(hi) << 64) + m128(lo);
auto other = (m128(b.hi) << 64) + m128(b.lo);
self /= other;
hi = static_cast<uint64_t>(self >> 64);
lo = static_cast<uint64_t>((self << 64 ) >> 64);
return *this;
}
uint128& uint128::operator%=(const uint128 &b)
{
uint128 quotient;
divide(*this, b, quotient, *this);
return *this;
}
uint128& uint128::operator*=(const uint128 &b)
{
uint64_t a0 = (uint32_t) (this->lo );
uint64_t a1 = (uint32_t) (this->lo >> 0x20);
uint64_t a2 = (uint32_t) (this->hi );
uint64_t a3 = (uint32_t) (this->hi >> 0x20);
uint64_t b0 = (uint32_t) (b.lo );
uint64_t b1 = (uint32_t) (b.lo >> 0x20);
uint64_t b2 = (uint32_t) (b.hi );
uint64_t b3 = (uint32_t) (b.hi >> 0x20);
// (a0 + (a1 << 0x20) + (a2 << 0x40) + (a3 << 0x60)) *
// (b0 + (b1 << 0x20) + (b2 << 0x40) + (b3 << 0x60)) =
// a0 * b0
//
// (a1 * b0 + a0 * b1) << 0x20
// (a2 * b0 + a1 * b1 + a0 * b2) << 0x40
// (a3 * b0 + a2 * b1 + a1 * b2 + a0 * b3) << 0x60
//
// all other cross terms are << 0x80 or higher, thus do not appear in result
this->hi = 0;
this->lo = a3*b0;
(*this) += a2*b1;
(*this) += a1*b2;
(*this) += a0*b3;
(*this) <<= 0x20;
(*this) += a2*b0;
(*this) += a1*b1;
(*this) += a0*b2;
(*this) <<= 0x20;
(*this) += a1*b0;
(*this) += a0*b1;
(*this) <<= 0x20;
(*this) += a0*b0;
return *this;
}
void uint128::full_product( const uint128& a, const uint128& b, uint128& result_hi, uint128& result_lo )
{
// (ah * 2**64 + al) * (bh * 2**64 + bl)
// = (ah * bh * 2**128 + al * bh * 2**64 + ah * bl * 2**64 + al * bl
// = P * 2**128 + (Q + R) * 2**64 + S
// = Ph * 2**192 + Pl * 2**128
// + Qh * 2**128 + Ql * 2**64
// + Rh * 2**128 + Rl * 2**64
// + Sh * 2**64 + Sl
//
uint64_t ah = a.hi;
uint64_t al = a.lo;
uint64_t bh = b.hi;
uint64_t bl = b.lo;
uint128 s = al;
s *= bl;
uint128 r = ah;
r *= bl;
uint128 q = al;
q *= bh;
uint128 p = ah;
p *= bh;
uint64_t sl = s.lo;
uint64_t sh = s.hi;
uint64_t rl = r.lo;
uint64_t rh = r.hi;
uint64_t ql = q.lo;
uint64_t qh = q.hi;
uint64_t pl = p.lo;
uint64_t ph = p.hi;
uint64_t y[4]; // final result
y[0] = sl;
uint128_t acc = sh;
acc += ql;
acc += rl;
y[1] = acc.lo;
acc = acc.hi;
acc += qh;
acc += rh;
acc += pl;
y[2] = acc.lo;
y[3] = acc.hi + ph;
result_hi = uint128( y[3], y[2] );
result_lo = uint128( y[1], y[0] );
}
static uint8_t _popcount_64( uint64_t x )
{
static const uint64_t m[] = {
0x5555555555555555ULL,
0x3333333333333333ULL,
0x0F0F0F0F0F0F0F0FULL,
0x00FF00FF00FF00FFULL,
0x0000FFFF0000FFFFULL,
0x00000000FFFFFFFFULL
};
// TODO future optimization: replace slow, portable version
// with fast, non-portable __builtin_popcountll intrinsic
// (when available)
for( int i=0, w=1; i<6; i++, w+=w )
{
x = (x & m[i]) + ((x >> w) & m[i]);
}
return uint8_t(x);
}
uint8_t uint128::popcount()const
{
return _popcount_64( lo ) + _popcount_64( hi );
}
void to_variant( const uint128& var, variant& vo, uint32_t max_depth )
{
vo = std::string(var);
}
void from_variant( const variant& var, uint128& vo, uint32_t max_depth )
{
vo = uint128(var.as_string());
}
} // namespace fc
namespace std {
size_t hash<fc::uint128>::operator()( const fc::uint128& s )const
{
boost::endian::little_uint64_buf_t tmp[2];
tmp[0] = s.hi;
tmp[1] = s.lo;
return fc::city_hash_size_t((char*)&tmp, sizeof(tmp));
}
}
/*
* Portions of the above code were adapted from the work of Evan Teran.
*
* Copyright (c) 2008
* Evan Teran
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
* that the above copyright notice appears in all copies and that both the
* copyright notice and this permission notice appear in supporting
* documentation, and that the same name not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. We make no representations about the
* suitability this software for any purpose. It is provided "as is"
* without express or implied warranty.
*/

View file

@ -13,6 +13,7 @@
namespace fc
{
/**
* The TypeID is stored in the 'last byte' of the variant.
*/
@ -672,6 +673,16 @@ void from_variant( const variant& var, std::vector<char>& vo, uint32_t max_depth
}
}
void to_variant( const uint128_t& var, variant& vo, uint32_t max_depth )
{
vo = var.str();
}
void from_variant( const variant& var, uint128_t& vo, uint32_t max_depth )
{
vo = uint128_t( var.as_string() );
}
#ifdef __APPLE__
#elif !defined(_MSC_VER)
void to_variant( long long int s, variant& v, uint32_t max_depth ) { v = variant( int64_t(s) ); }