Rework hashing, use a more extensible framework similar to n3876 proposal

std::hash specialization is a pain to work with, stop using that, and
just specialize a 'size_t hash_value(const T&)' free function.
This commit is contained in:
Maxime Coste 2014-12-16 18:57:19 +00:00
parent dbd7bd41bb
commit ebecd60eb8
31 changed files with 240 additions and 214 deletions

View File

@ -3,8 +3,7 @@
#include "safe_ptr.hh"
#include "string.hh"
#include <unordered_map>
#include "unordered_map.hh"
namespace Kakoune
{
@ -24,7 +23,7 @@ private:
AliasRegistry() {}
safe_ptr<AliasRegistry> m_parent;
std::unordered_map<String, String> m_aliases;
UnorderedMap<String, String> m_aliases;
};
}

View File

@ -1,6 +1,8 @@
#ifndef color_hh_INCLUDED
#define color_hh_INCLUDED
#include "hash.hh"
namespace Kakoune
{
@ -32,13 +34,19 @@ struct Color
Color(Colors c) : color{c}, r{0}, g{0}, b{0} {}
Color(unsigned char r, unsigned char g, unsigned char b)
: color{Colors::RGB}, r{r}, g{g}, b{b} {}
bool operator==(Color c) const
{ return color == c.color and r == c.r and g == c.g and b == c.b; }
bool operator!=(Color c) const
{ return color != c.color or r != c.r or g != c.g or b != c.b; }
};
inline bool operator==(Color lhs, Color rhs)
{
return lhs.color == rhs.color and
lhs.r == rhs.r and lhs.g == rhs.g and lhs.b == rhs.b;
}
inline bool operator!=(Color lhs, Color rhs)
{
return not (lhs == rhs);
}
Color str_to_color(StringView color);
String color_to_str(Color color);
@ -47,6 +55,11 @@ void option_from_string(StringView str, Color& color);
bool is_color_name(StringView color);
inline size_t hash_value(const Color& val)
{
return hash_values(val.color, val.r, val.g, val.b);
}
}
#endif // color_hh_INCLUDED

View File

@ -189,7 +189,7 @@ Token parse_percent_token(StringView line, ByteCount& pos)
type_name + "'"};
Token::Type type = token_type<throw_on_unterminated>(type_name);
static const std::unordered_map<char, char> matching_delimiters = {
static const UnorderedMap<char, char> matching_delimiters = {
{ '(', ')' }, { '[', ']' }, { '{', '}' }, { '<', '>' }
};

View File

@ -9,8 +9,8 @@
#include "parameters_parser.hh"
#include "string.hh"
#include "utils.hh"
#include "unordered_map.hh"
#include <unordered_map>
#include <functional>
#include <initializer_list>
@ -88,7 +88,7 @@ private:
CommandFlags flags;
CommandCompleter completer;
};
using CommandMap = std::unordered_map<String, CommandDescriptor>;
using CommandMap = UnorderedMap<String, CommandDescriptor>;
CommandMap m_commands;
CommandMap::const_iterator find_command(const Context& context,

View File

@ -2,6 +2,7 @@
#define coord_hh_INCLUDED
#include "units.hh"
#include "hash.hh"
namespace Kakoune
{
@ -92,6 +93,11 @@ struct ByteCoord : LineAndColumn<ByteCoord, LineCount, ByteCount>
: LineAndColumn(line, column) {}
};
inline size_t hash_value(const ByteCoord& val)
{
return hash_values(val.line, val.column);
}
struct CharCoord : LineAndColumn<CharCoord, LineCount, CharCount>
{
[[gnu::always_inline]]
@ -99,6 +105,11 @@ struct CharCoord : LineAndColumn<CharCoord, LineCount, CharCount>
: LineAndColumn(line, column) {}
};
inline size_t hash_value(const CharCoord& val)
{
return hash_values(val.line, val.column);
}
struct ByteCoordAndTarget : ByteCoord
{
[[gnu::always_inline]]
@ -112,6 +123,11 @@ struct ByteCoordAndTarget : ByteCoord
CharCount target;
};
inline size_t hash_value(const ByteCoordAndTarget& val)
{
return hash_values(val.line, val.column, val.target);
}
}
#endif // coord_hh_INCLUDED

View File

