Fix -Wsign-compare
[folly.git] / folly / experimental / Bits.h
index df23d8594103fc723e33afc21f3ae5e338418d9c..0601513fd3ee596e4f2d23ad677d2b509179416a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2014 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <type_traits>
 #include <limits>
 
-#include "folly/Bits.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.
@@ -37,7 +41,8 @@ namespace detail {
  * (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
@@ -45,13 +50,39 @@ template <class T, class Enable=void> struct BitsTraits;
 // 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"
+#endif
+    return x.value;
+#pragma GCC diagnostic pop
+  }
+};
+
+// Special version that allows 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) {
+#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"
+#endif
     return x.value;
 #pragma GCC diagnostic pop
   }
@@ -60,17 +91,21 @@ struct BitsTraits<Unaligned<T>, typename std::enable_if<
 // 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"
+#endif
     return x;
 #pragma GCC diagnostic pop
   }
 };
+
 }  // namespace detail
 
 /**
@@ -88,8 +123,8 @@ struct Bits {
   /**
    * 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.
@@ -132,6 +167,7 @@ struct Bits {
    * 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);
 
@@ -164,6 +200,15 @@ struct Bits {
   }
 };
 
+// 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"
+#endif
+
 template <class T, class Traits>
 inline void Bits<T, Traits>::set(T* p, size_t bit) {
   T& block = p[blockIndex(bit)];
@@ -176,58 +221,77 @@ inline void Bits<T, Traits>::clear(T* p, size_t bit) {
   Traits::store(block, Traits::loadRMW(block) & ~(one << bitOffset(bit)));
 }
 
-template <class T, class Traits>
-inline bool Bits<T, Traits>::test(const T* p, size_t bit) {
-  return Traits::load(p[blockIndex(bit)]) & (one << bitOffset(bit));
-}
-
 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);
+  size_t cut = bitsPerBlock - count;
+  assert(value == (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 = value & ((one << countInThisBlock) - 1);
+    UnderlyingType nextBlock = value >> countInThisBlock;
+    if (std::is_signed<UnderlyingType>::value) {
+      nextBlock &= ones(countInNextBlock);
+    }
+    innerSet(p + idx, offset, countInThisBlock, thisBlock);
+    innerSet(p + idx + 1, 0, countInNextBlock, nextBlock);
   }
 }
 
+template <class T, class Traits>
+inline void Bits<T, Traits>::innerSet(T* p, size_t offset, size_t count,
+                                      UnderlyingType value) {
+  // Mask out bits and set new value
+  UnderlyingType v = Traits::loadRMW(*p);
+  v &= ~(ones(count) << offset);
+  v |= (value << offset);
+  Traits::store(*p, v);
+}
+
+#pragma GCC diagnostic pop
+
+template <class T, class Traits>
+inline bool Bits<T, Traits>::test(const T* p, size_t bit) {
+  return Traits::load(p[blockIndex(bit)]) & (one << bitOffset(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);
   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;
   }
-}
-
-template <class T, class Traits>
-inline void Bits<T, Traits>::innerSet(T* p, size_t offset, size_t count,
-                                      UnderlyingType value) {
-  // Mask out bits and set new value
-  UnderlyingType v = Traits::loadRMW(*p);
-  v &= ~(ones(count) << offset);
-  v |= (value << offset);
-  Traits::store(*p, v);
+  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);
 }
 
@@ -243,4 +307,3 @@ inline size_t Bits<T, Traits>::count(const T* begin, const T* end) {
 }  // namespace folly
 
 #endif /* FOLLY_EXPERIMENTAL_BITS_H_ */
-