/*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#ifndef FOLLY_EXPERIMENTAL_BITS_H_
-#define FOLLY_EXPERIMENTAL_BITS_H_
+#pragma once
#include <cstddef>
-#include <type_traits>
#include <limits>
+#include <type_traits>
+
+#include <glog/logging.h>
-#include "folly/Bits.h"
-#include "folly/Portability.h"
-#include "folly/Range.h"
+#include <folly/Bits.h>
+#include <folly/Portability.h>
+#include <folly/Range.h>
namespace folly {
+template <class T>
+struct UnalignedNoASan : public Unaligned<T> { };
+
// As a general rule, bit operations work on unsigned values only;
// right-shift is arithmetic for signed values, and that can lead to
// unpleasant bugs.
* (T, where T is an unsigned integral type) or unaligned values
* (Unaligned<T>, where T is an unsigned integral type)
*/
-template <class T, class Enable=void> struct BitsTraits;
+template <class T, class Enable = void>
+struct BitsTraits;
// Partial specialization for Unaligned<T>, where T is unsigned integral
// loadRMW is the same as load, but it indicates that it loads for a
// silence the GCC warning in that case.
template <class T>
struct BitsTraits<Unaligned<T>, typename std::enable_if<
- (std::is_integral<T>::value && std::is_unsigned<T>::value)>::type> {
+ (std::is_integral<T>::value)>::type> {
typedef T UnderlyingType;
static T load(const Unaligned<T>& x) { return x.value; }
static void store(Unaligned<T>& x, T v) { x.value = v; }
static T loadRMW(const Unaligned<T>& x) {
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wuninitialized"
-// make sure we compile without warning on gcc 4.6 with -Wpragmas
-#if __GNUC_PREREQ(4, 7)
-#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+ FOLLY_PUSH_WARNING
+ FOLLY_GCC_DISABLE_WARNING("-Wuninitialized")
+#if !__clang__ // for gcc version [4.8, ?)
+ FOLLY_GCC_DISABLE_WARNING("-Wmaybe-uninitialized")
+#endif
+ return x.value;
+ FOLLY_POP_WARNING
+ }
+};
+
+// Special version that allows one to disable address sanitizer on demand.
+template <class T>
+struct BitsTraits<UnalignedNoASan<T>, typename std::enable_if<
+ (std::is_integral<T>::value)>::type> {
+ typedef T UnderlyingType;
+ static T FOLLY_DISABLE_ADDRESS_SANITIZER
+ load(const UnalignedNoASan<T>& x) { return x.value; }
+ static void FOLLY_DISABLE_ADDRESS_SANITIZER
+ store(UnalignedNoASan<T>& x, T v) { x.value = v; }
+ static T FOLLY_DISABLE_ADDRESS_SANITIZER
+ loadRMW(const UnalignedNoASan<T>& x) {
+ FOLLY_PUSH_WARNING
+ FOLLY_GCC_DISABLE_WARNING("-Wuninitialized")
+#if !__clang__ // for gcc version [4.8, ?)
+ FOLLY_GCC_DISABLE_WARNING("-Wmaybe-uninitialized")
#endif
return x.value;
-#pragma GCC diagnostic pop
+ FOLLY_POP_WARNING
}
};
// Partial specialization for T, where T is unsigned integral
template <class T>
struct BitsTraits<T, typename std::enable_if<
- (std::is_integral<T>::value && std::is_unsigned<T>::value)>::type> {
+ (std::is_integral<T>::value)>::type> {
typedef T UnderlyingType;
static T load(const T& x) { return x; }
static void store(T& x, T v) { x = v; }
static T loadRMW(const T& x) {
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wuninitialized"
-#if __GNUC_PREREQ(4, 7)
-#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+ FOLLY_PUSH_WARNING
+ FOLLY_GCC_DISABLE_WARNING("-Wuninitialized")
+#if !__clang__ // for gcc version [4.8, ?)
+ FOLLY_GCC_DISABLE_WARNING("-Wmaybe-uninitialized")
#endif
return x;
-#pragma GCC diagnostic pop
+ FOLLY_POP_WARNING
}
};
-} // namespace detail
+
+} // namespace detail
/**
* Wrapper class with static methods for various bit-level operations,
/**
* Number of bits in a block.
*/
- static constexpr size_t bitsPerBlock =
- std::numeric_limits<UnderlyingType>::digits;
+ static constexpr size_t bitsPerBlock = std::numeric_limits<
+ typename std::make_unsigned<UnderlyingType>::type>::digits;
/**
* Byte index of the given bit.
* from the least significant count bits of value; little endian.
* (value & 1 becomes the bit at bitStart, etc)
* Precondition: count <= sizeof(T) * 8
+ * Precondition: value can fit in 'count' bits
*/
static void set(T* p, size_t bitStart, size_t count, UnderlyingType value);
static constexpr UnderlyingType zero = UnderlyingType(0);
static constexpr UnderlyingType one = UnderlyingType(1);
+ using UnsignedType = typename std::make_unsigned<UnderlyingType>::type;
static constexpr UnderlyingType ones(size_t count) {
- return count < bitsPerBlock ? (one << count) - 1 : ~zero;
+ return (count < bitsPerBlock)
+ ? static_cast<UnderlyingType>((UnsignedType{1} << count) - 1)
+ : ~zero;
}
};
// gcc 4.8 needs more -Wmaybe-uninitialized tickling, as it propagates the
// taint upstream from loadRMW
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wuninitialized"
-#if __GNUC_PREREQ(4, 7)
-#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+FOLLY_PUSH_WARNING
+FOLLY_GCC_DISABLE_WARNING("-Wuninitialized")
+#if !__clang__ // for gcc version [4.8, ?)
+FOLLY_GCC_DISABLE_WARNING("-Wmaybe-uninitialized")
#endif
template <class T, class Traits>
template <class T, class Traits>
inline void Bits<T, Traits>::set(T* p, size_t bitStart, size_t count,
UnderlyingType value) {
- assert(count <= sizeof(UnderlyingType) * 8);
+ DCHECK_LE(count, sizeof(UnderlyingType) * 8);
+ size_t cut = bitsPerBlock - count;
+ if (cut != 8 * sizeof(UnderlyingType)) {
+ using U = typename std::make_unsigned<UnderlyingType>::type;
+ DCHECK_EQ(value, UnderlyingType(U(value) << cut) >> cut);
+ }
size_t idx = blockIndex(bitStart);
size_t offset = bitOffset(bitStart);
+ if (std::is_signed<UnderlyingType>::value) {
+ value &= ones(count);
+ }
if (offset + count <= bitsPerBlock) {
innerSet(p + idx, offset, count, value);
} else {
size_t countInThisBlock = bitsPerBlock - offset;
size_t countInNextBlock = count - countInThisBlock;
- innerSet(p + idx, offset, countInThisBlock,
- value & ((one << countInThisBlock) - 1));
- innerSet(p + idx + 1, 0, countInNextBlock, value >> countInThisBlock);
+
+ UnderlyingType thisBlock = UnderlyingType(value & ones(countInThisBlock));
+ UnderlyingType nextBlock = UnderlyingType(value >> countInThisBlock);
+ if (std::is_signed<UnderlyingType>::value) {
+ nextBlock &= ones(countInNextBlock);
+ }
+ innerSet(p + idx, offset, countInThisBlock, thisBlock);
+ innerSet(p + idx + 1, 0, countInNextBlock, nextBlock);
}
}
Traits::store(*p, v);
}
-#pragma GCC diagnostic pop
+FOLLY_POP_WARNING
template <class T, class Traits>
inline bool Bits<T, Traits>::test(const T* p, size_t bit) {
template <class T, class Traits>
inline auto Bits<T, Traits>::get(const T* p, size_t bitStart, size_t count)
-> UnderlyingType {
- assert(count <= sizeof(UnderlyingType) * 8);
+ if (count == 0) {
+ return UnderlyingType{};
+ }
+
+ DCHECK_LE(count, sizeof(UnderlyingType) * 8);
size_t idx = blockIndex(bitStart);
size_t offset = bitOffset(bitStart);
+ UnderlyingType ret;
if (offset + count <= bitsPerBlock) {
- return innerGet(p + idx, offset, count);
+ ret = innerGet(p + idx, offset, count);
} else {
size_t countInThisBlock = bitsPerBlock - offset;
size_t countInNextBlock = count - countInThisBlock;
UnderlyingType thisBlockValue = innerGet(p + idx, offset, countInThisBlock);
UnderlyingType nextBlockValue = innerGet(p + idx + 1, 0, countInNextBlock);
- return (nextBlockValue << countInThisBlock) | thisBlockValue;
+ ret = (nextBlockValue << countInThisBlock) | thisBlockValue;
+ }
+ if (std::is_signed<UnderlyingType>::value) {
+ size_t emptyBits = bitsPerBlock - count;
+ ret <<= emptyBits;
+ ret >>= emptyBits;
}
+ return ret;
}
template <class T, class Traits>
inline auto Bits<T, Traits>::innerGet(const T* p, size_t offset, size_t count)
- -> UnderlyingType {
+ -> UnderlyingType {
return (Traits::load(*p) >> offset) & ones(count);
}
return n;
}
-} // namespace folly
-
-#endif /* FOLLY_EXPERIMENTAL_BITS_H_ */
-
+} // namespace folly