1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is C++ hashtable templates.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Benjamin Smedberg.
19 : * Portions created by the Initial Developer are Copyright (C) 2002
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #ifndef nsTHashtable_h__
39 : #define nsTHashtable_h__
40 :
41 : #include "nscore.h"
42 : #include "pldhash.h"
43 : #include "nsDebug.h"
44 : #include NEW_H
45 :
46 : // helper function for nsTHashtable::Clear()
47 : NS_COM_GLUE PLDHashOperator
48 : PL_DHashStubEnumRemove(PLDHashTable *table,
49 : PLDHashEntryHdr *entry,
50 : PRUint32 ordinal,
51 : void *userArg);
52 :
53 :
54 : /**
55 : * a base class for templated hashtables.
56 : *
57 : * Clients will rarely need to use this class directly. Check the derived
58 : * classes first, to see if they will meet your needs.
59 : *
60 : * @param EntryType the templated entry-type class that is managed by the
61 : * hashtable. <code>EntryType</code> must extend the following declaration,
62 : * and <strong>must not declare any virtual functions or derive from classes
63 : * with virtual functions.</strong> Any vtable pointer would break the
64 : * PLDHashTable code.
65 : *<pre> class EntryType : public PLDHashEntryHdr
66 : * {
67 : * public: or friend nsTHashtable<EntryType>;
68 : * // KeyType is what we use when Get()ing or Put()ing this entry
69 : * // this should either be a simple datatype (PRUint32, nsISupports*) or
70 : * // a const reference (const nsAString&)
71 : * typedef something KeyType;
72 : * // KeyTypePointer is the pointer-version of KeyType, because pldhash.h
73 : * // requires keys to cast to <code>const void*</code>
74 : * typedef const something* KeyTypePointer;
75 : *
76 : * EntryType(KeyTypePointer aKey);
77 : *
78 : * // the copy constructor must be defined, even if AllowMemMove() == true
79 : * // or you will cause link errors!
80 : * EntryType(const EntryType& aEnt);
81 : *
82 : * // the destructor must be defined... or you will cause link errors!
83 : * ~EntryType();
84 : *
85 : * // KeyEquals(): does this entry match this key?
86 : * bool KeyEquals(KeyTypePointer aKey) const;
87 : *
88 : * // KeyToPointer(): Convert KeyType to KeyTypePointer
89 : * static KeyTypePointer KeyToPointer(KeyType aKey);
90 : *
91 : * // HashKey(): calculate the hash number
92 : * static PLDHashNumber HashKey(KeyTypePointer aKey);
93 : *
94 : * // ALLOW_MEMMOVE can we move this class with memmove(), or do we have
95 : * // to use the copy constructor?
96 : * enum { ALLOW_MEMMOVE = PR_(TRUE or FALSE) };
97 : * }</pre>
98 : *
99 : * @see nsInterfaceHashtable
100 : * @see nsDataHashtable
101 : * @see nsClassHashtable
102 : * @author "Benjamin Smedberg <bsmedberg@covad.net>"
103 : */
104 :
105 : template<class EntryType>
106 : class nsTHashtable
107 : {
108 : public:
109 : /**
110 : * A dummy constructor; you must call Init() before using this class.
111 : */
112 : nsTHashtable();
113 :
114 : /**
115 : * destructor, cleans up and deallocates
116 : */
117 : ~nsTHashtable();
118 :
119 : /**
120 : * Initialize the table. This function must be called before any other
121 : * class operations. This can fail due to OOM conditions.
122 : * @param initSize the initial number of buckets in the hashtable, default 16
123 : * @return true if the class was initialized properly.
124 : */
125 : bool Init(PRUint32 initSize = PL_DHASH_MIN_SIZE);
126 :
127 : /**
128 : * Check whether the table has been initialized. This can be useful for static hashtables.
129 : * @return the initialization state of the class.
130 : */
131 7046 : bool IsInitialized() const { return !!mTable.entrySize; }
132 :
133 : /**
134 : * Return the generation number for the table. This increments whenever
135 : * the table data items are moved.
136 : */
137 : PRUint32 GetGeneration() const { return mTable.generation; }
138 :
139 : /**
140 : * KeyType is typedef'ed for ease of use.
141 : */
142 : typedef typename EntryType::KeyType KeyType;
143 :
144 : /**
145 : * KeyTypePointer is typedef'ed for ease of use.
146 : */
147 : typedef typename EntryType::KeyTypePointer KeyTypePointer;
148 :
149 : /**
150 : * Return the number of entries in the table.
151 : * @return number of entries
152 : */
153 66850 : PRUint32 Count() const { return mTable.entryCount; }
154 :
155 : /**
156 : * Get the entry associated with a key.
157 : * @param aKey the key to retrieve
158 : * @return pointer to the entry class, if the key exists; nsnull if the
159 : * key doesn't exist
160 : */
161 8795104 : EntryType* GetEntry(KeyType aKey) const
162 : {
163 8795104 : NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly.");
164 :
165 : EntryType* entry =
166 : reinterpret_cast<EntryType*>
167 : (PL_DHashTableOperate(
168 : const_cast<PLDHashTable*>(&mTable),
169 : EntryType::KeyToPointer(aKey),
170 8795104 : PL_DHASH_LOOKUP));
171 8795104 : return PL_DHASH_ENTRY_IS_BUSY(entry) ? entry : nsnull;
172 : }
173 :
174 : /**
175 : * Return true if an entry for the given key exists, false otherwise.
176 : * @param aKey the key to retrieve
177 : * @return true if the key exists, false if the key doesn't exist
178 : */
179 5513 : bool Contains(KeyType aKey) const
180 : {
181 5513 : return !!GetEntry(aKey);
182 : }
183 :
184 : /**
185 : * Get the entry associated with a key, or create a new entry,
186 : * @param aKey the key to retrieve
187 : * @return pointer to the entry class retreived; nsnull only if memory
188 : can't be allocated
189 : */
190 13023149 : EntryType* PutEntry(KeyType aKey)
191 : {
192 13023149 : NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly.");
193 :
194 : return static_cast<EntryType*>
195 : (PL_DHashTableOperate(
196 : &mTable,
197 : EntryType::KeyToPointer(aKey),
198 13023149 : PL_DHASH_ADD));
199 : }
200 :
201 : /**
202 : * Remove the entry associated with a key.
203 : * @param aKey of the entry to remove
204 : */
205 44113 : void RemoveEntry(KeyType aKey)
206 : {
207 44113 : NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly.");
208 :
209 44113 : PL_DHashTableOperate(&mTable,
210 : EntryType::KeyToPointer(aKey),
211 : PL_DHASH_REMOVE);
212 44113 : }
213 :
214 : /**
215 : * Remove the entry associated with a key, but don't resize the hashtable.
216 : * This is a low-level method, and is not recommended unless you know what
217 : * you're doing and you need the extra performance. This method can be used
218 : * during enumeration, while RemoveEntry() cannot.
219 : * @param aEntry the entry-pointer to remove (obtained from GetEntry or
220 : * the enumerator
221 : */
222 412 : void RawRemoveEntry(EntryType* aEntry)
223 : {
224 412 : PL_DHashTableRawRemove(&mTable, aEntry);
225 412 : }
226 :
227 : /**
228 : * client must provide an <code>Enumerator</code> function for
229 : * EnumerateEntries
230 : * @param aEntry the entry being enumerated
231 : * @param userArg passed unchanged from <code>EnumerateEntries</code>
232 : * @return combination of flags
233 : * @link PLDHashOperator::PL_DHASH_NEXT PL_DHASH_NEXT @endlink ,
234 : * @link PLDHashOperator::PL_DHASH_STOP PL_DHASH_STOP @endlink ,
235 : * @link PLDHashOperator::PL_DHASH_REMOVE PL_DHASH_REMOVE @endlink
236 : */
237 : typedef PLDHashOperator (* Enumerator)(EntryType* aEntry, void* userArg);
238 :
239 : /**
240 : * Enumerate all the entries of the function.
241 : * @param enumFunc the <code>Enumerator</code> function to call
242 : * @param userArg a pointer to pass to the
243 : * <code>Enumerator</code> function
244 : * @return the number of entries actually enumerated
245 : */
246 10595 : PRUint32 EnumerateEntries(Enumerator enumFunc, void* userArg)
247 : {
248 10595 : NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly.");
249 :
250 10595 : s_EnumArgs args = { enumFunc, userArg };
251 10595 : return PL_DHashTableEnumerate(&mTable, s_EnumStub, &args);
252 : }
253 :
254 : /**
255 : * remove all entries, return hashtable to "pristine" state ;)
256 : */
257 36650 : void Clear()
258 : {
259 36650 : NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly.");
260 :
261 36650 : PL_DHashTableEnumerate(&mTable, PL_DHashStubEnumRemove, nsnull);
262 36650 : }
263 :
264 : /**
265 : * client must provide a <code>SizeOfEntryExcludingThisFun</code> function for
266 : * SizeOfExcludingThis.
267 : * @param aEntry the entry being enumerated
268 : * @param mallocSizeOf the function used to measure heap-allocated blocks
269 : * @param arg passed unchanged from <code>SizeOf{In,Ex}cludingThis</code>
270 : * @return summed size of the things pointed to by the entries
271 : */
272 : typedef size_t (* SizeOfEntryExcludingThisFun)(EntryType* aEntry,
273 : nsMallocSizeOfFun mallocSizeOf,
274 : void *arg);
275 :
276 : /**
277 : * Measure the size of the table's entry storage, and if
278 : * |sizeOfEntryExcludingThis| is non-NULL, measure the size of things pointed
279 : * to by entries.
280 : *
281 : * @param sizeOfEntryExcludingThis the
282 : * <code>SizeOfEntryExcludingThisFun</code> function to call
283 : * @param mallocSizeOf the function used to measure heap-allocated blocks
284 : * @param userArg a pointer to pass to the
285 : * <code>SizeOfEntryExcludingThisFun</code> function
286 : * @return the summed size of all the entries
287 : */
288 0 : size_t SizeOfExcludingThis(SizeOfEntryExcludingThisFun sizeOfEntryExcludingThis,
289 : nsMallocSizeOfFun mallocSizeOf, void *userArg = NULL) const
290 : {
291 0 : if (IsInitialized()) {
292 0 : s_SizeOfArgs args = { sizeOfEntryExcludingThis, userArg };
293 0 : return PL_DHashTableSizeOfExcludingThis(&mTable, s_SizeOfStub, mallocSizeOf, &args);
294 : }
295 0 : return 0;
296 : }
297 :
298 : #ifdef DEBUG
299 : /**
300 : * Mark the table as constant after initialization.
301 : *
302 : * This will prevent assertions when a read-only hash is accessed on multiple
303 : * threads without synchronization.
304 : */
305 1419 : void MarkImmutable()
306 : {
307 1419 : NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly.");
308 :
309 1419 : PL_DHashMarkTableImmutable(&mTable);
310 1419 : }
311 : #endif
312 :
313 : protected:
314 : PLDHashTable mTable;
315 :
316 : static const void* s_GetKey(PLDHashTable *table,
317 : PLDHashEntryHdr *entry);
318 :
319 : static PLDHashNumber s_HashKey(PLDHashTable *table,
320 : const void *key);
321 :
322 : static bool s_MatchEntry(PLDHashTable *table,
323 : const PLDHashEntryHdr *entry,
324 : const void *key);
325 :
326 : static void s_CopyEntry(PLDHashTable *table,
327 : const PLDHashEntryHdr *from,
328 : PLDHashEntryHdr *to);
329 :
330 : static void s_ClearEntry(PLDHashTable *table,
331 : PLDHashEntryHdr *entry);
332 :
333 : static bool s_InitEntry(PLDHashTable *table,
334 : PLDHashEntryHdr *entry,
335 : const void *key);
336 :
337 : /**
338 : * passed internally during enumeration. Allocated on the stack.
339 : *
340 : * @param userFunc the Enumerator function passed to
341 : * EnumerateEntries by the client
342 : * @param userArg the userArg passed unaltered
343 : */
344 : struct s_EnumArgs
345 : {
346 : Enumerator userFunc;
347 : void* userArg;
348 : };
349 :
350 : static PLDHashOperator s_EnumStub(PLDHashTable *table,
351 : PLDHashEntryHdr *entry,
352 : PRUint32 number,
353 : void *arg);
354 :
355 : /**
356 : * passed internally during sizeOf counting. Allocated on the stack.
357 : *
358 : * @param userFunc the SizeOfEntryExcludingThisFun passed to
359 : * SizeOf{In,Ex}cludingThis by the client
360 : * @param userArg the userArg passed unaltered
361 : */
362 : struct s_SizeOfArgs
363 : {
364 : SizeOfEntryExcludingThisFun userFunc;
365 : void* userArg;
366 : };
367 :
368 : static size_t s_SizeOfStub(PLDHashEntryHdr *entry,
369 : nsMallocSizeOfFun mallocSizeOf,
370 : void *arg);
371 :
372 : private:
373 : // copy constructor, not implemented
374 : nsTHashtable(nsTHashtable<EntryType>& toCopy);
375 :
376 : // assignment operator, not implemented
377 : nsTHashtable<EntryType>& operator= (nsTHashtable<EntryType>& toEqual);
378 : };
379 :
380 : //
381 : // template definitions
382 : //
383 :
384 : template<class EntryType>
385 307333 : nsTHashtable<EntryType>::nsTHashtable()
386 : {
387 : // entrySize is our "I'm initialized" indicator
388 307333 : mTable.entrySize = 0;
389 307333 : }
390 :
391 : template<class EntryType>
392 307292 : nsTHashtable<EntryType>::~nsTHashtable()
393 : {
394 307292 : if (mTable.entrySize)
395 262864 : PL_DHashTableFinish(&mTable);
396 307292 : }
397 :
398 : template<class EntryType>
399 : bool
400 262017 : nsTHashtable<EntryType>::Init(PRUint32 initSize)
401 : {
402 262017 : if (mTable.entrySize)
403 : {
404 0 : NS_ERROR("nsTHashtable::Init() should not be called twice.");
405 0 : return true;
406 : }
407 :
408 : static PLDHashTableOps sOps =
409 : {
410 : ::PL_DHashAllocTable,
411 : ::PL_DHashFreeTable,
412 : s_HashKey,
413 : s_MatchEntry,
414 : ::PL_DHashMoveEntryStub,
415 : s_ClearEntry,
416 : ::PL_DHashFinalizeStub,
417 : s_InitEntry
418 : };
419 :
420 : if (!EntryType::ALLOW_MEMMOVE)
421 : {
422 768 : sOps.moveEntry = s_CopyEntry;
423 : }
424 :
425 262017 : if (!PL_DHashTableInit(&mTable, &sOps, nsnull, sizeof(EntryType), initSize))
426 : {
427 : // if failed, reset "flag"
428 0 : mTable.entrySize = 0;
429 0 : return false;
430 : }
431 :
432 262017 : return true;
433 : }
434 :
435 : // static definitions
436 :
437 : template<class EntryType>
438 : PLDHashNumber
439 21862359 : nsTHashtable<EntryType>::s_HashKey(PLDHashTable *table,
440 : const void *key)
441 : {
442 21862359 : return EntryType::HashKey(reinterpret_cast<const KeyTypePointer>(key));
443 : }
444 :
445 : template<class EntryType>
446 : bool
447 4389268 : nsTHashtable<EntryType>::s_MatchEntry(PLDHashTable *table,
448 : const PLDHashEntryHdr *entry,
449 : const void *key)
450 : {
451 : return ((const EntryType*) entry)->KeyEquals(
452 4389268 : reinterpret_cast<const KeyTypePointer>(key));
453 : }
454 :
455 : template<class EntryType>
456 : void
457 0 : nsTHashtable<EntryType>::s_CopyEntry(PLDHashTable *table,
458 : const PLDHashEntryHdr *from,
459 : PLDHashEntryHdr *to)
460 : {
461 : EntryType* fromEntry =
462 0 : const_cast<EntryType*>(reinterpret_cast<const EntryType*>(from));
463 :
464 0 : new(to) EntryType(*fromEntry);
465 :
466 0 : fromEntry->~EntryType();
467 0 : }
468 :
469 : template<class EntryType>
470 : void
471 12913912 : nsTHashtable<EntryType>::s_ClearEntry(PLDHashTable *table,
472 : PLDHashEntryHdr *entry)
473 : {
474 12913912 : reinterpret_cast<EntryType*>(entry)->~EntryType();
475 12913912 : }
476 :
477 : template<class EntryType>
478 : bool
479 12952969 : nsTHashtable<EntryType>::s_InitEntry(PLDHashTable *table,
480 : PLDHashEntryHdr *entry,
481 : const void *key)
482 : {
483 12952969 : new(entry) EntryType(reinterpret_cast<KeyTypePointer>(key));
484 12952969 : return true;
485 : }
486 :
487 : template<class EntryType>
488 : PLDHashOperator
489 37487 : nsTHashtable<EntryType>::s_EnumStub(PLDHashTable *table,
490 : PLDHashEntryHdr *entry,
491 : PRUint32 number,
492 : void *arg)
493 : {
494 : // dereferences the function-pointer to the user's enumeration function
495 : return (* reinterpret_cast<s_EnumArgs*>(arg)->userFunc)(
496 : reinterpret_cast<EntryType*>(entry),
497 37487 : reinterpret_cast<s_EnumArgs*>(arg)->userArg);
498 : }
499 :
500 : template<class EntryType>
501 : size_t
502 0 : nsTHashtable<EntryType>::s_SizeOfStub(PLDHashEntryHdr *entry,
503 : nsMallocSizeOfFun mallocSizeOf,
504 : void *arg)
505 : {
506 : // dereferences the function-pointer to the user's enumeration function
507 : return (* reinterpret_cast<s_SizeOfArgs*>(arg)->userFunc)(
508 : reinterpret_cast<EntryType*>(entry),
509 : mallocSizeOf,
510 0 : reinterpret_cast<s_SizeOfArgs*>(arg)->userArg);
511 : }
512 :
513 : #endif // nsTHashtable_h__
|