Ptex
PtexHashMap.h
Go to the documentation of this file.
1 /*
2 PTEX SOFTWARE
3 Copyright 2014 Disney Enterprises, Inc. All rights reserved
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
8 
9  * Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11 
12  * Redistributions in binary form must reproduce the above copyright
13  notice, this list of conditions and the following disclaimer in
14  the documentation and/or other materials provided with the
15  distribution.
16 
17  * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
18  Studios" or the names of its contributors may NOT be used to
19  endorse or promote products derived from this software without
20  specific prior written permission from Walt Disney Pictures.
21 
22 Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
23 CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
25 FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
26 IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
27 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
31 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
34 */
40 #ifndef PtexHashMap_h
41 #define PtexHashMap_h
42 
43 #include <vector>
44 #include <string>
45 #include "PtexPlatform.h"
46 #include "PtexMutex.h"
47 
49 
50 inline bool memCompare(const char* a, const char* b, int len)
51 {
52  int len64 = len & ~7;
53  uint64_t val64[2];
54  for (int i = 0; i < len64; i+=8) {
55  memcpy(&val64[0], &a[i], 8);
56  memcpy(&val64[1], &b[i], 8);
57  if (val64[0] != val64[1]) return 1;
58  }
59  return memcmp(&a[len64], &b[len64], len & 7);
60 }
61 
62 
63 class StringKey
64 {
65  const char* volatile _val;
66  uint16_t volatile _len;
67  uint16_t volatile _ownsVal;
68  uint32_t volatile _hash;
69 
70  void operator=(const StringKey& key); // disallow
71  StringKey(const StringKey& key); // disallow
72 
73 public:
74  StringKey() : _val(0), _len(0), _ownsVal(false), _hash(0) {}
75  StringKey(const char* val)
76  {
77  _val = val;
78  _len = uint16_t(strlen(val));
79  _hash = uint32_t(std::hash<std::string_view>{}(std::string_view(_val, _len)));
80  _ownsVal = false;
81  }
82 
83  ~StringKey() { if (_ownsVal) delete [] _val; }
84 
85  void copy(volatile StringKey& key) volatile
86  {
87  char* newval = new char[key._len+1];
88  memcpy(newval, key._val, key._len+1);
89  _val = newval;
90  _len = key._len;
91  _hash = key._hash;
92  _ownsVal = true;
93  }
94 
95  void move(volatile StringKey& key) volatile
96  {
97  _val = key._val;
98  _len = key._len;
99  _hash = key._hash;
100  _ownsVal = key._ownsVal;
101  key._ownsVal = false;
102  }
103 
104  bool matches(const StringKey& key) const volatile
105  {
106  return key._hash == _hash && key._len == _len && _val && 0 == memCompare(key._val, _val, _len);
107  }
108 
109  bool isEmpty() const volatile { return _val==0; }
110 
111  uint32_t hash() const volatile
112  {
113  return _hash;
114  }
115 };
116 
117 class IntKey
118 {
119  int _val;
120 
121 public:
122  IntKey() : _val(0) {}
123  IntKey(int val) : _val(val) {}
124  void copy(volatile IntKey& key) volatile { _val = key._val; }
125  void move(volatile IntKey& key) volatile { _val = key._val; }
126  bool matches(const IntKey& key) const volatile { return _val == key._val; }
127  bool isEmpty() volatile const { return _val==0; }
128  uint32_t hash() volatile const { return (_val*7919) & ~0xf; }
129 };
130 
131 template <typename Key, typename Value>
133 {
134  // a table is a TableHeader followed in memory by an array of Entry records
135 
136  struct TableHeader {
137  uint32_t numEntries;
138  uint32_t size;
139  };
140 
141  class Entry {
142  Entry(const Entry&); // disallow
143  void operator=(const Entry&); // disallow
144  public:
145  Entry() : key(), value(0) {}
146  Key volatile key;
147  Value volatile value;
148  };
149 
150  PtexHashMap(const PtexHashMap&); // disallow
151  void operator=(const PtexHashMap&); // disallow
152 
154  {
155  size_t memUsed;
156  _table = allocTable(16, memUsed);
157  }
158 
160  {
161  TableHeader* header;
162  Entry* entries;
163  getTable(_table, header, entries);
164 
165  for (uint32_t i = 0; i < header->numEntries; ++i) {
166  entries[i].key.~Key();
167  if (entries[i].value) delete entries[i].value;
168  }
169  free(_table);
170  for (size_t i = 0; i < _oldTables.size(); ++i) {
171  free(_oldTables[i]);
172  }
173  std::vector<void*>().swap(_oldTables);
174  }
175 
176 public:
178  {
179  initContents();
180  }
181 
183  {
184  deleteContents();
185  }
186 
187  void clear()
188  {
189  deleteContents();
190  initContents();
191  }
192 
193  uint32_t size() const {
194  return ((TableHeader*)_table)->size;
195  }
196 
197  Value get(Key& key) const
198  {
199  const TableHeader* header;
200  const Entry* entries;
201  getTable(_table, header, entries);
202  uint32_t mask = header->numEntries-1;
203  uint32_t hash = key.hash();
204 
205  Value result = 0;
206  for (uint32_t i = hash;; ++i) {
207  const Entry& e = entries[i & mask];
208  if (e.key.matches(key)) {
209  result = e.value;
210  break;
211  }
212  if (e.value == 0) {
213  break;
214  }
215  }
216 
217  return result;
218  }
219 
220  Value tryInsert(Key& key, Value value, size_t& newMemUsed)
221  {
222  void* table = lockTableAndGrowIfNeeded(newMemUsed);
223  TableHeader* header;
224  Entry* entries;
225  getTable(table, header, entries);
226  uint32_t mask = header->numEntries-1;
227  uint32_t hash = key.hash();
228 
229  Value result = 0;
230  for (uint32_t i = hash;; ++i) {
231  Entry& e = entries[i & mask];
232  if (e.value == 0) {
233  e.value = value;
234  ++header->size;
235  PtexMemoryFence(); // must write key after value
236  e.key.copy(key);
237  result = e.value;
238  break;
239  }
240  while (e.key.isEmpty()) ;
241  if (e.key.matches(key)) {
242  result = e.value;
243  break;
244  }
245  }
246  unlockTable(table);
247  return result;
248  }
249 
250  template <typename Fn>
251  void foreach(Fn& fn) const
252  {
253  const TableHeader* header;
254  const Entry* entries;
255  getTable(_table, header, entries);
256  for (uint32_t i = 0; i < header->numEntries; ++i) {
257  Value v = entries[i].value;
258  if (v) fn(v);
259  }
260  }
261 
262 private:
263  void* allocTable(int32_t numEntries, size_t& memsize)
264  {
265  memsize = sizeof(TableHeader) + sizeof(Entry) * numEntries;
266  void* table = malloc(memsize);
267  memset(table, 0, memsize);
268  TableHeader* header = new (table) TableHeader;
269  header->numEntries = numEntries;
270  header->size = 0;
271  Entry* entries = (Entry*)((char*)table + sizeof(TableHeader));
272  for (int32_t i = 0; i < numEntries; ++i)
273  {
274  new (&entries[i]) Entry();
275  }
276  return table;
277  }
278 
279  static void getTable(const void* table, const TableHeader*& header, const Entry*& entries)
280  {
281  header = (const TableHeader*) table;
282  entries = (const Entry*)((const char*)table + sizeof(TableHeader));
283  }
284 
285  static void getTable(void* table, TableHeader*& header, Entry*& entries)
286  {
287  header = (TableHeader*) table;
288  entries = (Entry*)((char*)table + sizeof(TableHeader));
289  }
290 
291  void unlockTable(void* table)
292  {
293  _table = table;
294  _lock.unlock();
295  }
296 
297  void* lockTableAndGrowIfNeeded(size_t& newMemUsed)
298  {
299  _lock.lock();
300  void* table = _table;
301  TableHeader* header;
302  Entry* entries;
303  getTable(table, header, entries);
304 
305  if (header->size*2 >= header->numEntries) {
306  table = grow(table, newMemUsed);
307  }
308  return table;
309  }
310 
311  void* grow(void* oldTable, size_t& newMemUsed)
312  {
313  TableHeader* oldHeader;
314  Entry* oldEntries;
315  getTable(oldTable, oldHeader, oldEntries);
316 
317  _oldTables.push_back(oldTable);
318  void* newTable = allocTable(oldHeader->numEntries*2, newMemUsed);
319  TableHeader* newHeader;
320  Entry* newEntries;
321  getTable(newTable, newHeader, newEntries);
322  uint32_t mask = newHeader->numEntries-1;
323  for (uint32_t oldIndex = 0; oldIndex < oldHeader->numEntries; ++oldIndex) {
324  Entry& oldEntry = oldEntries[oldIndex];
325  if (oldEntry.value) {
326  for (int newIndex = oldEntry.key.hash();; ++newIndex) {
327  Entry& newEntry = newEntries[newIndex&mask];
328  if (!newEntry.value) {
329  newEntry.key.move(oldEntry.key);
330  newEntry.value = oldEntry.value;
331  break;
332  }
333  }
334  }
335  }
336  newHeader->size = oldHeader->size;
337  return newTable;
338  }
339 
340  void* _table;
342  std::vector<void*> _oldTables;
343 };
344 
346 
347 #endif
PTEX_NAMESPACE_BEGIN bool memCompare(const char *a, const char *b, int len)
Definition: PtexHashMap.h:50
Platform-specific classes, functions, and includes.
PTEX_INLINE void PtexMemoryFence()
Definition: PtexPlatform.h:292
#define PTEX_NAMESPACE_END
Definition: PtexVersion.h:62
IntKey(int val)
Definition: PtexHashMap.h:123
bool matches(const IntKey &key) const volatile
Definition: PtexHashMap.h:126
int _val
Definition: PtexHashMap.h:119
void copy(volatile IntKey &key) volatile
Definition: PtexHashMap.h:124
uint32_t hash() volatile const
Definition: PtexHashMap.h:128
void move(volatile IntKey &key) volatile
Definition: PtexHashMap.h:125
bool isEmpty() volatile const
Definition: PtexHashMap.h:127
void unlock()
Definition: PtexPlatform.h:145
void lock()
Definition: PtexPlatform.h:143
Key volatile key
Definition: PtexHashMap.h:146
Entry(const Entry &)
void operator=(const Entry &)
Value volatile value
Definition: PtexHashMap.h:147
uint32_t size() const
Definition: PtexHashMap.h:193
void deleteContents()
Definition: PtexHashMap.h:159
void * _table
Definition: PtexHashMap.h:340
static void getTable(const void *table, const TableHeader *&header, const Entry *&entries)
Definition: PtexHashMap.h:279
void * lockTableAndGrowIfNeeded(size_t &newMemUsed)
Definition: PtexHashMap.h:297
PtexHashMap(const PtexHashMap &)
std::vector< void * > _oldTables
Definition: PtexHashMap.h:342
void clear()
Definition: PtexHashMap.h:187
void unlockTable(void *table)
Definition: PtexHashMap.h:291
Value get(Key &key) const
Definition: PtexHashMap.h:197
void * allocTable(int32_t numEntries, size_t &memsize)
Definition: PtexHashMap.h:263
void initContents()
Definition: PtexHashMap.h:153
static void getTable(void *table, TableHeader *&header, Entry *&entries)
Definition: PtexHashMap.h:285
void operator=(const PtexHashMap &)
Value tryInsert(Key &key, Value value, size_t &newMemUsed)
Definition: PtexHashMap.h:220
void * grow(void *oldTable, size_t &newMemUsed)
Definition: PtexHashMap.h:311
bool isEmpty() const volatile
Definition: PtexHashMap.h:109
StringKey(const char *val)
Definition: PtexHashMap.h:75
uint16_t volatile _ownsVal
Definition: PtexHashMap.h:67
uint16_t volatile _len
Definition: PtexHashMap.h:66
uint32_t hash() const volatile
Definition: PtexHashMap.h:111
bool matches(const StringKey &key) const volatile
Definition: PtexHashMap.h:104
const char *volatile _val
Definition: PtexHashMap.h:65
uint32_t volatile _hash
Definition: PtexHashMap.h:68
void operator=(const StringKey &key)
void move(volatile StringKey &key) volatile
Definition: PtexHashMap.h:95
void copy(volatile StringKey &key) volatile
Definition: PtexHashMap.h:85
StringKey(const StringKey &key)