Skip to content

Commit

Permalink
Merge pull request #657 from manolama/utils
Browse files Browse the repository at this point in the history
[core] Add byte to double and long conversion methods to Utils.
  • Loading branch information
busbey committed Mar 21, 2016
2 parents 432e958 + ac76572 commit 4d44594
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 1 deletion.
63 changes: 62 additions & 1 deletion core/src/main/java/com/yahoo/ycsb/Utils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2010 Yahoo! Inc. All rights reserved.
* Copyright (c) 2010 Yahoo! Inc., 2016 YCSB contributors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You
Expand Down Expand Up @@ -113,4 +113,65 @@ public static long FNVhash64(long val)
}
return Math.abs(hashval);
}

/**
* Reads a big-endian 8-byte long from an offset in the given array.
* @param bytes The array to read from.
* @return A long integer.
* @throws IndexOutOfBoundsException if the byte array is too small.
* @throws NullPointerException if the byte array is null.
*/
public static long bytesToLong(final byte[] bytes) {
return (bytes[0] & 0xFFL) << 56
| (bytes[1] & 0xFFL) << 48
| (bytes[2] & 0xFFL) << 40
| (bytes[3] & 0xFFL) << 32
| (bytes[4] & 0xFFL) << 24
| (bytes[5] & 0xFFL) << 16
| (bytes[6] & 0xFFL) << 8
| (bytes[7] & 0xFFL) << 0;
}

/**
* Writes a big-endian 8-byte long at an offset in the given array.
* @param val The value to encode.
* @throws IndexOutOfBoundsException if the byte array is too small.
*/
public static byte[] longToBytes(final long val) {
final byte[] bytes = new byte[8];
bytes[0] = (byte) (val >>> 56);
bytes[1] = (byte) (val >>> 48);
bytes[2] = (byte) (val >>> 40);
bytes[3] = (byte) (val >>> 32);
bytes[4] = (byte) (val >>> 24);
bytes[5] = (byte) (val >>> 16);
bytes[6] = (byte) (val >>> 8);
bytes[7] = (byte) (val >>> 0);
return bytes;
}

/**
* Parses the byte array into a double.
* The byte array must be at least 8 bytes long and have been encoded using
* {@link #doubleToBytes}. If the array is longer than 8 bytes, only the
* first 8 bytes are parsed.
* @param bytes The byte array to parse, at least 8 bytes.
* @return A double value read from the byte array.
* @throws IllegalArgumentException if the byte array is not 8 bytes wide.
*/
public static double bytesToDouble(final byte[] bytes) {
if (bytes.length < 8) {
throw new IllegalArgumentException("Byte array must be 8 bytes wide.");
}
return Double.longBitsToDouble(bytesToLong(bytes));
}

/**
* Encodes the double value as an 8 byte array.
* @param val The double value to encode.
* @return A byte array of length 8.
*/
public static byte[] doubleToBytes(final double val) {
return longToBytes(Double.doubleToRawLongBits(val));
}
}
129 changes: 129 additions & 0 deletions core/src/test/java/com/yahoo/ycsb/TestUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/**
* Copyright (c) 2016 YCSB contributors. All rights reserved.
*
* Licensed 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. See accompanying
* LICENSE file.
*/

package com.yahoo.ycsb;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;

import java.util.Arrays;

import org.testng.annotations.Test;

