// // 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.vm; import cmu.conditional.Conditional; //import gov.nasa.jpf.util.LongVector; /** * Implements StateSet based on Jenkins hashes. */ public class JenkinsStateSet extends SerializingStateSet { static final double MAX_LOAD = 0.7; static final int INIT_SIZE = 65536; int lastStateId = -1; //LongVector fingerprints; long[] fingerprints; int[] hashtable; int nextRehash; public JenkinsStateSet() { lastStateId = -1; hashtable = new int[INIT_SIZE]; nextRehash = (int) (MAX_LOAD * INIT_SIZE); //fingerprints = new LongVector(nextRehash / 2); fingerprints = new long[nextRehash/2]; } public int size () { return lastStateId + 1; } public static long longLookup3Hash(int[] val) { // Jenkins' LOOKUP3 hash (May 2006) int a = 0x510fb60d; int b = 0xa4cb30d9 + (val.length); int c = 0x9e3779b9; int i; int max = val.length - 2; for (i = 0; i < max; i += 3) { a += val[i]; b += val[i + 1]; c += val[i + 2]; a -= c; a ^= (c << 4) ^ (c >>> 28); c += b; b -= a; b ^= (a << 6) ^ (a >>> 26); a += c; c -= b; c ^= (b << 8) ^ (b >>> 24); b += a; a -= c; a ^= (c << 16)^ (c >>> 16); c += b; b -= a; b ^= (a << 19)^ (a >>> 13); a += c; c -= b; c ^= (b << 4) ^ (b >>> 28); b += a; } switch (val.length - i) { case 2: c += val[val.length - 2]; b += val[val.length - 1]; break; case 1: b += val[val.length - 1]; break; } c ^= b; c -= (b << 14) ^ (b >>> 18); a ^= c; a -= (c << 11) ^ (c >>> 21); b ^= a; b -= (a << 25) ^ (a >>> 7); c ^= b; c -= (b << 16) ^ (b >>> 16); a ^= c; a -= (c << 4) ^ (c >>> 28); b ^= a; b -= (a << 14) ^ (a >>> 18); c ^= b; c -= (b << 24) ^ (b >>> 8); return ((long)c << 32) ^ b ^ a; } @Override protected int add(Conditional<Integer>[] state) { int[] out = new int[state.length]; for (int i = 0;i < state.length;i++) { out[i] = state[i].getValue(true); } return add(out); } public int add (int[] val) { long hash = longLookup3Hash(val); // this is the expensive part int i; // hash table lookup & add; open-addressed, double hashing int mask = hashtable.length - 1; int idx = (int)(hash >> 32) & mask; int delta = (int)hash | 1; // must be odd! int oidx = idx; while (hashtable[idx] != 0) { int id = hashtable[idx] - 1; // in table, 1 higher //if (fingerprints.get(id) == hash) { if (fingerprints[id] == hash){ return id; } idx = (idx + delta) & mask; assert (idx != oidx); // should never wrap around } assert (hashtable[idx] == 0); // should never fill up if (lastStateId >= nextRehash) { // too full hashtable = null; // run GC here? hashtable = new int[(mask + 1) << 1]; mask = hashtable.length - 1; nextRehash = (int) (MAX_LOAD * mask); for (i = 0; i <= lastStateId; i++) { //long h = fingerprints.get(i); long h = fingerprints[i]; idx = (int)(h >> 32) & mask; delta = (int)h | 1; while (hashtable[idx] != 0) { // we know enough slots exist idx = (idx + delta) & mask; } hashtable[idx] = i + 1; // in table, add 1 } // done with rehash; now get idx to empty slot idx = (int)(hash >> 32) & mask; delta = (int)hash | 1; // must be odd! while (hashtable[idx] != 0) { // we know enough slots exist and state is // new idx = (idx + delta) & mask; } } else { // idx already pointing to empty slot } //--- only reached if state is new lastStateId++; hashtable[idx] = lastStateId + 1; // in table, add 1 //fingerprints.set(lastStateId, hash); try { // this happens rarely enough to save on nominal branch instructions fingerprints[lastStateId] = hash; } catch (ArrayIndexOutOfBoundsException ix){ growFingerprint(lastStateId); fingerprints[lastStateId] = hash; } return lastStateId; } void growFingerprint (int minSize){ // we don't try to be fancy here int newSize = fingerprints.length *2; if (newSize < minSize) { newSize = minSize; } long[] newFingerprints = new long[newSize]; System.arraycopy( fingerprints, 0, newFingerprints, 0, fingerprints.length); fingerprints = newFingerprints; } /** * Main for testing speed, mostly. */ public static void main(String[] args) { try { int vlen = Integer.parseInt(args[0]); int adds = Integer.parseInt(args[1]); int queries = Integer.parseInt(args[2]); if (queries > adds) { queries = adds; System.err.println("Truncating queries to " + queries); } int[] v = new int[vlen]; int i; for (i = 0; i < vlen; i++) { v[i] = i - 42; } JenkinsStateSet set = new JenkinsStateSet(); long t1 = System.currentTimeMillis(); for (i = 0; i < adds; i++) { v[0] = i * 3; set.add(v); assert set.size() == i+1; } for (i = 0; i < queries; i++) { v[0] = i * 3; set.add(v); assert set.size() == adds; } long t2 = System.currentTimeMillis(); System.out.println("duration: " + (t2 - t1)); } catch (RuntimeException re) { re.printStackTrace(); System.err.println("args: vector_length #adds #queries"); } } }