| /* |
| * 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 java.util.Arrays; |
| import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory; |
| import org.apache.ignite.internal.util.typedef.F; |
| import org.jetbrains.annotations.Nullable; |
| |
| /** |
| * 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; |
| } |
| } |
| } |