diff options
Diffstat (limited to 'debian/uncrustify-trinity/uncrustify-trinity-0.78.1/tests/expected/d/40000-HashMap.d')
-rw-r--r-- | debian/uncrustify-trinity/uncrustify-trinity-0.78.1/tests/expected/d/40000-HashMap.d | 1111 |
1 files changed, 1111 insertions, 0 deletions
diff --git a/debian/uncrustify-trinity/uncrustify-trinity-0.78.1/tests/expected/d/40000-HashMap.d b/debian/uncrustify-trinity/uncrustify-trinity-0.78.1/tests/expected/d/40000-HashMap.d new file mode 100644 index 00000000..e90087c4 --- /dev/null +++ b/debian/uncrustify-trinity/uncrustify-trinity-0.78.1/tests/expected/d/40000-HashMap.d @@ -0,0 +1,1111 @@ +/******************************************************************************* + + @file HashMap.d + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for damages + of any kind arising from the use of this software. + + Permission is hereby granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and/or + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment within documentation of + said product would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any distribution + of the source. + + 4. Derivative works are permitted, but they must carry this notice + in full and credit the original source. + + + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + Written by Doug Lea with assistance from members of JCP JSR-166 + Expert Group and released to the public domain, as explained at + http://creativecommons.org/licenses/publicdomain + + @version Initial version, July 2004 + @author Doug Lea; ported/modified by Kris + +*******************************************************************************/ + +module mango.cache.HashMap; + +/****************************************************************************** + +******************************************************************************/ + +extern (C) +{ +int memcmp(char *, char *, uint); +} + + +/** + * A hash table supporting full concurrency of retrievals and + * adjustable expected concurrency for updates. This class obeys the + * same functional specification as {@link java.util.Hashtable}, and + * includes versions of methods corresponding to each method of + * <tt>Hashtable</tt>. However, even though all operations are + * thread-safe, retrieval operations do <em>not</em> entail locking, + * and there is <em>not</em> any support for locking the entire table + * in a way that prevents all access. This class is fully + * interoperable with <tt>Hashtable</tt> in programs that rely on its + * thread safety but not on its synchronization details. + * + * <p> Retrieval operations (including <tt>get</tt>) generally do not + * block, so may overlap with update operations (including + * <tt>put</tt> and <tt>remove</tt>). Retrievals reflect the results + * of the most recently <em>completed</em> update operations holding + * upon their onset. For aggregate operations such as <tt>putAll</tt> + * and <tt>clear</tt>, concurrent retrievals may reflect insertion or + * removal of only some entries. Similarly, Iterators and + * Enumerations return elements reflecting the state of the hash table + * at some point at or since the creation of the iterator/enumeration. + * They do <em>not</em> throw + * {@link ConcurrentModificationException}. However, iterators are + * designed to be used by only one thread at a time. + * + * <p> The allowed concurrency among update operations is guided by + * the optional <tt>concurrencyLevel</tt> constructor argument + * (default 16), which is used as a hint for internal sizing. The + * table is internally partitioned to try to permit the indicated + * number of concurrent updates without contention. Because placement + * in hash tables is essentially random, the actual concurrency will + * vary. Ideally, you should choose a value to accommodate as many + * threads as will ever concurrently modify the table. Using a + * significantly higher value than you need can waste space and time, + * and a significantly lower value can lead to thread contention. But + * overestimates and underestimates within an order of magnitude do + * not usually have much noticeable impact. A value of one is + * appropriate when it is known that only one thread will modify and + * all others will only read. Also, resizing this or any other kind of + * hash table is a relatively slow operation, so, when possible, it is + * a good idea to provide estimates of expected table sizes in + * constructors. + * + * <p>This class and its views and iterators implement all of the + * <em>optional</em> methods of the {@link Map} and {@link Iterator} + * interfaces. + * + * <p> Like {@link java.util.Hashtable} but unlike {@link + * java.util.HashMap}, this class does NOT allow <tt>null</tt> to be + * used as a key or value. + * + * <p>This class is a member of the + * <a href="{@docRoot}/../guide/collections/index.html"> + * Java Collections Framework</a>. + * + * @since 1.5 + * @author Doug Lea + * @param <K> the type of keys maintained by this map + * @param <V> the type of mapped values + */ + +class HashMap +{ + alias void[] K; + alias Object V; + alias jhash hash; // jhash, fnv, or walter + + /* + * The basic strategy is to subdivide the table among Segments, + * each of which itself is a concurrently readable hash table. + */ + + /* ---------------- Constants -------------- */ + + /** + * The default initial number of table slots for this table. + * Used when not otherwise specified in constructor. + */ + private const uint DEFAULT_INITIAL_CAPACITY = 16; + + /** + * The maximum capacity, used if a higher value is implicitly + * specified by either of the constructors with arguments. MUST + * be a power of two <= 1<<30 to ensure that entries are indexible + * using ints. + */ + private const uint MAXIMUM_CAPACITY = 1 << 30; + + /** + * The default load factor for this table. Used when not + * otherwise specified in constructor. + */ + private const float DEFAULT_LOAD_FACTOR = 0.75f; + + /** + * The default number of concurrency control segments. + **/ + private const uint DEFAULT_SEGMENTS = 16; + + /** + * The maximum number of segments to allow; used to bound + * constructor arguments. + */ + private const uint MAX_SEGMENTS = 1 << 16; // slightly conservative + + + /* ---------------- Fields -------------- */ + + /** + * Mask value for indexing into segments. The upper bits of a + * key's hash code are used to choose the segment. + **/ + private final int segmentMask; + + /** + * Shift value for indexing within segments. + **/ + private final int segmentShift; + + /** + * The segments, each of which is a specialized hash table + */ + private final Segment[] segments; + + + /* ---------------- Small Utilities -------------- */ + + /** + * Returns a hash code for non-null Object x. + * Uses the same hash code spreader as most other java.util hash tables. + * @param x the object serving as a key + * @return the hash code + */ + private static final uint walter(K x) + { + uint h = typeid(char[]).getHash(&x); + + h += ~(h << 9); + h ^= (h >>> 14); + h += (h << 4); + h ^= (h >>> 10); + return h; + } + + /** + * Returns a hash code for non-null Object x. + * uses the FNV hash function + * @param x the object serving as a key + * @return the hash code + */ + private static final uint fnv(K x) + { + uint hash = 2_166_136_261; + + foreach (ubyte c; cast(ubyte[])x) + { + hash ^= c; + hash *= 16_777_619; + } + return hash; + } + + + + /** + * hash() -- hash a variable-length key into a 32-bit value + * k : the key (the unaligned variable-length array of bytes) + * len : the length of the key, counting by bytes + * level : can be any 4-byte value + * Returns a 32-bit value. Every bit of the key affects every bit of + * the return value. Every 1-bit and 2-bit delta achieves avalanche. + * About 36+6len instructions. + * + * The best hash table sizes are powers of 2. There is no need to do + * mod a prime (mod is sooo slow!). If you need less than 32 bits, + * use a bitmask. For example, if you need only 10 bits, do + * h = (h & hashmask(10)); + * In which case, the hash table should have hashsize(10) elements. + * + * If you are hashing n strings (ub1 **)k, do it like this: + * for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h); + * + * By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this + * code any way you wish, private, educational, or commercial. It's free. + * + * See http://burlteburtle.net/bob/hash/evahash.html + * Use for hash table lookup, or anything where one collision in 2^32 is + * acceptable. Do NOT use for cryptographic purposes. + */ + + static final uint jhash(K x) + { + ubyte *k; + uint a, + b, + c, + len; + + len = x.length; + k = cast(ubyte *)x; + a = b = 0x9e3779b9; + + // the previous hash value + c = 0; + + // handle most of the key + while (len >= 12) + { + a += *cast(uint *)(k + 0); + b += *cast(uint *)(k + 4); + c += *cast(uint *)(k + 8); + + a -= b; a -= c; a ^= (c >> 13); + b -= c; b -= a; b ^= (a << 8); + c -= a; c -= b; c ^= (b >> 13); + a -= b; a -= c; a ^= (c >> 12); + b -= c; b -= a; b ^= (a << 16); + c -= a; c -= b; c ^= (b >> 5); + a -= b; a -= c; a ^= (c >> 3); + b -= c; b -= a; b ^= (a << 10); + c -= a; c -= b; c ^= (b >> 15); + k += 12; len -= 12; + } + + // handle the last 11 bytes + c += x.length; + switch (len) + { + case 11: c += (cast(uint)k[10] << 24); + + case 10: c += (cast(uint)k[9] << 16); + + case 9: c += (cast(uint)k[8] << 8); + + case 8: b += (cast(uint)k[7] << 24); + + case 7: b += (cast(uint)k[6] << 16); + + case 6: b += (cast(uint)k[5] << 8); + + case 5: b += k[4]; + + case 4: a += (cast(uint)k[3] << 24); + + case 3: a += (cast(uint)k[2] << 16); + + case 2: a += (cast(uint)k[1] << 8); + + case 1: a += k[0]; + + default: + } + + a -= b; a -= c; a ^= (c >> 13); + b -= c; b -= a; b ^= (a << 8); + c -= a; c -= b; c ^= (b >> 13); + a -= b; a -= c; a ^= (c >> 12); + b -= c; b -= a; b ^= (a << 16); + c -= a; c -= b; c ^= (b >> 5); + a -= b; a -= c; a ^= (c >> 3); + b -= c; b -= a; b ^= (a << 10); + c -= a; c -= b; c ^= (b >> 15); + + return c; + } + + + /** + * Returns the segment that should be used for key with given hash + * @param hash the hash code for the key + * @return the segment + */ + private final Segment segmentFor(uint hash) + { + return segments[(hash >>> segmentShift) & segmentMask]; + } + + /* ---------------- Inner Classes -------------- */ + + /** + * ConcurrentHashMap list entry. Note that this is never exported + * out as a user-visible Map.Entry. + * + * Because the value field is volatile, not final, it is legal wrt + * the Java Memory Model for an unsynchronized reader to see null + * instead of initial value when read via a data race. Although a + * reordering leading to this is not likely to ever actually + * occur, the Segment.readValueUnderLock method is used as a + * backup in case a null (pre-initialized) value is ever seen in + * an unsynchronized access method. + */ + private static class HashEntry + { + final K key; + final uint hash; + final V value; + final HashEntry next; + + this(K key, uint hash, HashEntry next, V value) + { + this.key = key; + this.hash = hash; + this.next = next; + this.value = value; + } + } + + /** + * Segments are specialized versions of hash tables. This + * subclasses from ReentrantLock opportunistically, just to + * simplify some locking and avoid separate construction. + **/ + static class Segment + { + /* + * Segments maintain a table of entry lists that are ALWAYS + * kept in a consistent state, so can be read without locking. + * Next fields of nodes are immutable (final). All list + * additions are performed at the front of each bin. This + * makes it easy to check changes, and also fast to traverse. + * When nodes would otherwise be changed, new nodes are + * created to replace them. This works well for hash tables + * since the bin lists tend to be short. (The average length + * is less than two for the default load factor threshold.) + * + * Read operations can thus proceed without locking, but rely + * on selected uses of volatiles to ensure that completed + * write operations performed by other threads are + * noticed. For most purposes, the "count" field, tracking the + * number of elements, serves as that volatile variable + * ensuring visibility. This is convenient because this field + * needs to be read in many read operations anyway: + * + * - All (unsynchronized) read operations must first read the + * "count" field, and should not look at table entries if + * it is 0. + * + * - All (synchronized) write operations should write to + * the "count" field after structurally changing any bin. + * The operations must not take any action that could even + * momentarily cause a concurrent read operation to see + * inconsistent data. This is made easier by the nature of + * the read operations in Map. For example, no operation + * can reveal that the table has grown but the threshold + * has not yet been updated, so there are no atomicity + * requirements for this with respect to reads. + * + * As a guide, all critical volatile reads and writes to the + * count field are marked in code comments. + */ + + /** + * The number of elements in this segment's region. + **/ + int count; + + /** + * The table is rehashed when its size exceeds this threshold. + * (The value of this field is always (int)(capacity * + * loadFactor).) + */ + int threshold; + + /** + * The per-segment table. Declared as a raw type, casted + * to HashEntry<K,V> on each use. + */ + HashEntry[] table; + + /** + * The load factor for the hash table. Even though this value + * is same for all segments, it is replicated to avoid needing + * links to outer object. + * @serial + */ + final float loadFactor; + + this(int initialCapacity, float lf) + { + loadFactor = lf; + setTable(new HashEntry[initialCapacity]); + } + + /** + * Set table to new HashEntry array. + * Call only while holding lock or in constructor. + **/ + private final void setTable(HashEntry[] newTable) + { + threshold = cast(int)(newTable.length * loadFactor); + volatile table = newTable; + } + + /** + * Return properly casted first entry of bin for given hash + */ + private final HashEntry getFirst(uint hash) + { + HashEntry[] tab; + + volatile tab = table; + return tab[hash & (tab.length - 1)]; + } + + /** + * Return true if the two keys match + */ + private static final bool matchKey(K a, K b) + { + if (a.length == b.length) + return cast(bool)(memcmp(cast(char *)a, cast(char *)b, a.length) == 0); + + return false; + } + + /* Specialized implementations of map methods */ + + final V get(K key, uint hash) + { + int c; + + // read-volatile + volatile c = count; + if (c) + { + HashEntry e = getFirst(hash); + + while (e) + { + if (hash == e.hash && matchKey(key, e.key)) + { + V v; + + volatile v = e.value; + if (v) + return v; + + synchronized (this) + return e.value; + } + e = e.next; + } + } + return null; + } + + + final bool containsKey(K key, uint hash) + { + int c; + + // read-volatile + volatile c = count; + if (c) + { + HashEntry e = getFirst(hash); + + while (e) + { + if (e.hash == hash && matchKey(key, e.key)) + return true; + + e = e.next; + } + } + return false; + } + + + + final synchronized V replace(K key, uint hash, V newValue) + { + HashEntry e = getFirst(hash); + + while (e && (e.hash != hash || !matchKey(key, e.key))) + e = e.next; + + V oldValue = null; + + if (e) + volatile + { + oldValue = e.value; + e.value = newValue; + } + return oldValue; + } + + + final synchronized V put(K key, uint hash, V value, bool onlyIfAbsent) + { + int c; + + volatile c = count; + if (c++ > threshold) + rehash(); + + HashEntry[] tab; + + volatile tab = table; + uint index = hash & (tab.length - 1); + HashEntry first = tab[index]; + HashEntry e = first; + + while (e && (e.hash != hash || !matchKey(key, e.key))) + e = e.next; + + V oldValue; + + if (e) + { + volatile oldValue = e.value; + if (!onlyIfAbsent) + volatile e.value = value; + } + else + { + oldValue = null; + tab[index] = new HashEntry(key, hash, first, value); + + // write-volatile + volatile count = c; + } + return oldValue; + } + + + private final void rehash() + { + HashEntry[] oldTable; + + volatile oldTable = table; + int oldCapacity = oldTable.length; + + if (oldCapacity >= MAXIMUM_CAPACITY) + return; + + /* + * Reclassify nodes in each list to new Map. Because we are + * using power-of-two expansion, the elements from each bin + * must either stay at same index, or move with a power of two + * offset. We eliminate unnecessary node creation by catching + * cases where old nodes can be reused because their next + * fields won't change. Statistically, at the default + * threshold, only about one-sixth of them need cloning when + * a table doubles. The nodes they replace will be garbage + * collectable as soon as they are no longer referenced by any + * reader thread that may be in the midst of traversing table + * right now. + */ + + HashEntry[] newTable = new HashEntry[oldCapacity << 1]; + + threshold = cast(int)(newTable.length * loadFactor); + int sizeMask = newTable.length - 1; + + for (int i = 0; i < oldCapacity; ++i) + { + // We need to guarantee that any existing reads of old Map can + // proceed. So we cannot yet null out each bin. + HashEntry e = oldTable[i]; + + if (e) + { + HashEntry next = e.next; + uint idx = e.hash & sizeMask; + + // Single node on list + if (next is null) + newTable[idx] = e; + else + { + // Reuse trailing consecutive sequence at same slot + HashEntry lastRun = e; + int lastIdx = idx; + + for (HashEntry last = next; last; last = last.next) + { + uint k = last.hash & sizeMask; + + if (k != lastIdx) + { + lastIdx = k; + lastRun = last; + } + } + newTable[lastIdx] = lastRun; + + // Clone all remaining nodes + for (HashEntry p = e; p !is lastRun; p = p.next) + { + uint k = p.hash & sizeMask; + HashEntry n = newTable[k]; + + newTable[k] = new HashEntry(p.key, p.hash, n, p.value); + } + } + } + } + volatile table = newTable; + } + + /** + * Remove; match on key only if value null, else match both. + */ + final synchronized V remove(K key, uint hash, V value) + { + int c; + HashEntry[] tab; + + volatile c = count - 1; + volatile tab = table; + + uint index = hash & (tab.length - 1); + HashEntry first = tab[index]; + HashEntry e = first; + + while (e && (e.hash != hash || !matchKey(key, e.key))) + e = e.next; + + V oldValue = null; + + if (e) + { + V v; + + volatile v = e.value; + if (value is null || value == v) + { + oldValue = v; + + // All entries following removed node can stay + // in list, but all preceding ones need to be + // cloned. + HashEntry newFirst = e.next; + + for (HashEntry p = first; p !is e; p = p.next) + newFirst = new HashEntry(p.key, p.hash, newFirst, p.value); + tab[index] = newFirst; + + // write-volatile + volatile count = c; + } + } + return oldValue; + } + + + final synchronized void clear() + { + if (count) + { + HashEntry[] tab; + + volatile tab = table; + + for (int i = 0; i < tab.length; i++) + tab[i] = null; + + // write-volatile + volatile count = 0; + } + } + } + + + + /* ---------------- Public operations -------------- */ + + /** + * Creates a new, empty map with the specified initial + * capacity and the specified load factor. + * + * @param initialCapacity the initial capacity. The implementation + * performs internal sizing to accommodate this many elements. + * @param loadFactor the load factor threshold, used to control resizing. + * @param concurrencyLevel the estimated number of concurrently + * updating threads. The implementation performs internal sizing + * to try to accommodate this many threads. + * @throws IllegalArgumentException if the initial capacity is + * negative or the load factor or concurrencyLevel are + * nonpositive. + */ + public this(uint initialCapacity, float loadFactor, uint concurrencyLevel) + { + assert(loadFactor > 0); + + if (concurrencyLevel > MAX_SEGMENTS) + concurrencyLevel = MAX_SEGMENTS; + + // Find power-of-two sizes best matching arguments + int sshift = 0; + int ssize = 1; + + while (ssize < concurrencyLevel) + { + ++sshift; + ssize <<= 1; + } + + segmentShift = 32 - sshift; + segmentMask = ssize - 1; + this.segments = new Segment[ssize]; + + if (initialCapacity > MAXIMUM_CAPACITY) + initialCapacity = MAXIMUM_CAPACITY; + + int c = initialCapacity / ssize; + + if (c * ssize < initialCapacity) + ++c; + + int cap = 1; + + while (cap < c) + cap <<= 1; + + for (int i = 0; i < this.segments.length; ++i) + this.segments[i] = new Segment(cap, loadFactor); + } + + /** + * Creates a new, empty map with the specified initial + * capacity, and with default load factor and concurrencyLevel. + * + * @param initialCapacity The implementation performs internal + * sizing to accommodate this many elements. + * @throws IllegalArgumentException if the initial capacity of + * elements is negative. + */ + public this(uint initialCapacity) + { + this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS); + } + + /** + * Creates a new, empty map with a default initial capacity, + * load factor, and concurrencyLevel. + */ + public this() + { + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS); + } + + /** + * Returns the value to which the specified key is mapped in this table. + * + * @param key a key in the table. + * @return the value to which the key is mapped in this table; + * <tt>null</tt> if the key is not mapped to any value in + * this table. + * @throws NullPointerException if the key is + * <tt>null</tt>. + */ + public V get(K key) + { + uint hash = hash(key); // throws NullPointerException if key null + + return segmentFor(hash).get(key, hash); + } + + /** + * Tests if the specified object is a key in this table. + * + * @param key possible key. + * @return <tt>true</tt> if and only if the specified object + * is a key in this table, as determined by the + * <tt>equals</tt> method; <tt>false</tt> otherwise. + * @throws NullPointerException if the key is + * <tt>null</tt>. + */ + public bool containsKey(K key) + { + uint hash = hash(key); // throws NullPointerException if key null + + return segmentFor(hash).containsKey(key, hash); + } + + /** + * Maps the specified <tt>key</tt> to the specified + * <tt>value</tt> in this table. Neither the key nor the + * value can be <tt>null</tt>. + * + * <p> The value can be retrieved by calling the <tt>get</tt> method + * with a key that is equal to the original key. + * + * @param key the table key. + * @param value the value. + * @return the previous value of the specified key in this table, + * or <tt>null</tt> if it did not have one. + * @throws NullPointerException if the key or value is + * <tt>null</tt>. + */ + public V put(K key, V value) + { + assert(value); + + uint hash = hash(key); + + return segmentFor(hash).put(key, hash, value, false); + } + + /** + * If the specified key is not already associated + * with a value, associate it with the given value. + * This is equivalent to + * <pre> + * if (!map.containsKey(key)) + * return map.put(key, value); + * else + * return map.get(key); + * </pre> + * Except that the action is performed atomically. + * @param key key with which the specified value is to be associated. + * @param value value to be associated with the specified key. + * @return previous value associated with specified key, or <tt>null</tt> + * if there was no mapping for key. + * @throws NullPointerException if the specified key or value is + * <tt>null</tt>. + */ + public V putIfAbsent(K key, V value) + { + assert(value); + + uint hash = hash(key); + + return segmentFor(hash).put(key, hash, value, true); + } + + + /** + * Removes the key (and its corresponding value) from this + * table. This method does nothing if the key is not in the table. + * + * @param key the key that needs to be removed. + * @return the value to which the key had been mapped in this table, + * or <tt>null</tt> if the key did not have a mapping. + * @throws NullPointerException if the key is + * <tt>null</tt>. + */ + public V remove(K key) + { + uint hash = hash(key); + + return segmentFor(hash).remove(key, hash, null); + } + + /** + * Remove entry for key only if currently mapped to given value. + * Acts as + * <pre> + * if (map.get(key).equals(value)) { + * map.remove(key); + * return true; + * } else return false; + * </pre> + * except that the action is performed atomically. + * @param key key with which the specified value is associated. + * @param value value associated with the specified key. + * @return true if the value was removed + * @throws NullPointerException if the specified key is + * <tt>null</tt>. + */ + public bool remove(K key, V value) + { + uint hash = hash(key); + + return cast(bool)(segmentFor(hash).remove(key, hash, value) !is null); + } + + + /** + * Replace entry for key only if currently mapped to some value. + * Acts as + * <pre> + * if ((map.containsKey(key)) { + * return map.put(key, value); + * } else return null; + * </pre> + * except that the action is performed atomically. + * @param key key with which the specified value is associated. + * @param value value to be associated with the specified key. + * @return previous value associated with specified key, or <tt>null</tt> + * if there was no mapping for key. + * @throws NullPointerException if the specified key or value is + * <tt>null</tt>. + */ + public V replace(K key, V value) + { + assert(value); + + uint hash = hash(key); + + return segmentFor(hash).replace(key, hash, value); + } + + + /** + * Removes all mappings from this map. + */ + public void clear() + { + for (int i = 0; i < segments.length; ++i) + segments[i].clear(); + } + + + /** + * Returns an enumeration of the keys in this table. + * + * @return an enumeration of the keys in this table. + * @see #keySet + */ + public KeyIterator keys() + { + return new KeyIterator(this); + } + + /** + * Returns an enumeration of the values in this table. + * + * @return an enumeration of the values in this table. + * @see #values + */ + public ValueIterator elements() + { + return new ValueIterator(this); + } + + /********************************************************************** + + Iterate over all keys in hashmap + + **********************************************************************/ + + int opApply(int delegate(inout char[]) dg) + { + int result = 0; + KeyIterator iterator = keys(); + + while (iterator.hasNext) + { + char[] ca = cast(char[])iterator.next; + + if ((result = dg(ca)) != 0) + break; + } + return result; + } + + /********************************************************************** + + Iterate over all keys in hashmap + + **********************************************************************/ + + int opApply(int delegate(inout char[], inout Object) dg) + { + int result = 0; + KeyIterator iterator = keys(); + + while (iterator.hasNext) + { + HashEntry he = iterator.nextElement; + char[] ca = cast(char[])he.key; + + if ((result = dg(ca, he.value)) != 0) + break; + } + return result; + } + + + /* ---------------- Iterator Support -------------- */ + + abstract static class HashIterator + { + int nextSegmentIndex; + int nextTableIndex; + HashEntry[] currentTable; + HashEntry nextEntry; + HashEntry lastReturned; + HashMap map; + + this(HashMap map) + { + this.map = map; + nextSegmentIndex = map.segments.length - 1; + nextTableIndex = -1; + advance(); + } + + final void advance() + { + if (nextEntry !is null && (nextEntry = nextEntry.next) !is null) + return; + + while (nextTableIndex >= 0) + { + if ((nextEntry = currentTable[nextTableIndex--]) !is null) + return; + } + + while (nextSegmentIndex >= 0) + { + Segment seg = map.segments[nextSegmentIndex--]; + + volatile if (seg.count) + { + currentTable = seg.table; + for (int j = currentTable.length - 1; j >= 0; --j) + { + if ((nextEntry = currentTable[j]) !is null) + { + nextTableIndex = j - 1; + return; + } + } + } + } + } + + public bool hasNext() + { + return cast(bool)(nextEntry !is null); + } + + HashEntry nextElement() + { + if (nextEntry is null) + throw new Exception("no such element in HashMap"); + + lastReturned = nextEntry; + advance(); + return lastReturned; + } + } + + static class KeyIterator : HashIterator + { + this(HashMap map) { + super(map); + } + public K next() + { + return super.nextElement().key; + } + } + + static class ValueIterator : HashIterator + { + this(HashMap map) { + super(map); + } + public V next() + { + volatile return super.nextElement().value; + } + } +} |