All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mapdb20.BTreeKeySerializer Maven / Gradle / Ivy

package org.mapdb20;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Comparator;
import java.util.UUID;

/**
 * Custom serializer for BTreeMap keys which enables [Delta encoding](https://en.wikipedia.org/wiki/Delta_encoding).
 *
 * Keys in BTree Nodes are sorted, this enables number of tricks to save disk space.
 * For example for numbers we may store only difference between subsequent numbers, for string we can only take suffix, etc...
 *
 * @param  type of key
 * @param  type of object which holds multiple keys (
 */
public abstract class BTreeKeySerializer{


    /**
     * Serialize keys from single BTree Node.
     *
     * @param out output stream where to put ata
     * @param keys An object which represents keys
     *
     * @throws IOException in case of an writting error
     */
    public abstract void serialize(DataOutput out, KEYS keys) throws IOException;

    /**
     * Deserializes keys for single BTree Node. To
     *
     * @param in input stream to read data from
     * @param nodeSize number of keys in deserialized node
     * @return an object which represents keys
     *
     * @throws IOException in case of an reading error
     */
    public abstract KEYS deserialize(DataInput in, int nodeSize) throws IOException;


    public abstract int compare(KEYS keys, int pos1, int pos2);


    public abstract int compare(KEYS keys, int pos, KEY key);

    public boolean compareIsSmaller(KEYS keys, int pos, KEY key) {
        //TODO override in Strings and other implementations
        return compare(keys,pos,key)<0;
    }


    public abstract KEY getKey(KEYS keys, int pos);


    public static final BTreeKeySerializer BASIC = new BTreeKeySerializer.BasicKeySerializer(Serializer.BASIC, Fun.COMPARATOR);

    public abstract Comparator comparator();

    public abstract KEYS emptyKeys();

    public abstract int length(KEYS keys);

    /** expand keys array by one and put {@code newKey} at position {@code pos}
     *
     *  @param keys array of keys to put new key into
     *  @param pos of new key
     *  @param newKey new key to insert
     *
     *  @return array of keys with new key at given position
     */
    public abstract KEYS putKey(KEYS keys, int pos, KEY newKey);


    public abstract KEYS copyOfRange(KEYS keys, int from, int to);

    public abstract KEYS deleteKey(KEYS keys, int pos);

    /**
     * Find the first children node with a key equal or greater than the given key.
     * If all items are smaller it returns {@code keyser.length(keys)}
     *
     * @param node BTree Node to find position in
     * @param key key whose position needs to be find
     * @return position of key in node
     */
    public int findChildren(final BTreeMap.BNode node, final Object key) {
        KEYS keys = (KEYS) node.keys;
        int keylen = length(keys);
        int left = 0;
        int right = keylen;

        int middle;
        //$DELAY$
        // binary search
        for(;;) {
            //$DELAY$
            middle = (left + right) / 2;
            if(middle==keylen)
                return middle+node.leftEdgeInc(); //null is positive infinitive
            if (compareIsSmaller(keys,middle, (KEY) key)) {
                left = middle + 1;
            } else {
                right = middle;
            }
            if (left >= right) {
                return  right+node.leftEdgeInc();
            }
        }
    }

    public int findChildren2(final BTreeMap.BNode node, final Object key) {
        KEYS keys = (KEYS) node.keys;
        int keylen = length(keys);

        int left = 0;
        int right = keylen;
        int comp;
        int middle;
        //$DELAY$
        // binary search
        while (true) {
            //$DELAY$
            middle = (left + right) / 2;
            if(middle==keylen)
                return -1-(middle+node.leftEdgeInc()); //null is positive infinitive
            comp = compare(keys, middle, (KEY) key);
            if(comp==0){
                //try one before last, in some cases it might be duplicate of last
                if(!node.isRightEdge() && middle==keylen-1 && middle>0
                        && compare(keys,middle-1,(KEY)key)==0){
                    middle--;
                }
                return middle+node.leftEdgeInc();
            } else if ( comp< 0) {
                left = middle +1;
            } else {
                right = middle;
            }
            if (left >= right) {
                return  -1-(right+node.leftEdgeInc());
            }
        }

    }


    public abstract KEYS arrayToKeys(Object[] keys);

