#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2021 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_PIXELS_UINT128_H #define NUCLEX_PIXELS_UINT128_H #include "Nuclex/Pixels/Config.h" #include // for std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t #include // for assert() #include // for std::numeric_limits #include // for std::true_type namespace Nuclex { namespace Pixels { // ------------------------------------------------------------------------------------------- // /// 128 bit unsigned intgeer class NUCLEX_PIXELS_TYPE UInt128 { /// Initializes a new 128 bit unsigned integer /// /// To emulate the behavior of other C++ primitive types, the value remains /// uninitialized and it is up to the user to make sure a value is assigned before /// the variable is accessed. /// public: constexpr NUCLEX_PIXELS_API UInt128() : #if defined(NUCLEX_PIXELS_LITTLE_ENDIAN) leastSignificant(0), mostSignificant(0) {} #else mostSignificant(0), leastSignificant(0) {} #endif /// Initializes an 128 bit integer using two 64 bit integers /// /// 64 bit integer that will be used to fill the 64 most significant bits /// /// /// 64 bit integer that will be used to fill the 64 least significant bits /// public: constexpr NUCLEX_PIXELS_API explicit UInt128( std::uint64_t mostSignificant, std::uint64_t leastSignificant ) : #if defined(NUCLEX_PIXELS_LITTLE_ENDIAN) leastSignificant(leastSignificant), mostSignificant(mostSignificant) {} #else mostSignificant(mostSignificant), leastSignificant(leastSignificant) {} #endif /// Initializes a new 128 bit unsigned integer from an integer /// Value with which the 128 bit integer will be initialized public: constexpr NUCLEX_PIXELS_API UInt128(int value) : #if defined(NUCLEX_PIXELS_LITTLE_ENDIAN) leastSignificant(static_cast(value)), mostSignificant((value < 0) ? 0xFFFFFFFFFFFFFFFF : 0) {} #else mostSignificant((value < 0) ? 0xFFFFFFFFFFFFFFFF : 0), leastSignificant(static_cast(value)) {} #endif /// Initializes a new 128 bit unsigned integer from an 8 bit integer /// Value with which the 128 bit integer will be initialized public: constexpr NUCLEX_PIXELS_API UInt128(std::uint8_t value) : #if defined(NUCLEX_PIXELS_LITTLE_ENDIAN) leastSignificant(value), mostSignificant(0) {} #else mostSignificant(0), leastSignificant(value) {} #endif /// Initializes a new 128 bit unsigned integer from a 16 bit integer /// Value with which the 128 bit integer will be initialized public: constexpr NUCLEX_PIXELS_API UInt128(std::uint16_t value) : #if defined(NUCLEX_PIXELS_LITTLE_ENDIAN) leastSignificant(value), mostSignificant(0) {} #else mostSignificant(0), leastSignificant(value) {} #endif /// Initializes a new 128 bit unsigned integer from a 32 bit integer /// Value with which the 128 bit integer will be initialized public: constexpr NUCLEX_PIXELS_API UInt128(std::uint32_t value) : #if defined(NUCLEX_PIXELS_LITTLE_ENDIAN) leastSignificant(value), mostSignificant(0) {} #else mostSignificant(0), leastSignificant(value) {} #endif /// Initializes a new 128 bit unsigned integer from a 64 bit integer /// Value with which the 128 bit integer will be initialized public: constexpr NUCLEX_PIXELS_API UInt128(std::uint64_t value) : #if defined(NUCLEX_PIXELS_LITTLE_ENDIAN) leastSignificant(value), mostSignificant(0) {} #else mostSignificant(0), leastSignificant(value) {} #endif #if defined(NUCLEX_PIXELS_HAVE_BUILTIN_INT128) #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpedantic" // ISO C++ does not support ‘__int128’ #elif defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" // ISO C++ does not support ‘__int128’ #endif /// Initializes a new 128 bit unsigned integer from a 128 bit integer /// Value with which the 128 bit integer will be initialized public: constexpr NUCLEX_PIXELS_API UInt128(unsigned __int128 value) : #if defined(NUCLEX_PIXELS_LITTLE_ENDIAN) leastSignificant(static_cast(value)), mostSignificant(static_cast(value >> 64)) {} #else mostSignificant(static_cast(value >> 64)), leastSignificant(static_cast(value)) {} #endif #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic pop #endif #endif // defined(NUCLEX_PIXELS_HAVE_BUILTIN_INT128) /// Returns only the lower 8 bits of the 128 bit integer /// The lower 8 bits of the 128 bit integer public: NUCLEX_PIXELS_API explicit operator std::uint8_t() const { return static_cast(this->leastSignificant); } /// Returns only the lower 16 bits of the 128 bit integer /// The lower 16 bits of the 128 bit integer public: NUCLEX_PIXELS_API explicit operator std::uint16_t() const { return static_cast(this->leastSignificant); } /// Returns only the lower 32 bits of the 128 bit integer /// The lower 32 bits of the 128 bit integer public: NUCLEX_PIXELS_API explicit operator std::uint32_t() const { return static_cast(this->leastSignificant); } /// Returns only the lower 64 bits of the 128 bit integer /// The lower 64 bits of the 128 bit integer public: NUCLEX_PIXELS_API explicit operator std::uint64_t() const { return this->leastSignificant; } /// Initializes the 128 bit integer from an 8 bit integer /// 8 bit integer the 128 integer will be initialized from /// The 128 bit integer public: NUCLEX_PIXELS_API UInt128 &operator =(std::uint8_t value) { this->mostSignificant = 0; this->leastSignificant = value; return *this; } /// Initializes the 128 bit integer from a 16 bit integer /// 16 bit integer the 128 integer will be initialized from /// The 128 bit integer public: NUCLEX_PIXELS_API UInt128 &operator =(std::uint16_t value) { this->mostSignificant = 0; this->leastSignificant = value; return *this; } /// Initializes the 128 bit integer from a 32 bit integer /// 32 bit integer the 128 integer will be initialized from /// The 128 bit integer public: NUCLEX_PIXELS_API UInt128 &operator =(std::uint32_t value) { this->mostSignificant = 0; this->leastSignificant = value; return *this; } /// Initializes the 128 bit integer from a 64 bit integer /// 64 bit integer the 128 integer will be initialized from /// The 128 bit integer public: NUCLEX_PIXELS_API UInt128 &operator =(std::uint64_t value) { this->mostSignificant = 0; this->leastSignificant = value; return *this; } /// Checks whether another 128 bit integer is equal to this one /// Other 128 bit integer that will be compared /// True if the other 128 bit integer has the same value as this public: NUCLEX_PIXELS_API bool operator ==(const UInt128 &other) const { return ( (this->leastSignificant == other.leastSignificant) && (this->mostSignificant == other.mostSignificant) ); } /// Checks whether another 128 bit integer is different from this one /// Other 128 bit integer that will be compared /// True if the other 128 bit integer has a different value to this public: NUCLEX_PIXELS_API bool operator !=(const UInt128 &other) const { return ( (this->leastSignificant != other.leastSignificant) || (this->mostSignificant != other.mostSignificant) ); } /// Returns the integer bit-shifted to the left /// Number of bits the integer will be shifted /// The bit-shifted copy of the integer public: NUCLEX_PIXELS_API constexpr UInt128 operator <<(int bitOffset) const { assert((bitOffset >= 0) && u8"UInt128 shift operator doesn't support negative shifts"); if(bitOffset == 0) { // Using <64 case would result in undefined behavior return *this; } else if(bitOffset < 64) { UInt128 shifted; shifted.mostSignificant = (this->mostSignificant << bitOffset); shifted.mostSignificant |= (this->leastSignificant >> (64 - bitOffset)); shifted.leastSignificant = this->leastSignificant << bitOffset; return shifted; } else { UInt128 shifted; shifted.mostSignificant = (this->leastSignificant << (bitOffset - 64)); shifted.leastSignificant = 0; return shifted; } } /// Returns the integer bit-shifted to the right /// Number of bits the integer will be shifted /// The bit-shifted copy of the integer public: NUCLEX_PIXELS_API constexpr UInt128 operator >>(int bitOffset) const { assert((bitOffset >= 0) && u8"UInt128 shift operator doesn't support negative shifts"); if(bitOffset == 0) { // Using <64 case would result in undefined behavior return *this; } else if(bitOffset < 64) { UInt128 shifted; shifted.leastSignificant = (this->leastSignificant >> bitOffset); shifted.leastSignificant |= (this->mostSignificant << (64 - bitOffset)); shifted.mostSignificant = this->mostSignificant >> bitOffset; return shifted; } else { UInt128 shifted; shifted.leastSignificant = (this->mostSignificant >> (bitOffset - 64)); shifted.mostSignificant = 0; return shifted; } } /// Bit shifts the integer to the left /// Number of bits the integer will be shifted /// The bit-shifted integer public: NUCLEX_PIXELS_API constexpr UInt128 &operator <<=(int bitOffset) { assert((bitOffset >= 0) && u8"UInt128 shift operator doesn't support negative shifts"); if(bitOffset == 0) { // Can't use generic method because shift by 64 is undefined return *this; } else if(bitOffset < 64) { this->mostSignificant <<= bitOffset; this->mostSignificant |= (this->leastSignificant >> (64 - bitOffset)); this->leastSignificant <<= bitOffset; return *this; } else { this->mostSignificant = (this->leastSignificant << (bitOffset - 64)); this->leastSignificant = 0; return *this; } } /// Bit shifts the integer to the right /// Number of bits the integer will be shifted /// The bit-shifted integer public: NUCLEX_PIXELS_API constexpr UInt128 &operator >>=(int bitOffset) { assert((bitOffset >= 0) && u8"UInt128 shift operator doesn't support negative shifts"); if(bitOffset == 0) { return *this; // Can't use generic method because shift by 64 is undefined } else if(bitOffset < 64) { this->leastSignificant >>= bitOffset; this->leastSignificant |= (this->mostSignificant << (64 - bitOffset)); this->mostSignificant >>= bitOffset; return *this; } else { this->leastSignificant = (this->mostSignificant >> (bitOffset - 64)); this->mostSignificant = 0; return *this; } } /// Returns the integer ORed with another integer /// Other integer that will be ORed with this one /// The ORed copy of the integer public: NUCLEX_PIXELS_API constexpr UInt128 operator |(const UInt128 &other) const { UInt128 combined; combined.mostSignificant = this->mostSignificant | other.mostSignificant; combined.leastSignificant = this->leastSignificant | other.leastSignificant; return combined; } /// Returns the integer ANDed with another integer /// Other integer that will be ANDed with this one /// The ANDed copy of the integer public: NUCLEX_PIXELS_API constexpr UInt128 operator &(const UInt128 &other) const { UInt128 combined; combined.mostSignificant = this->mostSignificant & other.mostSignificant; combined.leastSignificant = this->leastSignificant & other.leastSignificant; return combined; } /// Returns the integer XORed with another integer /// Other integer that will be XORed with this one /// The XORed copy of the integer public: NUCLEX_PIXELS_API constexpr UInt128 operator ^(const UInt128 &other) const { UInt128 combined; combined.mostSignificant = this->mostSignificant ^ other.mostSignificant; combined.leastSignificant = this->leastSignificant ^ other.leastSignificant; return combined; } /// Returns the integer ORed with another integer /// Other integer that will be ORed with this one /// The ORed copy of the integer public: NUCLEX_PIXELS_API constexpr UInt128 &operator |=(const UInt128 &other) { this->mostSignificant |= other.mostSignificant; this->leastSignificant |= other.leastSignificant; return *this; } /// Returns the integer ANDed with another integer /// Other integer that will be ANDed with this one /// The ANDed copy of the integer public: NUCLEX_PIXELS_API constexpr UInt128 &operator &=(const UInt128 &other) { this->mostSignificant &= other.mostSignificant; this->leastSignificant &= other.leastSignificant; return *this; } #if defined(NUCLEX_PIXELS_LITTLE_ENDIAN) /// Stores the least signifcant 64 bits of the 128 bit integer private: std::uint64_t leastSignificant; /// Stores the most signifcant 64 bits of the 128 bit integer private: std::uint64_t mostSignificant; #else /// Stores the most signifcant 64 bits of the 128 bit integer private: std::uint64_t mostSignificant; /// Stores the least signifcant 64 bits of the 128 bit integer private: std::uint64_t leastSignificant; #endif }; // ------------------------------------------------------------------------------------------- // #if defined(NUCLEX_PIXELS_HAVE_BUILTIN_INT128) #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpedantic" // ISO C++ does not support ‘__int128’ #elif defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" // ISO C++ does not support ‘__int128’ #endif /// Alias for the best 128 bit integer implementation to use typedef unsigned __int128 uint128_t; #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic pop #endif #else /// Alias for the best 128 bit integer implementation to use typedef UInt128 uint128_t; #endif // ------------------------------------------------------------------------------------------- // }} // namespace Nuclex::Pixels namespace std { // ------------------------------------------------------------------------------------------- // // CHECK: C++ std says I'm not allowed to specialize std::is_arithmetic ;..( #if NUCLEX_SUPPORT_UINT128_I_HAVE_IMPLEMENTED_ARITHMETIC_OPS /// Declares that the 128 bit integer supports arithmetics template<> struct is_arithmetic : public std::true_type {}; #endif /// Declares that the 128 bit integer is an integral type template<> struct is_integral : public std::true_type {}; /// Declares that the 128 bit integer is an unsigned type template<> struct is_unsigned : public std::true_type {}; /// Numeric limits for 128 bit unsigned integers /// /// Users are not allowed to add anything to the std namespace. However, an exception /// has been defined for supporting custom, non-complex types in std::numeric_limits. /// template<> class numeric_limits { /// Type for which the class is providing informations public: typedef Nuclex::Pixels::UInt128 _Ty; /// /// Minimum finite positive value that is representable by a 128 bit integer /// public: static _Ty min() throw() { return _Ty(0U); } /// /// Maximum finite value that is representable by a 128 bit integer /// public: static _Ty max() throw() { #if defined(NUCLEX_PIXELS_HAVE_BUILTIN_INT128) return (_Ty(18446744073709551615U) << 64) | _Ty(18446744073709551615U); #else return _Ty(18446744073709551615U, 18446744073709551615U); #endif } /// /// Lowest finite value that is representable by the 128 bit integer /// public: static _Ty lowest() throw() { return _Ty(0U); } /// Smallest effective increment from the value 1.0 public: static _Ty epsilon() throw() { return _Ty(1U); } }; // ------------------------------------------------------------------------------------------- // } #endif // NUCLEX_PIXELS_UINT128_H