mirror of
https://github.com/greg7mdp/parallel-hashmap.git
synced 2026-08-29 08:34:39 +08:00
manually merge some changes from abseil-cpp
This commit is contained in:
Vendored
+39
-17
@@ -858,14 +858,14 @@ namespace priv {
|
||||
// Upper bound for the available space for values. This is largest for leaf
|
||||
// nodes, which have overhead of at least a pointer + 4 bytes (for storing
|
||||
// 3 field_types and an enum).
|
||||
kNodeValueSpace =
|
||||
TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4),
|
||||
kNodeSlotSpace =
|
||||
TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4),
|
||||
};
|
||||
|
||||
// This is an integral type large enough to hold as many
|
||||
// ValueSize-values as will fit a node of TargetNodeSize bytes.
|
||||
using node_count_type =
|
||||
phmap::conditional_t<(kNodeValueSpace / sizeof(value_type) >
|
||||
phmap::conditional_t<(kNodeSlotSpace / sizeof(slot_type) >
|
||||
(std::numeric_limits<uint8_t>::max)()),
|
||||
uint16_t, uint8_t>; // NOLINT
|
||||
|
||||
@@ -2501,7 +2501,7 @@ namespace priv {
|
||||
"key comparison function must return phmap::{weak,strong}_ordering or "
|
||||
"bool.");
|
||||
|
||||
// Test the assumption made in setting kNodeValueSpace.
|
||||
// Test the assumption made in setting kNodeSlotSpace.
|
||||
static_assert(node_type::MinimumOverhead() >= sizeof(void *) + 4,
|
||||
"node space assumption incorrect");
|
||||
|
||||
@@ -3314,6 +3314,12 @@ namespace priv {
|
||||
const_reverse_iterator crend() const { return tree_.rend(); }
|
||||
|
||||
// Lookup routines.
|
||||
// ----------------
|
||||
template <typename K = key_type>
|
||||
size_type count(const key_arg<K> &key) const {
|
||||
auto equal_range = this->equal_range(key);
|
||||
return std::distance(equal_range.first, equal_range.second);
|
||||
}
|
||||
template <typename K = key_type>
|
||||
iterator find(const key_arg<K> &key) {
|
||||
return tree_.find(key);
|
||||
@@ -3350,7 +3356,11 @@ namespace priv {
|
||||
iterator erase(const_iterator first, const_iterator last) {
|
||||
return tree_.erase(iterator(first), iterator(last)).second;
|
||||
}
|
||||
|
||||
template <typename K = key_type>
|
||||
size_type erase(const key_arg<K> &key) {
|
||||
auto equal_range = this->equal_range(key);
|
||||
return tree_.erase_range(equal_range.first, equal_range.second).first;
|
||||
}
|
||||
node_type extract(iterator position) {
|
||||
// Use Move instead of Transfer, because the rebalancing code expects to
|
||||
// have a valid object to scribble metadata bits on top of.
|
||||
@@ -3449,6 +3459,10 @@ namespace priv {
|
||||
const allocator_type &alloc = allocator_type())
|
||||
: btree_set_container(init.begin(), init.end(), comp, alloc) {}
|
||||
|
||||
btree_set_container(std::initializer_list<init_type> init,
|
||||
const allocator_type &alloc)
|
||||
: btree_set_container(init.begin(), init.end(), alloc) {}
|
||||
|
||||
// Lookup routines.
|
||||
template <typename K = key_type>
|
||||
size_type count(const key_arg<K> &key) const {
|
||||
@@ -3467,23 +3481,23 @@ namespace priv {
|
||||
init_type v(std::forward<Args>(args)...);
|
||||
return this->tree_.insert_unique(params_type::key(v), std::move(v));
|
||||
}
|
||||
iterator insert(const_iterator position, const value_type &x) {
|
||||
iterator insert(const_iterator hint, const value_type &x) {
|
||||
return this->tree_
|
||||
.insert_hint_unique(iterator(position), params_type::key(x), x)
|
||||
.insert_hint_unique(iterator(hint), params_type::key(x), x)
|
||||
.first;
|
||||
}
|
||||
iterator insert(const_iterator position, value_type &&x) {
|
||||
iterator insert(const_iterator hint, value_type &&x) {
|
||||
return this->tree_
|
||||
.insert_hint_unique(iterator(position), params_type::key(x),
|
||||
.insert_hint_unique(iterator(hint), params_type::key(x),
|
||||
std::move(x))
|
||||
.first;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
iterator emplace_hint(const_iterator position, Args &&... args) {
|
||||
iterator emplace_hint(const_iterator hint, Args &&... args) {
|
||||
init_type v(std::forward<Args>(args)...);
|
||||
return this->tree_
|
||||
.insert_hint_unique(iterator(position), params_type::key(v),
|
||||
.insert_hint_unique(iterator(hint), params_type::key(v),
|
||||
std::move(v))
|
||||
.first;
|
||||
}
|
||||
@@ -3705,11 +3719,11 @@ namespace priv {
|
||||
iterator insert(value_type &&x) {
|
||||
return this->tree_.insert_multi(std::move(x));
|
||||
}
|
||||
iterator insert(const_iterator position, const value_type &x) {
|
||||
return this->tree_.insert_hint_multi(iterator(position), x);
|
||||
iterator insert(const_iterator hint, const value_type &x) {
|
||||
return this->tree_.insert_hint_multi(iterator(hint), x);
|
||||
}
|
||||
iterator insert(const_iterator position, value_type &&x) {
|
||||
return this->tree_.insert_hint_multi(iterator(position), std::move(x));
|
||||
iterator insert(const_iterator hint, value_type &&x) {
|
||||
return this->tree_.insert_hint_multi(iterator(hint), std::move(x));
|
||||
}
|
||||
template <typename InputIterator>
|
||||
void insert(InputIterator b, InputIterator e) {
|
||||
@@ -3723,9 +3737,9 @@ namespace priv {
|
||||
return this->tree_.insert_multi(init_type(std::forward<Args>(args)...));
|
||||
}
|
||||
template <typename... Args>
|
||||
iterator emplace_hint(const_iterator position, Args &&... args) {
|
||||
iterator emplace_hint(const_iterator hint, Args &&... args) {
|
||||
return this->tree_.insert_hint_multi(
|
||||
iterator(position), init_type(std::forward<Args>(args)...));
|
||||
iterator(hint), init_type(std::forward<Args>(args)...));
|
||||
}
|
||||
iterator insert(node_type &&node) {
|
||||
if (!node) return this->end();
|
||||
@@ -3839,6 +3853,8 @@ namespace priv {
|
||||
using Base::contains;
|
||||
using Base::count;
|
||||
using Base::equal_range;
|
||||
using Base::lower_bound;
|
||||
using Base::upper_bound;
|
||||
using Base::find;
|
||||
using Base::get_allocator;
|
||||
using Base::key_comp;
|
||||
@@ -3896,6 +3912,8 @@ namespace priv {
|
||||
using Base::contains;
|
||||
using Base::count;
|
||||
using Base::equal_range;
|
||||
using Base::lower_bound;
|
||||
using Base::upper_bound;
|
||||
using Base::find;
|
||||
using Base::get_allocator;
|
||||
using Base::key_comp;
|
||||
@@ -3956,6 +3974,8 @@ namespace priv {
|
||||
using Base::contains;
|
||||
using Base::count;
|
||||
using Base::equal_range;
|
||||
using Base::lower_bound;
|
||||
using Base::upper_bound;
|
||||
using Base::find;
|
||||
using Base::operator[];
|
||||
using Base::get_allocator;
|
||||
@@ -4013,6 +4033,8 @@ namespace priv {
|
||||
using Base::contains;
|
||||
using Base::count;
|
||||
using Base::equal_range;
|
||||
using Base::lower_bound;
|
||||
using Base::upper_bound;
|
||||
using Base::find;
|
||||
using Base::get_allocator;
|
||||
using Base::key_comp;
|
||||
|
||||
Vendored
+95
-26
@@ -34,6 +34,58 @@
|
||||
// limitations under the License.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IMPLEMENTATION DETAILS
|
||||
//
|
||||
// The table stores elements inline in a slot array. In addition to the slot
|
||||
// array the table maintains some control state per slot. The extra state is one
|
||||
// byte per slot and stores empty or deleted marks, or alternatively 7 bits from
|
||||
// the hash of an occupied slot. The table is split into logical groups of
|
||||
// slots, like so:
|
||||
//
|
||||
// Group 1 Group 2 Group 3
|
||||
// +---------------+---------------+---------------+
|
||||
// | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||
// +---------------+---------------+---------------+
|
||||
//
|
||||
// On lookup the hash is split into two parts:
|
||||
// - H2: 7 bits (those stored in the control bytes)
|
||||
// - H1: the rest of the bits
|
||||
// The groups are probed using H1. For each group the slots are matched to H2 in
|
||||
// parallel. Because H2 is 7 bits (128 states) and the number of slots per group
|
||||
// is low (8 or 16) in almost all cases a match in H2 is also a lookup hit.
|
||||
//
|
||||
// On insert, once the right group is found (as in lookup), its slots are
|
||||
// filled in order.
|
||||
//
|
||||
// On erase a slot is cleared. In case the group did not have any empty slots
|
||||
// before the erase, the erased slot is marked as deleted.
|
||||
//
|
||||
// Groups without empty slots (but maybe with deleted slots) extend the probe
|
||||
// sequence. The probing algorithm is quadratic. Given N the number of groups,
|
||||
// the probing function for the i'th probe is:
|
||||
//
|
||||
// P(0) = H1 % N
|
||||
//
|
||||
// P(i) = (P(i - 1) + i) % N
|
||||
//
|
||||
// This probing function guarantees that after N probes, all the groups of the
|
||||
// table will be probed exactly once.
|
||||
//
|
||||
// The control state and slot array are stored contiguously in a shared heap
|
||||
// allocation. The layout of this allocation is: `capacity()` control bytes,
|
||||
// one sentinel control byte, `Group::kWidth - 1` cloned control bytes,
|
||||
// <possible padding>, `capacity()` slots. The sentinel control byte is used in
|
||||
// iteration so we know when we reach the end of the table. The cloned control
|
||||
// bytes at the end of the table are cloned from the beginning of the table so
|
||||
// groups that begin near the end of the table can see a full group. In cases in
|
||||
// which there are more than `capacity()` cloned control bytes, the extra bytes
|
||||
// are `kEmpty`, and these ensure that we always see at least one empty slot and
|
||||
// can stop an unsuccessful search.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
|
||||
@@ -76,6 +128,17 @@ namespace phmap {
|
||||
|
||||
namespace priv {
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
template <typename AllocType>
|
||||
void SwapAlloc(AllocType& lhs, AllocType& rhs,
|
||||
std::true_type /* propagate_on_container_swap */) {
|
||||
using std::swap;
|
||||
swap(lhs, rhs);
|
||||
}
|
||||
template <typename AllocType>
|
||||
void SwapAlloc(AllocType& /*lhs*/, AllocType& /*rhs*/,
|
||||
std::false_type /* propagate_on_container_swap */) {}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
template <size_t Width>
|
||||
class probe_seq
|
||||
@@ -181,26 +244,24 @@ public:
|
||||
return *this;
|
||||
}
|
||||
explicit operator bool() const { return mask_ != 0; }
|
||||
int operator*() const { return LowestBitSet(); }
|
||||
int LowestBitSet() const {
|
||||
uint32_t operator*() const { return LowestBitSet(); }
|
||||
uint32_t LowestBitSet() const {
|
||||
return priv::TrailingZeros(mask_) >> Shift;
|
||||
}
|
||||
int HighestBitSet() const {
|
||||
return (sizeof(T) * CHAR_BIT - priv::LeadingZeros(mask_) -
|
||||
1) >>
|
||||
Shift;
|
||||
uint32_t HighestBitSet() const {
|
||||
return (sizeof(T) * CHAR_BIT - priv::LeadingZeros(mask_) - 1) >> Shift;
|
||||
}
|
||||
|
||||
BitMask begin() const { return *this; }
|
||||
BitMask end() const { return BitMask(0); }
|
||||
|
||||
int TrailingZeros() const {
|
||||
uint32_t TrailingZeros() const {
|
||||
return priv::TrailingZeros(mask_) >> Shift;
|
||||
}
|
||||
|
||||
int LeadingZeros() const {
|
||||
constexpr int total_significant_bits = SignificantBits << Shift;
|
||||
constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits;
|
||||
uint32_t LeadingZeros() const {
|
||||
constexpr uint32_t total_significant_bits = SignificantBits << Shift;
|
||||
constexpr uint32_t extra_bits = sizeof(T) * 8 - total_significant_bits;
|
||||
return priv::LeadingZeros(mask_ << extra_bits) >> Shift;
|
||||
}
|
||||
|
||||
@@ -286,10 +347,10 @@ inline size_t H1(size_t hashval, const ctrl_t* ) {
|
||||
#endif
|
||||
|
||||
|
||||
inline ctrl_t H2(size_t hashval) { return (ctrl_t)(hashval & 0x7F); }
|
||||
inline h2_t H2(size_t hashval) { return (ctrl_t)(hashval & 0x7F); }
|
||||
|
||||
inline bool IsEmpty(ctrl_t c) { return c == kEmpty; }
|
||||
inline bool IsFull(ctrl_t c) { return c >= 0; }
|
||||
inline bool IsFull(ctrl_t c) { return c >= static_cast<ctrl_t>(0); }
|
||||
inline bool IsDeleted(ctrl_t c) { return c == kDeleted; }
|
||||
inline bool IsEmptyOrDeleted(ctrl_t c) { return c < kSentinel; }
|
||||
|
||||
@@ -338,7 +399,7 @@ struct GroupSse2Impl
|
||||
BitMask<uint32_t, kWidth> Match(h2_t hash) const {
|
||||
auto match = _mm_set1_epi8((char)hash);
|
||||
return BitMask<uint32_t, kWidth>(
|
||||
_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl)));
|
||||
static_cast<uint32_t>(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))));
|
||||
}
|
||||
|
||||
// Returns a bitmask representing the positions of empty slots.
|
||||
@@ -347,7 +408,7 @@ struct GroupSse2Impl
|
||||
#if PHMAP_HAVE_SSSE3
|
||||
// This only works because kEmpty is -128.
|
||||
return BitMask<uint32_t, kWidth>(
|
||||
_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl)));
|
||||
static_cast<uint32_t>(_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))));
|
||||
#else
|
||||
return Match(static_cast<h2_t>(kEmpty));
|
||||
#endif
|
||||
@@ -356,17 +417,17 @@ struct GroupSse2Impl
|
||||
// Returns a bitmask representing the positions of empty or deleted slots.
|
||||
// -----------------------------------------------------------------------
|
||||
BitMask<uint32_t, kWidth> MatchEmptyOrDeleted() const {
|
||||
auto special = _mm_set1_epi8(kSentinel);
|
||||
auto special = _mm_set1_epi8(static_cast<uint8_t>(kSentinel));
|
||||
return BitMask<uint32_t, kWidth>(
|
||||
_mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)));
|
||||
static_cast<uint32_t>(_mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl))));
|
||||
}
|
||||
|
||||
// Returns the number of trailing empty or deleted elements in the group.
|
||||
// ----------------------------------------------------------------------
|
||||
uint32_t CountLeadingEmptyOrDeleted() const {
|
||||
auto special = _mm_set1_epi8(kSentinel);
|
||||
auto special = _mm_set1_epi8(static_cast<uint8_t>(kSentinel));
|
||||
return TrailingZeros(
|
||||
_mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1);
|
||||
static_cast<uint32_t>(_mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -453,6 +514,11 @@ struct GroupPortableImpl
|
||||
using Group = GroupPortableImpl;
|
||||
#endif
|
||||
|
||||
// The number of cloned control bytes that we copy from the beginning to the
|
||||
// end of the control bytes array.
|
||||
// -------------------------------------------------------------------------
|
||||
constexpr size_t NumClonedBytes() { return Group::kWidth - 1; }
|
||||
|
||||
template <class Policy, class Hash, class Eq, class Alloc>
|
||||
class raw_hash_set;
|
||||
|
||||
@@ -1341,7 +1407,9 @@ public:
|
||||
}
|
||||
|
||||
iterator insert(const_iterator, node_type&& node) {
|
||||
return insert(std::move(node)).first;
|
||||
auto res = insert(std::move(node));
|
||||
node = std::move(res.node);
|
||||
return res.position;
|
||||
}
|
||||
|
||||
// This overload kicks in if we can deduce the key from args. This enables us
|
||||
@@ -1759,7 +1827,7 @@ private:
|
||||
auto seq = probe(hashval);
|
||||
while (true) {
|
||||
Group g{ ctrl_ + seq.offset() };
|
||||
for (int i : g.Match((h2_t)H2(hashval))) {
|
||||
for (uint32_t i : g.Match((h2_t)H2(hashval))) {
|
||||
offset = seq.offset((size_t)i);
|
||||
if (PHMAP_PREDICT_TRUE(PolicyTraits::apply(
|
||||
EqualElement<K>{key, eq_ref()},
|
||||
@@ -2033,7 +2101,7 @@ private:
|
||||
auto seq = probe(hashval);
|
||||
while (true) {
|
||||
Group g{ctrl_ + seq.offset()};
|
||||
for (int i : g.Match((h2_t)H2(hashval))) {
|
||||
for (uint32_t i : g.Match((h2_t)H2(hashval))) {
|
||||
if (PHMAP_PREDICT_TRUE(PolicyTraits::element(slots_ + seq.offset((size_t)i)) ==
|
||||
elem))
|
||||
return true;
|
||||
@@ -2095,7 +2163,7 @@ protected:
|
||||
auto seq = probe(hashval);
|
||||
while (true) {
|
||||
Group g{ctrl_ + seq.offset()};
|
||||
for (int i : g.Match((h2_t)H2(hashval))) {
|
||||
for (uint32_t i : g.Match((h2_t)H2(hashval))) {
|
||||
if (PHMAP_PREDICT_TRUE(PolicyTraits::apply(
|
||||
EqualElement<K>{key, eq_ref()},
|
||||
PolicyTraits::element(slots_ + seq.offset((size_t)i)))))
|
||||
@@ -2261,9 +2329,10 @@ public:
|
||||
using key_arg = typename KeyArgImpl::template type<K, key_type>;
|
||||
|
||||
static_assert(!std::is_reference<key_type>::value, "");
|
||||
// TODO(alkis): remove this assertion and verify that reference mapped_type is
|
||||
// supported.
|
||||
static_assert(!std::is_reference<mapped_type>::value, "");
|
||||
|
||||
// TODO(b/187807849): Evaluate whether to support reference mapped_type and
|
||||
// remove this assertion if/when it is supported.
|
||||
static_assert(!std::is_reference<mapped_type>::value, "");
|
||||
|
||||
using iterator = typename raw_hash_map::raw_hash_set::iterator;
|
||||
using const_iterator = typename raw_hash_map::raw_hash_set::const_iterator;
|
||||
@@ -4327,7 +4396,7 @@ struct HashtableDebugAccess<Set, phmap::void_t<typename Set::raw_hash_set>>
|
||||
auto seq = set.probe(hashval);
|
||||
while (true) {
|
||||
priv::Group g{set.ctrl_ + seq.offset()};
|
||||
for (int i : g.Match(priv::H2(hashval))) {
|
||||
for (uint32_t i : g.Match(priv::H2(hashval))) {
|
||||
if (Traits::apply(
|
||||
typename Set::template EqualElement<typename Set::key_type>{
|
||||
key, set.eq_ref()},
|
||||
|
||||
Vendored
+4
-3
@@ -2856,6 +2856,7 @@ class node_handle<Policy, PolicyTraits, Alloc,
|
||||
: public node_handle_base<PolicyTraits, Alloc>
|
||||
{
|
||||
using Base = node_handle_base<PolicyTraits, Alloc>;
|
||||
using slot_type = typename PolicyTraits::slot_type;
|
||||
|
||||
public:
|
||||
using key_type = typename Policy::key_type;
|
||||
@@ -3888,7 +3889,7 @@ public:
|
||||
constexpr size_t Offset() const {
|
||||
static_assert(N < NumOffsets, "Index out of bounds");
|
||||
return adl_barrier::Align(
|
||||
Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1],
|
||||
Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * size_[N - 1],
|
||||
ElementAlignment<N>::value);
|
||||
}
|
||||
|
||||
@@ -4081,7 +4082,7 @@ public:
|
||||
constexpr size_t AllocSize() const {
|
||||
static_assert(NumTypes == NumSizes, "You must specify sizes of all fields");
|
||||
return Offset<NumTypes - 1>() +
|
||||
SizeOf<ElementType<NumTypes - 1>>() * size_[NumTypes - 1];
|
||||
SizeOf<ElementType<NumTypes - 1>>::value * size_[NumTypes - 1];
|
||||
}
|
||||
|
||||
// If built with --config=asan, poisons padding bytes (if any) in the
|
||||
@@ -4105,7 +4106,7 @@ public:
|
||||
// The `if` is an optimization. It doesn't affect the observable behaviour.
|
||||
if (ElementAlignment<N - 1>::value % ElementAlignment<N>::value) {
|
||||
size_t start =
|
||||
Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1];
|
||||
Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * size_[N - 1];
|
||||
ASAN_POISON_MEMORY_REGION(p + start, Offset<N>() - start);
|
||||
}
|
||||
#endif
|
||||
|
||||
Vendored
+13
-12
@@ -315,19 +315,19 @@ PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) {
|
||||
#endif
|
||||
}
|
||||
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) {
|
||||
int zeroes = 28;
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE uint32_t CountLeadingZeros32Slow(uint64_t n) {
|
||||
uint32_t zeroes = 28;
|
||||
if (n >> 16) zeroes -= 16, n >>= 16;
|
||||
if (n >> 8) zeroes -= 8, n >>= 8;
|
||||
if (n >> 4) zeroes -= 4, n >>= 4;
|
||||
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
|
||||
}
|
||||
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) {
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE uint32_t CountLeadingZeros32(uint32_t n) {
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if (_BitScanReverse(&result, n)) {
|
||||
return (int)(31 - result);
|
||||
return (uint32_t)(31 - result);
|
||||
}
|
||||
return 32;
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
@@ -348,8 +348,8 @@ PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) {
|
||||
#endif
|
||||
}
|
||||
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) {
|
||||
int c = 63;
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE uint32_t CountTrailingZerosNonZero64Slow(uint64_t n) {
|
||||
uint32_t c = 63;
|
||||
n &= ~n + 1;
|
||||
if (n & 0x00000000FFFFFFFF) c -= 32;
|
||||
if (n & 0x0000FFFF0000FFFF) c -= 16;
|
||||
@@ -360,11 +360,11 @@ PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n)
|
||||
return c;
|
||||
}
|
||||
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) {
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE uint32_t CountTrailingZerosNonZero64(uint64_t n) {
|
||||
#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64)
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
_BitScanForward64(&result, n);
|
||||
return (int)result;
|
||||
return (uint32_t)result;
|
||||
#elif defined(_MSC_VER) && !defined(__clang__)
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if (static_cast<uint32_t>(n) == 0) {
|
||||
@@ -382,8 +382,8 @@ PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) {
|
||||
#endif
|
||||
}
|
||||
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) {
|
||||
int c = 31;
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE uint32_t CountTrailingZerosNonZero32Slow(uint32_t n) {
|
||||
uint32_t c = 31;
|
||||
n &= ~n + 1;
|
||||
if (n & 0x0000FFFF) c -= 16;
|
||||
if (n & 0x00FF00FF) c -= 8;
|
||||
@@ -393,11 +393,11 @@ PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n)
|
||||
return c;
|
||||
}
|
||||
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) {
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE uint32_t CountTrailingZerosNonZero32(uint32_t n) {
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
_BitScanForward(&result, n);
|
||||
return (int)result;
|
||||
return (uint32_t)result;
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
static_assert(sizeof(int) == sizeof(n),
|
||||
"__builtin_ctz does not take 32-bit arg");
|
||||
@@ -568,6 +568,7 @@ namespace little_endian {
|
||||
#endif /* ENDIAN */
|
||||
|
||||
// Functions to do unaligned loads and stores in little-endian order.
|
||||
// ------------------------------------------------------------------
|
||||
inline uint16_t Load16(const void *p) {
|
||||
return ToHost16(PHMAP_INTERNAL_UNALIGNED_LOAD16(p));
|
||||
}
|
||||
|
||||
Vendored
+23
-5
@@ -128,15 +128,33 @@
|
||||
|
||||
#define PHMAP_BRANCHLESS 1
|
||||
|
||||
#ifdef __has_feature
|
||||
#define PHMAP_HAVE_FEATURE(f) __has_feature(f)
|
||||
#else
|
||||
#define PHMAP_HAVE_FEATURE(f) 0
|
||||
#endif
|
||||
|
||||
// Portable check for GCC minimum version:
|
||||
// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
|
||||
#if defined(__GNUC__) && defined(__GNUC_MINOR__)
|
||||
#define PHMAP_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y))
|
||||
#else
|
||||
#define PHMAP_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) 0
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__)
|
||||
#define PHMAP_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) (__clang_major__ > (x) || __clang_major__ == (x) && __clang_minor__ >= (y))
|
||||
#else
|
||||
#define PHMAP_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) 0
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Checks whether `std::is_trivially_destructible<T>` is supported.
|
||||
// ----------------------------------------------------------------
|
||||
#ifdef PHMAP_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
|
||||
#error PHMAP_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set
|
||||
#elif defined(_LIBCPP_VERSION) || \
|
||||
(!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && \
|
||||
(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \
|
||||
defined(_MSC_VER)
|
||||
#elif defined(_LIBCPP_VERSION) || defined(_MSC_VER) || \
|
||||
(!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && PHMAP_INTERNAL_HAVE_MIN_GNUC_VERSION(4, 8))
|
||||
#define PHMAP_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1
|
||||
#endif
|
||||
|
||||
@@ -150,7 +168,7 @@
|
||||
#error PHMAP_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set
|
||||
#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \
|
||||
(!defined(__clang__) && defined(__GNUC__) && \
|
||||
(__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)) && \
|
||||
PHMAP_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 1) && \
|
||||
(defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \
|
||||
(defined(_MSC_VER) && !defined(__NVCC__))
|
||||
#define PHMAP_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1
|
||||
|
||||
Reference in New Issue
Block a user