/js/src/ds/InlineMap.h

http://github.com/zpao/v8monkey · C Header · 410 lines · 282 code · 76 blank · 52 comment · 39 complexity · df4c34323beb9c83a54cc009050912f6 MD5 · raw file

  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. #ifndef InlineMap_h__
  41. #define InlineMap_h__
  42. #include "js/HashTable.h"
  43. namespace js {
  44. /*
  45. * A type can only be used as an InlineMap key if zero is an invalid key value
  46. * (and thus may be used as a tombstone value by InlineMap).
  47. */
  48. template <typename T> struct ZeroIsReserved { static const bool result = false; };
  49. template <typename T> struct ZeroIsReserved<T *> { static const bool result = true; };
  50. template <typename K, typename V, size_t InlineElems>
  51. class InlineMap
  52. {
  53. public:
  54. typedef HashMap<K, V, DefaultHasher<K>, TempAllocPolicy> WordMap;
  55. struct InlineElem
  56. {
  57. K key;
  58. V value;
  59. };
  60. private:
  61. typedef typename WordMap::Ptr WordMapPtr;
  62. typedef typename WordMap::AddPtr WordMapAddPtr;
  63. typedef typename WordMap::Range WordMapRange;
  64. size_t inlNext;
  65. size_t inlCount;
  66. InlineElem inl[InlineElems];
  67. WordMap map;
  68. void checkStaticInvariants() {
  69. JS_STATIC_ASSERT(ZeroIsReserved<K>::result);
  70. }
  71. bool usingMap() const {
  72. return inlNext > InlineElems;
  73. }
  74. bool switchToMap() {
  75. JS_ASSERT(inlNext == InlineElems);
  76. if (map.initialized()) {
  77. map.clear();
  78. } else {
  79. if (!map.init(count()))
  80. return false;
  81. JS_ASSERT(map.initialized());
  82. }
  83. for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
  84. if (it->key && !map.putNew(it->key, it->value))
  85. return false;
  86. }
  87. inlNext = InlineElems + 1;
  88. JS_ASSERT(map.count() == inlCount);
  89. JS_ASSERT(usingMap());
  90. return true;
  91. }
  92. JS_NEVER_INLINE
  93. bool switchAndAdd(const K &key, const V &value) {
  94. if (!switchToMap())
  95. return false;
  96. return map.putNew(key, value);
  97. }
  98. public:
  99. explicit InlineMap(JSContext *cx)
  100. : inlNext(0), inlCount(0), map(cx) {
  101. checkStaticInvariants(); /* Force the template to instantiate the static invariants. */
  102. }
  103. class Entry
  104. {
  105. friend class InlineMap;
  106. const K &key_;
  107. const V &value_;
  108. Entry(const K &key, const V &value) : key_(key), value_(value) {}
  109. public:
  110. const K &key() { return key_; }
  111. const V &value() { return value_; }
  112. }; /* class Entry */
  113. class Ptr
  114. {
  115. friend class InlineMap;
  116. WordMapPtr mapPtr;
  117. InlineElem *inlPtr;
  118. bool isInlinePtr;
  119. typedef Ptr ******* ConvertibleToBool;
  120. explicit Ptr(WordMapPtr p) : mapPtr(p), isInlinePtr(false) {}
  121. explicit Ptr(InlineElem *ie) : inlPtr(ie), isInlinePtr(true) {}
  122. void operator==(const Ptr &other);
  123. public:
  124. /* Leaves Ptr uninitialized. */
  125. Ptr() {
  126. #ifdef DEBUG
  127. inlPtr = (InlineElem *) 0xbad;
  128. isInlinePtr = true;
  129. #endif
  130. }
  131. /* Default copy constructor works for this structure. */
  132. bool found() const {
  133. return isInlinePtr ? bool(inlPtr) : mapPtr.found();
  134. }
  135. operator ConvertibleToBool() const {
  136. return ConvertibleToBool(found());
  137. }
  138. K &key() {
  139. JS_ASSERT(found());
  140. return isInlinePtr ? inlPtr->key : mapPtr->key;
  141. }
  142. V &value() {
  143. JS_ASSERT(found());
  144. return isInlinePtr ? inlPtr->value : mapPtr->value;
  145. }
  146. }; /* class Ptr */
  147. class AddPtr
  148. {
  149. friend class InlineMap;
  150. WordMapAddPtr mapAddPtr;
  151. InlineElem *inlAddPtr;
  152. bool isInlinePtr;
  153. /* Indicates whether inlAddPtr is a found result or an add pointer. */
  154. bool inlPtrFound;
  155. AddPtr(InlineElem *ptr, bool found)
  156. : inlAddPtr(ptr), isInlinePtr(true), inlPtrFound(found)
  157. {}
  158. AddPtr(const WordMapAddPtr &p) : mapAddPtr(p), isInlinePtr(false) {}
  159. void operator==(const AddPtr &other);
  160. typedef AddPtr ******* ConvertibleToBool;
  161. public:
  162. AddPtr() {}
  163. bool found() const {
  164. return isInlinePtr ? inlPtrFound : mapAddPtr.found();
  165. }
  166. operator ConvertibleToBool() const {
  167. return found() ? ConvertibleToBool(1) : ConvertibleToBool(0);
  168. }
  169. V &value() {
  170. JS_ASSERT(found());
  171. if (isInlinePtr)
  172. return inlAddPtr->value;
  173. return mapAddPtr->value;
  174. }
  175. }; /* class AddPtr */
  176. size_t count() {
  177. return usingMap() ? map.count() : inlCount;
  178. }
  179. bool empty() const {
  180. return usingMap() ? map.empty() : !inlCount;
  181. }
  182. void clear() {
  183. inlNext = 0;
  184. inlCount = 0;
  185. }
  186. bool isMap() const {
  187. return usingMap();
  188. }
  189. const WordMap &asMap() const {
  190. JS_ASSERT(isMap());
  191. return map;
  192. }
  193. const InlineElem *asInline() const {
  194. JS_ASSERT(!isMap());
  195. return inl;
  196. }
  197. const InlineElem *inlineEnd() const {
  198. JS_ASSERT(!isMap());
  199. return inl + inlNext;
  200. }
  201. JS_ALWAYS_INLINE
  202. Ptr lookup(const K &key) {
  203. if (usingMap())
  204. return Ptr(map.lookup(key));
  205. for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
  206. if (it->key == key)
  207. return Ptr(it);
  208. }
  209. return Ptr(NULL);
  210. }
  211. JS_ALWAYS_INLINE
  212. AddPtr lookupForAdd(const K &key) {
  213. if (usingMap())
  214. return AddPtr(map.lookupForAdd(key));
  215. for (InlineElem *it = inl, *end = inl + inlNext; it != end; ++it) {
  216. if (it->key == key)
  217. return AddPtr(it, true);
  218. }
  219. /*
  220. * The add pointer that's returned here may indicate the limit entry of
  221. * the linear space, in which case the |add| operation will initialize
  222. * the map if necessary and add the entry there.
  223. */
  224. return AddPtr(inl + inlNext, false);
  225. }
  226. JS_ALWAYS_INLINE
  227. bool add(AddPtr &p, const K &key, const V &value) {
  228. JS_ASSERT(!p);
  229. if (p.isInlinePtr) {
  230. InlineElem *addPtr = p.inlAddPtr;
  231. JS_ASSERT(addPtr == inl + inlNext);
  232. /* Switching to map mode before we add this pointer. */
  233. if (addPtr == inl + InlineElems)
  234. return switchAndAdd(key, value);
  235. JS_ASSERT(!p.found());
  236. JS_ASSERT(uintptr_t(inl + inlNext) == uintptr_t(p.inlAddPtr));
  237. p.inlAddPtr->key = key;
  238. p.inlAddPtr->value = value;
  239. ++inlCount;
  240. ++inlNext;
  241. return true;
  242. }
  243. return map.add(p.mapAddPtr, key, value);
  244. }
  245. JS_ALWAYS_INLINE
  246. bool put(const K &key, const V &value) {
  247. AddPtr p = lookupForAdd(key);
  248. if (p) {
  249. p.value() = value;
  250. return true;
  251. }
  252. return add(p, key, value);
  253. }
  254. void remove(Ptr p) {
  255. JS_ASSERT(p);
  256. if (p.isInlinePtr) {
  257. JS_ASSERT(inlCount > 0);
  258. JS_ASSERT(p.inlPtr->key != NULL);
  259. p.inlPtr->key = NULL;
  260. --inlCount;
  261. return;
  262. }
  263. JS_ASSERT(map.initialized() && usingMap());
  264. map.remove(p.mapPtr);
  265. }
  266. void remove(const K &key) {
  267. if (Ptr p = lookup(key))
  268. remove(p);
  269. }
  270. class Range
  271. {
  272. friend class InlineMap;
  273. WordMapRange mapRange;
  274. InlineElem *cur;
  275. InlineElem *end;
  276. bool isInline;
  277. explicit Range(WordMapRange r)
  278. : cur(NULL), end(NULL), /* Avoid GCC 4.3.3 over-warning. */
  279. isInline(false) {
  280. mapRange = r;
  281. JS_ASSERT(!isInlineRange());
  282. }
  283. Range(const InlineElem *begin, const InlineElem *end_)
  284. : cur(const_cast<InlineElem *>(begin)),
  285. end(const_cast<InlineElem *>(end_)),
  286. isInline(true) {
  287. advancePastNulls(cur);
  288. JS_ASSERT(isInlineRange());
  289. }
  290. bool checkInlineRangeInvariants() const {
  291. JS_ASSERT(uintptr_t(cur) <= uintptr_t(end));
  292. JS_ASSERT_IF(cur != end, cur->key != NULL);
  293. return true;
  294. }
  295. bool isInlineRange() const {
  296. JS_ASSERT_IF(isInline, checkInlineRangeInvariants());
  297. return isInline;
  298. }
  299. void advancePastNulls(InlineElem *begin) {
  300. InlineElem *newCur = begin;
  301. while (newCur < end && NULL == newCur->key)
  302. ++newCur;
  303. JS_ASSERT(uintptr_t(newCur) <= uintptr_t(end));
  304. cur = newCur;
  305. }
  306. void bumpCurPtr() {
  307. JS_ASSERT(isInlineRange());
  308. advancePastNulls(cur + 1);
  309. }
  310. void operator==(const Range &other);
  311. public:
  312. bool empty() const {
  313. return isInlineRange() ? cur == end : mapRange.empty();
  314. }
  315. Entry front() {
  316. JS_ASSERT(!empty());
  317. if (isInlineRange())
  318. return Entry(cur->key, cur->value);
  319. return Entry(mapRange.front().key, mapRange.front().value);
  320. }
  321. void popFront() {
  322. JS_ASSERT(!empty());
  323. if (isInlineRange())
  324. bumpCurPtr();
  325. else
  326. mapRange.popFront();
  327. }
  328. }; /* class Range */
  329. Range all() const {
  330. return usingMap() ? Range(map.all()) : Range(inl, inl + inlNext);
  331. }
  332. }; /* class InlineMap */
  333. } /* namespace js */
  334. #endif