blob: 43750732caca53dd652e6de2760c98ac04f6fa5d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.ignite.internal.binary;
import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory;
import org.apache.ignite.internal.util.typedef.F;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
/**
* Compares fiels in serialized form when possible.
*/
public class BinarySerializedFieldComparator {
/** Position: not found. */
private static final int POS_NOT_FOUND = -1;
/** Original object. */
private final BinaryObjectExImpl obj;
/** Pointer to data (onheap). */
private final byte[] arr;
/** Pointer to data (offheap). */
private final long ptr;
/** Object start offset. */
private final int startOff;
/** Order base. */
private final int orderBase;
/** Order multiplier. */
private final int orderMultiplier;
/** Field offset length. */
private final int fieldOffLen;
/** Current field order. */
private int curFieldOrder;
/** Current field offset. */
private int curFieldPos;
/**
* Constructor.
*
* @param obj Original object.
* @param arr Array.
* @param ptr Pointer.
* @param startOff Start offset.
* @param orderBase Order base.
* @param orderMultiplier Order multiplier.
* @param fieldOffLen Field offset length.
*/
public BinarySerializedFieldComparator(BinaryObjectExImpl obj, byte[] arr, long ptr, int startOff, int orderBase,
int orderMultiplier, int fieldOffLen) {
assert arr != null && ptr == 0L || arr == null && ptr != 0L;
this.obj = obj;
this.arr = arr;
this.ptr = ptr;
this.startOff = startOff;
this.orderBase = orderBase;
this.orderMultiplier = orderMultiplier;
this.fieldOffLen = fieldOffLen;
}
/**
* Locate the field.
*
* @param order Field order.
*/
public void findField(int order) {
curFieldOrder = order;
if (order == BinarySchema.ORDER_NOT_FOUND)
curFieldPos = POS_NOT_FOUND;
else {
int pos = orderBase + order * orderMultiplier;
if (fieldOffLen == BinaryUtils.OFFSET_1) {
byte val = offheap() ? BinaryPrimitives.readByte(ptr, pos) : BinaryPrimitives.readByte(arr, pos);
curFieldPos = startOff + ((int)val & 0xFF);
}
else if (fieldOffLen == BinaryUtils.OFFSET_2) {
short val = offheap() ? BinaryPrimitives.readShort(ptr, pos) : BinaryPrimitives.readShort(arr, pos);
curFieldPos = startOff + ((int)val & 0xFFFF);
}
else {
int val = offheap() ? BinaryPrimitives.readInt(ptr, pos) : BinaryPrimitives.readInt(arr, pos);
curFieldPos = startOff + val;
}
}
}
/**
* Get field type.
*
* @return Field type.
*/
private byte fieldType() {
if (curFieldPos == POS_NOT_FOUND)
return GridBinaryMarshaller.NULL;
else
return offheap() ?
BinaryPrimitives.readByte(ptr, curFieldPos) : BinaryPrimitives.readByte(arr, curFieldPos);
}
/**
* @return Whether this is offheap object.
*/
private boolean offheap() {
return ptr != 0L;
}
/**
* Get current field.
*
* @return Current field.
*/
private Object currentField() {
return obj.fieldByOrder(curFieldOrder);
}
/**
* Read byte value.
*
* @param off Offset.
* @return Value.
*/
private byte readByte(int off) {
if (offheap())
return BinaryPrimitives.readByte(ptr, curFieldPos + off);
else
return arr[curFieldPos + off];
}
/**
* Read short value.
*
* @param off Offset.
* @return Value.
*/
private short readShort(int off) {
if (offheap())
return BinaryPrimitives.readShort(ptr, curFieldPos + off);
else
return BinaryPrimitives.readShort(arr, curFieldPos + off);
}
/**
* Read int value.
*
* @param off Offset.
* @return Value.
*/
private int readInt(int off) {
if (offheap())
return BinaryPrimitives.readInt(ptr, curFieldPos + off);
else
return BinaryPrimitives.readInt(arr, curFieldPos + off);
}
/**
* Read long value.
*
* @param off Offset.
* @return Value.
*/
private long readLong(int off) {
if (offheap())
return BinaryPrimitives.readLong(ptr, curFieldPos + off);
else
return BinaryPrimitives.readLong(arr, curFieldPos + off);
}
/**
* Compare fields.
*
* @param c1 First comparer.
* @param c2 Second comparer.
* @return {@code True} if both fields are equal.
*/
public static boolean equals(BinarySerializedFieldComparator c1, BinarySerializedFieldComparator c2) {
// Compare field types.
byte typ = c1.fieldType();
if (typ != c2.fieldType())
return false;
// Switch by type and compare.
switch (typ) {
case GridBinaryMarshaller.BYTE:
case GridBinaryMarshaller.BOOLEAN:
return c1.readByte(1) == c2.readByte(1);
case GridBinaryMarshaller.SHORT:
case GridBinaryMarshaller.CHAR:
return c1.readShort(1) == c2.readShort(1);
case GridBinaryMarshaller.INT:
case GridBinaryMarshaller.FLOAT:
return c1.readInt(1) == c2.readInt(1);
case GridBinaryMarshaller.LONG:
case GridBinaryMarshaller.DOUBLE:
case GridBinaryMarshaller.DATE:
return c1.readLong(1) == c2.readLong(1);
case GridBinaryMarshaller.TIMESTAMP:
return c1.readLong(1) == c2.readLong(1) && c1.readInt(1 + 8) == c2.readInt(1 + 8);
case GridBinaryMarshaller.TIME:
return c1.readLong(1) == c2.readLong(1);
case GridBinaryMarshaller.UUID:
return c1.readLong(1) == c2.readLong(1) && c1.readLong(1 + 8) == c2.readLong(1 + 8);
case GridBinaryMarshaller.STRING:
return compareByteArrays(c1, c2, 1);
case GridBinaryMarshaller.DECIMAL:
return c1.readInt(1) == c2.readInt(1) && compareByteArrays(c1, c2, 5);
case GridBinaryMarshaller.NULL:
return true;
default:
Object val1 = c1.currentField();
Object val2 = c2.currentField();
return isArray(val1) ? compareArrays(val1, val2) : F.eq(val1, val2);
}
}
/**
* Compare arrays.
*
* @param val1 Value 1.
* @param val2 Value 2.
* @return Result.
*/
private static boolean compareArrays(Object val1, Object val2) {
if (val1.getClass() == val2.getClass()) {
if (val1 instanceof byte[])
return Arrays.equals((byte[])val1, (byte[])val2);
else if (val1 instanceof boolean[])
return Arrays.equals((boolean[])val1, (boolean[])val2);
else if (val1 instanceof short[])
return Arrays.equals((short[])val1, (short[])val2);
else if (val1 instanceof char[])
return Arrays.equals((char[])val1, (char[])val2);
else if (val1 instanceof int[])
return Arrays.equals((int[])val1, (int[])val2);
else if (val1 instanceof long[])
return Arrays.equals((long[])val1, (long[])val2);
else if (val1 instanceof float[])
return Arrays.equals((float[])val1, (float[])val2);
else if (val1 instanceof double[])
return Arrays.equals((double[])val1, (double[])val2);
else
return Arrays.deepEquals((Object[])val1, (Object[])val2);
}
return false;
}
/**
* @param field Field.
* @return {@code True} if field is array.
*/
private static boolean isArray(@Nullable Object field) {
return field != null && field.getClass().isArray();
}
/**
* Compare byte arrays.
*
* @param c1 Comparer 1.
* @param c2 Comparer 2.
* @param off Offset (where length is located).
* @return {@code True} if equal.
*/
private static boolean compareByteArrays(BinarySerializedFieldComparator c1, BinarySerializedFieldComparator c2,
int off) {
int len = c1.readInt(off);
if (len != c2.readInt(off))
return false;
else {
off += 4;
if (c1.offheap()) {
if (c2.offheap())
// Case 1: both offheap.
return GridUnsafeMemory.compare(c1.curFieldPos + c1.ptr + off, c2.curFieldPos + c2.ptr + off, len);
}
else {
if (!c2.offheap()) {
// Case 2: both onheap.
for (int i = 0; i < len; i++) {
if (c1.arr[c1.curFieldPos + off + i] != c2.arr[c2.curFieldPos + off + i])
return false;
}
return true;
}
else {
// Swap.
BinarySerializedFieldComparator tmp = c1;
c1 = c2;
c2 = tmp;
}
}
// Case 3: offheap vs onheap.
assert c1.offheap() && !c2.offheap();
for (int i = 0; i < len; i++) {
if (BinaryPrimitives.readByte(c1.ptr, c1.curFieldPos + off + i) != c2.arr[c2.curFieldPos + off + i])
return false;
}
return true;
}
}
}