1 : /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99 ft=cpp:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is SpiderMonkey JavaScript engine.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Mozilla Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 2011
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Chris Leary <cdleary@mozilla.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #ifndef InlineMap_h__
42 : #define InlineMap_h__
43 :
44 : #include "js/HashTable.h"
45 :
46 : namespace js {
47 :
48 : /*
49 : * A type can only be used as an InlineMap key if zero is an invalid key value
50 : * (and thus may be used as a tombstone value by InlineMap).
51 : */
52 : template <typename T> struct ZeroIsReserved { static const bool result = false; };
53 : template <typename T> struct ZeroIsReserved<T *> { static const bool result = true; };
54 :
55 : template <typename K, typename V, size_t InlineElems>
56 : class InlineMap
57 : {
58 : public:
59 : typedef HashMap<K, V, DefaultHasher<K>, TempAllocPolicy> WordMap;
60 :
61 : struct InlineElem
62 : {
63 : K key;
64 : V value;
65 : };
66 :
67 : private:
68 : typedef typename WordMap::Ptr WordMapPtr;
69 : typedef typename WordMap::AddPtr WordMapAddPtr;
70 : typedef typename WordMap::Range WordMapRange;
71 :
72 : size_t inlNext;
73 : size_t inlCount;
74 : InlineElem inl[InlineElems];
75 : WordMap map;
76 :
77 226506 : void checkStaticInvariants() {
78 : JS_STATIC_ASSERT(ZeroIsReserved<K>::result);
79 226506 : }
80 :
81 14284613 : bool usingMap() const {
82 14284613 : return inlNext > InlineElems;
83 : }
84 :
85 1341 : bool switchToMap() {
86 1341 : JS_ASSERT(inlNext == InlineElems);
87 :
88 1341 : if (map.initialized()) {
89 261 : map.clear();
90 : } else {
91 1080 : if (!map.init(count()))
92 0 : return false;
93 1080 : JS_ASSERT(map.initialized());
94 : }
95 :
96 33525 : for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
97 32184 : if (it->key && !map.putNew(it->key, it->value))
98 0 : return false;
99 : }
100 :
101 1341 : inlNext = InlineElems + 1;
102 1341 : JS_ASSERT(map.count() == inlCount);
103 1341 : JS_ASSERT(usingMap());
104 1341 : return true;
105 : }
106 :
107 : JS_NEVER_INLINE
108 1341 : bool switchAndAdd(const K &key, const V &value) {
109 1341 : if (!switchToMap())
110 0 : return false;
111 :
112 1341 : return map.putNew(key, value);
113 : }
114 :
115 : public:
116 226506 : explicit InlineMap(JSContext *cx)
117 226506 : : inlNext(0), inlCount(0), map(cx) {
118 226506 : checkStaticInvariants(); /* Force the template to instantiate the static invariants. */
119 226506 : }
120 :
121 : class Entry
122 : {
123 : friend class InlineMap;
124 : const K &key_;
125 : const V &value_;
126 :
127 289603 : Entry(const K &key, const V &value) : key_(key), value_(value) {}
128 :
129 : public:
130 94473 : const K &key() { return key_; }
131 195130 : const V &value() { return value_; }
132 : }; /* class Entry */
133 :
134 : class Ptr
135 : {
136 : friend class InlineMap;
137 :
138 : WordMapPtr mapPtr;
139 : InlineElem *inlPtr;
140 : bool isInlinePtr;
141 :
142 : typedef Ptr ******* ConvertibleToBool;
143 :
144 1265040 : explicit Ptr(WordMapPtr p) : mapPtr(p), isInlinePtr(false) {}
145 4242045 : explicit Ptr(InlineElem *ie) : inlPtr(ie), isInlinePtr(true) {}
146 : void operator==(const Ptr &other);
147 :
148 : public:
149 : /* Leaves Ptr uninitialized. */
150 391628 : Ptr() {
151 : #ifdef DEBUG
152 391628 : inlPtr = (InlineElem *) 0xbad;
153 391628 : isInlinePtr = true;
154 : #endif
155 391628 : }
156 :
157 : /* Default copy constructor works for this structure. */
158 :
159 6956132 : bool found() const {
160 6956132 : return isInlinePtr ? bool(inlPtr) : mapPtr.found();
161 : }
162 :
163 5831483 : operator ConvertibleToBool() const {
164 5831483 : return ConvertibleToBool(found());
165 : }
166 :
167 : K &key() {
168 : JS_ASSERT(found());
169 : return isInlinePtr ? inlPtr->key : mapPtr->key;
170 : }
171 :
172 1124649 : V &value() {
173 1124649 : JS_ASSERT(found());
174 1124649 : return isInlinePtr ? inlPtr->value : mapPtr->value;
175 : }
176 : }; /* class Ptr */
177 :
178 : class AddPtr
179 5332454 : {
180 : friend class InlineMap;
181 :
182 : WordMapAddPtr mapAddPtr;
183 : InlineElem *inlAddPtr;
184 : bool isInlinePtr;
185 : /* Indicates whether inlAddPtr is a found result or an add pointer. */
186 : bool inlPtrFound;
187 :
188 4665635 : AddPtr(InlineElem *ptr, bool found)
189 4665635 : : inlAddPtr(ptr), isInlinePtr(true), inlPtrFound(found)
190 4665635 : {}
191 :
192 666819 : AddPtr(const WordMapAddPtr &p) : mapAddPtr(p), isInlinePtr(false) {}
193 :
194 : void operator==(const AddPtr &other);
195 :
196 : typedef AddPtr ******* ConvertibleToBool;
197 :
198 : public:
199 : AddPtr() {}
200 :
201 11965365 : bool found() const {
202 11965365 : return isInlinePtr ? inlPtrFound : mapAddPtr.found();
203 : }
204 :
205 7190842 : operator ConvertibleToBool() const {
206 7190842 : return found() ? ConvertibleToBool(1) : ConvertibleToBool(0);
207 : }
208 :
209 3479877 : V &value() {
210 3479877 : JS_ASSERT(found());
211 3479877 : if (isInlinePtr)
212 3375450 : return inlAddPtr->value;
213 104427 : return mapAddPtr->value;
214 : }
215 : }; /* class AddPtr */
216 :
217 2171059 : size_t count() {
218 2171059 : return usingMap() ? map.count() : inlCount;
219 : }
220 :
221 65147 : bool empty() const {
222 65147 : return usingMap() ? map.empty() : !inlCount;
223 : }
224 :
225 688356 : void clear() {
226 688356 : inlNext = 0;
227 688356 : inlCount = 0;
228 688356 : }
229 :
230 776382 : bool isMap() const {
231 776382 : return usingMap();
232 : }
233 :
234 648 : const WordMap &asMap() const {
235 648 : JS_ASSERT(isMap());
236 648 : return map;
237 : }
238 :
239 258362 : const InlineElem *asInline() const {
240 258362 : JS_ASSERT(!isMap());
241 258362 : return inl;
242 : }
243 :
244 258362 : const InlineElem *inlineEnd() const {
245 258362 : JS_ASSERT(!isMap());
246 258362 : return inl + inlNext;
247 : }
248 :
249 : JS_ALWAYS_INLINE
250 5507085 : Ptr lookup(const K &key) {
251 5507085 : if (usingMap())
252 1265040 : return Ptr(map.lookup(key));
253 :
254 7170940 : for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
255 3676048 : if (it->key == key)
256 747153 : return Ptr(it);
257 : }
258 :
259 3494892 : return Ptr(NULL);
260 : }
261 :
262 : JS_ALWAYS_INLINE
263 5332454 : AddPtr lookupForAdd(const K &key) {
264 5332454 : if (usingMap())
265 666819 : return AddPtr(map.lookupForAdd(key));
266 :
267 17500888 : for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
268 16204718 : if (it->key == key)
269 3369465 : return AddPtr(it, true);
270 : }
271 :
272 : /*
273 : * The add pointer that's returned here may indicate the limit entry of
274 : * the linear space, in which case the |add| operation will initialize
275 : * the map if necessary and add the entry there.
276 : */
277 1296170 : return AddPtr(inl + inlNext, false);
278 : }
279 :
280 : JS_ALWAYS_INLINE
281 1858388 : bool add(AddPtr &p, const K &key, const V &value) {
282 1858388 : JS_ASSERT(!p);
283 :
284 1858388 : if (p.isInlinePtr) {
285 1295987 : InlineElem *addPtr = p.inlAddPtr;
286 1295987 : JS_ASSERT(addPtr == inl + inlNext);
287 :
288 : /* Switching to map mode before we add this pointer. */
289 1295987 : if (addPtr == inl + InlineElems)
290 1341 : return switchAndAdd(key, value);
291 :
292 1294646 : JS_ASSERT(!p.found());
293 1294646 : JS_ASSERT(uintptr_t(inl + inlNext) == uintptr_t(p.inlAddPtr));
294 1294646 : p.inlAddPtr->key = key;
295 1294646 : p.inlAddPtr->value = value;
296 1294646 : ++inlCount;
297 1294646 : ++inlNext;
298 1294646 : return true;
299 : }
300 :
301 562401 : return map.add(p.mapAddPtr, key, value);
302 : }
303 :
304 : JS_ALWAYS_INLINE
305 216 : bool put(const K &key, const V &value) {
306 432 : AddPtr p = lookupForAdd(key);
307 216 : if (p) {
308 18 : p.value() = value;
309 18 : return true;
310 : }
311 198 : return add(p, key, value);
312 : }
313 :
314 324398 : void remove(Ptr p) {
315 324398 : JS_ASSERT(p);
316 324398 : if (p.isInlinePtr) {
317 20729 : JS_ASSERT(inlCount > 0);
318 20729 : JS_ASSERT(p.inlPtr->key != NULL);
319 20729 : p.inlPtr->key = NULL;
320 20729 : --inlCount;
321 20729 : return;
322 : }
323 303669 : JS_ASSERT(map.initialized() && usingMap());
324 303669 : map.remove(p.mapPtr);
325 : }
326 :
327 3429 : void remove(const K &key) {
328 3429 : if (Ptr p = lookup(key))
329 3411 : remove(p);
330 3429 : }
331 :
332 : class Range
333 : {
334 : friend class InlineMap;
335 :
336 : WordMapRange mapRange;
337 : InlineElem *cur;
338 : InlineElem *end;
339 : bool isInline;
340 :
341 189 : explicit Range(WordMapRange r)
342 : : cur(NULL), end(NULL), /* Avoid GCC 4.3.3 over-warning. */
343 189 : isInline(false) {
344 189 : mapRange = r;
345 189 : JS_ASSERT(!isInlineRange());
346 189 : }
347 :
348 127287 : Range(const InlineElem *begin, const InlineElem *end_)
349 : : cur(const_cast<InlineElem *>(begin)),
350 : end(const_cast<InlineElem *>(end_)),
351 127287 : isInline(true) {
352 127287 : advancePastNulls(cur);
353 127287 : JS_ASSERT(isInlineRange());
354 127287 : }
355 :
356 1559414 : bool checkInlineRangeInvariants() const {
357 1559414 : JS_ASSERT(uintptr_t(cur) <= uintptr_t(end));
358 1559414 : JS_ASSERT_IF(cur != end, cur->key != NULL);
359 1559414 : return true;
360 : }
361 :
362 1591346 : bool isInlineRange() const {
363 1591346 : JS_ASSERT_IF(isInline, checkInlineRangeInvariants());
364 1591346 : return isInline;
365 : }
366 :
367 312669 : void advancePastNulls(InlineElem *begin) {
368 312669 : InlineElem *newCur = begin;
369 626724 : while (newCur < end && NULL == newCur->key)
370 1386 : ++newCur;
371 312669 : JS_ASSERT(uintptr_t(newCur) <= uintptr_t(end));
372 312669 : cur = newCur;
373 312669 : }
374 :
375 185382 : void bumpCurPtr() {
376 185382 : JS_ASSERT(isInlineRange());
377 185382 : advancePastNulls(cur + 1);
378 185382 : }
379 :
380 : void operator==(const Range &other);
381 :
382 : public:
383 798265 : bool empty() const {
384 798265 : return isInlineRange() ? cur == end : mapRange.empty();
385 : }
386 :
387 289603 : Entry front() {
388 289603 : JS_ASSERT(!empty());
389 289603 : if (isInlineRange())
390 281683 : return Entry(cur->key, cur->value);
391 7920 : return Entry(mapRange.front().key, mapRange.front().value);
392 : }
393 :
394 190620 : void popFront() {
395 190620 : JS_ASSERT(!empty());
396 190620 : if (isInlineRange())
397 185382 : bumpCurPtr();
398 : else
399 5238 : mapRange.popFront();
400 190620 : }
401 : }; /* class Range */
402 :
403 127476 : Range all() const {
404 127476 : return usingMap() ? Range(map.all()) : Range(inl, inl + inlNext);
405 : }
406 : }; /* class InlineMap */
407 :
408 : } /* namespace js */
409 :
410 : #endif
|