001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.util;
014    
015    import java.util.Iterator;
016    import java.util.NoSuchElementException;
017    
018    import org.jikesrvm.VM;
019    import org.jikesrvm.mm.mminterface.MemoryManager;
020    
021    /**
022     * Common super class for all VM hash sets.
023     */
024    abstract class AbstractHashSetRVM<T>  implements Iterable<T> {
025    
026      protected static final int DEFAULT_SIZE = 7;
027      private static final float LOAD = 3;
028      protected AbstractBucket<T>[] buckets;
029      protected int numElems = 0;
030    
031      abstract static class AbstractBucket<T> {
032        /**
033         * Change the next bucket after this bucket, possibly constructing a new
034         * abstract bucket
035         */
036        abstract AbstractBucket<T> setNext(AbstractBucket<T> next);
037    
038        abstract AbstractBucket<T> getNext();
039    
040        abstract T getKey();
041      }
042    
043      abstract AbstractBucket<T> createNewBucket(T key, AbstractBucket<T> next);
044    
045      @SuppressWarnings("unchecked")
046      protected AbstractBucket<T>[] newBucketArray(int size) {
047        return new AbstractBucket[size];
048      }
049    
050      AbstractHashSetRVM(int size) {
051        buckets = newBucketArray(size);
052      }
053    
054      public int size() {
055        return numElems;
056      }
057    
058      /**
059       * Advise against growing the buckets if they are immortal, as it will lead
060       * to multiple sets of buckets that will be scanned
061       */
062      private boolean growMapAllowed() {
063        return !VM.runningVM || !MemoryManager.isImmortal(buckets);
064      }
065    
066      public void add(T key) {
067        if (VM.VerifyAssertions) VM._assert(key != null);
068        if (growMapAllowed() && numElems > (buckets.length * LOAD)) {
069          growMap();
070        }
071    
072        int bucketIdx = bucketIndex(key, buckets.length);
073        AbstractBucket<T> cur = buckets[bucketIdx];
074        while (cur != null && !cur.getKey().equals(key)) {
075          cur = cur.getNext();
076        }
077        if (cur == null) {
078          buckets[bucketIdx] = createNewBucket(key, buckets[bucketIdx]);
079          numElems++;
080        }
081      }
082    
083      public T get(T key) {
084        if (key == null) {
085          return null;
086        }
087        int bucketIdx = bucketIndex(key, buckets.length);
088        AbstractBucket<T> cur = buckets[bucketIdx];
089        while (cur != null && !cur.getKey().equals(key)) {
090          cur = cur.getNext();
091        }
092        if (cur == null) {
093          return null;
094        } else {
095          return cur.getKey();
096        }
097      }
098    
099      public boolean contains(T key) {
100        return get(key) != null;
101      }
102    
103      public void addAll(AbstractHashSetRVM<T> c) {
104        for (T t : c) {
105          add(t);
106        }
107      }
108    
109      private void growMap() {
110        AbstractBucket<T>[] newBuckets = newBucketArray(buckets.length * 2 + 1);
111        for (AbstractBucket<T> cur : buckets) {
112          while (cur != null) {
113            AbstractBucket<T> next = cur.getNext();
114            int newIdx = bucketIndex(cur.getKey(), newBuckets.length);
115            cur = cur.setNext(newBuckets[newIdx]);
116            newBuckets[newIdx] = cur;
117            cur = next;
118          }
119        }
120        buckets = newBuckets;
121      }
122    
123      public void remove(T key) {
124        if (VM.VerifyAssertions) VM._assert(key != null);
125        int bucketIdx = bucketIndex(key, buckets.length);
126        AbstractBucket<T> cur = buckets[bucketIdx];
127        AbstractBucket<T> prev = null;
128        while (cur != null && !cur.getKey().equals(key)) {
129          prev = cur;
130          cur = cur.getNext();
131        }
132        if (cur != null) {
133          if (prev == null) {
134            // removing first bucket in chain.
135            buckets[bucketIdx] = cur.getNext();
136          } else {
137            AbstractBucket<T> newPrev = prev.setNext(cur.getNext());
138            if (newPrev != prev) {
139              throw new UnsupportedOperationException();
140            }
141          }
142          numElems--;
143        }
144      }
145    
146      public void removeAll() {
147        for (int i=0; i<buckets.length; i++) {
148          buckets[i] = null;
149        }
150        numElems = 0;
151      }
152    
153      @Override
154      public Iterator<T> iterator() {
155        return new SetIterator();
156      }
157    
158      private int bucketIndex(T key, int divisor) {
159        if (key == null) {
160          return 0;
161        } else {
162          return (key.hashCode() & 0x7fffffff) % divisor;
163        }
164      }
165    
166      /**
167       * Iterator
168       */
169      class SetIterator implements Iterator<T> {
170        private int bucketIndex = 0;
171        private AbstractBucket<T> next = null;
172        private int numVisited = 0;
173    
174        @Override
175        public T next() {
176          if (!hasNext()) {
177            throw new NoSuchElementException();
178          }
179    
180          while (next == null) {
181            next = buckets[bucketIndex++];
182          }
183          AbstractBucket<T> ans = next;
184          next = ans.getNext();
185          numVisited++;
186          return ans.getKey();
187        }
188    
189        @Override
190        public boolean hasNext() {
191          return numVisited < numElems;
192        }
193    
194        @Override
195        public void remove() {
196          throw new UnsupportedOperationException();
197        }
198      }
199    }