1 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
2 /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is [Open Source Virtual Machine.].
18 * The Initial Developer of the Original Code is
19 * Adobe System Incorporated.
20 * Portions created by the Initial Developer are Copyright (C) 2004-2006
21 * the Initial Developer. All Rights Reserved.
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #ifndef __GCHashtable_inlines_
41 #define __GCHashtable_inlines_
45 template <typename VAL
, class KEYHANDLER
, class ALLOCHANDLER
>
46 GCHashtableBase
<VAL
, KEYHANDLER
,ALLOCHANDLER
>::GCHashtableBase(uint32_t capacity
) :
58 // appear as full table so grow, numValues will go to zero on rehash
65 template <typename VAL
, class KEYHANDLER
, class ALLOCHANDLER
>
66 GCHashtableBase
<VAL
, KEYHANDLER
,ALLOCHANDLER
>::~GCHashtableBase()
68 if (table
&& table
!= EMPTY
)
69 ALLOCHANDLER::free(table
);
76 template <typename VAL
, class KEYHANDLER
, class ALLOCHANDLER
>
77 void GCHashtableBase
<VAL
, KEYHANDLER
,ALLOCHANDLER
>::clear()
79 if (table
&& table
!= EMPTY
)
80 ALLOCHANDLER::free(table
);
87 template <typename VAL
, class KEYHANDLER
, class ALLOCHANDLER
>
88 uint32_t GCHashtableBase
<VAL
, KEYHANDLER
,ALLOCHANDLER
>::find(const void* key
, Entry
* table
, uint32_t tableSize
)
90 GCAssert(key
!= DELETED
);
93 // bitmask is defined in Symbian OS headers. changing to bitMask
94 uint32_t const bitMask
= (tableSize
-1);
95 uint32_t i
= KEYHANDLER::hash(key
) & bitMask
;
97 while ((k
= table
[i
].key
) != NULL
&& !KEYHANDLER::equal(k
, key
))
99 i
= (i
+ (n
+= 1)) & bitMask
; // quadratic probe
101 GCAssert(i
<= bitMask
);
105 template <typename VAL
, class KEYHANDLER
, class ALLOCHANDLER
>
106 void GCHashtableBase
<VAL
, KEYHANDLER
,ALLOCHANDLER
>::put(const void* key
, VAL value
)
108 GCAssert(table
!= NULL
);
110 // this is basically an inlined version of find() with one minor difference:
111 // it notices if there are DELETED slots along the probe, and if there is one,
112 // we recycle it rather than adding to the end of the probe chain. this allows us
113 // to recycle deleted slots MUCH more quickly (without having to wait for a full rehash)
114 // and can dramatically reduce the average probe depth if there are a lot of
115 // insertions and removals.
116 const uint32_t NO_DELINDEX
= 0xffffffff;
117 uint32_t delindex
= NO_DELINDEX
;
118 // Quadratic probe: Hashtable size (S) is power of two
119 // Probe function used: p(K,i) = (i*i + i)/2 [i = 0,1,2....S,....]
120 // ith value in the probe sequence: [h(K) + p(K,i)] % S
122 uint32_t const bitMask
= (tableSize
- 1);
123 uint32_t i
= KEYHANDLER::hash(key
) & bitMask
;
125 while ((k
= table
[i
].key
) != NULL
&& !KEYHANDLER::equal(k
, key
))
127 // note that we can't just stop at the first DELETED value we find --
128 // we might have a matching value later in the chain. We choose
129 // the first such entry so that subsequent searches are as short as possible
130 // (choosing any other entry would be fine, just suboptimal)
131 if (k
== DELETED
&& delindex
== NO_DELINDEX
) delindex
= i
;
132 i
= (i
+ (n
+= 1)) & bitMask
; // quadratic probe
134 GCAssert(k
== NULL
|| KEYHANDLER::equal(k
, key
));
137 if (delindex
!= NO_DELINDEX
)
139 // there's a deleted entry we can replace
142 // note that we don't increment numValues here!
146 // .75 load factor, note we don't take numDeleted into account
147 // numValues includes numDeleted
148 if (numValues
* 4 >= tableSize
* 3)
151 // grow rehashes, so no DELETED items, thus normal find() is OK
152 GCAssert(numDeleted
== 0);
153 i
= find(key
, table
, tableSize
);
154 GCAssert(!(table
[i
].key
));
160 table
[i
].value
= value
;
163 template <typename VAL
, class KEYHANDLER
, class ALLOCHANDLER
>
164 VAL GCHashtableBase
<VAL
, KEYHANDLER
,ALLOCHANDLER
>::remove(const void* key
, bool allowRehash
)
167 uint32_t i
= find(key
, table
, tableSize
);
168 if (table
[i
].key
== key
)
170 table
[i
].key
= DELETED
;
171 ret
= table
[i
].value
;
174 // this helps a bit on pathologic memory profiler use case, needs more investigation
175 // 20% deleted == rehash
176 if (allowRehash
&& (numValues
- numDeleted
) * 5 < tableSize
)
184 template <typename VAL
, class KEYHANDLER
, class ALLOCHANDLER
>
185 void GCHashtableBase
<VAL
, KEYHANDLER
,ALLOCHANDLER
>::prune()
187 // this helps a bit on pathologic memory profiler use case, needs more investigation
188 // 20% deleted == rehash
190 if ((numValues
- numDeleted
) * 5 < tableSize
)
196 template <typename VAL
, class KEYHANDLER
, class ALLOCHANDLER
>
197 void GCHashtableBase
<VAL
, KEYHANDLER
,ALLOCHANDLER
>::grow(bool isRemoval
)
201 // Bugzilla 553679: Skip table resizing in a removal situation if the heap
202 // is in an abort state, we don't want to allocate during abort if we can
203 // avoid it, and 'grow' is called when the collector clears its weak references.
204 if (GCHeap::GetGCHeap()->GetStatus() == kMemAbort
)
208 uint32_t newTableSize
= tableSize
;
210 uint32_t occupiedSlots
= numValues
- numDeleted
;
211 GCAssert(numValues
>= numDeleted
);
213 // grow or shrink as appropriate:
214 // if we're greater than 50% full grow
215 // if we're less than 10% shrink
216 // else stay the same
217 if (2 * occupiedSlots
> tableSize
)
219 else if (5 * occupiedSlots
< tableSize
&& tableSize
> kDefaultSize
&& table
)
223 newTable
= (Entry
*)ALLOCHANDLER::alloc(newTableSize
*sizeof(Entry
), isRemoval
);
227 VMPI_memset(newTable
, 0, newTableSize
*sizeof(Entry
));
234 for (uint32_t i
=0; i
< tableSize
; i
++)
237 if ((oldKey
=table
[i
].key
) != NULL
)
239 // inlined & simplified version of put()
240 if (oldKey
!= DELETED
) {
241 uint32_t j
= find(oldKey
, newTable
, newTableSize
);
242 newTable
[j
].key
= oldKey
;
243 newTable
[j
].value
= table
[i
].value
;
250 if (table
&& table
!= EMPTY
)
252 ALLOCHANDLER::free(table
);
255 tableSize
= newTableSize
;
256 GCAssert(table
!= NULL
);
259 template <typename VAL
, class KEYHANDLER
, class ALLOCHANDLER
>
260 int32_t GCHashtableBase
<VAL
, KEYHANDLER
,ALLOCHANDLER
>::nextIndex(int32_t index
)
262 while(index
< tableSize
)
264 if (table
[index
].key
> DELETED
)