package jelectrum.db; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.HashMap; import java.util.Collection; import java.nio.ByteBuffer; import com.google.protobuf.ByteString; import jelectrum.SerializedTransaction; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ByteArrayOutputStream; import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.StoredBlock; import org.bitcoinj.core.NetworkParameters; import org.junit.Assert; import duckutil.TimeRecord; /** * Converts the high level types to and from the simple * ByteStrings used by the DB layer. * Knows how to efficently convert some of our well used types * and falls back to native java serialization. */ public class ObjectConversionMap<K, V> implements Map<K, V> { private DBMap inner; private ConversionMode mode; private NetworkParameters params; public enum ConversionMode { STRING, SHA256HASH, OBJECT, SERIALIZEDTRANSACTION, UTXONODE, STOREDBLOCK, EXISTENCE } public ObjectConversionMap(ConversionMode mode, DBMap inner) { this(mode, inner, null); } public ObjectConversionMap(ConversionMode mode, DBMap inner, NetworkParameters params) { this.inner = inner; this.mode = mode; this.params = params; if (mode==ConversionMode.STOREDBLOCK) { Assert.assertNotNull(params); } } public boolean containsKey(Object key) { String k = key.toString(); return inner.containsKey(k); } public V get(Object key) { long t1 = System.nanoTime(); String k = key.toString(); ByteString buff = inner.get(k); TimeRecord.record(t1, "db_get_inner"); if (buff == null) return null; long t1_convert = System.nanoTime(); try { if (mode==ConversionMode.STRING) { return (V) buff.toStringUtf8(); } if (mode==ConversionMode.SHA256HASH) { return (V) Sha256Hash.wrap(buff.toByteArray()); } if (mode==ConversionMode.OBJECT) { try { ObjectInputStream oin = new ObjectInputStream(buff.newInput()); Object o = oin.readObject(); return (V) o; } catch(java.io.IOException e) { System.out.println("Exception reading key: " + key); throw new RuntimeException(e); } catch(ClassNotFoundException e) { throw new RuntimeException(e); } } if (mode==ConversionMode.SERIALIZEDTRANSACTION) { return (V) new SerializedTransaction(buff.toByteArray()); } /*if (mode==ConversionMode.UTXONODE) { return (V) new UtxoTrieNode(buff); }*/ if (mode==ConversionMode.STOREDBLOCK) { ByteBuffer ba = ByteBuffer.wrap(buff.toByteArray()); return (V) StoredBlock.deserializeCompact(params, ba); } if (mode==ConversionMode.EXISTENCE) { throw new RuntimeException("Get called on existence only map"); } throw new RuntimeException("No conversion found"); } finally { TimeRecord.record(t1_convert, "db_get_convert"); } } public V put(K key, V value) { try { long t1 = System.nanoTime(); ByteString b = convertV(value); TimeRecord.record(t1, "db_put_convert"); long t1_put = System.nanoTime(); inner.put(key.toString(), b); TimeRecord.record(t1_put,"db_put_inner"); } catch(java.io.IOException e){throw new RuntimeException(e);} return null; } public void putAll(Map<? extends K,? extends V> m) { try { long t1 = System.nanoTime(); Map<String, ByteString> write_map = new HashMap<String, ByteString>(m.size()*2, 0.75f); for(Map.Entry<? extends K,? extends V> me : m.entrySet()) { write_map.put(me.getKey().toString(), convertV(me.getValue())); } TimeRecord.record(t1, "db_put_convert"); long t1_put = System.nanoTime(); inner.putAll(write_map); TimeRecord.record(t1_put,"db_put_inner"); } catch(java.io.IOException e){throw new RuntimeException(e);} } private ByteString convertV(V value) throws java.io.IOException { ByteString b = null; if (value != null) { if (mode==ConversionMode.STRING) { b = ByteString.copyFromUtf8(value.toString()); } if (mode==ConversionMode.SHA256HASH) { Sha256Hash h = (Sha256Hash) value; b = ByteString.copyFrom(h.getBytes()); } if (mode==ConversionMode.OBJECT) { try { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream oout = new ObjectOutputStream(bout); oout.writeObject(value); oout.flush(); b = ByteString.copyFrom(bout.toByteArray()); //System.out.println("" + value.getClass().getName() + " - " + b.size()); } catch(java.io.IOException e) { throw new RuntimeException(e); } } if (mode==ConversionMode.SERIALIZEDTRANSACTION) { SerializedTransaction stx = (SerializedTransaction)value; b = ByteString.copyFrom(stx.getBytes()); } if (mode==ConversionMode.STOREDBLOCK) { StoredBlock sb = (StoredBlock) value; byte[] buff = new byte[StoredBlock.COMPACT_SERIALIZED_SIZE]; sb.serializeCompact(ByteBuffer.wrap(buff)); b = ByteString.copyFrom(buff); } if (mode==ConversionMode.EXISTENCE) { byte[] b1= new byte[1]; b = ByteString.copyFrom(b1); } } return b; } public void clear() { throw new RuntimeException("not implemented - is stupid"); } public boolean containsValue(Object value) { throw new RuntimeException("not implemented - is stupid"); } public Set<Map.Entry<K,V>> entrySet() { throw new RuntimeException("not implemented - is stupid"); } public boolean equals(Object o) { throw new RuntimeException("not implemented - is stupid"); } public int hashCode() { throw new RuntimeException("not implemented - is stupid"); } public boolean isEmpty() { throw new RuntimeException("not implemented - is stupid"); } public int size() { throw new RuntimeException("not implemented - is stupid"); } public Set<K> keySet() { throw new RuntimeException("not implemented - is stupid"); } public V remove(Object key) { throw new RuntimeException("not implemented - is stupid"); } public Collection<V> values() { throw new RuntimeException("not implemented - is stupid"); } }