// // Copyright (C) 2006 United States Government as represented by the // Administrator of the National Aeronautics and Space Administration // (NASA). All Rights Reserved. // // This software is distributed under the NASA Open Source Agreement // (NOSA), version 1.3. The NOSA has been approved by the Open Source // Initiative. See the file NOSA-1.3-JPF at the top of the distribution // directory tree for the complete NOSA document. // // THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY // KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT // LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO // SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR // A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT // THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT // DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. // package gov.nasa.jpf.util; import static java.lang.Integer.MIN_VALUE; import java.util.Arrays; /** * This has approximately the interface of ObjVector but uses a hash table * instead of an array. Also, does not require allocation with each add. */ public class SparseObjVector<E> { @SuppressWarnings("unused") private static final boolean DEBUG = false; static final double MAX_LOAD_WIPE = 0.6; static final double MAX_LOAD_REHASH = 0.4; static final int DEFAULT_POW = 10; int[] idxTable; // MIN_VALUE => unoccupied Object[] valTable; // can be bound to null int count; int pow; int mask; int nextWipe; int nextRehash; /** * Creates a SimplePool that holds about 716 elements before first * rehash. */ public SparseObjVector() { this(DEFAULT_POW); } /** * Creates a SimplePool that holds about 0.7 * 2**pow elements before * first rehash. */ public SparseObjVector(int pow) { this.pow = pow; newTable(); count = 0; mask = valTable.length - 1; nextWipe = (int)(MAX_LOAD_WIPE * mask); nextRehash = (int)(MAX_LOAD_REHASH * mask); } public void clear() { pow = DEFAULT_POW; newTable(); count = 0; mask = valTable.length - 1; nextWipe = (int) (MAX_LOAD_WIPE * mask); nextRehash = (int) (MAX_LOAD_REHASH * mask); } // INTERNAL // protected void newTable() { valTable = new Object[1 << pow]; idxTable = new int[1 << pow]; Arrays.fill(idxTable, MIN_VALUE); } protected int mix(int x) { int y = 0x9e3779b9; x ^= 0x510fb60d; y += (x >> 8) + (x << 3); x ^= (y >> 5) + (y << 2); return y - x; } // ********************* Public API ******************** // @SuppressWarnings("unchecked") public E get(int idx) { int code = mix(idx); int pos = code & mask; int delta = (code >> (pow - 1)) | 1; // must be odd! int oidx = pos; for(;;) { int tidx = idxTable[pos]; if (tidx == MIN_VALUE) { return null; } if (tidx == idx) { return (E) valTable[pos]; } pos = (pos + delta) & mask; assert (pos != oidx); // should never wrap around } } public void set(int idx, E e) { int code = mix(idx); int pos = code & mask; int delta = (code >> (pow - 1)) | 1; // must be odd! int oidx = pos; for(;;) { int tidx = idxTable[pos]; if (tidx == MIN_VALUE) { break; } if (tidx == idx) { valTable[pos] = e; // update return; // and we're done } pos = (pos + delta) & mask; assert (pos != oidx); // should never wrap around } // idx not in table; add it if ((count+1) >= nextWipe) { // too full if (count >= nextRehash) { pow++; } /** // determine if size needs to be increased or just wipe null blocks int oldCount = count; count = 0; for (int i = 0; i < idxTable.length; i++) { if (idxTable[i] != MIN_VALUE && valTable[i] != null) { count++; } } if (count >= nextRehash) { pow++; // needs to be increased in size if (DEBUG) { System.out.println("Rehash to capacity: 2**" + pow); } } else { if (DEBUG) { System.out.println("Rehash reclaiming this many nulls: " + (oldCount - count)); } } **/ Object[] oldValTable = valTable; int[] oldIdxTable = idxTable; newTable(); mask = idxTable.length - 1; nextWipe = (int)(MAX_LOAD_WIPE * mask); nextRehash = (int)(MAX_LOAD_REHASH * mask); int oldLen = oldIdxTable.length; for (int i = 0; i < oldLen; i++) { int tidx = oldIdxTable[i]; if (tidx == MIN_VALUE) continue; Object o = oldValTable[i]; //if (o == null) continue; // otherwise: code = mix(tidx); pos = code & mask; delta = (code >> (pow - 1)) | 1; // must be odd! while (idxTable[pos] != MIN_VALUE) { // we know enough slots exist pos = (pos + delta) & mask; } idxTable[pos] = tidx; valTable[pos] = o; } // done with rehash; now get idx to empty slot code = mix(idx); pos = code & mask; delta = (code >> (pow - 1)) | 1; // must be odd! while (idxTable[pos] != MIN_VALUE) { // we know enough slots exist pos = (pos + delta) & mask; } } else { // pos already pointing to empty slot } count++; idxTable[pos] = idx; valTable[pos] = e; } public void remove(int idx) { set(idx, null); } // ************************** Test main ************************ // public static void main(String[] args) { SparseObjVector<Integer> vect = new SparseObjVector<Integer>(3); // add some for (int i = -4200; i < 4200; i += 10) { vect.set(i, new Integer(i)); } // check for added & non-added for (int i = -4200; i < 4200; i += 10) { Integer v = vect.get(i); if (v.intValue() != i) { throw new IllegalStateException(); } } for (int i = -4205; i < 4200; i += 10) { Integer v = vect.get(i); if (v != null) { throw new IllegalStateException(); } } // add some more for (int i = -4201; i < 4200; i += 10) { vect.set(i, new Integer(i)); } // check all added for (int i = -4200; i < 4200; i += 10) { Integer v = vect.get(i); if (v.intValue() != i) { throw new IllegalStateException(); } } for (int i = -4201; i < 4200; i += 10) { Integer v = vect.get(i); if (v.intValue() != i) { throw new IllegalStateException(); } } // "remove" some for (int i = -4200; i < 4200; i += 10) { vect.remove(i); } // check for added & non-added for (int i = -4201; i < 4200; i += 10) { Integer v = vect.get(i); if (v.intValue() != i) { throw new IllegalStateException(); } } for (int i = -4200; i < 4200; i += 10) { Integer v = vect.get(i); if (v != null) { throw new IllegalStateException(); } } // add even more for (int i = -4203; i < 4200; i += 10) { vect.set(i, new Integer(i)); } for (int i = -4204; i < 4200; i += 10) { vect.set(i, new Integer(i)); } } }