/* * 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.processors.query.h2.database.util; import java.math.BigDecimal; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteException; import org.apache.ignite.internal.util.GridUnsafe; import org.h2.util.MathUtils; import org.h2.value.Value; /** * */ public class CompareUtils { /** */ private static final int UTF_8_MIN_2_BYTES = 0x80; /** */ private static final int UTF_8_MIN_3_BYTES = 0x800; /** */ private static final int UTF_8_MIN_4_BYTES = 0x10000; /** */ private static final int UTF_8_MAX_CODE_POINT = 0x10ffff; /** */ private static final BigDecimal MAX_LONG_DECIMAL = BigDecimal.valueOf(Long.MAX_VALUE); /** */ private static final BigDecimal MIN_LONG_DECIMAL = BigDecimal.valueOf(Long.MIN_VALUE); /** * @param x Value. * @return Byte value. */ public static byte convertToByte(long x) { if (x > Byte.MAX_VALUE || x < Byte.MIN_VALUE) throw new IgniteException("Numeric value out of range: " + x); return (byte) x; } /** * @param x Value. * @return Short value. */ public static short convertToShort(long x) { if (x > Short.MAX_VALUE || x < Short.MIN_VALUE) throw new IgniteException("Numeric value out of range: " + x); return (short) x; } /** * @param x Value. * @return Int value. */ public static int convertToInt(long x) { if (x > Integer.MAX_VALUE || x < Integer.MIN_VALUE) throw new IgniteException("Numeric value out of range: " + x); return (int) x; } /** * @param x Value. * @return Long value. */ public static long convertToLong(double x) { if (x > Long.MAX_VALUE || x < Long.MIN_VALUE) throw new IgniteException("Numeric value out of range: " + x); return Math.round(x); } /** * @param x Value. * @return Long value. */ public static long convertToLong(BigDecimal x) { if (x.compareTo(MAX_LONG_DECIMAL) > 0 || x.compareTo(MIN_LONG_DECIMAL) < 0) throw new IgniteException("Numeric value out of range: " + x); return x.setScale(0, BigDecimal.ROUND_HALF_UP).longValue(); } /** * @param v Value1. * @param val Value2. * @return Compare result. */ public static int compareBoolean(boolean v, Value val) { boolean v2 = val.getBoolean(); return (v == v2) ? 0 : (v ? 1 : -1); } /** * @param v Value1. * @param val Value2. * @return Compare result. */ public static int compareByte(byte v, Value val) { byte v2 = val.getByte(); return MathUtils.compareInt(v, v2); } /** * @param val1Addr First string UTF-8 bytes address. * @param val1Len Number of bytes in first string. * @param val2Bytes Second string bytes. * @param val2Off Second string offset. * @param val2Len Number of bytes in second string. * @return Compare result. * @throws IgniteCheckedException In case of error. */ public static int compareUtf8(long val1Addr, int val1Len, byte[] val2Bytes, int val2Off, int val2Len) throws IgniteCheckedException { int len = Math.min(val1Len, val2Len); for (int i = 0; i < len; i++) { int b1 = GridUnsafe.getByte(val1Addr + i) & 0xFF; int b2 = val2Bytes[val2Off + i] & 0xFF; if (b1 != b2) return b1 > b2 ? 1 : -1; } return Integer.compare(val1Len, val2Len); } /** * @param addr UTF-8 bytes address. * @param len Number of bytes to decode. * @param str String to compare with. * @return Compare result. * @throws IgniteCheckedException In case of error. */ public static int compareUtf8(long addr, int len, String str) throws IgniteCheckedException { int pos = 0; int cntr = 0; int strLen = str.length(); // ASCII only optimized loop. while (pos < len) { byte ch = GridUnsafe.getByte(addr + pos); if (ch >= 0) { char c0 = (char)ch; if (cntr < strLen) { char c1 = str.charAt(cntr); if (c0 != c1) return c0 - c1; } else return 1; cntr++; pos++; } else break; } // TODO: check index bounds. while (pos < len) { int ch = GridUnsafe.getByte(addr + pos++) & 0xff; // Convert UTF-8 to 21-bit codepoint. if (ch < 0x80) { // 0xxxxxxx -- length 1. } else if (ch < 0xc0) { // 10xxxxxx -- illegal! throw new IgniteException("Illegal UTF-8 sequence."); } else if (ch < 0xe0) { // 110xxxxx 10xxxxxx ch = ((ch & 0x1f) << 6); checkByte(GridUnsafe.getByte(addr + pos)); ch |= (GridUnsafe.getByte(addr + pos++) & 0x3f); checkMinimal(ch, UTF_8_MIN_2_BYTES); } else if (ch < 0xf0) { // 1110xxxx 10xxxxxx 10xxxxxx ch = ((ch & 0x0f) << 12); checkByte(GridUnsafe.getByte(addr + pos)); ch |= ((GridUnsafe.getByte(addr + pos++) & 0x3f) << 6); checkByte(GridUnsafe.getByte(addr + pos)); ch |= (GridUnsafe.getByte(addr + pos++) & 0x3f); checkMinimal(ch, UTF_8_MIN_3_BYTES); } else if (ch < 0xf8) { // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx ch = ((ch & 0x07) << 18); checkByte(GridUnsafe.getByte(addr + pos)); ch |= ((GridUnsafe.getByte(addr + pos++) & 0x3f) << 12); checkByte(GridUnsafe.getByte(addr + pos)); ch |= ((GridUnsafe.getByte(addr + pos++) & 0x3f) << 6); checkByte(GridUnsafe.getByte(addr + pos)); ch |= (GridUnsafe.getByte(addr + pos++) & 0x3f); checkMinimal(ch, UTF_8_MIN_4_BYTES); } else throw new IgniteException("Illegal UTF-8 sequence."); if (ch > UTF_8_MAX_CODE_POINT) throw new IgniteException("Illegal UTF-8 sequence."); // Convert 21-bit codepoint to Java chars: // 0..ffff are represented directly as a single char // 10000..10ffff are represented as a "surrogate pair" of two chars if (ch > 0xffff) { // Use a surrogate pair to represent it. ch -= 0x10000; // ch is now 0..fffff (20 bits) char c0 = (char)(0xd800 + (ch >> 10)); // Top 10 bits. if (cntr < strLen) { char c1 = str.charAt(cntr); if (c0 != c1) return c0 - c1; } else return 1; cntr++; c0 = (char)(0xdc00 + (ch & 0x3ff)); // Bottom 10 bits. if (cntr < strLen) { char c1 = str.charAt(cntr); if (c0 != c1) return c0 - c1; } else return 1; cntr++; } else if (ch >= 0xd800 && ch < 0xe000) // Not allowed to encode the surrogate range directly. throw new IgniteException("Illegal UTF-8 sequence."); else { // Normal case. char c0 = (char)ch; if (cntr < strLen) { char c1 = str.charAt(cntr); if (c0 != c1) return c0 - c1; } else return 1; cntr++; } } // Check if we ran past the end without seeing an exception. if (pos > len) throw new IgniteException("Illegal UTF-8 sequence."); return cntr - strLen; } /** * @param ch UTF-8 byte. * @throws IgniteException In case of error. */ private static void checkByte(int ch) { if ((ch & 0xc0) != 0x80) throw new IgniteException("Illegal UTF-8 sequence."); } /** * @param ch UTF-8 byte. * @param minVal Minimum value. * @throws IgniteException In case of error. */ private static void checkMinimal(int ch, int minVal) { if (ch >= minVal) return; throw new IgniteException("Illegal UTF-8 sequence."); } }