// Copyright (c) Corporation for National Research Initiatives package org.python.core; /** * A faster Dictionary where the keys have to be strings. * <p> * This is the default for all __dict__ instances. */ public class PyStringMap extends PyObject { //Table of primes to cycle through private static final int[] primes = { 7, 13, 31, 61, 127, 251, 509, 1021, 2017, 4093, 5987, 9551, 15683, 19609, 31397, 65521, 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, 134217689, 268435399, 536870909, 1073741789, }; private transient String[] keys; private transient PyObject[] values; private int size; private transient int filled; private transient int prime; private transient int popfinger; /* Override serialization behavior */ private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { out.defaultWriteObject(); String[] keyTable = keys; PyObject[] valueTable = values; int n = keyTable.length; for (int i = 0; i < n; i++) { //String key = keyTable[i]; PyObject value = valueTable[i]; if (value == null) continue; out.writeUTF(keys[i]); out.writeObject(values[i]); } } private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { in.defaultReadObject(); prime = 1; keys = null; values = null; int n = size; resize(n); for (int i = 0; i < n; i++) { String key = in.readUTF().intern(); insertkey(key, (PyObject) in.readObject()); } } public PyStringMap(int capacity) { prime = 0; keys = null; values = null; resize(capacity); } public PyStringMap() { this(4); } public PyStringMap(PyObject elements[]) { this(elements.length); for (int i = 0; i < elements.length; i += 2) { __setitem__(elements[i], elements[i + 1]); } } public synchronized int __len__() { return size; } public synchronized boolean __nonzero__() { return size != 0; } public synchronized PyObject __finditem__(String key) { String[] table = keys; int maxindex = table.length; int index = (System.identityHashCode(key) & 0x7fffffff) % maxindex; // Fairly aribtrary choice for stepsize... int stepsize = maxindex / 5; // Cycle through possible positions for the key; //int collisions = 0; while (true) { String tkey = table[index]; if (tkey == key) { //if (collisions > 0) { // System.err.println("key: "+key+", "+collisions+", "+ // maxindex+", "+System.identityHashCode(key)); //} return values[index]; } if (tkey == null) return values[index]; //collisions++; index = (index + stepsize) % maxindex; } } public PyObject __finditem__(PyObject key) { //System.err.println("oops: "+key); if (key instanceof PyString) { return __finditem__(((PyString) key).internedString()); } else { return null; } } public PyObject __iter__() { return new PyStringMapIter(keys, values); } private final void insertkey(String key, PyObject value) { String[] table = keys; int maxindex = table.length; int index = (System.identityHashCode(key) & 0x7fffffff) % maxindex; // Fairly aribtrary choice for stepsize... int stepsize = maxindex / 5; int free_index = -1; // Cycle through possible positions for the key; while (true) { String tkey = table[index]; if (tkey == null) { if (free_index == -1) { filled++; free_index = index; } break; } else if (tkey == key) { values[index] = value; return; } else if (tkey == "<deleted key>" && free_index == -1) { free_index = index; } index = (index + stepsize) % maxindex; } table[free_index] = key; values[free_index] = value; size++; return; } private synchronized final void resize(int capacity) { int p = prime; for (; p < primes.length; p++) { if (primes[p] >= capacity) break; } if (primes[p] < capacity) { throw Py.ValueError("can't make hashtable of size: " + capacity); } //System.err.println("resize: "+(keys != null ? keys.length : -1)+ // ", "+primes[p]); capacity = primes[p]; prime = p; String[] oldKeys = keys; PyObject[] oldValues = values; keys = new String[capacity]; values = new PyObject[capacity]; size = 0; filled = 0; if (oldValues != null) { int n = oldValues.length; for (int i = 0; i < n; i++) { PyObject value = oldValues[i]; if (value == null) continue; insertkey(oldKeys[i], value); } } } public synchronized void __setitem__(String key, PyObject value) { if (2 * filled > keys.length) resize(keys.length + 1); insertkey(key, value); } public void __setitem__(PyObject key, PyObject value) { if (key instanceof PyString) { __setitem__(((PyString) key).internedString(), value); } else { throw Py.TypeError("keys in namespace must be strings"); } } public synchronized void __delitem__(String key) { String[] table = keys; int maxindex = table.length; int index = (System.identityHashCode(key) & 0x7fffffff) % maxindex; // Fairly aribtrary choice for stepsize... int stepsize = maxindex / 5; // Cycle through possible positions for the key; while (true) { String tkey = table[index]; if (tkey == null) { throw Py.KeyError(key); } if (tkey == key) { table[index] = "<deleted key>"; values[index] = null; size--; break; } index = (index + stepsize) % maxindex; } } public void __delitem__(PyObject key) { if (key instanceof PyString) { __delitem__(((PyString) key).internedString()); } else { throw Py.KeyError(key.toString()); } } /** * Remove all items from the dictionary. */ public synchronized void clear() { for (int i = 0; i < keys.length; i++) { keys[i] = null; values[i] = null; } size = 0; } public synchronized String toString() { ThreadState ts = Py.getThreadState(); if (!ts.enterRepr(this)) { return "{...}"; } String[] keyTable = keys; PyObject[] valueTable = values; int n = keyTable.length; StringBuffer buf = new StringBuffer("{"); for (int i = 0; i < n; i++) { //String key = keyTable[i]; PyObject value = valueTable[i]; if (value == null) continue; buf.append("'"); buf.append(keyTable[i]); buf.append("': "); buf.append(value.__repr__().toString()); buf.append(", "); } // A hack to remove the final ", " from the string repr int len = buf.length(); if (len > 4) { buf.setLength(len - 2); } buf.append("}"); ts.exitRepr(this); return buf.toString(); } public synchronized int __cmp__(PyObject other) { if (!(other instanceof PyStringMap || other instanceof PyDictionary)) { return -2; } int an = __len__(); int bn = other.__len__(); if (an < bn) return -1; if (an > bn) return 1; PyList akeys = keys(); PyList bkeys = null; if (other instanceof PyStringMap) { bkeys = ((PyStringMap) other).keys(); } else { bkeys = ((PyDictionary) other).keys(); } akeys.sort(); bkeys.sort(); for (int i = 0; i < bn; i++) { PyObject akey = akeys.pyget(i); PyObject bkey = bkeys.pyget(i); int c = akey._cmp(bkey); if (c != 0) return c; PyObject avalue = __finditem__(akey); PyObject bvalue = other.__finditem__(bkey); c = avalue._cmp(bvalue); if (c != 0) return c; } return 0; } /** * Return true if the key exist in the dictionary. */ public boolean has_key(PyObject key) { return __finditem__(key) != null; } /** * Return this[key] if the key exists in the mapping, default_object * is returned otherwise. * * @param key the key to lookup in the mapping. * @param default_object the value to return if the key does not * exists in the mapping. */ public PyObject get(PyObject key, PyObject default_object) { PyObject o = __finditem__(key); if (o == null) return default_object; else return o; } /** * Return this[key] if the key exists in the mapping, None * is returned otherwise. * * @param key the key to lookup in the mapping. */ public PyObject get(PyObject key) { return get(key, Py.None); } /** * Return a shallow copy of the dictionary. */ public synchronized PyStringMap copy() { int n = keys.length; PyStringMap map = new PyStringMap(n); System.arraycopy(keys, 0, map.keys, 0, n); System.arraycopy(values, 0, map.values, 0, n); map.filled = filled; map.size = size; map.prime = prime; return map; } /** * Insert all the key:value pairs from <code>map</code> into * this mapping. */ public synchronized void update(PyStringMap map) { String[] keyTable = map.keys; PyObject[] valueTable = map.values; int n = keyTable.length; if (2 * filled + n > keys.length) resize(2 * filled + n); for (int i = 0; i < n; i++) { String key = keyTable[i]; if (key == null || key == "<deleted key>") continue; insertkey(key, valueTable[i]); } } /** * Insert all the key:value pairs from <code>dict</code> into * this mapping. */ public void update(PyDictionary dict) { java.util.Hashtable table = dict.table; java.util.Enumeration ek = table.keys(); java.util.Enumeration ev = table.elements(); int n = table.size(); for (int i = 0; i < n; i++) { __setitem__((PyObject) ek.nextElement(), (PyObject) ev.nextElement()); } } /** * Return this[key] if the key exist, otherwise insert key with * a None value and return None. * * @param key the key to lookup in the mapping. */ public PyObject setdefault(PyObject key) { return setdefault(key, Py.None); } /** * Return this[key] if the key exist, otherwise insert key with * the value of failobj and return failobj * * @param key the key to lookup in the mapping. * @param failobj the default value to insert in the mapping * if key does not already exist. */ public PyObject setdefault(PyObject key, PyObject failobj) { PyObject o = __finditem__(key); if (o == null) __setitem__(key, o = failobj); return o; } /** * Return a random (key, value) tuple pair and remove the pair * from the mapping. */ public synchronized PyObject popitem() { if (size == 0) throw Py.KeyError("popitem(): dictionary is empty"); String[] table = keys; int maxindex = table.length; int index = popfinger; if (index >= maxindex || index < 0) index = 1; while (true) { String tKey = table[index]; if (tKey != null && tKey != "<deleted key>") break; index++; if (index >= maxindex) index = 0; } popfinger = index + 1; PyObject key = Py.newString(table[index]); PyObject val = (PyObject) values[index]; table[index] = "<deleted key>"; values[index] = null; size--; return new PyTuple(new PyObject[] { key, val }); } /** * Return a copy of the mappings list of (key, value) tuple * pairs. */ public synchronized PyList items() { String[] keyTable = keys; PyObject[] valueTable = values; int n = keyTable.length; PyList l = new PyList(); for (int i = 0; i < n; i++) { String key = keyTable[i]; if (key == null || key == "<deleted key>" || values[i] == null) continue; l.append(new PyTuple(new PyObject[] { new PyString(key), valueTable[i] })); } return l; } synchronized String[] jkeys() { String[] keyTable = keys; //PyObject[] valueTable = values; int n = keyTable.length; String[] newKeys = new String[size]; int j = 0; for (int i = 0; i < n; i++) { String key = keyTable[i]; if (key == null || key == "<deleted key>") continue; newKeys[j++] = key; } return newKeys; } /** * Return a copy of the mappings list of keys. */ public synchronized PyList keys() { String[] keyTable = keys; //PyObject[] valueTable = values; int n = keyTable.length; PyList l = new PyList(); for (int i = 0; i < n; i++) { String key = keyTable[i]; if (key == null || key == "<deleted key>" || values[i] == null) continue; l.append(new PyString(key)); } return l; } /** * Return a copy of the mappings list of values. */ public synchronized PyList values() { PyObject[] valueTable = values; int n = valueTable.length; PyList l = new PyList(); for (int i = 0; i < n; i++) { PyObject value = valueTable[i]; if (value == null) continue; l.append(value); } return l; } /** * return an iterator over (key, value) pairs */ public synchronized PyObject iteritems() { return new PyStringMapIter(keys, values, PyStringMapIter.ITEMS); } /** * return an iterator over the keys */ public synchronized PyObject iterkeys() { return new PyStringMapIter(keys, values, PyStringMapIter.KEYS); } /** * return an iterator over the values */ public synchronized PyObject itervalues() { return new PyStringMapIter(keys, values, PyStringMapIter.VALUES); } } /* extended, based on PyDictionaryIter */ class PyStringMapIter extends PyIterator { String[] keyTable; PyObject[] valTable; private int idx; private int type; public static final int KEYS = 0; public static final int VALUES = 1; public static final int ITEMS = 2; public PyStringMapIter(String[] keys, PyObject[] values) { this(keys, values, KEYS); } public PyStringMapIter(String[] keys, PyObject[] values, int type) { this.keyTable = keys; this.valTable = values; this.idx = 0; this.type = type; } public PyObject __iternext__() { int n = keyTable.length; for (; idx < n; idx++) { String key = keyTable[idx]; PyObject val = valTable[idx]; if (key == null || key == "<deleted key>" || val == null) continue; idx++; switch (type) { case VALUES: return val; case ITEMS: return new PyTuple(new PyObject[] { Py.newString(key), val }); default: // KEYS return Py.newString(key); } } return null; } }