@ -1,13 +1,13 @@
#ifndef env_vars_hh_INCLUDED
#define env_vars_hh_INCLUDED
#include <unordered_map>
#include "unordered_map.hh"
namespace Kakoune
{
class String;
using EnvVarMap = std::unordered_map<String, String>;
using EnvVarMap = UnorderedMap<String, String>;
EnvVarMap get_env_vars();

View File

@ -4,8 +4,7 @@
#include "face.hh"
#include "utils.hh"
#include "completion.hh"
#include <unordered_map>
#include "unordered_map.hh"
namespace Kakoune
{
@ -30,7 +29,7 @@ private:
FaceOrAlias(Face face = Face{}) : face(face) {}
};
std::unordered_map<String, FaceOrAlias> m_aliases;
UnorderedMap<String, FaceOrAlias> m_aliases;
};
inline Face get_face(const String& facedesc)

View File

@ -356,7 +356,7 @@ std::vector<String> complete_command(StringView prefix, ByteCount cursor_pos)
TimeSpec mtime = {};
std::vector<String> commands;
};
static std::unordered_map<String, CommandCache> command_cache;
static UnorderedMap<String, CommandCache> command_cache;
std::vector<StringView> path;
if (dir_end != -1)

68
src/hash.cc Normal file
View File

@ -0,0 +1,68 @@
#include "hash.hh"
#include <cstdint>
namespace Kakoune
{
[[gnu::always_inline]]
static inline uint32_t rotl(uint32_t x, int8_t r)
{
return (x << r) | (x >> (32 - r));
}
[[gnu::always_inline]]
static inline uint32_t fmix(uint32_t h)
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
// murmur3 hash, based on https://github.com/PeterScott/murmur3
size_t hash_data(const char* input, size_t len)
{
const uint8_t* data = reinterpret_cast<const uint8_t*>(input);
uint32_t hash = 0x1235678;
constexpr uint32_t c1 = 0xcc9e2d51;
constexpr uint32_t c2 = 0x1b873593;
const int nblocks = len / 4;
const uint32_t* blocks = reinterpret_cast<const uint32_t*>(data + nblocks*4);
for (int i = -nblocks; i; ++i)
{
uint32_t key = blocks[i];
key *= c1;
key = rotl(key, 15);
key *= c2;
hash ^= key;
hash = rotl(hash, 13);
hash = hash * 5 + 0xe6546b64;
}
const uint8_t* tail = data + nblocks * 4;
uint32_t key = 0;
switch (len & 3)
{
case 3: key ^= tail[2] << 16;
case 2: key ^= tail[1] << 8;
case 1: key ^= tail[0];
key *= c1;
key = rotl(key,15);
key *= c2;
hash ^= key;
}
hash ^= len;
hash = fmix(hash);
return hash;
}
}

57
src/hash.hh Normal file
View File

@ -0,0 +1,57 @@
#ifndef hash_hh_INCLUDED
#define hash_hh_INCLUDED
#include <type_traits>
#include <functional>
namespace Kakoune
{
size_t hash_data(const char* data, size_t len);
template<typename Type>
typename std::enable_if<not std::is_enum<Type>::value, size_t>::type
hash_value(const Type& val)
{
return std::hash<Type>()(val);
}
template<typename Type>
typename std::enable_if<std::is_enum<Type>::value, size_t>::type
hash_value(const Type& val)
{
return hash_value((typename std::underlying_type<Type>::type)val);
}
template<typename Type>
size_t hash_values(Type&& t)
{
return hash_value(std::forward<Type>(t));
}
template<typename Type, typename... RemainingTypes>
size_t hash_values(Type&& t, RemainingTypes&&... rt)
{
size_t seed = hash_values(std::forward<RemainingTypes>(rt)...);
return seed ^ (hash_value(std::forward<Type>(t)) + 0x9e3779b9 +
(seed << 6) + (seed >> 2));
}
template<typename T1, typename T2>
size_t hash_value(const std::pair<T1, T2>& val)
{
return hash_values(val.first, val.second);
}
template<typename Type>
struct Hash
{
size_t operator()(const Type& val) const
{
return hash_value(val);
}
};
}
#endif // hash_hh_INCLUDED

View File

@ -9,22 +9,6 @@
#include <functional>
namespace std
{
template<>
struct hash<Kakoune::ByteCoord>
{
size_t operator()(const Kakoune::ByteCoord& val) const
{
size_t seed = std::hash<int>()((int)val.line);
return seed ^ (std::hash<int>()((int)val.column) + 0x9e3779b9 +
(seed << 6) + (seed >> 2));
}
};
}
namespace Kakoune
{

View File

@ -1017,7 +1017,7 @@ private:
{
size_t timestamp = 0;
std::vector<RegionMatches> matches;
std::unordered_map<BufferRange, RegionList> regions;
UnorderedMap<BufferRange, RegionList> regions;
};
BufferSideCache<Cache> m_cache;

View File

@ -2,8 +2,7 @@
#define hook_manager_hh_INCLUDED
#include "id_map.hh"
#include <unordered_map>
#include "unordered_map.hh"
namespace Kakoune
{
@ -29,7 +28,7 @@ private:
friend class Scope;
HookManager* m_parent;
std::unordered_map<String, id_map<HookFunc>> m_hook;
UnorderedMap<String, id_map<HookFunc>> m_hook;
};
}

