org.janusgraph.graphdb.database.idhandling.VariableLong.java Source code

Java tutorial

Introduction

Here is the source code for org.janusgraph.graphdb.database.idhandling.VariableLong.java

Source

// Copyright 2017 JanusGraph Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.janusgraph.graphdb.database.idhandling;

import com.google.common.base.Preconditions;
import org.janusgraph.diskstorage.ReadBuffer;
import org.janusgraph.diskstorage.ScanBuffer;
import org.janusgraph.diskstorage.StaticBuffer;
import org.janusgraph.diskstorage.WriteBuffer;
import org.janusgraph.diskstorage.util.WriteByteBuffer;

/**
 * @author Matthias Broecheler (me@matthiasb.com)
 */

public class VariableLong {

    public static int unsignedByte(byte b) {
        return b < 0 ? b + 256 : b;
    }

    public static byte unsignedByte(int value) {
        Preconditions.checkArgument(value >= 0 && value < 256, "Value overflow: %s", value);
        return (byte) (value & 0xFF);
    }

    //Move stop bit back to front => rewrite prefix variable encoding by custom writing first byte

    private static final byte BIT_MASK = 127;
    private static final byte STOP_MASK = -128;

    private static long readUnsigned(ScanBuffer in) {
        long value = 0;
        byte b;
        do {
            b = in.getByte();
            value = value << 7 | (b & BIT_MASK);
        } while (b >= 0);
        return value;
    }

    private static void writeUnsigned(WriteBuffer out, final long value) {
        writeUnsigned(out, unsignedBlockBitLength(value), value);
    }

    private static void writeUnsigned(WriteBuffer out, int offset, final long value) {
        assert offset % 7 == 0;
        while (offset > 0) {
            offset -= 7;
            byte b = (byte) ((value >>> offset) & BIT_MASK);
            if (offset == 0) {
                b = (byte) (b | STOP_MASK);
            }
            out.putByte(b);
        }
    }

    private static int unsignedBlockBitLength(final long value) {
        return unsignedNumBlocks(value) * 7;
    }

    private static int unsignedNumBlocks(final long value) {
        return numVariableBlocks(unsignedBitLength(value));
    }

    private static int numVariableBlocks(final int numBits) {
        assert numBits > 0;
        return (numBits - 1) / 7 + 1;
    }

    public static int unsignedBitLength(final long value) {
        return (value == 0) ? 1 : Long.SIZE - Long.numberOfLeadingZeros(value);
    }

    /* ##################################
      Read and write positive longs
       ################################## */

    public static long readPositive(ScanBuffer in) {
        long value = readUnsigned(in);
        assert value >= 0;
        return value;
    }

    public static void writePositive(WriteBuffer out, final long value) {
        assert value >= 0;
        writeUnsigned(out, value);
    }

    public static StaticBuffer positiveBuffer(final long value) {
        WriteBuffer buffer = new WriteByteBuffer(positiveLength(value));
        writePositive(buffer, value);
        return buffer.getStaticBuffer();
    }

    public static StaticBuffer positiveBuffer(long[] value) {
        int len = 0;
        for (long aValue : value)
            len += positiveLength(aValue);

        WriteBuffer buffer = new WriteByteBuffer(len);

        for (long aValue : value)
            writePositive(buffer, aValue);

        return buffer.getStaticBuffer();
    }

    public static int positiveLength(long value) {
        assert value >= 0;
        return unsignedNumBlocks(value);
    }

    /* ##################################
      Read and write arbitrary longs
    ################################## */

    private static long convert2Unsigned(final long value) {
        assert value >= 0 || value > Long.MIN_VALUE;
        return Math.abs(value) << 1 | (value < 0 ? 1 : 0);
    }

    private static long convertFromUnsigned(final long value) {
        return ((value & 1) == 1) ? -(value >>> 1) : value >>> 1;
    }

    public static int length(long value) {
        return unsignedNumBlocks(convert2Unsigned(value));
    }

    public static void write(WriteBuffer out, final long value) {
        writeUnsigned(out, convert2Unsigned(value));
    }

    public static long read(ScanBuffer in) {
        return convertFromUnsigned(readUnsigned(in));
    }

    /* ##################################
      Read and write positive longs with a specified binary prefix of fixed length
    ################################## */

