#pragma once /// Packed versions HashMap, please keep in sync with HashMap.h #include /// A pair that does not initialize the elements, if not needed. /// /// NOTE: makePairNoInit() is omitted for PackedPairNoInit since it is not /// required for PackedHashMap (see mergeBlockWithPipe() for details) template struct __attribute__((packed)) PackedPairNoInit { First first; Second second; PackedPairNoInit() {} /// NOLINT template PackedPairNoInit(FirstValue && first_, NoInitTag) : first(std::forward(first_)) { } template PackedPairNoInit(FirstValue && first_, SecondValue && second_) : first(std::forward(first_)) , second(std::forward(second_)) { } }; /// The difference with ZeroTraits is that PackedZeroTraits accepts PackedPairNoInit instead of Key. namespace PackedZeroTraits { template class PackedPairNoInit> bool check(const PackedPairNoInit p) { return p.first == First{}; } template class PackedPairNoInit> void set(PackedPairNoInit & p) { p.first = First{}; } } /// setZero() should be overwritten to pass the pair instead of key, to avoid /// "reference binding to misaligned address" errors from UBsan. template struct PackedHashMapCell : public HashMapCell> { using Base = HashMapCell>; using State = typename Base::State; using value_type = typename Base::value_type; using key_type = typename Base::key_type; using Mapped = typename Base::Mapped; using Base::Base; void setZero() { PackedZeroTraits::set(this->value); } Key getKey() const { return this->value.first; } static Key getKey(const value_type & value_) { return value_.first; } Mapped & getMapped() { return this->value.second; } Mapped getMapped() const { return this->value.second; } value_type getValue() const { return this->value; } bool keyEquals(const Key key_) const { return bitEqualsByValue(this->value.first, key_); } bool keyEquals(const Key key_, size_t /*hash_*/) const { return bitEqualsByValue(this->value.first, key_); } bool keyEquals(const Key key_, size_t /*hash_*/, const State & /*state*/) const { return bitEqualsByValue(this->value.first, key_); } bool isZero(const State & state) const { return isZero(this->value.first, state); } static bool isZero(const Key key, const State & /*state*/) { return ZeroTraits::check(key); } static bool bitEqualsByValue(key_type a, key_type b) { return a == b; } template auto get() const { if constexpr (I == 0) return this->value.first; else if constexpr (I == 1) return this->value.second; } }; namespace std { template struct tuple_size> : std::integral_constant { }; template struct tuple_element<0, PackedHashMapCell> { using type = Key; }; template struct tuple_element<1, PackedHashMapCell> { using type = TMapped; }; } /// Packed HashMap - HashMap with structure without padding /// /// Sometimes padding in structure can be crucial, consider the following /// example as in this case the padding overhead /// is 0.375, and this can be major in case of lots of keys. /// /// Note, there is no need to provide PackedHashSet, since it cannot have padding. template < typename Key, typename Mapped, typename Hash = DefaultHash, typename Grower = HashTableGrower<>, typename Allocator = HashTableAllocator> using PackedHashMap = HashMapTable, Hash, Grower, Allocator>;