Skip to content

Commit

Permalink
Move raw bits comparison to schema package
Browse files Browse the repository at this point in the history
To avoid polluting values module with raw bits capabilities
  • Loading branch information
burqen authored and tinwelint committed Jun 27, 2017
1 parent 21e6a75 commit 07ab222
Show file tree
Hide file tree
Showing 8 changed files with 320 additions and 174 deletions.
@@ -0,0 +1,147 @@
/*
* Copyright (c) 2002-2017 "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.index.schema;

import org.neo4j.values.NumberValue;
import org.neo4j.values.NumberValues;
import org.neo4j.values.Values;

/**
* Useful to compare values stored as raw bits and value type without having to box them as {@link NumberValue number values}.
*/
class RawBits
{
static final byte BYTE = 0;
static final byte SHORT = 1;
static final byte INT = 2;
static final byte LONG = 3;
static final byte FLOAT = 4;
static final byte DOUBLE = 5;

/**
* Convert value represented by type and raw bits to corresponding {@link NumberValue}. If type is not {@link #BYTE}, {@link #SHORT},
* {@link #INT}, {@link #LONG}, {@link #FLOAT} or {@link #DOUBLE}, the raw bits will be interpreted as a long.
*
* @param rawBits Raw bits of value
* @param type Type of value
* @return {@link NumberValue} with type and value given by provided raw bits and type.
*/
static NumberValue asNumberValue( long rawBits, byte type )
{
switch ( type )
{
case BYTE:
return (NumberValue) Values.byteValue( (byte) rawBits );
case SHORT:
return (NumberValue) Values.shortValue( (short) rawBits );
case INT:
return (NumberValue) Values.intValue( (int) rawBits );
case LONG:
return (NumberValue) Values.longValue( rawBits );
case FLOAT:
return (NumberValue) Values.floatValue( Float.intBitsToFloat( (int) rawBits ) );
case DOUBLE:
return (NumberValue) Values.doubleValue( Double.longBitsToDouble( rawBits ) );
default:
// If type is not recognized, interpret as long.
return (NumberValue) Values.longValue( rawBits );
}
}

/**
* Compare number values represented by type and raw bits. If type is not {@link #BYTE}, {@link #SHORT}, {@link #INT}, {@link #LONG},
* {@link #FLOAT} or {@link #DOUBLE}, the raw bits will be compared as long.
*
* @param lhsRawBits Raw bits of left hand side value
* @param lhsType Type of left hand side value
* @param rhsRawBits Raw bits of right hand side value
* @param rhsType Type of right hand side value
* @return An int less that 0 if lhs value is numerically less than rhs value. An int equal to 0 if lhs and rhs value are
* numerically equal (independent of type) and an int greater than 0 if lhs value is greater than rhs value.
*/
static int compare( long lhsRawBits, byte lhsType, long rhsRawBits, byte rhsType )
{
// case integral - integral
if ( lhsType == BYTE ||
lhsType == SHORT ||
lhsType == INT ||
lhsType == LONG )
{
return compareLongAgainstRawType( lhsRawBits, rhsRawBits, rhsType );
}
else if ( lhsType == FLOAT )
{
double lhsFloat = Float.intBitsToFloat( (int) lhsRawBits );
return compareDoubleAgainstRawType( lhsFloat, rhsRawBits, rhsType );
}
else if ( lhsType == DOUBLE )
{
double lhsDouble = Double.longBitsToDouble( lhsRawBits );
return compareDoubleAgainstRawType( lhsDouble, rhsRawBits, rhsType );
}
// We can not throw here because we will visit this method inside a pageCursor.shouldRetry() block.
// Just return a comparison that at least will be commutative.
return Long.compare( lhsRawBits, rhsRawBits );
}

private static int compareLongAgainstRawType( long lhs, long rhsRawBits, byte rhsType )
{
if ( rhsType == BYTE ||
rhsType == SHORT ||
rhsType == INT ||
rhsType == LONG )
{
return Long.compare( lhs, rhsRawBits );
}
else if ( rhsType == FLOAT )
{
return NumberValues.compareLongAgainstDouble( lhs, Float.intBitsToFloat( (int) rhsRawBits ) );
}
else if ( rhsType == DOUBLE )
{
return NumberValues.compareLongAgainstDouble( lhs, Double.longBitsToDouble( rhsRawBits ) );
}
// We can not throw here because we will visit this method inside a pageCursor.shouldRetry() block.
// Just return a comparison that at least will be commutative.
return Long.compare( lhs, rhsRawBits );
}

private static int compareDoubleAgainstRawType( double lhsDouble, long rhsRawBits, byte rhsType )
{
if ( rhsType == BYTE ||
rhsType == SHORT ||
rhsType == INT ||
rhsType == LONG )
{
return NumberValues.compareDoubleAgainstLong( lhsDouble, rhsRawBits );
}
else if ( rhsType == FLOAT )
{
return Double.compare( lhsDouble, Float.intBitsToFloat( (int) rhsRawBits ) );
}
else if ( rhsType == DOUBLE )
{
return Double.compare( lhsDouble, Double.longBitsToDouble( rhsRawBits ) );
}
// We can not throw here because we will visit this method inside a pageCursor.shouldRetry() block.
// Just return a comparison that at least will be commutative.
return Long.compare( Double.doubleToLongBits( lhsDouble ), rhsRawBits );
}
}
Expand Up @@ -21,8 +21,6 @@

