/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is [Open Source Virtual Machine]. * * The Initial Developer of the Original Code is * Adobe System Incorporated. * Portions created by the Initial Developer are Copyright (C) 2004-2007 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Adobe AS3 Team * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef __nanojit_Containers__ #define __nanojit_Containers__ namespace nanojit { /** simple linear bit array, memory taken from Allocator * warning: when bit array grows, old memory is wasted since it * was allocated from Allocator. pre-size the bitmap when possible * by passing nbits to the constructor. */ class BitSet { Allocator &allocator; int cap; int64_t *bits; static const int64_t ONE = 1; static const int SHIFT = 6; inline int bitnum2word(int i) { return i >> 6; } inline int64_t bitnum2mask(int i) { return ONE << (i & 63); } /** keep doubling array to fit at least w words */ void grow(int w); public: BitSet(Allocator& allocator, int nbits=128); /** clear all bits */ void reset(); /** perform a bitwise or with BitSet other, return true if * this bitset was modified */ bool setFrom(BitSet& other); /** return bit i as a bool */ bool get(int i) { NanoAssert(i >= 0); int w = bitnum2word(i); if (w < cap) return (bits[w] & bitnum2mask(i)) != 0; return false; } /** set bit i */ void set(int i) { NanoAssert(i >= 0); int w = bitnum2word(i); if (w >= cap) grow(w); bits[w] |= bitnum2mask(i); } /** clear bit i */ void clear(int i) { NanoAssert(i >= 0); int w = bitnum2word(i); if (w < cap) bits[w] &= ~bitnum2mask(i); } }; /** Seq is a single node in a linked list */ template class Seq { public: Seq(T head, Seq* tail=NULL) : head(head), tail(tail) {} T head; Seq* tail; }; /** SeqBuilder is used to create a linked list of Seq by inserting * nodes either at the beginning, with insert(), or at the end, with * add(). Once built, the actual list can be retained while this * SeqBuilder can be discarded. */ template class SeqBuilder { public: SeqBuilder(Allocator& allocator) : allocator(allocator) , items(NULL) , last(NULL) { } /** add item to beginning of list */ void insert(T item) { Seq* e = new (allocator) Seq(item, items); if (last == NULL) last = e; items = e; } /** add item to end of list */ void add(T item) { Seq* e = new (allocator) Seq(item); if (last == NULL) items = e; else last->tail = e; last = e; } /** return first item in sequence */ Seq* get() const { return items; } /** self explanitory */ bool isEmpty() const { return items == NULL; } /** de-reference all items */ void clear() { items = last = NULL; } private: Allocator& allocator; Seq* items; Seq* last; }; #ifdef NANOJIT_64BIT static inline size_t murmurhash(const void *key, size_t len) { const uint64_t m = 0xc6a4a7935bd1e995; const int r = 47; uint64_t h = 0; const uint64_t *data = (const uint64_t*)key; const uint64_t *end = data + (len/8); while(data != end) { uint64_t k = *data++; k *= m; k ^= k >> r; k *= m; h ^= k; h *= m; } const unsigned char *data2 = (const unsigned char*)data; switch(len & 7) { case 7: h ^= uint64_t(data2[6]) << 48; case 6: h ^= uint64_t(data2[5]) << 40; case 5: h ^= uint64_t(data2[4]) << 32; case 4: h ^= uint64_t(data2[3]) << 24; case 3: h ^= uint64_t(data2[2]) << 16; case 2: h ^= uint64_t(data2[1]) << 8; case 1: h ^= uint64_t(data2[0]); h *= m; }; h ^= h >> r; h *= m; h ^= h >> r; return (size_t)h; } #else static inline size_t murmurhash(const void * key, size_t len) { const uint32_t m = 0x5bd1e995; const int r = 24; uint32_t h = 0; const unsigned char * data = (const unsigned char *)key; while(len >= 4) { uint32_t k = *(size_t *)(void*)data; k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; data += 4; len -= 4; } switch(len) { case 3: h ^= data[2] << 16; case 2: h ^= data[1] << 8; case 1: h ^= data[0]; h *= m; }; h ^= h >> 13; h *= m; h ^= h >> 15; return (size_t)h; } #endif template struct DefaultHash { static size_t hash(const K &k) { return murmurhash(&k, sizeof(K)); } }; template struct DefaultHash { static size_t hash(K* k) { uintptr_t h = (uintptr_t) k; // move the low 3 bits higher up since they're often 0 h = (h>>3) ^ (h<<((sizeof(uintptr_t) * 8) - 3)); return (size_t) h; } }; /** Bucket hashtable with a fixed # of buckets (never rehash) * Intended for use when a reasonable # of buckets can be estimated ahead of time. */ template > class HashMap { Allocator& allocator; size_t nbuckets; class Node { public: K key; T value; Node(K k, T v) : key(k), value(v) { } }; Seq** buckets; /** return the node containing K, and the bucket index, or NULL if not found */ Node* find(K k, size_t &i) { i = H::hash(k) % nbuckets; for (Seq* p = buckets[i]; p != NULL; p = p->tail) { if (p->head.key == k) return &p->head; } return NULL; } public: HashMap(Allocator& a, size_t nbuckets = 16) : allocator(a) , nbuckets(nbuckets) , buckets(new (a) Seq*[nbuckets]) { NanoAssert(nbuckets > 0); clear(); } /** clear all buckets. Since we allocate all memory from Allocator, * nothing needs to be freed. */ void clear() { VMPI_memset(buckets, 0, sizeof(Seq*) * nbuckets); } /** add (k,v) to the map. If k is already in the map, replace the value */ void put(const K& k, const T& v) { size_t i; Node* n = find(k, i); if (n) { n->value = v; return; } buckets[i] = new (allocator) Seq(Node(k,v), buckets[i]); } /** return v for element k, or T(0) if k is not present */ T get(const K& k) { size_t i; Node* n = find(k, i); return n ? n->value : 0; } /** returns true if k is in the map. */ bool containsKey(const K& k) { size_t i; return find(k, i) != 0; } /** remove k from the map, if it is present. if not, remove() * silently returns */ void remove(const K& k) { size_t i = H::hash(k) % nbuckets; Seq** prev = &buckets[i]; for (Seq* p = buckets[i]; p != NULL; p = p->tail) { if (p->head.key == k) { (*prev) = p->tail; return; } prev = &p->tail; } } /** Iter is an iterator for HashMap, intended to be instantiated on * the stack. Iteration order is undefined. Mutating the hashmap * while iteration is in progress gives undefined results. All iteration * state is in class Iter, so multiple iterations can be in progress * at the same time. for example: * * HashMap::Iter iter(map); * while (iter.next()) { * K *k = iter.key(); * T *t = iter.value(); * } */ class Iter { friend class HashMap; const HashMap ↦ int bucket; const Seq* current; public: Iter(HashMap& map) : map(map), bucket((int)map.nbuckets-1), current(NULL) { } /** return true if more (k,v) remain to be visited */ bool next() { if (current) current = current->tail; while (bucket >= 0 && !current) current = map.buckets[bucket--]; return current != NULL; } /** return the current key */ const K& key() const { NanoAssert(current != NULL); return current->head.key; } /** return the current value */ const T& value() const { NanoAssert(current != NULL); return current->head.value; } }; /** return true if the hashmap has no elements */ bool isEmpty() { Iter iter(*this); return !iter.next(); } }; /** * Simple binary tree. No balancing is performed under the assumption * that the only users of this structure are not performance critical. */ template class TreeMap { Allocator& alloc; class Node { public: Node* left; Node* right; K key; T value; Node(K k, T v) : left(NULL), right(NULL), key(k), value(v) { } }; Node* root; /** * helper method to recursively insert (k,v) below Node n or a child * of n so that the binary search tree remains well formed. */ void insert(Node* &n, K k, T v) { if (!n) n = new (alloc) Node(k, v); else if (k == n->key) n->value = v; else if (k < n->key) insert(n->left, k, v); else insert(n->right, k, v); } /** * search for key k below Node n and return n if found, or the * closest parent n where k should be inserted. */ Node* find(Node* n, K k) { if (!n) return NULL; if (k == n->key) return n; if (k < n->key) return find(n->left, k); if (n->right) return find(n->right, k); return n; } public: TreeMap(Allocator& alloc) : alloc(alloc), root(NULL) { } /** set k = v in the map. if k already exists, replace its value */ void put(K k, T v) { insert(root, k, v); } /** return the closest key that is <= k, or NULL if k is smaller than every key in the Map. */ K findNear(K k) { Node* n = find(root, k); return n ? n->key : 0; } /** returns the value for k or NULL */ T get(K k) { Node* n = find(root, k); return (n && n->key == k) ? n->value : 0; } /** returns true iff k is in the Map. */ bool containsKey(K k) { Node* n = find(root, k); return n && n->key == k; } /** make the tree empty. trivial since we dont manage elements */ void clear() { root = NULL; } }; } #endif // __nanojit_Containers__