/**
* Copyright (c) 2002-2013 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.util;
import java.util.Arrays;
import org.neo4j.kernel.impl.nioneo.store.Buffer;
/**
* Got bits to store, shift and retrieve and they are more than what fits in a long?
* Use {@link Bits} then.
*/
public class Bits
{
// 3: ...
// 2: [ 23 ][ 22 ][ 21 ][ 20 ][ 19 ][ 18 ][ 17 ][ 16 ] <--\
// |
// /---------------------------------------------------------------------------------------------/
// |
// 1: \-[ 15 ][ 14 ][ 13 ][ 12 ][ 11 ][ 10 ][ 9 ][ 8 ] <--\
// |
// /---------------------------------------------------------------------------------------------/
// |
// 0: \-[ 7 ][ 6 ][ 5 ][ 4 ][ 3 ][ 2 ][ 1 ][ 0 ] <---- START
private final long[] longs;
private final int numberOfBytes;
private int writePosition;
private int readPosition;
/*
* Calculate all the right overflow masks
*/
private static final long[] RIGHT_OVERFLOW_MASKS;
static
{
RIGHT_OVERFLOW_MASKS = new long[Long.SIZE];
long mask = 1L;
for ( int i = 0; i < RIGHT_OVERFLOW_MASKS.length; i++ )
{
RIGHT_OVERFLOW_MASKS[i] = mask;
mask <<= 1;
mask |= 0x1L;
}
}
public static Bits bits( int numberOfBytes )
{
int requiredLongs = (numberOfBytes-1)/8+1;
return new Bits( new long[requiredLongs], numberOfBytes );
}
public static Bits bitsFromLongs( long[] longs )
{
return new Bits( longs, longs.length*8 );
}
public static Bits bitsFromBytes( byte[] bytes )
{
Bits bits = bits( bytes.length );
for ( byte value : bytes )
{
bits.put( value );
}
return bits;
}
private Bits( long[] longs, int numberOfBytes )
{
this.longs = longs;
this.numberOfBytes = numberOfBytes;
}
/**
* A mask which has the {@code steps} most significant bits set to 1, all others 0.
* It's used to carry bits over between carriers (longs) when shifting left.
* @param steps the number of most significant bits to have set to 1 in the mask.
* @return the created mask.
*/
public static long leftOverflowMask( int steps )
{
long mask = 0L;
for ( int i = 0; i < steps; i++ )
{
mask >>= 1;
mask |= 0x8000000000000000L;
}
return mask;
}
/**
* A mask which has the {@code steps} least significant bits set to 1, all others 0.
* It's used to carry bits over between carriers (longs) when shifting right.
* @param steps the number of least significant bits to have set to 1 in the mask.
* @return the created mask.
*/
public static long rightOverflowMask( int steps )
{
return RIGHT_OVERFLOW_MASKS[steps-1];
}
/**
* Returns the underlying long values that has got all the bits applied.
* The first item in the array has got the most significant bits.
* @return the underlying long values that has got all the bits applied.
*/
public long[] getLongs()
{
return longs;
}
public byte[] asBytes()
{
int readPositionBefore = readPosition;
readPosition = 0;
try
{
byte[] result = new byte[numberOfBytes];
for ( int i = 0; i < result.length; i++ )
{
result[i] = getByte();
}
return result;
}
finally
{
readPosition = readPositionBefore;
}
}
/**
* Writes all bits to {@code buffer}.
* @param buffer the {@link Buffer} to write to.
* @return this instance.
*/
public Bits apply( Buffer buffer )
{
int readPositionBefore = readPosition;
readPosition = 0;
try
{
// TODO byte for byte?
int rest = numberOfBytes;
while ( rest-- > 0 )
{
buffer.put( getByte() );
}
return this;
}
finally
{
readPosition = readPositionBefore;
}
}
/**
* Reads from {@code buffer} and fills up all the bits.
* @param buffer the {@link Buffer} to read from.
* @return this instance.
*/
public Bits read( Buffer buffer )
{
// TODO byte for byte?
int rest = numberOfBytes;
while ( rest > 0 )
{
byte value = buffer.get();
put( value );
rest--;
}
return this;
}
/**
* A very nice toString, showing each bit, divided into groups of bytes and
* lines of 8 bytes.
*/
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
for ( int longIndex = longs.length-1; longIndex >= 0; longIndex-- )
{
long value = longs[longIndex];
if ( builder.length() > 0 ) builder.append( "\n" );
builder.append( longIndex + ":[" );
for ( int i = 63; i >= 0; i-- )
{
boolean isSet = (value & (1L << i)) != 0;
builder.append( isSet ? "1" : "0" );
if ( i%8 == 0 && i > 0 )
{
builder.append( "," );
}
}
builder.append( "]" );
if ( longIndex == 0 ) builder.append( " <-- START" );
}
return builder.toString();
}
@Override
public Bits clone()
{
return new Bits( Arrays.copyOf( longs, longs.length ), numberOfBytes );
}
public Bits put( byte value )
{
return put( value, Byte.SIZE );
}
public Bits put( byte value, int steps )
{
return put( (long)value, steps );
}
public Bits put( short value )
{
return put( value, Short.SIZE );
}
public Bits put( short value, int steps )
{
return put( (long)value, steps );
}
public Bits put( int value )
{
return put( value, Integer.SIZE );
}
public Bits put( int value, int steps )
{
return put( (long)value, steps );
}
public Bits put( long value )
{
return put( value, Long.SIZE );
}
public Bits put( long value, int steps )
{
int lowLongIndex = writePosition >> 6; // /64
int lowBitInLong = writePosition%64;
int lowBitsAvailable = 64-lowBitInLong;
long lowValueMask = rightOverflowMask( Math.min( lowBitsAvailable, steps ) );
longs[lowLongIndex] |= ((((long)value)&lowValueMask) << lowBitInLong);
if ( steps > lowBitsAvailable )
{ // High bits
long highValueMask = rightOverflowMask( steps-lowBitsAvailable );
longs[lowLongIndex+1] |= (((long)value) >>> lowBitsAvailable)&highValueMask;
}
writePosition += steps;
return this;
}
public boolean available()
{
return readPosition < writePosition;
}
public byte getByte()
{
return getByte( Byte.SIZE );
}
public byte getByte( int steps )
{
return (byte) getLong( steps );
}
public short getShort()
{
return getShort( Short.SIZE );
}
public short getShort( int steps )
{
return (short) getLong( steps );
}
public int getInt()
{
return getInt( Integer.SIZE );
}
public int getInt( int steps )
{
return (int) getLong( steps );
}
public long getUnsignedInt()
{
return getInt( Integer.SIZE ) & 0xFFFFFFFFL;
}
public long getLong()
{
return getLong( Long.SIZE );
}
public long getLong( int steps )
{
int lowLongIndex = readPosition >> 6; // 64
int lowBitInLong = readPosition%64;
int lowBitsAvailable = 64-lowBitInLong;
long lowLongMask = rightOverflowMask( Math.min( lowBitsAvailable, steps ) ) << lowBitInLong;
long lowValue = longs[lowLongIndex] & lowLongMask;
long result = lowValue >>> lowBitInLong;
if ( steps > lowBitsAvailable )
{ // High bits
long highLongMask = rightOverflowMask( steps-lowBitsAvailable );
result |= ((longs[lowLongIndex+1] & highLongMask) << lowBitsAvailable);
}
readPosition += steps;
return result;
}
}