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 571145 : void checkStaticInvariants() {
78 : JS_STATIC_ASSERT(ZeroIsReserved<K>::result);
79 571145 : }
80 :
81 111864166 : bool usingMap() const {
82 111864166 : return inlNext > InlineElems;
83 : }
84 :
85 92830 : bool switchToMap() {
86 92830 : JS_ASSERT(inlNext == InlineElems);
87 :
88 92830 : if (map.initialized()) {
89 69887 : map.clear();
90 : } else {
91 22943 : if (!map.init(count()))
92 0 : return false;
93 22943 : JS_ASSERT(map.initialized());
94 : }
95 :
96 2320750 : for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
97 2227920 : if (it->key && !map.putNew(it->key, it->value))
98 0 : return false;
99 : }
100 :
101 92830 : inlNext = InlineElems + 1;
102 92830 : JS_ASSERT(map.count() == inlCount);
103 92830 : JS_ASSERT(usingMap());
104 92830 : return true;
105 : }
106 :
107 : JS_NEVER_INLINE
108 92830 : bool switchAndAdd(const K &key, const V &value) {
109 92830 : if (!switchToMap())
110 0 : return false;
111 :
112 92830 : return map.putNew(key, value);
113 : }
114 :
115 : public:
116 571145 : explicit InlineMap(JSContext *cx)
117 571145 : : inlNext(0), inlCount(0), map(cx) {
118 571145 : checkStaticInvariants(); /* Force the template to instantiate the static invariants. */
119 571145 : }
120 :
121 : class Entry
122 : {
123 : friend class InlineMap;
124 : const K &key_;
125 : const V &value_;
126 :
127 12420190 : Entry(const K &key, const V &value) : key_(key), value_(value) {}
128 :
129 : public:
130 2424962 : const K &key() { return key_; }
131 9995228 : 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 3054045 : explicit Ptr(WordMapPtr p) : mapPtr(p), isInlinePtr(false) {}
145 25297898 : explicit Ptr(InlineElem *ie) : inlPtr(ie), isInlinePtr(true) {}
146 : void operator==(const Ptr &other);
147 :
148 : public:
149 : /* Leaves Ptr uninitialized. */
150 1636333 : Ptr() {
151 : #ifdef DEBUG
152 1636333 : inlPtr = (InlineElem *) 0xbad;
153 1636333 : isInlinePtr = true;
154 : #endif
155 1636333 : }
156 :
157 : /* Default copy constructor works for this structure. */
158 :
159 37949121 : bool found() const {
160 37949121 : return isInlinePtr ? bool(inlPtr) : mapPtr.found();
161 : }
162 :
163 29163025 : operator ConvertibleToBool() const {
164 29163025 : return ConvertibleToBool(found());
165 : }
166 :
167 : K &key() {
168 : JS_ASSERT(found());
169 : return isInlinePtr ? inlPtr->key : mapPtr->key;
170 : }
171 :
172 8786096 : V &value() {
173 8786096 : JS_ASSERT(found());
174 8786096 : return isInlinePtr ? inlPtr->value : mapPtr->value;
175 : }
176 : }; /* class Ptr */
177 :
178 : class AddPtr
179 : {
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 32138147 : AddPtr(InlineElem *ptr, bool found)
189 32138147 : : inlAddPtr(ptr), isInlinePtr(true), inlPtrFound(found)
190 32138147 : {}
191 :
192 10732854 : 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 99749062 : bool found() const {
202 99749062 : return isInlinePtr ? inlPtrFound : mapAddPtr.found();
203 : }
204 :
205 62894845 : operator ConvertibleToBool() const {
206 62894845 : return found() ? ConvertibleToBool(1) : ConvertibleToBool(0);
207 : }
208 :
209 22871537 : V &value() {
210 22871537 : JS_ASSERT(found());
211 22871537 : if (isInlinePtr)
212 18083348 : return inlAddPtr->value;
213 4788189 : return mapAddPtr->value;
214 : }
215 : }; /* class AddPtr */
216 :
217 31867074 : size_t count() {
218 31867074 : return usingMap() ? map.count() : inlCount;
219 : }
220 :
221 1630960 : bool empty() const {
222 1630960 : return usingMap() ? map.empty() : !inlCount;
223 : }
224 :
225 3288711 : void clear() {
226 3288711 : inlNext = 0;
227 3288711 : inlCount = 0;
228 3288711 : }
229 :
230 3254171 : bool isMap() const {
231 3254171 : return usingMap();
232 : }
233 :
234 73192 : const WordMap &asMap() const {
235 73192 : JS_ASSERT(isMap());
236 73192 : return map;
237 : }
238 :
239 1035929 : const InlineElem *asInline() const {
240 1035929 : JS_ASSERT(!isMap());
241 1035929 : return inl;
242 : }
243 :
244 1035929 : const InlineElem *inlineEnd() const {
245 1035929 : JS_ASSERT(!isMap());
246 1035929 : return inl + inlNext;
247 : }
248 :
249 : JS_ALWAYS_INLINE
250 28351943 : Ptr lookup(const K &key) {
251 28351943 : if (usingMap())
252 3054045 : return Ptr(map.lookup(key));
253 :
254 98944072 : for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
255 80768936 : if (it->key == key)
256 7122762 : return Ptr(it);
257 : }
258 :
259 18175136 : return Ptr(NULL);
260 : }
261 :
262 : JS_ALWAYS_INLINE
263 42871001 : AddPtr lookupForAdd(const K &key) {
264 42871001 : if (usingMap())
265 10732854 : return AddPtr(map.lookupForAdd(key));
266 :
267 167085451 : for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
268 153009758 : if (it->key == key)
269 18062454 : 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 14075693 : return AddPtr(inl + inlNext, false);
278 : }
279 :
280 : JS_ALWAYS_INLINE
281 20023844 : bool add(AddPtr &p, const K &key, const V &value) {
282 20023844 : JS_ASSERT(!p);
283 :
284 20023844 : if (p.isInlinePtr) {
285 14075510 : InlineElem *addPtr = p.inlAddPtr;
286 14075510 : JS_ASSERT(addPtr == inl + inlNext);
287 :
288 : /* Switching to map mode before we add this pointer. */
289 14075510 : if (addPtr == inl + InlineElems)
290 92830 : return switchAndAdd(key, value);
291 :
292 13982680 : JS_ASSERT(!p.found());
293 13982680 : JS_ASSERT(uintptr_t(inl + inlNext) == uintptr_t(p.inlAddPtr));
294 13982680 : p.inlAddPtr->key = key;
295 13982680 : p.inlAddPtr->value = value;
296 13982680 : ++inlCount;
297 13982680 : ++inlNext;
298 13982680 : return true;
299 : }
300 :
301 5948334 : return map.add(p.mapAddPtr, key, value);
302 : }
303 :
304 : JS_ALWAYS_INLINE
305 232 : bool put(const K &key, const V &value) {
306 232 : AddPtr p = lookupForAdd(key);
307 232 : if (p) {
308 20 : p.value() = value;
309 20 : return true;
310 : }
311 212 : return add(p, key, value);
312 : }
313 :
314 811082 : void remove(Ptr p) {
315 811082 : JS_ASSERT(p);
316 811082 : if (p.isInlinePtr) {
317 463582 : JS_ASSERT(inlCount > 0);
318 463582 : JS_ASSERT(p.inlPtr->key != NULL);
319 463582 : p.inlPtr->key = NULL;
320 463582 : --inlCount;
321 463582 : return;
322 : }
323 347500 : JS_ASSERT(map.initialized() && usingMap());
324 347500 : map.remove(p.mapPtr);
325 : }
326 :
327 46381 : void remove(const K &key) {
328 46381 : if (Ptr p = lookup(key))
329 46361 : remove(p);
330 46381 : }
331 :
332 : class Range
333 : {
334 : friend class InlineMap;
335 :
336 : WordMapRange mapRange;
337 : InlineElem *cur;
338 : InlineElem *end;
339 : bool isInline;
340 :
341 12754 : explicit Range(WordMapRange r)
342 : : cur(NULL), end(NULL), /* Avoid GCC 4.3.3 over-warning. */
343 12754 : isInline(false) {
344 12754 : mapRange = r;
345 12754 : JS_ASSERT(!isInlineRange());
346 12754 : }
347 :
348 3435933 : Range(const InlineElem *begin, const InlineElem *end_)
349 : : cur(const_cast<InlineElem *>(begin)),
350 : end(const_cast<InlineElem *>(end_)),
351 3435933 : isInline(true) {
352 3435933 : advancePastNulls(cur);
353 3435933 : JS_ASSERT(isInlineRange());
354 3435933 : }
355 :
356 69265361 : bool checkInlineRangeInvariants() const {
357 69265361 : JS_ASSERT(uintptr_t(cur) <= uintptr_t(end));
358 69265361 : JS_ASSERT_IF(cur != end, cur->key != NULL);
359 69265361 : return true;
360 : }
361 :
362 71061010 : bool isInlineRange() const {
363 71061010 : JS_ASSERT_IF(isInline, checkInlineRangeInvariants());
364 71061010 : return isInline;
365 : }
366 :
367 13026915 : void advancePastNulls(InlineElem *begin) {
368 13026915 : InlineElem *newCur = begin;
369 26266567 : while (newCur < end && NULL == newCur->key)
370 212737 : ++newCur;
371 13026915 : JS_ASSERT(uintptr_t(newCur) <= uintptr_t(end));
372 13026915 : cur = newCur;
373 13026915 : }
374 :
375 9590982 : void bumpCurPtr() {
376 9590982 : JS_ASSERT(isInlineRange());
377 9590982 : advancePastNulls(cur + 1);
378 9590982 : }
379 :
380 : void operator==(const Range &other);
381 :
382 : public:
383 35688166 : bool empty() const {
384 35688166 : return isInlineRange() ? cur == end : mapRange.empty();
385 : }
386 :
387 12420190 : Entry front() {
388 12420190 : JS_ASSERT(!empty());
389 12420190 : if (isInlineRange())
390 12018124 : return Entry(cur->key, cur->value);
391 402066 : return Entry(mapRange.front().key, mapRange.front().value);
392 : }
393 :
394 9912985 : void popFront() {
395 9912985 : JS_ASSERT(!empty());
396 9912985 : if (isInlineRange())
397 9590982 : bumpCurPtr();
398 : else
399 322003 : mapRange.popFront();
400 9912985 : }
401 : }; /* class Range */
402 :
403 3448687 : Range all() const {
404 3448687 : return usingMap() ? Range(map.all()) : Range(inl, inl + inlNext);
405 : }
406 : }; /* class InlineMap */
407 :
408 : } /* namespace js */
409 :
410 : #endif
|