#ifndef ROSE_INTEGEROPS_H #define ROSE_INTEGEROPS_H #include #include #include #include #include #include namespace IntegerOpsPrivate { template struct NumBits { DYN_STATIC_ASSERT (std::numeric_limits::radix == 2); DYN_STATIC_ASSERT (std::numeric_limits::is_integer); static const size_t value = std::numeric_limits::digits; }; template struct SHL1Helper; template struct SHL1Helper { static const T value = 0; }; template struct SHL1Helper { static const T value = T(1) << Count; }; } /** Bit-wise operations on integers. * * Many of these are function templates for the best optimization, but we also provide non-template versions for those cases * when the arguments are not known at compile time. The non-template versions typically have "2" appended to their names to * indicate that they should be the second choice--used only when the template version cannot be used. */ namespace IntegerOps { /** Bitmask constant with bit @p n set. Handles the case where @p n is greater than the width of type @p T. */ template struct SHL1: public IntegerOpsPrivate::SHL1Helper= IntegerOpsPrivate::NumBits::value)> {}; /** Bitmask with bit @p n set. Handles the case where @p n is greater than the width of type @p T. */ template inline T shl1(size_t n) { return (n >= IntegerOpsPrivate::NumBits::value) ? T(0) : (T(1) << n); } /** Bit mask constant with bits 0 through @p n-1 set. */ template struct GenMask { static const T value = SHL1::value - T(1); }; /** Bitmask with bits 0 through @p N-1 set. */ template inline T genMask(size_t n) { return shl1(n) - 1; } /** Generate a bitmask. The return value has bits @p lobit (inclusive) through @p hibit (inclusive) set, and all other bits * are clear. */ template inline T genMask(size_t lobit, size_t hibit) { assert(hibit<8*sizeof(T)); assert(hibit>=lobit); return genMask(1+hibit-lobit) << lobit; } /** Returns true if the sign bit is set, false if clear. * @{ */ template inline bool signBit(T value) { return (value & SHL1::value) != T(0); } template inline bool signBit2(T value, size_t width=8*sizeof(T)) { assert(width>0 && width<=8*sizeof(T)); T sign_mask = shl1(width-1); return 0 != (value & sign_mask); } /** @} */ /** Sign extend value. If the bit @p FromBits-1 is set set for @p value, then the result will have bits @p FromBits through @p * ToBits-1 also set (other bits are unchanged). If @p ToBits is less than or equal to @p FromBits then nothing happens. * @{ */ template inline T signExtend(T value) { return value | (signBit(value) ? (GenMask::value ^ GenMask::value) : T(0)); } template inline T signExtend2(T value, size_t from_width, size_t to_width) { assert(from_width<=8*sizeof(T)); assert(to_width<=8*sizeof(T)); return value | (signBit2(value, from_width) ? (genMask(to_width) ^ genMask(from_width)) : T(0)); } /** @} */ /** Shifts bits of @p value left by @p count bits. * @{ */ template inline T shiftLeft(T value, size_t count) { return (value * shl1(count)) & GenMask::value; } template inline T shiftLeft2(T value, size_t count, size_t width=8*sizeof(T)) { assert(width>0 && width<=8*sizeof(T)); return (value * shl1(count)) & genMask(width); } /** @} */ /** Shifts bits of @p value right by @p count bits without sign extension. * @{ */ template inline T shiftRightLogical(T value, size_t count) { return (count >= NBits) ? T(0) : (value >> count); } template inline T shiftRightLogical2(T value, size_t count, size_t width=8*sizeof(T)) { assert(width>0 && width<=8*sizeof(T)); return (count >= width) ? T(0) : (value >> count); } /** @} */ /** Shifts bits of @p value right by @p count bits with sign extension. * @{ */ template inline T shiftRightArithmetic(T value, size_t count) { if (count >= NBits) { return signBit(value) ? GenMask::value : T(0); } else { return (shiftRightLogical(value, count) | (signBit(value) ? (GenMask::value ^ genMask(NBits - count)) : T(0))); } } template inline T shiftRightArithmetic2(T value, size_t count, size_t width=8*sizeof(T)) { if (count >= width) { return signBit2(value, width) ? genMask(width) : T(0); } else { return (shiftRightLogical2(value, count, width) | (signBit2(value, width) ? (genMask(width) ^ genMask(width-count)) : T(0))); } } /** @} */ /** Rotate the bits of the value left by count bits. * @{ */ template inline T rotateLeft(T value, size_t count) { count %= NBits; return ((value << count) | (value >> (NBits - count))) & GenMask::value; } template inline T rotateLeft2(T value, size_t count, size_t width=8*sizeof(T)) { assert(width>0 && width<=8*sizeof(T)); count %= width; return ((value << count) | (value >> (width-count))) & genMask(width); } /** @} */ /** Rotate bits of the value right by @p count bits. * @{ */ template inline T rotateRight(T value, size_t count) { count %= NBits; return ((value >> count) | (value << (NBits - count))) & GenMask::value; } template inline T rotateRight2(T value, size_t count, size_t width=8*sizeof(T)) { assert(width>0 && width<=8*sizeof(T)); return ((value >> count) | (value << (width - count))) & genMask(width); } /** @} */ /** Returns true if the value is a power of two. Zero is considered a power of two. */ template inline bool isPowerOfTwo(T value) { assert(sizeof(T) <= sizeof(uint64_t)); if (0 != (value & SHL1::value)) value = ~value + 1; uint64_t uval = value; while (uval) { if (uval & 1) return 1==uval; uval >>= 1u; } return true; // treat zero as a power of two } /** Returns the base-2 logorithm of @p value. If @p value is not a power of two then the return value is rounded up to the * next integer. The @p value is treated as an unsigned value. Returns zero if @p value is zero. */ template inline T log2max(T value) { assert(sizeof(T) <= sizeof(uint64_t)); uint64_t uval = value; bool low_bits_set = false; T retval = 0; while (uval) { if (1==uval) return retval + (low_bits_set ? 1 : 0); if (uval & 1) low_bits_set = true; ++retval; uval >>= 1; } return retval; } template inline T log2(T a) { T n = T(1); T i = 0; while (n != 0 && n < a) { n <<= 1; ++i; } return i; } /** Create a shifted value. The return value is created by shifting @p value to the specified position in the result. Other * bits of the return value are clear. The @p hibit is specified so that we can check at run-time that a valid value was * specified (i.e., the value isn't too wide). * @{ */ template inline T shift_to(T value) { assert(hibit<8*sizeof(T)); assert(hibit>=lobit); assert(0==(value & ~GenMask::value)); return shiftLeft<8*sizeof(T)>(value & GenMask::value, lobit); } template inline T shift_to2(size_t lobit, size_t hibit, T value) { assert(hibit<8*sizeof(T)); assert(hibit>=lobit); assert(0==(value & ~genMask(1+hibit-lobit))); return shiftLeft2(value & genMask(1+hibit-lobit), lobit); } /** @} */ /** Extract bits from a value. Bits @p lobit through @p hibit, inclusive, are right shifted into the result and higher-order * bits of the result are cleared. * @{ */ template inline T extract(T bits) { assert(hibit<8*sizeof(T)); assert(hibit>=lobit); return shiftRightLogical<8*sizeof(T)>(bits, lobit) & GenMask::value; } template inline T extract2(size_t lobit, size_t hibit, T bits) { assert(hibit<8*sizeof(T)); assert(hibit>=lobit); return shiftRightLogical<8*sizeof(T)>(bits, lobit) & genMask(1+hibit-lobit); } /** @} */ /** Determines if one bitmask is a subset of another. Returns true if the bits set in the first argument form a subset of the * bits set in the second argument. */ template inline bool bitmask_subset(T m1, T m2) { return 0 == (~m1 & m2); // m2 must not contain bits that are not in m1 } /** Counts how many bits are set (one). */ template inline size_t countSet(T val) { size_t retval = 0; for (size_t i=0; i<8*sizeof(T); ++i) { if (0 != (val & shl1(i))) ++retval; } return retval; } /** Counts how many bits are clear (zero). */ template inline size_t countClear(T val) { size_t retval = 0; for (size_t i=0; i<8*sizeof(T); ++i) { if (0 == (val & shl1(i))) ++retval; } return retval; } /** Optionally returns the zero-origin position of the most significant set bit. Returns nothing if no bits are set. */ template inline dyncompat::optional msb_set(T val) { if (val!=0) { for (size_t bitno = 8*sizeof(T); bitno>0; --bitno) { if (0 != (val & shl1(bitno-1))) return dyncompat::optional(bitno-1); } } return dyncompat::optional(); } } // namespace #endif // ROSE_INTEGEROPS_H