1 /* 2 * multihash.h 3 * Copyright 2013-2017 John Lindgren 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions, and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions, and the following disclaimer in the documentation 13 * provided with the distribution. 14 * 15 * This software is provided "as is" and without any warranty, express or 16 * implied. In no event shall the authors be liable for any damages arising from 17 * the use of this software. 18 */ 19 20 #ifndef LIBAUDCORE_MULTIHASH_H 21 #define LIBAUDCORE_MULTIHASH_H 22 23 #include <libaudcore/threads.h> 24 #include <utility> 25 26 /* HashBase is a low-level hash table implementation. It is used as a backend 27 * for SimpleHash as well as for a single channel of MultiHash. */ 28 29 class HashBase 30 { 31 public: 32 /* Skeleton structure containing internal members of a hash node (except for 33 * "refs", which is not used internally and included here only to fill an 34 * alignment gap). Actual node structures should subclass Node. */ 35 struct Node 36 { 37 Node * next; 38 unsigned hash; 39 unsigned refs; 40 }; 41 42 /* Represents the location of a node within the table. */ 43 struct NodeLoc 44 { 45 Node ** ptr; 46 Node * next; 47 }; 48 49 /* Callback. Returns true if <node> matches <data>, otherwise false. */ 50 typedef bool (*MatchFunc)(const Node * node, const void * data); 51 52 /* Callback. Called when a node is found. Returns true if <node> is to be 53 * removed, otherwise false. */ 54 typedef bool (*FoundFunc)(Node * node, void * state); 55 HashBase()56 constexpr HashBase() : buckets(nullptr), size(0), used(0) {} 57 clear()58 void clear() // use as destructor 59 { 60 delete[] buckets; 61 *this = HashBase(); 62 } 63 n_items()64 int n_items() const { return used; } 65 66 /* Adds a node. Does not check for duplicates. */ 67 void add(Node * node, unsigned hash); 68 69 /* Locates the node matching <data>. Returns null if no node is found. If 70 * <loc> is not null, also returns the location of the node, which can be 71 * used to remove it from the table. */ 72 Node * lookup(MatchFunc match, const void * data, unsigned hash, 73 NodeLoc * loc = nullptr) const; 74 75 /* Removes a node, given a location returned by lookup_full(). */ 76 void remove(const NodeLoc & loc); 77 78 /* Iterates over all nodes in the table, removing them as desired. */ 79 void iterate(FoundFunc func, void * state); 80 81 private: 82 static constexpr unsigned InitialSize = 16; 83 84 void resize(unsigned new_size); 85 86 Node ** buckets; 87 unsigned size, used; 88 }; 89 90 /* MultiHash is a generic, thread-safe hash table. It scales well to multiple 91 * processors by the use of multiple channels, each with a separate lock. The 92 * hash value of a given node decides what channel it is stored in. Hence, 93 * different processors will tend to hit different channels, keeping lock 94 * contention to a minimum. The all-purpose lookup function enables a variety 95 * of atomic operations, such as allocating and adding a node only if not 96 * already present. */ 97 98 class MultiHash 99 { 100 public: 101 static constexpr int Found = 1 << 0; 102 static constexpr int Added = 1 << 1; 103 static constexpr int Removed = 1 << 2; 104 105 typedef HashBase::Node Node; 106 typedef HashBase::MatchFunc MatchFunc; 107 typedef HashBase::FoundFunc FoundFunc; 108 typedef void (*FinalFunc)(void * state); 109 110 /* Callback. May create a new node representing <data> to be added to the 111 * table. Returns the new node or null. */ 112 typedef Node * (*AddFunc)(const void * data, void * state); 113 MultiHash(MatchFunc match)114 MultiHash(MatchFunc match) : match(match), locks(), channels() {} 115 116 /* There is no destructor. In some instances, such as the string pool, it 117 * is never safe to destroy the hash table, since it can be referenced from 118 * the destructors of other static objects. It is left to the operating 119 * system to reclaim the memory used by the hash table upon process exit. */ 120 121 /* All-purpose lookup function. The caller passes in the data to be looked 122 * up along with its hash value. The two callbacks are optional. <add> is 123 * called if no matching node is found, and may return a new node to add to 124 * the table. <found> is called if a matching node is found, and may return 125 * true to remove the node from the table. Returns the status of the lookup 126 * as a bitmask of Found, Added, and Removed. */ 127 int lookup(const void * data, unsigned hash, AddFunc add, FoundFunc found, 128 void * state); 129 130 /* All-purpose iteration function. All channels of the table are locked 131 * simultaneously during the iteration to freeze the table in a consistent 132 * state. <func> is called on each node in order, and may return true to 133 * remove the node from the table. */ 134 void iterate(FoundFunc func, void * state); 135 136 /* Variant of iterate() which runs a second callback after the iteration 137 * is complete, while the table is still locked. This is useful when some 138 * operation needs to be performed with the table in a known state. */ 139 void iterate(FoundFunc func, void * state, FinalFunc final, void * fstate); 140 141 private: 142 static constexpr int Channels = 16; /* must be a power of two */ 143 static constexpr int Shift = 24; /* bit shift for channel selection */ 144 145 const MatchFunc match; 146 aud::spinlock locks[Channels]; 147 HashBase channels[Channels]; 148 }; 149 150 /* Type-safe version using templates. */ 151 152 template<class Node_T, class Data_T> 153 class MultiHash_T : private MultiHash 154 { 155 public: 156 // Required interfaces: 157 // 158 // class Node_T : public Node 159 // { 160 // bool match (const Data_T * data) const; 161 // }; 162 // 163 // class Operation 164 // { 165 // Node_T * add (const Data_T * data); 166 // bool found (Node_T * node); 167 // }; 168 MultiHash_T()169 MultiHash_T() : MultiHash(match_cb) {} 170 clear()171 void clear() { MultiHash::iterate(remove_cb, nullptr); } 172 173 template<class Op> lookup(const Data_T * data,unsigned hash,Op & op)174 int lookup(const Data_T * data, unsigned hash, Op & op) 175 { 176 return MultiHash::lookup(data, hash, WrapOp<Op>::add, WrapOp<Op>::found, 177 &op); 178 } 179 180 template<class F> iterate(F func)181 void iterate(F func) 182 { 183 MultiHash::iterate(WrapIterate<F>::run, &func); 184 } 185 186 template<class F, class Final> iterate(F func,Final final)187 void iterate(F func, Final final) 188 { 189 MultiHash::iterate(WrapIterate<F>::run, &func, WrapFinal<Final>::run, 190 &final); 191 } 192 193 private: match_cb(const Node * node,const void * data)194 static bool match_cb(const Node * node, const void * data) 195 { 196 return (static_cast<const Node_T *>(node)) 197 ->match(static_cast<const Data_T *>(data)); 198 } 199 remove_cb(Node * node,void *)200 static bool remove_cb(Node * node, void *) 201 { 202 delete static_cast<Node_T *>(node); 203 return true; 204 } 205 206 template<class Op> 207 struct WrapOp 208 { addWrapOp209 static Node * add(const void * data, void * op) 210 { 211 return (static_cast<Op *>(op)) 212 ->add(static_cast<const Data_T *>(data)); 213 } foundWrapOp214 static bool found(Node * node, void * op) 215 { 216 return (static_cast<Op *>(op))->found(static_cast<Node_T *>(node)); 217 } 218 }; 219 220 template<class F> 221 struct WrapIterate 222 { runWrapIterate223 static bool run(Node * node, void * func) 224 { 225 return (*static_cast<F *>(func))(static_cast<Node_T *>(node)); 226 } 227 }; 228 229 template<class Final> 230 struct WrapFinal 231 { runWrapFinal232 static void run(void * func) { (*static_cast<Final *>(func))(); } 233 }; 234 }; 235 236 /* Simpler single-thread hash table. */ 237 238 template<class Key, class Value> 239 class SimpleHash : private HashBase 240 { 241 public: 242 typedef void (*IterFunc)(const Key & key, Value & value, void * state); 243 ~SimpleHash()244 ~SimpleHash() { clear(); } 245 246 using HashBase::n_items; 247 lookup(const Key & key)248 Value * lookup(const Key & key) 249 { 250 auto node = 251 static_cast<Node *>(HashBase::lookup(match_cb, &key, key.hash())); 252 return node ? &node->value : nullptr; 253 } 254 add(const Key & key,Value && value)255 Value * add(const Key & key, Value && value) 256 { 257 unsigned hash = key.hash(); 258 auto node = static_cast<Node *>(HashBase::lookup(match_cb, &key, hash)); 259 260 if (node) 261 node->value = std::move(value); 262 else 263 { 264 node = new Node(key, std::move(value)); 265 HashBase::add(node, hash); 266 } 267 268 return &node->value; 269 } 270 remove(const Key & key)271 void remove(const Key & key) 272 { 273 NodeLoc loc; 274 auto node = static_cast<Node *>( 275 HashBase::lookup(match_cb, &key, key.hash(), &loc)); 276 277 if (node) 278 { 279 delete node; 280 HashBase::remove(loc); 281 } 282 } 283 clear()284 void clear() 285 { 286 HashBase::iterate(remove_cb, nullptr); 287 HashBase::clear(); 288 } 289 290 template<class F> iterate(F func)291 void iterate(F func) 292 { 293 HashBase::iterate(WrapIterate<F>::run, &func); 294 } 295 296 private: 297 struct Node : public HashBase::Node 298 { NodeNode299 Node(const Key & key, Value && value) 300 : key(key), value(std::move(value)) 301 { 302 } 303 304 Key key; 305 Value value; 306 }; 307 308 struct IterData 309 { 310 IterFunc func; 311 void * state; 312 }; 313 match_cb(const HashBase::Node * node,const void * data)314 static bool match_cb(const HashBase::Node * node, const void * data) 315 { 316 return (static_cast<const Node *>(node))->key == 317 *static_cast<const Key *>(data); 318 } 319 remove_cb(HashBase::Node * node,void *)320 static bool remove_cb(HashBase::Node * node, void *) 321 { 322 delete static_cast<Node *>(node); 323 return true; 324 } 325 326 // C-style callback wrapping generic iteration functor 327 template<class F> 328 struct WrapIterate 329 { runWrapIterate330 static bool run(HashBase::Node * node_, void * func) 331 { 332 auto node = static_cast<Node *>(node_); 333 (*static_cast<F *>(func))(node->key, node->value); 334 return false; 335 } 336 }; 337 }; 338 339 #endif /* LIBAUDCORE_MULTIHASH_H */ 340