import org.neo4j.index.internal.gbptree.GBPTree;
import org.neo4j.values.NumberValue;
import org.neo4j.values.NumberValues;
import org.neo4j.values.PrimitiveNumberType;
import org.neo4j.values.Value;
import org.neo4j.values.ValueWriter;
import org.neo4j.values.Values;
Expand Down Expand Up @@ -59,14 +57,14 @@ class SchemaNumberKey extends ValueWriter.Adapter
*/
boolean entityIdIsSpecialTieBreaker;

void from( long entityId, Value[] values )
void from( long entityId, Value... values )
{
extractValue( assertValidSingleNumber( values ) );
extractRawBitsAndType( assertValidSingleNumber( values ) );
this.entityId = entityId;
entityIdIsSpecialTieBreaker = false;
}

private static NumberValue assertValidSingleNumber( Value[] values )
private static NumberValue assertValidSingleNumber( Value... values )
{
// TODO: support multiple values, right?
if ( values.length > 1 )
Expand All @@ -87,21 +85,21 @@ private static NumberValue assertValidSingleNumber( Value[] values )

String propertiesAsString()
{
return PrimitiveNumberType.from( type ).valueFromRawBits( rawValueBits ).toString();
return RawBits.asNumberValue( rawValueBits, type ).toString();
}

void initAsLowest()
{
rawValueBits = Double.doubleToLongBits( Double.NEGATIVE_INFINITY );
type = PrimitiveNumberType.DOUBLE.byteRepresentation();
type = RawBits.DOUBLE;
entityId = Long.MIN_VALUE;
entityIdIsSpecialTieBreaker = true;
}

void initAsHighest()
{
rawValueBits = Double.doubleToLongBits( Double.POSITIVE_INFINITY );
type = PrimitiveNumberType.DOUBLE.byteRepresentation();
type = RawBits.DOUBLE;
entityId = Long.MAX_VALUE;
entityIdIsSpecialTieBreaker = true;
}
Expand All @@ -115,15 +113,15 @@ void initAsHighest()
*/
int compareValueTo( SchemaNumberKey other )
{
return NumberValues.compare( rawValueBits, type, other.rawValueBits, other.type );
return RawBits.compare( rawValueBits, type, other.rawValueBits, other.type );
}

/**
* Extracts data from a {@link NumberValue} into state of this {@link SchemaNumberKey} instance.
* Extracts raw bits and type from a {@link NumberValue} and store as state of this {@link SchemaNumberKey} instance.
*
* @param value actual {@link NumberValue} value.
*/
private void extractValue( NumberValue value )
private void extractRawBitsAndType( NumberValue value )
{
value.writeTo( this );
}
Expand All @@ -132,48 +130,48 @@ private void extractValue( NumberValue value )
public String toString()
{
return format( "type=%d,rawValue=%d,value=%s,entityId=%d",
type, rawValueBits, PrimitiveNumberType.from( type ).valueFromRawBits( rawValueBits ).toString(), entityId );
type, rawValueBits, RawBits.asNumberValue( rawValueBits, type ), entityId );
}

@Override
public void writeInteger( byte value )
{
type = PrimitiveNumberType.BYTE.byteRepresentation();
type = RawBits.BYTE;
rawValueBits = value;
}

@Override
public void writeInteger( short value )
{
type = PrimitiveNumberType.SHORT.byteRepresentation();
type = RawBits.SHORT;
rawValueBits = value;
}

@Override
public void writeInteger( int value )
{
type = PrimitiveNumberType.INT.byteRepresentation();
type = RawBits.INT;
rawValueBits = value;
}

@Override
public void writeInteger( long value )
{
type = PrimitiveNumberType.LONG.byteRepresentation();
type = RawBits.LONG;
rawValueBits = value;
}

@Override
public void writeFloatingPoint( float value )
{
type = PrimitiveNumberType.FLOAT.byteRepresentation();
type = RawBits.FLOAT;
rawValueBits = Float.floatToIntBits( value );
}

@Override
public void writeFloatingPoint( double value )
{
type = PrimitiveNumberType.DOUBLE.byteRepresentation();
type = RawBits.DOUBLE;
rawValueBits = Double.doubleToLongBits( value );
}
}
Expand Up @@ -36,7 +36,7 @@ class SchemaNumberValue

static final SchemaNumberValue INSTANCE = new SchemaNumberValue();

void from( Value[] values )
void from( Value... values )
{
// not needed a.t.m.
}
Expand Down
Expand Up @@ -71,9 +71,9 @@
/**
* Tests for
* <ul>
* <li>{@link NativeSchemaNumberIndexAccessor}</li>
* <li>{@link NativeSchemaNumberIndexUpdater}</li>
* <li>{@link NativeSchemaNumberIndexReader}</li>
* <li>{@link NativeSchemaNumberIndexAccessor}</li>
* <li>{@link NativeSchemaNumberIndexUpdater}</li>
* <li>{@link NativeSchemaNumberIndexReader}</li>
* </ul>
*/
public abstract class NativeSchemaNumberIndexAccessorTest<KEY extends SchemaNumberKey, VALUE extends SchemaNumberValue>
Expand Down

0 comments on commit 07ab222

Please sign in to comment.