View File

@ -9,12 +9,11 @@
#include "normal.hh"
#include "regex.hh"
#include "register_manager.hh"
#include "unordered_map.hh"
#include "user_interface.hh"
#include "utf8.hh"
#include "window.hh"
#include <unordered_map>
namespace Kakoune
{
@ -736,10 +735,10 @@ private:
bool m_autoshowcompl;
Mode m_mode = Mode::Default;
static std::unordered_map<String, std::vector<String>> ms_history;
static UnorderedMap<String, std::vector<String>> ms_history;
std::vector<String>::iterator m_history_it;
};
std::unordered_map<String, std::vector<String>> Prompt::ms_history;
UnorderedMap<String, std::vector<String>> Prompt::ms_history;
class NextKey : public InputMode
{

View File

@ -3,8 +3,7 @@
#include "string.hh"
#include "utils.hh"
#include <unordered_map>
#include "unordered_map.hh"
namespace Kakoune
{
@ -20,7 +19,7 @@ private:
void acquire(size_t slot);
void release(size_t slot) noexcept;
std::unordered_map<StringView, size_t> m_slot_map;
UnorderedMap<StringView, size_t> m_slot_map;
std::vector<size_t> m_free_slots;
using DataAndRefCount = std::pair<std::vector<char>, int>;
std::vector<DataAndRefCount> m_storage;
@ -118,18 +117,11 @@ private:
size_t m_slot = -1;
};
inline size_t hash_value(const Kakoune::InternedString& str)
{
return hash_data(str.data(), (int)str.length());
}
namespace std
{
template<>
struct hash<Kakoune::InternedString>
{
size_t operator()(const Kakoune::InternedString& str) const
{
return Kakoune::hash_data(str.data(), (int)str.length());
}
};
}
#endif // interned_string_hh_INCLUDED

View File

@ -1,19 +1,7 @@
#include "keymap_manager.hh"
#include "memoryview.hh"
namespace std
{
template<> struct hash<Kakoune::KeymapMode>
{
size_t operator()(Kakoune::KeymapMode val) const
{
return hash<int>{}((int)val);
}
};
}
#include "assert.hh"
namespace Kakoune
{

View File

@ -2,9 +2,9 @@
#define keymap_manager_hh_INCLUDED
#include "keys.hh"
#include "utils.hh"
#include "hash.hh"
#include "unordered_map.hh"
#include <unordered_map>
#include <vector>
namespace Kakoune
@ -43,7 +43,8 @@ private:
KeymapManager* m_parent;
using KeyList = std::vector<Key>;
using Keymap = std::unordered_map<std::pair<Key, KeymapMode>, KeyList>;
using KeyAndMode = std::pair<Key, KeymapMode>;
using Keymap = UnorderedMap<KeyAndMode, KeyList>;
Keymap m_mapping;
};

View File

@ -3,6 +3,7 @@
#include "unicode.hh"
#include "flags.hh"
#include "hash.hh"
#include <vector>
@ -11,7 +12,7 @@ namespace Kakoune
struct Key
{
enum class Modifiers
enum class Modifiers : int
{
None = 0,
Control = 1 << 0,
@ -78,21 +79,8 @@ constexpr Key alt(Codepoint key) { return { Key::Modifiers::Alt, key }; }
constexpr Key ctrl(Codepoint key) { return { Key::Modifiers::Control, key }; }
constexpr Key ctrlalt(Codepoint key) { return { Key::Modifiers::ControlAlt, key }; }
}
namespace std
{
template<>
struct hash<Kakoune::Key>
{
size_t operator()(Kakoune::Key key) const
{
return static_cast<size_t>(key.modifiers) * 1024 + key.key;
}
};
inline size_t hash_value(const Key& key) { return hash_values(key.modifiers, key.key); }
}
#endif // keys_hh_INCLUDED

View File

@ -166,7 +166,7 @@ static int nc_color(Color color)
static int get_color_pair(const Face& face)
{
using ColorPair = std::pair<Color, Color>;
static std::map<ColorPair, int> colorpairs;
static UnorderedMap<ColorPair, int> colorpairs;
static int next_pair = 1;
ColorPair colors{face.fg, face.bg};

View File

@ -2,9 +2,9 @@
#define normal_hh_INCLUDED
#include "keys.hh"
#include "unordered_map.hh"
#include <functional>
#include <unordered_map>
namespace Kakoune
{
@ -23,7 +23,7 @@ struct NormalCmdDesc
std::function<void (Context& context, NormalParams params)> func;
};
using KeyMap = std::unordered_map<Key, NormalCmdDesc>;
using KeyMap = UnorderedMap<Key, NormalCmdDesc>;
extern KeyMap keymap;
}

View File

@ -6,10 +6,10 @@
#include "units.hh"
#include "coord.hh"
#include "memoryview.hh"
#include "unordered_map.hh"
#include <tuple>
#include <vector>
#include <unordered_map>
namespace Kakoune
{
@ -68,7 +68,7 @@ bool option_add(std::vector<T>& opt, const std::vector<T>& vec)
}
template<typename Key, typename Value>
String option_to_string(const std::unordered_map<Key, Value>& opt)
String option_to_string(const UnorderedMap<Key, Value>& opt)
{
String res;
for (auto it = begin(opt); it != end(opt); ++it)
@ -83,7 +83,7 @@ String option_to_string(const std::unordered_map<Key, Value>& opt)
}
template<typename Key, typename Value>
void option_from_string(StringView str, std::unordered_map<Key, Value>& opt)
void option_from_string(StringView str, UnorderedMap<Key, Value>& opt)
{
opt.clear();
std::vector<String> elems = split(str, list_separator, '\\');

View File

@ -3,8 +3,8 @@
#include "register.hh"
#include "utils.hh"
#include "unordered_map.hh"
#include <unordered_map>
#include <vector>
#include <functional>
@ -21,7 +21,7 @@ public:
void register_dynamic_register(char reg, RegisterRetriever function);
protected:
std::unordered_map<char, std::unique_ptr<Register>> m_registers;
UnorderedMap<char, std::unique_ptr<Register>> m_registers;
};
}

View File

@ -82,7 +82,7 @@ public:
}
template<typename Key, typename Val>
void write(const std::unordered_map<Key, Val>& map)
void write(const UnorderedMap<Key, Val>& map)
{
write<uint32_t>(map.size());
for (auto& val : map)
@ -230,10 +230,10 @@ DisplayBuffer read<DisplayBuffer>(int socket)
}
template<typename Key, typename Val>
std::unordered_map<Key, Val> read_map(int socket)
UnorderedMap<Key, Val> read_map(int socket)
{
uint32_t size = read<uint32_t>(socket);
std::unordered_map<Key, Val> res;
UnorderedMap<Key, Val> res;
while (size--)
{
auto key = read<Key>(socket);

View File

@ -173,64 +173,4 @@ std::vector<StringView> wrap_lines(StringView text, CharCount max_width)
return lines;
}
[[gnu::always_inline]]
static inline uint32_t rotl(uint32_t x, int8_t r)
{
return (x << r) | (x >> (32 - r));
}
[[gnu::always_inline]]
static inline uint32_t fmix(uint32_t h)
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
// murmur3 hash, based on https://github.com/PeterScott/murmur3
size_t hash_data(const char* input, size_t len)
{
const uint8_t* data = reinterpret_cast<const uint8_t*>(input);
uint32_t hash = 0x1235678;
constexpr uint32_t c1 = 0xcc9e2d51;
constexpr uint32_t c2 = 0x1b873593;
const int nblocks = len / 4;
const uint32_t* blocks = reinterpret_cast<const uint32_t*>(data + nblocks*4);
for (int i = -nblocks; i; ++i)
{
uint32_t key = blocks[i];
key *= c1;
key = rotl(key, 15);
key *= c2;
hash ^= key;
hash = rotl(hash, 13);
hash = hash * 5 + 0xe6546b64;
}
const uint8_t* tail = data + nblocks * 4;
uint32_t key = 0;
switch (len & 3)
{
case 3: key ^= tail[2] << 16;
case 2: key ^= tail[1] << 8;
case 1: key ^= tail[0];
key *= c1;
key = rotl(key,15);
key *= c2;
hash ^= key;
}
hash ^= len;
hash = fmix(hash);
return hash;
}
}