public class TestUtils {

@Test
public void bytesToFromLong() throws Exception {
byte[] bytes = new byte[8];
assertEquals(Utils.bytesToLong(bytes), 0L);
assertArrayEquals(Utils.longToBytes(0), bytes);

bytes[7] = 1;
assertEquals(Utils.bytesToLong(bytes), 1L);
assertArrayEquals(Utils.longToBytes(1L), bytes);

bytes = new byte[] { 127, -1, -1, -1, -1, -1, -1, -1 };
assertEquals(Utils.bytesToLong(bytes), Long.MAX_VALUE);
assertArrayEquals(Utils.longToBytes(Long.MAX_VALUE), bytes);

bytes = new byte[] { -128, 0, 0, 0, 0, 0, 0, 0 };
assertEquals(Utils.bytesToLong(bytes), Long.MIN_VALUE);
assertArrayEquals(Utils.longToBytes(Long.MIN_VALUE), bytes);

bytes = new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF };
assertEquals(Utils.bytesToLong(bytes), -1L);
assertArrayEquals(Utils.longToBytes(-1L), bytes);

// if the array is too long we just skip the remainder
bytes = new byte[] { 0, 0, 0, 0, 0, 0, 0, 1, 42, 42, 42 };
assertEquals(Utils.bytesToLong(bytes), 1L);
}

@Test
public void bytesToFromDouble() throws Exception {
byte[] bytes = new byte[8];
assertEquals(Utils.bytesToDouble(bytes), 0, 0.0001);
assertArrayEquals(Utils.doubleToBytes(0), bytes);

bytes = new byte[] { 63, -16, 0, 0, 0, 0, 0, 0 };
assertEquals(Utils.bytesToDouble(bytes), 1, 0.0001);
assertArrayEquals(Utils.doubleToBytes(1), bytes);

bytes = new byte[] { -65, -16, 0, 0, 0, 0, 0, 0 };
assertEquals(Utils.bytesToDouble(bytes), -1, 0.0001);
assertArrayEquals(Utils.doubleToBytes(-1), bytes);

bytes = new byte[] { 127, -17, -1, -1, -1, -1, -1, -1 };
assertEquals(Utils.bytesToDouble(bytes), Double.MAX_VALUE, 0.0001);
assertArrayEquals(Utils.doubleToBytes(Double.MAX_VALUE), bytes);

bytes = new byte[] { 0, 0, 0, 0, 0, 0, 0, 1 };
assertEquals(Utils.bytesToDouble(bytes), Double.MIN_VALUE, 0.0001);
assertArrayEquals(Utils.doubleToBytes(Double.MIN_VALUE), bytes);

bytes = new byte[] { 127, -8, 0, 0, 0, 0, 0, 0 };
assertTrue(Double.isNaN(Utils.bytesToDouble(bytes)));
assertArrayEquals(Utils.doubleToBytes(Double.NaN), bytes);

bytes = new byte[] { 63, -16, 0, 0, 0, 0, 0, 0, 42, 42, 42 };
assertEquals(Utils.bytesToDouble(bytes), 1, 0.0001);
}

@Test (expectedExceptions = NullPointerException.class)
public void bytesToLongNull() throws Exception {
Utils.bytesToLong(null);
}

@Test (expectedExceptions = IndexOutOfBoundsException.class)
public void bytesToLongTooShort() throws Exception {
Utils.bytesToLong(new byte[] { 0, 0, 0, 0, 0, 0, 0 });
}

@Test (expectedExceptions = IllegalArgumentException.class)
public void bytesToDoubleTooShort() throws Exception {
Utils.bytesToDouble(new byte[] { 0, 0, 0, 0, 0, 0, 0 });
}

/**
* Since this version of TestNG doesn't appear to have an assertArrayEquals,
* this will compare the two to make sure they're the same.
* @param actual Actual array to validate
* @param expected What the array should contain
* @throws AssertionError if the test fails.
*/
public void assertArrayEquals(final byte[] actual, final byte[] expected) {
if (actual == null && expected != null) {
throw new AssertionError("Expected " + Arrays.toString(expected) +
" but found [null]");
}
if (actual != null && expected == null) {
throw new AssertionError("Expected [null] but found " +
Arrays.toString(actual));
}
if (actual.length != expected.length) {
throw new AssertionError("Expected length " + expected.length +
" but found " + actual.length);
}
for (int i = 0; i < expected.length; i++) {
if (actual[i] != expected[i]) {
throw new AssertionError("Expected byte [" + expected[i] +
"] at index " + i + " but found [" + actual[i] + "]");
}
}
}
}

0 comments on commit 4d44594

Please sign in to comment.