package org.mapdb.tuple; import org.jetbrains.annotations.NotNull; import org.mapdb.DB; import org.mapdb.DataInput2; import org.mapdb.DataOutput2; import org.mapdb.Serializer; import org.mapdb.serializer.GroupSerializerObjectArray; import java.io.IOException; import java.io.Serializable; import java.util.Comparator; import static org.mapdb.tuple.Tuple.compare2; import static org.mapdb.tuple.Tuple.hiIfNull; /** * Applies delta compression on array of tuple. First tuple value may be shared between consequentive tuples, so only * first occurrence is serialized. An example: * * <pre> * Value Serialized as * ------------------------- * Tuple(1, 1) 1, 1 * Tuple(1, 2) 2 * Tuple(1, 3) 3 * Tuple(1, 4) 4 * </pre> * * @param <A> first tuple value * @param <B> second tuple value */ public final class Tuple2Serializer<A,B> extends GroupSerializerObjectArray<Tuple2<A,B>> implements Serializable, DB.DBAware { private static final long serialVersionUID = 2183804367032891772L; protected Comparator<A> aComparator; protected Comparator<B> bComparator; protected Serializer<A> aSerializer; protected Serializer<B> bSerializer; public Tuple2Serializer(){ this(null, null, null, null); } public Tuple2Serializer(Serializer serializer){ this(serializer, serializer); } public Tuple2Serializer( Serializer<A> aSerializer, Serializer<B> bSerializer){ this( aSerializer, bSerializer, aSerializer, bSerializer ); } /** * Construct new TupleSerializer. You may pass null for some value, * In that case 'default' value will be used, Comparable comparator and Default Serializer from DB. * */ public Tuple2Serializer( Serializer<A> aSerializer, Serializer<B> bSerializer, Comparator<A> aComparator, Comparator<B> bComparator){ this.aComparator = aComparator; this.bComparator = bComparator; this.aSerializer = aSerializer; this.bSerializer = bSerializer; } @Override public void valueArraySerialize(DataOutput2 out, Object vals) throws IOException { Object[] keys = (Object[]) vals; int end = keys.length; int acount=0; for(int i=0;i<end;i++){ Tuple2<A,B> t = (Tuple2<A, B>) keys[i]; if(acount==0){ //write new A aSerializer.serialize(out,t.a); //count how many A are following acount=1; while(i+acount<end && aComparator.compare(t.a, ((Tuple2<A, B>) keys[i+acount]).a)==0){ acount++; } out.packInt(acount); } bSerializer.serialize(out,t.b); acount--; } } @Override public Object[] valueArrayDeserialize(DataInput2 in, int size) throws IOException { Object[] ret = new Object[size]; A a = null; int acount = 0; for(int i=0;i<size;i++){ if(acount==0){ //read new A a = aSerializer.deserialize(in,-1); acount = in.unpackInt(); } B b = bSerializer.deserialize(in,-1); ret[i]= Tuple.t2(a,b); acount--; } assert(acount==0); return ret; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Tuple2Serializer t = (Tuple2Serializer) o; return Tuple.eq(aComparator, t.aComparator) && Tuple.eq(aSerializer, t.aSerializer) && Tuple.eq(bSerializer, t.bSerializer); } @Override public int hashCode() { int result = aComparator != null ? aComparator.hashCode() : 0; result = -1640531527 * result + (aSerializer != null ? aSerializer.hashCode() : 0); result = -1640531527 * result + (bSerializer != null ? bSerializer.hashCode() : 0); return result; } @Override public void serialize(@NotNull DataOutput2 out, @NotNull Tuple2<A, B> value) throws IOException { aSerializer.serialize(out, value.a); bSerializer.serialize(out, value.b); } @Override public Tuple2<A, B> deserialize(@NotNull DataInput2 input, int available) throws IOException { return new Tuple2( aSerializer.deserialize(input, -1), bSerializer.deserialize(input, -1) ); } @Override public int compare(final Tuple2<A,B> o1, final Tuple2<A,B> o2) { int i = compare2(aComparator,o1.a,o2.a); if(i!=0) return i; return compare2(bComparator,o1.b,o2.b); } @Override public boolean equals(Tuple2<A, B> first, Tuple2<A, B> second) { return 0==compare(first,second); } @Override public int hashCode(@NotNull Tuple2<A, B> o, int seed) { seed = -1640531527 * seed + aSerializer.hashCode(o.a, seed); seed = -1640531527 * seed + bSerializer.hashCode(o.b, seed); return seed; } @Override public void callbackDB(@NotNull DB db) { if(aComparator==null) aComparator = (Comparator<A>) db.getDefaultSerializer(); if(bComparator==null) bComparator = (Comparator<B>) db.getDefaultSerializer(); if(aSerializer==null) aSerializer = (Serializer<A>) db.getDefaultSerializer(); if(bSerializer==null) bSerializer = (Serializer<B>) db.getDefaultSerializer(); } @Override public Tuple2<A, B> nextValue(Tuple2<A, B> v) { return new Tuple2(hiIfNull(v.a), hiIfNull(v.b)); } }