View File

@ -3,6 +3,7 @@
#include "units.hh"
#include "utf8.hh"
#include "hash.hh"
#include <string>
#include <climits>
@ -267,29 +268,16 @@ String expand_tabs(StringView line, CharCount tabstop, CharCount col = 0);
std::vector<StringView> wrap_lines(StringView text, CharCount max_width);
size_t hash_data(const char* data, size_t len);
inline size_t hash_value(const Kakoune::String& str)
{
return hash_data(str.data(), (int)str.length());
}
namespace std
inline size_t hash_value(const Kakoune::StringView& str)
{
template<>
struct hash<Kakoune::String> : hash<std::string>
{
size_t operator()(const Kakoune::String& str) const
{
return hash<std::string>::operator()(str);
}
};
return hash_data(str.data(), (int)str.length());
}
template<>
struct hash<Kakoune::StringView>
{
size_t operator()(Kakoune::StringView str) const
{
return Kakoune::hash_data(str.data(), (int)str.length());
}
};
}
#endif // string_hh_INCLUDED

View File

@ -1,6 +1,8 @@
#ifndef units_hh_INCLUDED
#define units_hh_INCLUDED
#include "hash.hh"
#include <type_traits>
namespace Kakoune
@ -119,6 +121,8 @@ struct LineCount : public StronglyTypedNumber<LineCount, int>
constexpr LineCount(int value = 0) : StronglyTypedNumber<LineCount>(value) {}
};
inline size_t hash_value(LineCount val) { return hash_value((int)val); }
[[gnu::always_inline]]
inline constexpr LineCount operator"" _line(unsigned long long int value)
{
@ -131,6 +135,8 @@ struct ByteCount : public StronglyTypedNumber<ByteCount, int>
constexpr ByteCount(int value = 0) : StronglyTypedNumber<ByteCount>(value) {}
};
inline size_t hash_value(ByteCount val) { return hash_value((int)val); }
[[gnu::always_inline]]
inline constexpr ByteCount operator"" _byte(unsigned long long int value)
{
@ -149,6 +155,8 @@ inline constexpr CharCount operator"" _char(unsigned long long int value)
return CharCount(value);
}
inline size_t hash_value(CharCount val) { return hash_value((int)val); }
}
#endif // units_hh_INCLUDED

