blob: d5122cf9c8f73b961f4c74e1942d46280846448a [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.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 + "').");
}
}
}