package water.util; import water.AutoBuffer; import water.Freezable; import water.H2O; import water.Iced; import java.io.Serializable; import java.util.Collection; import java.util.Map; import java.util.Set; /** * Iced / Freezable NonBlockingHashMap abstract base class. */ public abstract class IcedHashMapBase<K, V> extends Iced implements Map<K, V>, Cloneable, Serializable { private transient volatile boolean _write_lock; abstract protected Map<K,V> map(); public int size() { return map().size(); } public boolean isEmpty() { return map().isEmpty(); } public boolean containsKey(Object key) { return map().containsKey(key); } public boolean containsValue(Object value) { return map().containsValue(value); } public V get(Object key) { return (V)map().get(key); } public V put(K key, V value) { assert !_write_lock; return (V)map().put(key, value);} public V remove(Object key) { assert !_write_lock; return map().remove(key); } public void putAll(Map<? extends K, ? extends V> m) { assert !_write_lock; map().putAll(m); } public void clear() { assert !_write_lock; map().clear(); } public Set<K> keySet() { return map().keySet(); } public Collection<V> values() { return map().values(); } public Set<Entry<K, V>> entrySet() { return map().entrySet(); } public boolean equals(Object o) { return map().equals(o); } public int hashCode() { return map().hashCode(); } private boolean isStringKey(int mode){ return mode == 1 || mode == 2 || mode == 5; } // This comment is stolen from water.parser.Categorical: // // Since this is a *concurrent* hashtable, writing it whilst its being // updated is tricky. If the table is NOT being updated, then all is written // as expected. If the table IS being updated we only promise to write the // Keys that existed at the time the table write began. If elements are // being deleted, they may be written anyways. If the Values are changing, a // random Value is written. public final AutoBuffer write_impl( AutoBuffer ab ) { _write_lock = true; try { if (map().size() == 0) return ab.put1(0); // empty map Entry<K, V> entry = map().entrySet().iterator().next(); K key = entry.getKey(); V val = entry.getValue(); assert key != null && val != null; int mode; if (key instanceof String) { if (val instanceof String) { mode = 1; } else { assert (val instanceof Freezable || val instanceof Freezable[]):"incompatible class " + val.getClass(); mode = val instanceof Freezable ? 2 : 5; } } else { assert key instanceof Iced; if (val instanceof String) { mode = 3; } else { assert (val instanceof Freezable || val instanceof Freezable[]); mode = val instanceof Freezable ? 4 : 6; } } ab.put1(mode); // Type of hashmap being serialized writeMap(ab, mode); // Do the hard work of writing the map return isStringKey(mode) ? ab.putStr(null) : ab.put(null); } catch(Throwable t){ System.err.println("Iced hash map serialization failed! " + t.toString() + ", msg = " + t.getMessage()); t.printStackTrace(); throw H2O.fail("Iced hash map serialization failed!" + t.toString() + ", msg = " + t.getMessage()); } finally { _write_lock = false; } } abstract protected Map<K,V> init(); protected void writeMap(AutoBuffer ab, int mode) { for( Entry<K, V> e : map().entrySet() ) { K key = e.getKey(); assert key != null; V val = e.getValue(); assert val != null; // put key if( mode==1 || mode==2 || mode==5 ) ab.putStr((String)key); else ab.put((Freezable)key); // put value if( mode==1 || mode==3 ) ab.putStr((String)val); else if( mode==5 || mode==6 ) { ab.put4(((Freezable[]) val).length); for (Freezable v : (Freezable[]) val) ab.put(v); } else ab.put((Freezable)val); } } /** * Helper for serialization - fills the mymap() from K-V pairs in the AutoBuffer object * @param ab Contains the serialized K-V pairs */ public final IcedHashMapBase read_impl(AutoBuffer ab) { try { assert map() == null || map().isEmpty(); // Fresh from serializer, no constructor has run Map<K, V> map = init(); int mode = ab.get1(); if (mode == 0) return this; K key; V val; while ((key = (isStringKey(mode) ? (K) ab.getStr() : (K) ab.get())) != null) { if (mode == 5 || mode == 6) { Freezable[] vals = new Freezable[ab.get4()]; for (int i = 0; i < vals.length; ++i) vals[i] = ab.get(); map.put(key, (V) vals); } else { val = ((mode == 1 || mode == 3) ? (V) ab.getStr() : (V) ab.get()); map.put(key, val); } } return this; } catch(Throwable t) { t.printStackTrace(); if (null == t.getCause()) { throw H2O.fail("IcedHashMap deserialization failed! + " + t.toString() + ", msg = " + t.getMessage() + ", cause: null"); } else { throw H2O.fail("IcedHashMap deserialization failed! + " + t.toString() + ", msg = " + t.getMessage() + ", cause: " + t.getCause().toString() + ", cause msg: " + t.getCause().getMessage() + ", cause stacktrace: " + java.util.Arrays.toString(t.getCause().getStackTrace())); } } } public final IcedHashMapBase readJSON_impl( AutoBuffer ab ) {throw H2O.unimpl();} public final AutoBuffer writeJSON_impl( AutoBuffer ab ) { boolean first = true; for (Entry<K, V> entry : map().entrySet()) { K key = entry.getKey(); V value = entry.getValue(); assert entry.getKey() instanceof String; assert value instanceof String || value instanceof String[] || value instanceof Integer || value instanceof Freezable || value instanceof Freezable[]; if (first) { first = false; } else {ab.put1(',').put1(' '); } ab.putJSONName((String) key); ab.put1(':'); if (value instanceof String) ab.putJSONName((String) value); else if (value instanceof String[]) ab.putJSONAStr((String[]) value); else if (value instanceof Integer) ab.putJSON4((Integer) value); else if (value instanceof Freezable) ab.putJSON((Freezable) value); else if (value instanceof Freezable[]) ab.putJSONA((Freezable[]) value); } // ab.put1('}'); // NOTE: the serialization framework adds this automagically return ab; } }