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 570197 : void checkStaticInvariants() {
78 : JS_STATIC_ASSERT(ZeroIsReserved<K>::result);
79 570197 : }
80 :
81 111645665 : bool usingMap() const {
82 111645665 : return inlNext > InlineElems;
83 : }
84 :
85 92653 : bool switchToMap() {
86 92653 : JS_ASSERT(inlNext == InlineElems);
87 :
88 92653 : if (map.initialized()) {
89 69751 : map.clear();
90 : } else {
91 22902 : if (!map.init(count()))
92 0 : return false;
93 22902 : JS_ASSERT(map.initialized());
94 : }
95 :
96 2316325 : for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
97 2223672 : if (it->key && !map.putNew(it->key, it->value))
98 0 : return false;
99 : }
100 :
101 92653 : inlNext = InlineElems + 1;
102 92653 : JS_ASSERT(map.count() == inlCount);
103 92653 : JS_ASSERT(usingMap());
104 92653 : return true;
105 : }
106 :
107 : JS_NEVER_INLINE
108 92653 : bool switchAndAdd(const K &key, const V &value) {
109 92653 : if (!switchToMap())
110 0 : return false;
111 :
112 92653 : return map.putNew(key, value);
113 : }
114 :
115 : public:
116 570197 : explicit InlineMap(JSContext *cx)
117 570197 : : inlNext(0), inlCount(0), map(cx) {
118 570197 : checkStaticInvariants(); /* Force the template to instantiate the static invariants. */
119 570197 : }
120 :
121 : class Entry
122 : {
123 : friend class InlineMap;
124 : const K &key_;
125 : const V &value_;
126 :
127 12399202 : Entry(const K &key, const V &value) : key_(key), value_(value) {}
128 :
129 : public:
130 2420120 : const K &key() { return key_; }
131 9979082 : 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 3050680 : explicit Ptr(WordMapPtr p) : mapPtr(p), isInlinePtr(false) {}
145 25251676 : explicit Ptr(InlineElem *ie) : inlPtr(ie), isInlinePtr(true) {}
146 : void operator==(const Ptr &other);
147 :
148 : public:
149 : /* Leaves Ptr uninitialized. */
150 1633908 : Ptr() {
151 : #ifdef DEBUG
152 1633908 : inlPtr = (InlineElem *) 0xbad;
153 1633908 : isInlinePtr = true;
154 : #endif
155 1633908 : }
156 :
157 : /* Default copy constructor works for this structure. */
158 :
159 37883727 : bool found() const {
160 37883727 : return isInlinePtr ? bool(inlPtr) : mapPtr.found();
161 : }
162 :
163 29112575 : operator ConvertibleToBool() const {
164 29112575 : return ConvertibleToBool(found());
165 : }
166 :
167 : K &key() {
168 : JS_ASSERT(found());
169 : return isInlinePtr ? inlPtr->key : mapPtr->key;
170 : }
171 :
172 8771152 : V &value() {
173 8771152 : JS_ASSERT(found());
174 8771152 : 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 32074137 : AddPtr(InlineElem *ptr, bool found)
189 32074137 : : inlAddPtr(ptr), isInlinePtr(true), inlPtrFound(found)
190 32074137 : {}
191 :
192 10711402 : 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 99548547 : bool found() const {
202 99548547 : return isInlinePtr ? inlPtrFound : mapAddPtr.found();
203 : }
204 :
205 62769744 : operator ConvertibleToBool() const {
206 62769744 : return found() ? ConvertibleToBool(1) : ConvertibleToBool(0);
207 : }
208 :
209 22825730 : V &value() {
210 22825730 : JS_ASSERT(found());
211 22825730 : if (isInlinePtr)
212 18049139 : return inlAddPtr->value;
213 4776591 : return mapAddPtr->value;
214 : }
215 : }; /* class AddPtr */
216 :
217 31798926 : size_t count() {
218 31798926 : return usingMap() ? map.count() : inlCount;
219 : }
220 :
221 1628355 : bool empty() const {
222 1628355 : return usingMap() ? map.empty() : !inlCount;
223 : }
224 :
225 3282077 : void clear() {
226 3282077 : inlNext = 0;
227 3282077 : inlCount = 0;
228 3282077 : }
229 :
230 3247596 : bool isMap() const {
231 3247596 : return usingMap();
232 : }
233 :
234 73041 : const WordMap &asMap() const {
235 73041 : JS_ASSERT(isMap());
236 73041 : return map;
237 : }
238 :
239 1033838 : const InlineElem *asInline() const {
240 1033838 : JS_ASSERT(!isMap());
241 1033838 : return inl;
242 : }
243 :
244 1033838 : const InlineElem *inlineEnd() const {
245 1033838 : JS_ASSERT(!isMap());
246 1033838 : return inl + inlNext;
247 : }
248 :
249 : JS_ALWAYS_INLINE
250 28302356 : Ptr lookup(const K &key) {
251 28302356 : if (usingMap())
252 3050680 : return Ptr(map.lookup(key));
253 :
254 98726275 : for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
255 80585670 : if (it->key == key)
256 7111071 : return Ptr(it);
257 : }
258 :
259 18140605 : return Ptr(NULL);
260 : }
261 :
262 : JS_ALWAYS_INLINE
263 42785539 : AddPtr lookupForAdd(const K &key) {
264 42785539 : if (usingMap())
265 10711402 : return AddPtr(map.lookupForAdd(key));
266 :
267 166731040 : for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
268 152685131 : if (it->key == key)
269 18028228 : 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 14045909 : return AddPtr(inl + inlNext, false);
278 : }
279 :
280 : JS_ALWAYS_INLINE
281 19984205 : bool add(AddPtr &p, const K &key, const V &value) {
282 19984205 : JS_ASSERT(!p);
283 :
284 19984205 : if (p.isInlinePtr) {
285 14045726 : InlineElem *addPtr = p.inlAddPtr;
286 14045726 : JS_ASSERT(addPtr == inl + inlNext);
287 :
288 : /* Switching to map mode before we add this pointer. */
289 14045726 : if (addPtr == inl + InlineElems)
290 92653 : return switchAndAdd(key, value);
291 :
292 13953073 : JS_ASSERT(!p.found());
293 13953073 : JS_ASSERT(uintptr_t(inl + inlNext) == uintptr_t(p.inlAddPtr));
294 13953073 : p.inlAddPtr->key = key;
295 13953073 : p.inlAddPtr->value = value;
296 13953073 : ++inlCount;
297 13953073 : ++inlNext;
298 13953073 : return true;
299 : }
300 :
301 5938479 : 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 810219 : void remove(Ptr p) {
315 810219 : JS_ASSERT(p);
316 810219 : if (p.isInlinePtr) {
317 462800 : JS_ASSERT(inlCount > 0);
318 462800 : JS_ASSERT(p.inlPtr->key != NULL);
319 462800 : p.inlPtr->key = NULL;
320 462800 : --inlCount;
321 462800 : return;
322 : }
323 347419 : JS_ASSERT(map.initialized() && usingMap());
324 347419 : map.remove(p.mapPtr);
325 : }
326 :
327 46218 : void remove(const K &key) {
328 46218 : if (Ptr p = lookup(key))
329 46198 : remove(p);
330 46218 : }
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 3430067 : Range(const InlineElem *begin, const InlineElem *end_)
349 : : cur(const_cast<InlineElem *>(begin)),
350 : end(const_cast<InlineElem *>(end_)),
351 3430067 : isInline(true) {
352 3430067 : advancePastNulls(cur);
353 3430067 : JS_ASSERT(isInlineRange());
354 3430067 : }
355 :
356 69148339 : bool checkInlineRangeInvariants() const {
357 69148339 : JS_ASSERT(uintptr_t(cur) <= uintptr_t(end));
358 69148339 : JS_ASSERT_IF(cur != end, cur->key != NULL);
359 69148339 : return true;
360 : }
361 :
362 70942971 : bool isInlineRange() const {
363 70942971 : JS_ASSERT_IF(isInline, checkInlineRangeInvariants());
364 70942971 : return isInline;
365 : }
366 :
367 13005105 : void advancePastNulls(InlineElem *begin) {
368 13005105 : InlineElem *newCur = begin;
369 26222911 : while (newCur < end && NULL == newCur->key)
370 212701 : ++newCur;
371 13005105 : JS_ASSERT(uintptr_t(newCur) <= uintptr_t(end));
372 13005105 : cur = newCur;
373 13005105 : }
374 :
375 9575038 : void bumpCurPtr() {
376 9575038 : JS_ASSERT(isInlineRange());
377 9575038 : advancePastNulls(cur + 1);
378 9575038 : }
379 :
380 : void operator==(const Range &other);
381 :
382 : public:
383 35629054 : bool empty() const {
384 35629054 : return isInlineRange() ? cur == end : mapRange.empty();
385 : }
386 :
387 12399202 : Entry front() {
388 12399202 : JS_ASSERT(!empty());
389 12399202 : if (isInlineRange())
390 11997367 : return Entry(cur->key, cur->value);
391 401835 : return Entry(mapRange.front().key, mapRange.front().value);
392 : }
393 :
394 9896856 : void popFront() {
395 9896856 : JS_ASSERT(!empty());
396 9896856 : if (isInlineRange())
397 9575038 : bumpCurPtr();
398 : else
399 321818 : mapRange.popFront();
400 9896856 : }
401 : }; /* class Range */
402 :
403 3442821 : Range all() const {
404 3442821 : return usingMap() ? Range(map.all()) : Range(inl, inl + inlNext);
405 : }
406 : }; /* class InlineMap */
407 :
408 : } /* namespace js */
409 :
410 : #endif
|