    public Object[] keysToArray(KEYS keys) {
        //$DELAY$
        Object[] ret = new Object[length(keys)];
        for (int i = 0; i  implements Serializable {

        private static final long serialVersionUID = 1654710710946309279L;

        protected final Serializer serializer;
        protected final Comparator comparator;

        public BasicKeySerializer(Serializer serializer, Comparator comparator) {
            if(serializer == null || comparator == null)
                throw new  NullPointerException();
            this.serializer = serializer;
            this.comparator = comparator;
        }

        /** used for deserialization*/
        BasicKeySerializer(SerializerBase serializerBase, DataInput is, SerializerBase.FastArrayList objectStack) throws IOException {
            objectStack.add(this);
            serializer = (Serializer) serializerBase.deserialize(is,objectStack);
            comparator = (Comparator) serializerBase.deserialize(is,objectStack);
            if(serializer == null || comparator == null)
                throw new  NullPointerException();
        }

        @Override
        public void serialize(DataOutput out, Object[] keys) throws IOException {
            for(Object o:keys){
                //$DELAY$
                serializer.serialize(out, o);
            }
        }

        @Override
        public Object[] deserialize(DataInput in, int nodeSize) throws IOException {
            //$DELAY$
            Object[] keys = new Object[nodeSize];
            for(int i=0;i LONG = new BTreeKeySerializer() {

        @Override
        public void serialize(DataOutput out, long[] keys) throws IOException {
            DataIO.DataOutputByteArray out2 = (DataIO.DataOutputByteArray) out; //TODO fallback option if cast fails
            long prev = keys[0];
            out2.packLong(prev);
            for(int i=1;i comparator() {
            return Fun.COMPARATOR;
        }

        @Override
        public long[] emptyKeys() {
            return new long[0];
        }

        @Override
        public int length(long[] keys) {
            return keys.length;
        }

        @Override
        public long[] putKey(long[] keys, int pos, Long newKey) {
            final long[] ret = Arrays.copyOf(keys,keys.length+1);
            if(pos=0;i--) {
                //$DELAY$
                ret[i] = (Long) keys[i];
            }
            return ret;
        }


        @Override
        public long[] deleteKey(long[] keys, int pos) {
            long[] keys2 = new long[keys.length-1];
            System.arraycopy(keys,0,keys2, 0, pos);
            //$DELAY$
            System.arraycopy(keys, pos+1, keys2, pos, keys2.length-pos);
            //$DELAY$
            return keys2;
        }

        @Override
        public final int findChildren(final BTreeMap.BNode node, final Object key) {
            long[] keys = (long[]) node.keys;
            long key2 = (Long)key;

            int left = 0;
            int right = keys.length;

            int middle;
            //$DELAY$
            // binary search
            for(;;) {
                //$DELAY$
                middle = (left + right) / 2;
                if(middle==keys.length)
                    return middle+node.leftEdgeInc(); //null is positive infinitive
                if (keys[middle]= right) {
                    return  right+node.leftEdgeInc();
                }
            }
        }

        @Override
        public final int findChildren2(final BTreeMap.BNode node, final Object key) {
            long[] keys = (long[]) node.keys;
            long key2 = (Long)key;

            int left = 0;
            int right = keys.length;
            int middle;
            //$DELAY$
            // binary search
            while (true) {
                //$DELAY$
                middle = (left + right) / 2;
                if(middle==keys.length)
                    return -1-(middle+node.leftEdgeInc()); //null is positive infinitive

                if(keys[middle]==key2){
                    //try one before last, in some cases it might be duplicate of last
                    if(!node.isRightEdge() && middle==keys.length-1 && middle>0
                            && keys[middle-1]==key2){
                        middle--;
                    }
                    return middle+node.leftEdgeInc();
                } else if ( keys[middle]= right) {
                    return  -1-(right+node.leftEdgeInc());
                }
            }
        }

    };

    /**
     * @deprecated use {@link org.mapdb20.BTreeKeySerializer#LONG}
     */
    public static final BTreeKeySerializer ZERO_OR_POSITIVE_LONG = LONG;

    /**
     * Applies delta packing on {@code java.lang.Integer}.
     * Difference between consequential numbers is also packed itself, so for small diffs it takes only single byte per
     * number.
     */
    public static final  BTreeKeySerializer INTEGER = new BTreeKeySerializer() {
        @Override
        public void serialize(DataOutput out, int[] keys) throws IOException {
            int prev = keys[0];
            DataIO.packIntBigger(out, prev);
            //$DELAY$
            for(int i=1;i comparator() {
            return Fun.COMPARATOR;
        }

        @Override
        public int[] emptyKeys() {
            return new int[0];
        }

        @Override
        public int length(int[] keys) {
            return keys.length;
        }

        @Override
        public int[] putKey(int[] keys, int pos, Integer newKey) {
            final int[] ret = Arrays.copyOf(keys,keys.length+1);
            //$DELAY$
            if(pos=0;i--)
                //$DELAY$
                ret[i] = (Integer)keys[i];
            return ret;
        }

        @Override
        public int[] deleteKey(int[] keys, int pos) {
            int[] keys2 = new int[keys.length-1];
            System.arraycopy(keys,0,keys2, 0, pos);
            //$DELAY$
            System.arraycopy(keys, pos+1, keys2, pos, keys2.length-pos);
            return keys2;
        }

        @Override
        public final int findChildren(final BTreeMap.BNode node, final Object key) {
            int[] keys = (int[]) node.keys;
            int key2 = (Integer)key;
            int left = 0;
            int right = keys.length;

            int middle;
            //$DELAY$
            // binary search
            for(;;) {
                //$DELAY$
                middle = (left + right) / 2;
                if(middle==keys.length)
                    return middle+node.leftEdgeInc(); //null is positive infinitive
                if (keys[middle]= right) {
                    return  right+node.leftEdgeInc();
                }
            }
        }

        @Override
        public final int findChildren2(final BTreeMap.BNode node, final Object key) {
            int[] keys = (int[]) node.keys;
            int key2 = (Integer)key;

            int left = 0;
            int right = keys.length;
            int middle;
            //$DELAY$
            // binary search
            while (true) {
                //$DELAY$
                middle = (left + right) / 2;
                if(middle==keys.length)
                    return -1-(middle+node.leftEdgeInc()); //null is positive infinitive

                if(keys[middle]==key2){
                    //try one before last, in some cases it might be duplicate of last
                    if(!node.isRightEdge() && middle==keys.length-1 && middle>0
                            && keys[middle-1]==key2){
                        middle--;
                    }
                    return middle+node.leftEdgeInc();
                } else if ( keys[middle]= right) {
                    return  -1-(right+node.leftEdgeInc());
                }
            }
        }
    };

    /**
     * @deprecated use {@link org.mapdb20.BTreeKeySerializer#INTEGER}
     */
    public static final  BTreeKeySerializer ZERO_OR_POSITIVE_INT = INTEGER;


    public static final BTreeKeySerializer ARRAY2 = new ArrayKeySerializer(
            new Comparator[]{Fun.COMPARATOR,Fun.COMPARATOR},
            new Serializer[]{Serializer.BASIC, Serializer.BASIC}
    );

    public static final BTreeKeySerializer ARRAY3 = new ArrayKeySerializer(
            new Comparator[]{Fun.COMPARATOR,Fun.COMPARATOR,Fun.COMPARATOR},
            new Serializer[]{Serializer.BASIC, Serializer.BASIC, Serializer.BASIC}
    );

    public static final BTreeKeySerializer ARRAY4 = new ArrayKeySerializer(
            new Comparator[]{Fun.COMPARATOR,Fun.COMPARATOR,Fun.COMPARATOR,Fun.COMPARATOR},
            new Serializer[]{Serializer.BASIC, Serializer.BASIC, Serializer.BASIC, Serializer.BASIC}
    );


    public final static class ArrayKeySerializer extends BTreeKeySerializer implements Serializable{

        private static final long serialVersionUID = 998929894238939892L;

        protected final int tsize;
        protected final Comparator[] comparators;
        protected final Serializer[] serializers;

        protected final Comparator comparator;

        public ArrayKeySerializer(Comparator[] comparators, Serializer[] serializers) {
            if(comparators.length!=serializers.length){
                throw new IllegalArgumentException("array sizes do not match");
            }

            this.tsize = comparators.length;
            this.comparators = comparators;
            this.serializers = serializers;

            this.comparator = new Fun.ArrayComparator(comparators);
        }

        /* used for deserialization, extra is to avoid argument collision */
        public ArrayKeySerializer(SerializerBase serializerBase, DataInput is,
                                  SerializerBase.FastArrayList objectStack) throws IOException {
            objectStack.add(this);
            tsize = DataIO.unpackInt(is);
            comparators = new Comparator[tsize];
            serializers = new Serializer[tsize];
            for(int i=0;i=0;j--){
                    counts[j]--;
                }
            }
        }

        @Override
        public Object[] deserialize(DataInput in, int nodeSize) throws IOException {
            Object[] ret = new Object[nodeSize*tsize];
            Object[] curr = new Object[tsize];
            int[] counts = new int[tsize-1];
            //$DELAY$
            for(int i=0;i=0;j--){
                    counts[j]--;
                }
            }

            if(CC.ASSERT){
                for(int j:counts){
                    if(j!=0)
                        throw new DBException.DataCorruption("inconsistent counts");
                }
            }
            return ret;

        }

        @Override
        public int compare(Object[] keys, int pos1, int pos2) {
            pos1 *=tsize;
            pos2 *=tsize;
            int res;
            //$DELAY$
            for(Comparator c:comparators){
                //$DELAY$
                res = c.compare(keys[pos1++],keys[pos2++]);
                if(res!=0) {
                    return res;
                }
            }
            return 0;
        }

        @Override
        public int compare(Object[] keys, int pos, Object[] tuple) {
            pos*=tsize;
            int len = Math.min(tuple.length, tsize);
            int r;
            //$DELAY$
            for(int i=0;i comparator() {
            return comparator;
        }

        @Override
        public Object[] emptyKeys() {
            return new Object[0];
        }

        @Override
        public int length(Object[] objects) {
            return objects.length/tsize;
        }

        @Override
        public Object[] putKey(Object[] keys, int pos, Object[] newKey) {
            if(CC.ASSERT && newKey.length!=tsize)
                throw new DBException.DataCorruption("inconsistent size");
            pos*=tsize;
            Object[] ret = new Object[keys.length+tsize];
            System.arraycopy(keys, 0, ret, 0, pos);
            //$DELAY$
            System.arraycopy(newKey,0,ret,pos,tsize);
            //$DELAY$
            System.arraycopy(keys,pos,ret,pos+tsize,keys.length-pos);
            return ret;
        }

        @Override
        public Object[] arrayToKeys(Object[] keys) {
            Object[] ret = new Object[keys.length*tsize];
            int pos=0;
            //$DELAY$
            for(Object o:keys){
                if(CC.ASSERT && ((Object[])o).length!=tsize)
                    throw new DBException.DataCorruption("keys have wrong size");
                System.arraycopy(o,0,ret,pos,tsize);
                //$DELAY$
                pos+=tsize;
            }
            return ret;
        }

        @Override
        public Object[] copyOfRange(Object[] keys, int from, int to) {
            from*=tsize;
            to*=tsize;
            return Arrays.copyOfRange(keys,from,to);
        }

        @Override
        public Object[] deleteKey(Object[] keys, int pos) {
            pos*=tsize;
            Object[] ret = new Object[keys.length-tsize];
            System.arraycopy(keys,0,ret,0,pos);
            //$DELAY$
            System.arraycopy(keys,pos+tsize,ret,pos,ret.length-pos);
            return ret;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            ArrayKeySerializer that = (ArrayKeySerializer) o;
            //$DELAY$
            if (tsize != that.tsize) return false;
            if (!Arrays.equals(comparators, that.comparators)) return false;
            //$DELAY$
            return Arrays.equals(serializers, that.serializers);
        }

        @Override
        public int hashCode() {
            int result = tsize;
            result = 31 * result + Arrays.hashCode(comparators);
            result = 31 * result + Arrays.hashCode(serializers);
            return result;
        }
    }

    public static final BTreeKeySerializer UUID = new BTreeKeySerializer() {

        @Override
        public void serialize(DataOutput out, long[] longs) throws IOException {
            //$DELAY$
            for(long l:longs){
                out.writeLong(l);
            }
        }

        @Override
        public long[] deserialize(DataInput in, int nodeSize) throws IOException {
            long[] ret= new long[nodeSize<<1];
            //$DELAY$
            for(int i=0;i comparator() {
            return Fun.COMPARATOR;
        }

        @Override
        public long[] emptyKeys() {
            return new long[0];
        }

        @Override
        public int length(long[] longs) {
            return longs.length/2;
        }

        @Override
        public long[] putKey(long[] keys, int pos, UUID newKey) {
            pos <<= 1; //*2
            long[] ret = new long[keys.length+2];
            System.arraycopy(keys, 0, ret, 0, pos);
            //$DELAY$
            ret[pos++] = newKey.getMostSignificantBits();
            ret[pos++] = newKey.getLeastSignificantBits();
            System.arraycopy(keys,pos-2,ret,pos,ret.length-pos);
            return ret;

        }

        @Override
        public long[] arrayToKeys(Object[] keys) {
            long[] ret = new long[keys.length<<1]; //*2
            int i=0;
            //$DELAY$
            for(Object o:keys){
                java.util.UUID u = (java.util.UUID) o;
                ret[i++]=u.getMostSignificantBits();
                ret[i++]=u.getLeastSignificantBits();
            }
            return ret;
        }

        @Override
        public long[] copyOfRange(long[] longs, int from, int to) {
            return Arrays.copyOfRange(longs,from<<1,to<<1);
        }

        @Override
        public long[] deleteKey(long[] keys, int pos) {
            pos <<= 1; //*2
            long[] ret = new long[keys.length-2];
            System.arraycopy(keys,0,ret,0,pos);
            //$DELAY$
            System.arraycopy(keys,pos+2,ret,pos,ret.length-pos);
            return ret;
        }
    };

    public interface StringArrayKeys {

        int commonPrefixLen();

        int length();

        int[] getOffset();

        BTreeKeySerializer.StringArrayKeys deleteKey(int pos);

        BTreeKeySerializer.StringArrayKeys copyOfRange(int from, int to);

        BTreeKeySerializer.StringArrayKeys putKey(int pos, String newKey);

        int compare(int pos1, String string);

        int compare(int pos1, int pos2);

        String getKeyString(int pos);

        boolean hasUnicodeChars();

        void serialize(DataOutput out, int prefixLen) throws IOException;
    }

    //TODO right now byte[] contains 7 bit characters, but it should be expandable to 8bit.
    public static final class ByteArrayKeys implements StringArrayKeys {
        final int[] offset;
        final byte[] array;

        ByteArrayKeys(int[] offset, byte[] array) {
            this.offset = offset;
            this.array = array;

            if(CC.ASSERT && ! (array.length==0 || array.length == offset[offset.length-1]))
                throw new DBException.DataCorruption("inconsistent array size");
        }

        ByteArrayKeys(DataInput in, int[] offsets, int prefixLen) throws IOException {
            this.offset = offsets;
            array = new byte[offsets[offsets.length-1]];

            in.readFully(array, 0, prefixLen);
            for(int i=0; i127)
                    return true;
            }
            return false;
        }

        public ByteArrayKeys putKey(int pos, byte[] newKey) {
            byte[] bb = new byte[array.length+ newKey.length];
            int split1 = pos==0? 0: offset[pos-1];
            System.arraycopy(array,0,bb,0,split1);
            //$DELAY$
            System.arraycopy(newKey,0,bb,split1,newKey.length);
            System.arraycopy(array,split1,bb,split1+newKey.length,array.length-split1);

            int[] offsets = new int[offset.length+1];

            int plus = 0;
            int plusI = 0;
            for(int i=0;i127)
                    return true;
            }
            return false;
        }

        @Override
        public void serialize(DataOutput out, int prefixLen) throws IOException {
                //write rest of the suffix
                outWrite(out, 0, prefixLen);
                //$DELAY$
                //write suffixes
                int aa = prefixLen;
                for(int o:offset){
                    outWrite(out,  aa, o);
                    aa = o+prefixLen;
                }
            }

        private void outWrite(DataOutput out, int from, int to) throws IOException {
            for(int i=from;i STRING2 = new BTreeKeySerializer() {

        @Override
        public void serialize(DataOutput out, char[][] chars) throws IOException {
            boolean unicode = false;
            //write lengths
            for(char[] b:chars){
                DataIO.packInt(out,b.length);
                //$DELAY$
                if(!unicode) {
                    for (char cc : b) {
                        if (cc>127)
                            unicode = true;
                    }
                }
            }


            //find common prefix
            int prefixLen = commonPrefixLen(chars);
            DataIO.packInt(out,(prefixLen<<1) | (unicode?1:0));
            for (int i = 0; i < prefixLen; i++) {
              DataIO.packInt(out, chars[0][i]);
            }
            //$DELAY$
            for(char[] b:chars){
                for (int i = prefixLen; i < b.length; i++) {
                    DataIO.packInt(out, b[i]);
                }
            }
        }

        @Override
        public char[][] deserialize(DataInput in, int nodeSize) throws IOException {
            char[][] ret = new char[nodeSize][];
            //$DELAY$
            //read lengths and init arrays
            for(int i=0;i>>=1;
            //$DELAY$
            for(int i=0;i comparator() {
            return Fun.COMPARATOR;
        }

        @Override
        public char[][] emptyKeys() {
            return new char[0][];
        }

        @Override
        public int length(char[][] chars) {
            return chars.length;
        }

        @Override
        public char[][] putKey(char[][] keys, int pos, String newKey) {
            return (char[][]) BTreeMap.arrayPut(keys, pos, newKey.toCharArray());
        }

        @Override
        public char[][] copyOfRange(char[][] keys, int from, int to) {
            return Arrays.copyOfRange( keys,from,to);
        }


        @Override
        public char[][] arrayToKeys(Object[] keys) {
            char[][] ret = new char[keys.length][];
            //$DELAY$
            for(int i=keys.length-1;i>=0;i--)
                ret[i] = ((String)keys[i]).toCharArray();
            return ret;
        }

        @Override
        public char[][] deleteKey(char[][] keys, int pos) {
            char[][] keys2 = new char[keys.length-1][];
            //$DELAY$
            System.arraycopy(keys,0,keys2, 0, pos);
            System.arraycopy(keys, pos+1, keys2, pos, keys2.length-pos);
            return keys2;
        }
    };

    protected static int commonPrefixLen(byte[][] bytes) {
        //TODO refactor to calculate minimal length first, to save comparations.
        for(int ret=0;;ret++){
            if(bytes[0].length==ret) {
                return ret;
            }
            byte byt = bytes[0][ret];
            for(int i=1;i STRING = new BTreeKeySerializer() {
        @Override
        public void serialize(DataOutput out, StringArrayKeys keys) throws IOException {
            int offset = 0;
            //write sizes
            for(int o: keys.getOffset()){
                DataIO.packInt(out,(o-offset));
                offset = o;
            }
            //$DELAY$
            int unicode = keys.hasUnicodeChars()?1:0;
            
            //find and write common prefix
            int prefixLen = keys.commonPrefixLen();
            DataIO.packInt(out,(prefixLen<<1) | unicode);
            keys.serialize(out, prefixLen);
        }

        @Override
        public StringArrayKeys deserialize(DataInput in, int nodeSize) throws IOException {
            //read data sizes
            int[] offsets = new int[nodeSize];
            int old=0;
            for(int i=0;i>>=1;
            //$DELAY$
            return useUnicode?
                    new CharArrayKeys(in,offsets,prefixLen):
                    new ByteArrayKeys(in,offsets,prefixLen);
        }

        @Override
        public int compare(StringArrayKeys byteArrayKeys, int pos1, int pos2) {
            return byteArrayKeys.compare(pos1,pos2);
        }

        @Override
        public int compare(StringArrayKeys byteArrayKeys, int pos1, String string) {
            return byteArrayKeys.compare(pos1,string);
        }



        @Override
        public String getKey(StringArrayKeys byteArrayKeys, int pos) {
            return byteArrayKeys.getKeyString(pos);
        }

        @Override
        public Comparator comparator() {
            return Fun.COMPARATOR;
        }

        @Override
        public ByteArrayKeys emptyKeys() {
            return new ByteArrayKeys(new int[0], new byte[0]);
        }

        @Override
        public int length(StringArrayKeys byteArrayKeys) {
            return byteArrayKeys.length();
        }

        @Override
        public StringArrayKeys putKey(StringArrayKeys byteArrayKeys, int pos, String string) {
            return byteArrayKeys.putKey(pos,string);
        }

        @Override
        public StringArrayKeys arrayToKeys(Object[] keys) {
            if(keys.length==0)
                return emptyKeys();
            //$DELAY$
            boolean unicode = false;

            //fill offsets
            int[] offsets = new int[keys.length];

            int old=0;
            for(int i=0;i BYTE_ARRAY2 = new BTreeKeySerializer() {

        @Override
        public void serialize(DataOutput out, byte[][] chars) throws IOException {
            //write lengths
            for(byte[] b:chars){
                DataIO.packInt(out,b.length);
            }
            //$DELAY$
            //find common prefix
            int prefixLen = commonPrefixLen(chars);
            DataIO.packInt(out,prefixLen);
            out.write(chars[0], 0, prefixLen);
            //$DELAY$
            for(byte[] b:chars){
                out.write(b,prefixLen,b.length-prefixLen);
            }
        }

        @Override
        public byte[][] deserialize(DataInput in, int nodeSize) throws IOException {
            byte[][] ret = new byte[nodeSize][];

            //read lengths and init arrays
            for(int i=0;i comparator() {
            return Fun.BYTE_ARRAY_COMPARATOR;
        }

        @Override
        public byte[][] emptyKeys() {
            return new byte[0][];
        }

        @Override
        public int length(byte[][] chars) {
            return chars.length;
        }

        @Override
        public byte[][] putKey(byte[][] keys, int pos, byte[] newKey) {
            return (byte[][]) BTreeMap.arrayPut(keys, pos, newKey);
        }

        @Override
        public byte[][] copyOfRange(byte[][] keys, int from, int to) {
            return Arrays.copyOfRange( keys,from,to);
        }


        @Override
        public byte[][] arrayToKeys(Object[] keys) {
            byte[][] ret = new byte[keys.length][];
            for(int i=keys.length-1;i>=0;i--)
                ret[i] = (byte[]) keys[i];
            return ret;
        }

        @Override
        public byte[][] deleteKey(byte[][] keys, int pos) {
            byte[][] keys2 = new byte[keys.length-1][];
            System.arraycopy(keys,0,keys2, 0, pos);
            //$DELAY$
            System.arraycopy(keys, pos+1, keys2, pos, keys2.length-pos);
            return keys2;
        }
    };

    public static final BTreeKeySerializer BYTE_ARRAY = new BTreeKeySerializer() {
        @Override
        public void serialize(DataOutput out, ByteArrayKeys keys) throws IOException {
            int offset = 0;
            //write sizes
            for(int o:keys.offset){
                DataIO.packInt(out,o-offset);
                offset = o;
            }
            //$DELAY$
            //find and write common prefix
            int prefixLen = keys.commonPrefixLen();
            DataIO.packInt(out, prefixLen);
            out.write(keys.array,0,prefixLen);
            //$DELAY$
            //write suffixes
            offset = prefixLen;
            for(int o:keys.offset){
                out.write(keys.array, offset, o-offset);
                offset = o+prefixLen;
            }
        }

        @Override
        public ByteArrayKeys deserialize(DataInput in, int nodeSize) throws IOException {
            //read data sizes
            int[] offsets = new int[nodeSize];
            int old=0;
            for(int i=0;i comparator() {
            return Fun.BYTE_ARRAY_COMPARATOR;
        }

        @Override
        public ByteArrayKeys emptyKeys() {
            return new ByteArrayKeys(new int[0], new byte[0]);
        }

        @Override
        public int length(ByteArrayKeys byteArrayKeys) {
            return byteArrayKeys.length();
        }

        @Override
        public ByteArrayKeys putKey(ByteArrayKeys byteArrayKeys, int pos, byte[] newKey) {
            return byteArrayKeys.putKey(pos,newKey);
        }

        @Override
        public ByteArrayKeys arrayToKeys(Object[] keys) {
            //fill offsets
            int[] offsets = new int[keys.length];

            int old=0;
            for(int i=0;i comparator() {
            return wrapped.comparator();
        }

        @Override
        public Object emptyKeys() {
            return wrapped.emptyKeys();
        }

        @Override
        public int length(Object o) {
            return wrapped.length(o);
        }

        @Override
        public Object putKey(Object o, int pos, Object newKey) {
            return wrapped.putKey(o, pos, newKey);
        }

        @Override
        public Object copyOfRange(Object o, int from, int to) {
            return wrapped.copyOfRange(o, from, to);
        }

        @Override
        public Object deleteKey(Object o, int pos) {
            return wrapped.deleteKey(o, pos);
        }

        @Override
        public int findChildren(BTreeMap.BNode node, Object key) {
            return wrapped.findChildren(node, key);
        }

        @Override
        public int findChildren2(BTreeMap.BNode node, Object key) {
            return wrapped.findChildren2(node, key);
        }

        @Override
        public Object arrayToKeys(Object[] keys) {
            return wrapped.arrayToKeys(keys);
        }

        @Override
        public Object[] keysToArray(Object o) {
            return wrapped.keysToArray(o);
        }

    }
}