/**
* 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.hadoop.hbase.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Random;
import junit.framework.TestCase;
import org.apache.hadoop.hbase.SmallTests;
import org.junit.Assert;
import org.junit.experimental.categories.Category;
@Category(SmallTests.class)
public class TestBytes extends TestCase {
public void testNullHashCode() {
byte [] b = null;
Exception ee = null;
try {
Bytes.hashCode(b);
} catch (Exception e) {
ee = e;
}
assertNotNull(ee);
}
public void testSplit() throws Exception {
byte [] lowest = Bytes.toBytes("AAA");
byte [] middle = Bytes.toBytes("CCC");
byte [] highest = Bytes.toBytes("EEE");
byte [][] parts = Bytes.split(lowest, highest, 1);
for (int i = 0; i < parts.length; i++) {
System.out.println(Bytes.toString(parts[i]));
}
assertEquals(3, parts.length);
assertTrue(Bytes.equals(parts[1], middle));
// Now divide into three parts. Change highest so split is even.
highest = Bytes.toBytes("DDD");
parts = Bytes.split(lowest, highest, 2);
for (int i = 0; i < parts.length; i++) {
System.out.println(Bytes.toString(parts[i]));
}
assertEquals(4, parts.length);
// Assert that 3rd part is 'CCC'.
assertTrue(Bytes.equals(parts[2], middle));
}
public void testSplit2() throws Exception {
// More split tests.
byte [] lowest = Bytes.toBytes("http://A");
byte [] highest = Bytes.toBytes("http://z");
byte [] middle = Bytes.toBytes("http://]");
byte [][] parts = Bytes.split(lowest, highest, 1);
for (int i = 0; i < parts.length; i++) {
System.out.println(Bytes.toString(parts[i]));
}
assertEquals(3, parts.length);
assertTrue(Bytes.equals(parts[1], middle));
}
public void testSplit3() throws Exception {
// Test invalid split cases
byte [] low = { 1, 1, 1 };
byte [] high = { 1, 1, 3 };
// If swapped, should throw IAE
try {
Bytes.split(high, low, 1);
assertTrue("Should not be able to split if low > high", false);
} catch(IllegalArgumentException iae) {
// Correct
}
// Single split should work
byte [][] parts = Bytes.split(low, high, 1);
for (int i = 0; i < parts.length; i++) {
System.out.println("" + i + " -> " + Bytes.toStringBinary(parts[i]));
}
assertTrue("Returned split should have 3 parts but has " + parts.length, parts.length == 3);
// If split more than once, this should fail
parts = Bytes.split(low, high, 2);
assertTrue("Returned split but should have failed", parts == null);
// Split 0 times should throw IAE
try {
parts = Bytes.split(low, high, 0);
assertTrue("Should not be able to split 0 times", false);
} catch(IllegalArgumentException iae) {
// Correct
}
}
public void testToInt() throws Exception {
int [] ints = {-1, 123, Integer.MIN_VALUE, Integer.MAX_VALUE};
for (int i = 0; i < ints.length; i++) {
byte [] b = Bytes.toBytes(ints[i]);
assertEquals(ints[i], Bytes.toInt(b));
byte [] b2 = bytesWithOffset(b);
assertEquals(ints[i], Bytes.toInt(b2, 1));
assertEquals(ints[i], Bytes.toInt(b2, 1, Bytes.SIZEOF_INT));
}
}
public void testToLong() throws Exception {
long [] longs = {-1l, 123l, Long.MIN_VALUE, Long.MAX_VALUE};
for (int i = 0; i < longs.length; i++) {
byte [] b = Bytes.toBytes(longs[i]);
assertEquals(longs[i], Bytes.toLong(b));
byte [] b2 = bytesWithOffset(b);
assertEquals(longs[i], Bytes.toLong(b2, 1));
assertEquals(longs[i], Bytes.toLong(b2, 1, Bytes.SIZEOF_LONG));
}
}
public void testToFloat() throws Exception {
float [] floats = {-1f, 123.123f, Float.MAX_VALUE};
for (int i = 0; i < floats.length; i++) {
byte [] b = Bytes.toBytes(floats[i]);
assertEquals(floats[i], Bytes.toFloat(b));
byte [] b2 = bytesWithOffset(b);
assertEquals(floats[i], Bytes.toFloat(b2, 1));
}
}
public void testToDouble() throws Exception {
double [] doubles = {Double.MIN_VALUE, Double.MAX_VALUE};
for (int i = 0; i < doubles.length; i++) {
byte [] b = Bytes.toBytes(doubles[i]);
assertEquals(doubles[i], Bytes.toDouble(b));
byte [] b2 = bytesWithOffset(b);
assertEquals(doubles[i], Bytes.toDouble(b2, 1));
}
}
public void testToBigDecimal() throws Exception {
BigDecimal [] decimals = {new BigDecimal("-1"), new BigDecimal("123.123"),
new BigDecimal("123123123123")};
for (int i = 0; i < decimals.length; i++) {
byte [] b = Bytes.toBytes(decimals[i]);
assertEquals(decimals[i], Bytes.toBigDecimal(b));
byte [] b2 = bytesWithOffset(b);
assertEquals(decimals[i], Bytes.toBigDecimal(b2, 1, b.length));
}
}
private byte [] bytesWithOffset(byte [] src) {
// add one byte in front to test offset
byte [] result = new byte[src.length + 1];
result[0] = (byte) 0xAA;
System.arraycopy(src, 0, result, 1, src.length);
return result;
}
public void testToBytesForByteBuffer() {
byte[] array = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
ByteBuffer target = ByteBuffer.wrap(array);
target.position(2);
target.limit(7);
byte[] actual = Bytes.toBytes(target);
byte[] expected = { 0, 1, 2, 3, 4, 5, 6 };
assertTrue(Arrays.equals(expected, actual));
assertEquals(2, target.position());
assertEquals(7, target.limit());
ByteBuffer target2 = target.slice();
assertEquals(0, target2.position());
assertEquals(5, target2.limit());
byte[] actual2 = Bytes.toBytes(target2);
byte[] expected2 = { 2, 3, 4, 5, 6 };
assertTrue(Arrays.equals(expected2, actual2));
assertEquals(0, target2.position());
assertEquals(5, target2.limit());
}
public void testGetBytesForByteBuffer() {
byte[] array = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
ByteBuffer target = ByteBuffer.wrap(array);
target.position(2);
target.limit(7);
byte[] actual = Bytes.getBytes(target);
byte[] expected = { 2, 3, 4, 5, 6 };
assertTrue(Arrays.equals(expected, actual));
assertEquals(2, target.position());
assertEquals(7, target.limit());
}
public void testToStringBinaryForBytes() {
byte[] array = { '0', '9', 'a', 'z', 'A', 'Z', '@', 1 };
String actual = Bytes.toStringBinary(array);
String expected = "09azAZ@\\x01";
assertEquals(expected, actual);
String actual2 = Bytes.toStringBinary(array, 2, 3);
String expected2 = "azA";
assertEquals(expected2, actual2);
}
public void testToStringBinaryForArrayBasedByteBuffer() {
byte[] array = { '0', '9', 'a', 'z', 'A', 'Z', '@', 1 };
ByteBuffer target = ByteBuffer.wrap(array);
String actual = Bytes.toStringBinary(target);
String expected = "09azAZ@\\x01";
assertEquals(expected, actual);
}
public void testToStringBinaryForReadOnlyByteBuffer() {
byte[] array = { '0', '9', 'a', 'z', 'A', 'Z', '@', 1 };
ByteBuffer target = ByteBuffer.wrap(array).asReadOnlyBuffer();
String actual = Bytes.toStringBinary(target);
String expected = "09azAZ@\\x01";
assertEquals(expected, actual);
}
public void testBinarySearch() throws Exception {
byte [][] arr = {
{1},
{3},
{5},
{7},
{9},
{11},
{13},
{15},
};
byte [] key1 = {3,1};
byte [] key2 = {4,9};
byte [] key2_2 = {4};
byte [] key3 = {5,11};
byte [] key4 = {0};
byte [] key5 = {2};
assertEquals(1, Bytes.binarySearch(arr, key1, 0, 1,
Bytes.BYTES_RAWCOMPARATOR));
assertEquals(0, Bytes.binarySearch(arr, key1, 1, 1,
Bytes.BYTES_RAWCOMPARATOR));
assertEquals(-(2+1), Arrays.binarySearch(arr, key2_2,
Bytes.BYTES_COMPARATOR));
assertEquals(-(2+1), Bytes.binarySearch(arr, key2, 0, 1,
Bytes.BYTES_RAWCOMPARATOR));
assertEquals(4, Bytes.binarySearch(arr, key2, 1, 1,
Bytes.BYTES_RAWCOMPARATOR));
assertEquals(2, Bytes.binarySearch(arr, key3, 0, 1,
Bytes.BYTES_RAWCOMPARATOR));
assertEquals(5, Bytes.binarySearch(arr, key3, 1, 1,
Bytes.BYTES_RAWCOMPARATOR));
assertEquals(-1,
Bytes.binarySearch(arr, key4, 0, 1, Bytes.BYTES_RAWCOMPARATOR));
assertEquals(-2,
Bytes.binarySearch(arr, key5, 0, 1, Bytes.BYTES_RAWCOMPARATOR));
// Search for values to the left and to the right of each item in the array.
for (int i = 0; i < arr.length; ++i) {
assertEquals(-(i + 1), Bytes.binarySearch(arr,
new byte[] { (byte) (arr[i][0] - 1) }, 0, 1,
Bytes.BYTES_RAWCOMPARATOR));
assertEquals(-(i + 2), Bytes.binarySearch(arr,
new byte[] { (byte) (arr[i][0] + 1) }, 0, 1,
Bytes.BYTES_RAWCOMPARATOR));
}
}
public void testToStringBytesBinaryReversible() {
// let's run test with 1000 randomly generated byte arrays
Random rand = new Random(System.currentTimeMillis());
byte[] randomBytes = new byte[1000];
for (int i = 0; i < 1000; i++) {
rand.nextBytes(randomBytes);
verifyReversibleForBytes(randomBytes);
}
// some specific cases
verifyReversibleForBytes(new byte[] {});
verifyReversibleForBytes(new byte[] {'\\', 'x', 'A', 'D'});
verifyReversibleForBytes(new byte[] {'\\', 'x', 'A', 'D', '\\'});
}
private void verifyReversibleForBytes(byte[] originalBytes) {
String convertedString = Bytes.toStringBinary(originalBytes);
byte[] convertedBytes = Bytes.toBytesBinary(convertedString);
if (Bytes.compareTo(originalBytes, convertedBytes) != 0) {
fail("Not reversible for\nbyte[]: " + Arrays.toString(originalBytes) +
",\nStringBinary: " + convertedString);
}
}
public void testStartsWith() {
assertTrue(Bytes.startsWith(Bytes.toBytes("hello"), Bytes.toBytes("h")));
assertTrue(Bytes.startsWith(Bytes.toBytes("hello"), Bytes.toBytes("")));
assertTrue(Bytes.startsWith(Bytes.toBytes("hello"), Bytes.toBytes("hello")));
assertFalse(Bytes.startsWith(Bytes.toBytes("hello"), Bytes.toBytes("helloworld")));
assertFalse(Bytes.startsWith(Bytes.toBytes(""), Bytes.toBytes("hello")));
}
public void testIncrementBytes() throws IOException {
assertTrue(checkTestIncrementBytes(10, 1));
assertTrue(checkTestIncrementBytes(12, 123435445));
assertTrue(checkTestIncrementBytes(124634654, 1));
assertTrue(checkTestIncrementBytes(10005460, 5005645));
assertTrue(checkTestIncrementBytes(1, -1));
assertTrue(checkTestIncrementBytes(10, -1));
assertTrue(checkTestIncrementBytes(10, -5));
assertTrue(checkTestIncrementBytes(1005435000, -5));
assertTrue(checkTestIncrementBytes(10, -43657655));
assertTrue(checkTestIncrementBytes(-1, 1));
assertTrue(checkTestIncrementBytes(-26, 5034520));
assertTrue(checkTestIncrementBytes(-10657200, 5));
assertTrue(checkTestIncrementBytes(-12343250, 45376475));
assertTrue(checkTestIncrementBytes(-10, -5));
assertTrue(checkTestIncrementBytes(-12343250, -5));
assertTrue(checkTestIncrementBytes(-12, -34565445));
assertTrue(checkTestIncrementBytes(-1546543452, -34565445));
}
private static boolean checkTestIncrementBytes(long val, long amount)
throws IOException {
byte[] value = Bytes.toBytes(val);
byte [] testValue = {-1, -1, -1, -1, -1, -1, -1, -1};
if (value[0] > 0) {
testValue = new byte[Bytes.SIZEOF_LONG];
}
System.arraycopy(value, 0, testValue, testValue.length - value.length,
value.length);
long incrementResult = Bytes.toLong(Bytes.incrementBytes(value, amount));
return (Bytes.toLong(testValue) + amount) == incrementResult;
}
public void testFixedSizeString() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
Bytes.writeStringFixedSize(dos, "Hello", 5);
Bytes.writeStringFixedSize(dos, "World", 18);
Bytes.writeStringFixedSize(dos, "", 9);
try {
// Use a long dash which is three bytes in UTF-8. If encoding happens
// using ISO-8859-1, this will fail.
Bytes.writeStringFixedSize(dos, "Too\u2013Long", 9);
fail("Exception expected");
} catch (IOException ex) {
assertEquals(
"Trying to write 10 bytes (Too\\xE2\\x80\\x93Long) into a field of " +
"length 9", ex.getMessage());
}
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
DataInputStream dis = new DataInputStream(bais);
assertEquals("Hello", Bytes.readStringFixedSize(dis, 5));
assertEquals("World", Bytes.readStringFixedSize(dis, 18));
assertEquals("", Bytes.readStringFixedSize(dis, 9));
}
public void testCopy() throws Exception {
byte [] bytes = Bytes.toBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
byte [] copy = Bytes.copy(bytes);
assertFalse(bytes == copy);
assertTrue(Bytes.equals(bytes, copy));
}
public void testToBytesBinaryTrailingBackslashes() throws Exception {
try {
Bytes.toBytesBinary("abc\\x00\\x01\\");
} catch (StringIndexOutOfBoundsException ex) {
fail("Illegal string access: " + ex.getMessage());
}
}
public void testToStringBinary_toBytesBinary_Reversable() throws Exception {
String bytes = Bytes.toStringBinary(Bytes.toBytes(2.17));
assertEquals(2.17, Bytes.toDouble(Bytes.toBytesBinary(bytes)), 0);
}
public void testUnsignedBinarySearch(){
byte[] bytes = new byte[]{0,5,123,127,-128,-100,-1};
Assert.assertEquals(Bytes.unsignedBinarySearch(bytes, 0, bytes.length, (byte)5), 1);
Assert.assertEquals(Bytes.unsignedBinarySearch(bytes, 0, bytes.length, (byte)127), 3);
Assert.assertEquals(Bytes.unsignedBinarySearch(bytes, 0, bytes.length, (byte)-128), 4);
Assert.assertEquals(Bytes.unsignedBinarySearch(bytes, 0, bytes.length, (byte)-100), 5);
Assert.assertEquals(Bytes.unsignedBinarySearch(bytes, 0, bytes.length, (byte)-1), 6);
Assert.assertEquals(Bytes.unsignedBinarySearch(bytes, 0, bytes.length, (byte)2), -1-1);
Assert.assertEquals(Bytes.unsignedBinarySearch(bytes, 0, bytes.length, (byte)-5), -6-1);
}
public void testUnsignedIncrement(){
byte[] a = Bytes.toBytes(0);
int a2 = Bytes.toInt(Bytes.unsignedCopyAndIncrement(a), 0);
Assert.assertTrue(a2==1);
byte[] b = Bytes.toBytes(-1);
byte[] actual = Bytes.unsignedCopyAndIncrement(b);
Assert.assertNotSame(b, actual);
byte[] expected = new byte[]{1,0,0,0,0};
Assert.assertArrayEquals(expected, actual);
byte[] c = Bytes.toBytes(255);//should wrap to the next significant byte
int c2 = Bytes.toInt(Bytes.unsignedCopyAndIncrement(c), 0);
Assert.assertTrue(c2==256);
}
}