    public static void writePositiveWithPrefix(final WriteBuffer out, long value, long prefix,
            final int prefixBitLen) {
        assert value >= 0;
        assert prefixBitLen > 0 && prefixBitLen < 6 && (prefix < (1L << prefixBitLen));
        //Write first byte
        int deltaLen = 8 - prefixBitLen;
        byte first = (byte) (prefix << deltaLen);
        int valueLen = unsignedBitLength(value);
        int mod = valueLen % 7;
        if (mod <= (deltaLen - 1)) {
            int offset = (valueLen - mod);
            first = (byte) (first | (value >>> offset));
            value = value & ((1l << offset) - 1);
            valueLen -= mod;
        } else {
            valueLen += (7 - mod);
        }
        assert valueLen >= 0;
        if (valueLen > 0) {
            //Add continue mask to indicate reading further
            first = (byte) (first | (1 << (deltaLen - 1)));
        }
        out.putByte(first);
        if (valueLen > 0) {
            //Keep writing
            writeUnsigned(out, valueLen, value);
        }
    }

    public static int positiveWithPrefixLength(final long value, final int prefixBitLen) {
        assert value >= 0;
        assert prefixBitLen > 0 && prefixBitLen < 6;
        return numVariableBlocks(unsignedBitLength(value) + prefixBitLen);
    }

    public static long[] readPositiveWithPrefix(final ReadBuffer in, final int prefixBitLen) {
        assert prefixBitLen > 0 && prefixBitLen < 6;

        int first = unsignedByte(in.getByte());
        int deltaLen = 8 - prefixBitLen;
        long prefix = first >> deltaLen;
        long value = first & ((1 << (deltaLen - 1)) - 1);
        if (((first >>> (deltaLen - 1)) & 1) == 1) { //Continue mask
            int deltaPos = in.getPosition();
            long remainder = readUnsigned(in);
            deltaPos = in.getPosition() - deltaPos;
            assert deltaPos > 0;
            value = (value << (deltaPos * 7)) + remainder;
        }
        return new long[] { value, prefix };
    }

    /* ##################################
      Write positive longs so that they can be read backwards
      Use positiveLength() for length
    ################################## */

    public static void writePositiveBackward(WriteBuffer out, long value) {
        assert value >= 0;
        writeUnsignedBackward(out, value);
    }

    public static int positiveBackwardLength(long value) {
        assert value >= 0;
        return unsignedBackwardLength(value);
    }

    public static long readPositiveBackward(ReadBuffer in) {
        return readUnsignedBackward(in);
    }

    /* ##################################
      Write arbitrary longs so that they can be read backwards
      Use length() for length
    ################################## */

    public static void writeBackward(WriteBuffer out, final long value) {
        writeUnsignedBackward(out, convert2Unsigned(value));
    }

    public static int backwardLength(final long value) {
        return unsignedBackwardLength(convert2Unsigned(value));
    }

    public static long readBackward(ReadBuffer in) {
        return convertFromUnsigned(readUnsignedBackward(in));
    }

    /**
     * The format used is this:
     * - The first bit indicates whether this is the first block (reading backwards, this would be the stopping criterion)
     * - In the first byte, the 3 bits after the first bit indicate the number of bytes written minus 3 (since 3 is
     * the minimum number of bytes written. So, if the 3 bits are 010 = 2 => 5 bytes written. The value is aligned to
     * the left to ensure that this encoding is byte order preserving.
     *
     * @param out
     * @param value
     */
    private static void writeUnsignedBackward(WriteBuffer out, final long value) {
        int numBytes = unsignedBackwardLength(value);
        int prefixLen = numBytes - 3;
        assert prefixLen >= 0 && prefixLen < 8; //Consumes 3 bits
        //Prepare first byte
        byte b = (byte) ((prefixLen << 4) | 0x80); //stop marker (first bit) and length
        for (int i = numBytes - 1; i >= 0; i--) {
            b = (byte) (b | (0x7F & (value >>> (i * 7))));
            out.putByte(b);
            b = 0;
        }
    }

    private static int unsignedBackwardLength(final long value) {
        int bitlength = unsignedBitLength(value);
        assert bitlength > 0 && bitlength <= 64;
        int numBytes = Math.max(3, 1 + (bitlength <= 4 ? 0 : (1 + (bitlength - 5) / 7)));
        return numBytes;
    }

    private static long readUnsignedBackward(ReadBuffer in) {
        int position = in.getPosition();
        int numBytes = 0;
        long value = 0;
        long b;
        while (true) {
            position--;
            b = in.getByte(position);
            if (b < 0) { //First byte
                value = value | ((b & 0x0F) << (7 * numBytes));
                assert ((b >>> 4) & 7) + 3 == numBytes + 1 : b + " vs " + numBytes; //verify correct length
                break;
            }
            value = value | (b << (7 * numBytes));
            numBytes++;
        }
        in.movePositionTo(position);
        return value;
    }

}