/* * 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.solr.util.hll; /** * A collection of utilities to work with numbers. */ class NumberUtil { // loge(2) (log-base e of 2) public static final double LOGE_2 = 0.6931471805599453; // ************************************************************************ /** * Computes the <code>log2</code> (log-base-two) of the specified value. * * @param value the <code>double</code> for which the <code>log2</code> is * desired. * @return the <code>log2</code> of the specified value */ public static double log2(final double value) { // REF: http://en.wikipedia.org/wiki/Logarithmic_scale (conversion of bases) return Math.log(value) / LOGE_2; } // ======================================================================== // the hex characters private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; // ------------------------------------------------------------------------ /** * Converts the specified array of <code>byte</code>s into a string of * hex characters (low <code>byte</code> first). * * @param bytes the array of <code>byte</code>s that are to be converted. * This cannot be <code>null</code> though it may be empty. * @param offset the offset in <code>bytes</code> at which the bytes will * be taken. This cannot be negative and must be less than * <code>bytes.length - 1</code>. * @param count the number of bytes to be retrieved from the specified array. * This cannot be negative. If greater than <code>bytes.length - offset</code> * then that value is used. * @return a string of at most <code>count</code> characters that represents * the specified byte array in hex. This will never be <code>null</code> * though it may be empty if <code>bytes</code> is empty or <code>count</code> * is zero. * @throws IllegalArgumentException if <code>offset</code> is greater than * or equal to <code>bytes.length</code>. * @see #fromHex(String, int, int) */ public static String toHex(final byte[] bytes, final int offset, final int count) { if(offset >= bytes.length) throw new IllegalArgumentException("Offset is greater than the length (" + offset + " >= " + bytes.length + ").")/*by contract*/; final int byteCount = Math.min( (bytes.length - offset), count); final int upperBound = byteCount + offset; final char[] chars = new char[byteCount * 2/*two chars per byte*/]; int charIndex = 0; for(int i=offset; i<upperBound; i++) { final byte value = bytes[i]; chars[charIndex++] = HEX[(value >>> 4) & 0x0F]; chars[charIndex++] = HEX[value & 0x0F]; } return new String(chars); } /** * Converts the specified array of hex characters into an array of <code>byte</code>s * (low <code>byte</code> first). * * @param string the string of hex characters to be converted into <code>byte</code>s. * This cannot be <code>null</code> though it may be blank. * @param offset the offset in the string at which the characters will be * taken. This cannot be negative and must be less than <code>string.length() - 1</code>. * @param count the number of characters to be retrieved from the specified * string. This cannot be negative and must be divisible by two * (since there are two characters per <code>byte</code>). * @return the array of <code>byte</code>s that were converted from the * specified string (in the specified range). This will never be * <code>null</code> though it may be empty if <code>string</code> * is empty or <code>count</code> is zero. * @throws IllegalArgumentException if <code>offset</code> is greater than * or equal to <code>string.length()</code> or if <code>count</code> * is not divisible by two. * @see #toHex(byte[], int, int) */ public static byte[] fromHex(final String string, final int offset, final int count) { if(offset >= string.length()) throw new IllegalArgumentException("Offset is greater than the length (" + offset + " >= " + string.length() + ").")/*by contract*/; if( (count & 0x01) != 0) throw new IllegalArgumentException("Count is not divisible by two (" + count + ").")/*by contract*/; final int charCount = Math.min((string.length() - offset), count); final int upperBound = offset + charCount; final byte[] bytes = new byte[charCount >>> 1/*aka /2*/]; int byteIndex = 0/*beginning*/; for(int i=offset; i<upperBound; i+=2) { bytes[byteIndex++] = (byte)(( (digit(string.charAt(i)) << 4) | digit(string.charAt(i + 1))) & 0xFF); } return bytes; } // ------------------------------------------------------------------------ /** * @param character a hex character to be converted to a <code>byte</code>. * This cannot be a character other than [a-fA-F0-9]. * @return the value of the specified character. This will be a value <code>0</code> * through <code>15</code>. * @throws IllegalArgumentException if the specified character is not in * [a-fA-F0-9] */ private static final int digit(final char character) { switch(character) { case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; case 'a': case 'A': return 10; case 'b': case 'B': return 11; case 'c': case 'C': return 12; case 'd': case 'D': return 13; case 'e': case 'E': return 14; case 'f': case 'F': return 15; default: throw new IllegalArgumentException("Character is not in [a-fA-F0-9] ('" + character + "')."); } } }