/*** * 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.tajo.util; import com.google.common.primitives.Longs; import com.google.common.primitives.UnsignedBytes; import com.google.common.primitives.UnsignedLongs; import sun.misc.Unsafe; import java.lang.reflect.Field; import java.nio.ByteOrder; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Comparator; /** * This is borrowed from Guava's UnsignedBytes. */ public class UnsafeComparer implements Comparator<byte[]> { public static final UnsafeComparer INSTANCE; static { INSTANCE = new UnsafeComparer(); } public Comparator<byte []> get() { return INSTANCE; } private UnsafeComparer() {} static final boolean littleEndian = ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN); /* * The following static final fields exist for performance reasons. * * In UnsignedBytesBenchmark, accessing the following objects via static * final fields is the fastest (more than twice as fast as the Java * implementation, vs ~1.5x with non-final static fields, on x86_32) * under the Hotspot server compiler. The reason is obviously that the * non-final fields need to be reloaded inside the loop. * * And, no, defining (final or not) local variables out of the loop still * isn't as good because the null check on the theUnsafe object remains * inside the loop and BYTE_ARRAY_BASE_OFFSET doesn't get * constant-folded. * * The compiler can treat static final fields as compile-time constants * and can constant-fold them while (final or not) local variables are * run time values. */ static final Unsafe theUnsafe; /** The offset to the first element in a byte array. */ static final int BYTE_ARRAY_BASE_OFFSET; static { theUnsafe = (Unsafe) AccessController.doPrivileged( new PrivilegedAction<Object>() { @Override public Object run() { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); return f.get(null); } catch (NoSuchFieldException e) { // It doesn't matter what we throw; // it's swallowed in getBestComparator(). throw new Error(); } catch (IllegalAccessException e) { throw new Error(); } } }); BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); // sanity check - this should never fail if (theUnsafe.arrayIndexScale(byte[].class) != 1) { throw new AssertionError(); } } @SuppressWarnings("unused") public static int compareStatic(byte[] left, byte[] right) { return INSTANCE.compare(left, right); } @Override public int compare(byte[] left, byte[] right) { int minLength = Math.min(left.length, right.length); int minWords = minLength / Longs.BYTES; /* * Compare 8 bytes at a time. Benchmarking shows comparing 8 bytes at a * time is no slower than comparing 4 bytes at a time even on 32-bit. * On the other hand, it is substantially faster on 64-bit. */ for (int i = 0; i < minWords * Longs.BYTES; i += Longs.BYTES) { long lw = theUnsafe.getLong(left, BYTE_ARRAY_BASE_OFFSET + (long) i); long rw = theUnsafe.getLong(right, BYTE_ARRAY_BASE_OFFSET + (long) i); long diff = lw ^ rw; if (diff != 0) { if (!littleEndian) { return UnsignedLongs.compare(lw, rw); } // Use binary search int n = 0; int y; int x = (int) diff; if (x == 0) { x = (int) (diff >>> 32); n = 32; } y = x << 16; if (y == 0) { n += 16; } else { x = y; } y = x << 8; if (y == 0) { n += 8; } return (int) (((lw >>> n) & 0xFFL) - ((rw >>> n) & 0xFFL)); } } // The epilogue to cover the last (minLength % 8) elements. for (int i = minWords * Longs.BYTES; i < minLength; i++) { int result = UnsignedBytes.compare(left[i], right[i]); if (result != 0) { return result; } } return left.length - right.length; } }