17
src/unordered_map.hh Normal file
View File

@ -0,0 +1,17 @@
#ifndef unordered_map_hh_INCLUDED
#define unordered_map_hh_INCLUDED
#include "hash.hh"
#include <unordered_map>
namespace Kakoune
{
template<typename Key, typename Value>
using UnorderedMap = std::unordered_map<Key, Value, Hash<Key>>;
}
#endif // unordered_map_hh_INCLUDED

View File

@ -2,9 +2,9 @@
#define user_interface_hh_INCLUDED
#include "safe_ptr.hh"
#include "unordered_map.hh"
#include <functional>
#include <unordered_map>
namespace Kakoune
{
@ -63,7 +63,7 @@ public:
virtual void set_input_callback(InputCallback callback) = 0;
using Options = std::unordered_map<String, String>;
using Options = UnorderedMap<String, String>;
virtual void set_ui_options(const Options& options) = 0;
};

View File

@ -185,21 +185,4 @@ bool is_in_range(const T& val, const T& min, const T& max)
}
// std::pair hashing
namespace std
{
template<typename T1, typename T2>
struct hash<std::pair<T1,T2>>
{
size_t operator()(const std::pair<T1,T2>& val) const
{
size_t seed = std::hash<T2>()(val.second);
return seed ^ (std::hash<T1>()(val.first) + 0x9e3779b9 +
(seed << 6) + (seed >> 2));
}
};
}
#endif // utils_hh_INCLUDED

View File

@ -1,11 +1,11 @@
#ifndef value_hh_INCLUDED
#define value_hh_INCLUDED
#include <memory>
#include <unordered_map>
#include "unordered_map.hh"
#include "units.hh"
#include <memory>
namespace Kakoune
{
@ -76,23 +76,10 @@ struct ValueId : public StronglyTypedNumber<ValueId, int>
}
};
using ValueMap = std::unordered_map<ValueId, Value>;
inline size_t hash_value(ValueId val) { return hash_value((int)val); }
using ValueMap = UnorderedMap<ValueId, Value>;
}
namespace std
{
template<>
struct hash<Kakoune::ValueId>
{
size_t operator()(Kakoune::ValueId val) const
{
return std::hash<int>()((int)val);
}
};
}
#endif // value_hh_INCLUDED

View File

@ -28,7 +28,7 @@ public:
UsedChars letters;
int refcount;
};
using WordList = std::unordered_map<InternedString, WordInfo>;
using WordList = UnorderedMap<InternedString, WordInfo>;
private:
using LineToWords = std::vector<std::vector<InternedString>>;