/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on May 7, 2011
*/
package com.bigdata.btree;
import it.unimi.dsi.io.InputBitStream;
import java.io.IOException;
import java.util.Formatter;
import java.util.Random;
import com.bigdata.util.Bytes;
import com.bigdata.util.BytesUtil;
import junit.framework.TestCase2;
/**
* Unit tests for {@link BytesUtil#getBits(byte[], int, int)}
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class TestGetBitsFromByteArray extends TestCase2 {
public TestGetBitsFromByteArray() {
}
public TestGetBitsFromByteArray(String name) {
super(name);
}
public void test_getBitsFromByteArray_correctRejection_nullArg() {
try {
BytesUtil.getBits(null, 0, 0);
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
}
/**
* offset may be zero, but not negative.
*/
public void test_getBitsFromByteArray_correctRejection_off_and_len_01() {
BytesUtil.getBits(new byte[1], 0, 0); // ok
try {
BytesUtil.getBits(new byte[1], -1, 0); // no good
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
}
/**
* length may be zero, but not negative.
*/
public void test_getBitsFromByteArray_correctRejection_off_and_len_02() {
BytesUtil.getBits(new byte[1], 0, 0); // ok
try {
BytesUtil.getBits(new byte[1], 0, -1); // no good
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
}
/**
* length may address all bits (8) in a single byte, but not the 9th bit.
*/
public void test_getBitsFromByteArray_correctRejection_off_and_len_03() {
BytesUtil.getBits(new byte[1], 0, 8); // ok
try {
BytesUtil.getBits(new byte[1], 0, 8 + 1); // no good
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
}
/**
* length may address all bits (32) in 4 bytes, but not 33 bits since the
* return value would be larger than an int32.
*/
public void test_getBitsFromByteArray_correctRejection_off_and_len_04() {
BytesUtil.getBits(new byte[4], 0, 32); // ok
try {
BytesUtil.getBits(new byte[4], 0, 33); // no good
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
}
/**
* length may address (32) bits in 5 bytes, but not 33 bits since the return
* value would be larger than an int32.
*/
public void test_getBitsFromByteArray_correctRejection_off_and_len_05() {
BytesUtil.getBits(new byte[5], 0, 32); // ok
try {
BytesUtil.getBits(new byte[5], 0, 33); // no good
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
}
/**
* You can request a zero length slice starting at bit zero of a zero length
* byte[].
*/
public void test_getBitsFromByteArray_zeroLength() {
assertEquals(0, BytesUtil.getBits(new byte[0], 0, 0));
}
/** byte[4] (32-bits) with all bits zero. */
public void test_getBitsFromByteArray_01() {
final byte[] b = new byte[4];
assertEquals(0x00000000, getBits(b, 0/* off */, 0/* len */));
assertEquals(0x00000000, getBits(b, 0/* off */, 1/* len */));
assertEquals(0x00000000, getBits(b, 0/* off */, 31/* len */));
assertEquals(0x00000000, getBits(b, 0/* off */, 32/* len */));
}
/** byte[4] (32-bits) with LSB ONE. */
public void test_getBitsFromByteArray_02() {
final byte[] b = new byte[4];
BytesUtil.setBit(b, 31/* off */, true);
assertEquals(0x00000000, getBits(b, 0/* off */, 0/* len */));
assertEquals(0x00000000, getBits(b, 0/* off */, 1/* len */));
assertEquals(0x00000000, getBits(b, 0/* off */, 31/* len */));//x
assertEquals(0x00000001, getBits(b, 0/* off */, 32/* len */));
assertEquals(0x00000001, getBits(b, 31/* off */, 1/* len */));//x
assertEquals(0x00000001, getBits(b, 30/* off */, 2/* len */));
}
/**
* byte[4] (32-bits) with bit ONE (1) set.
*/
public void test_getBitsFromByteArray_03() {
final byte[] b = new byte[4];
BytesUtil.setBit(b, 1/* off */, true);
assertEquals(0x00000000, getBits(b, 0/* off */, 0/* len */));
assertEquals(0x00000000, getBits(b, 0/* off */, 1/* len */));
assertEquals(0x00000001, getBits(b, 0/* off */, 2/* len */));
assertEquals(0x00000002, getBits(b, 1/* off */, 2/* len */));
assertEquals(0x00000004, getBits(b, 1/* off */, 3/* len */));
assertEquals(0x00000001, getBits(b, 1/* off */, 1/* len */));
assertEquals(0x40000000, getBits(b, 0/* off */, 32/* len */));
assertEquals(0x20000000, getBits(b, 0/* off */, 31/* len */));
assertEquals(0x10000000, getBits(b, 0/* off */, 30/* len */));
assertEquals(0x08000000, getBits(b, 0/* off */, 29/* len */));
}
/**
* byte[4] (32-bits) with MSB ONE (this test case is the mostly likely to
* run a foul of a sign bit extension).
*/
public void test_getBitsFromByteArray_04() {
final byte[] b = new byte[4];
BytesUtil.setBit(b, 0/* off */, true);
assertEquals(0x00000000, getBits(b, 0/* off */, 0/* len */));
assertEquals(0x00000001, getBits(b, 0/* off */, 1/* len */));
assertEquals(0x00000002, getBits(b, 0/* off */, 2/* len */));
assertEquals(0x00000004, getBits(b, 0/* off */, 3/* len */));
assertEquals(0x00000008, getBits(b, 0/* off */, 4/* len */));
assertEquals(0x00000000, getBits(b, 1/* off */, 0/* len */));
assertEquals(0x00000000, getBits(b, 1/* off */, 1/* len */));
assertEquals(0x00000000, getBits(b, 1/* off */, 2/* len */));
assertEquals(0x00000000, getBits(b, 1/* off */, 3/* len */));
}
/**
* byte[4] (32-bits) with slice in the 2nd byte.
*/
public void test_getBitsFromByteArray_05() {
final byte[] b = new byte[4];
// four bits on starting at bit 11.
BytesUtil.setBit(b, 11/* offset */, true);
BytesUtil.setBit(b, 12/* offset */, true);
BytesUtil.setBit(b, 13/* offset */, true);
BytesUtil.setBit(b, 14/* offset */, true);
/*
* Test with a window extending from bit zero with a variety of bit
* lengths ranging from an end bit index one less than the first ONE bit
* through an end bit index beyond the last ONE bit.
*/
assertEquals(0x00000000, getBits(b, 0/* off */, 11/* len */));
assertEquals(0x00000001, getBits(b, 0/* off */, 12/* len */));
assertEquals(0x00000003, getBits(b, 0/* off */, 13/* len */));
assertEquals(0x00000007, getBits(b, 0/* off */, 14/* len */));
assertEquals(0x0000000f, getBits(b, 0/* off */, 15/* len */));
assertEquals(0x0000001e, getBits(b, 0/* off */, 16/* len */));
assertEquals(0x0000003c, getBits(b, 0/* off */, 17/* len */));
assertEquals(0x00000078, getBits(b, 0/* off */, 18/* len */));
assertEquals(0x000000f0, getBits(b, 0/* off */, 19/* len */));
/*
* Test with a 4-bit window that slides over the ONE bits. The initial
* window is to the left of the first ONE bit. The window slides right
* by one bit position for each assertion.
*/
assertEquals(0x00000000, getBits(b, 7/* off */, 4/* len */));
assertEquals(0x00000001, getBits(b, 8/* off */, 4/* len */));
assertEquals(0x00000003, getBits(b, 9/* off */, 4/* len */));
assertEquals(0x00000007, getBits(b,10/* off */, 4/* len */));
assertEquals(0x0000000f, getBits(b,11/* off */, 4/* len */));
assertEquals(0x0000000e, getBits(b,12/* off */, 4/* len */));
assertEquals(0x0000000c, getBits(b,13/* off */, 4/* len */));
assertEquals(0x00000008, getBits(b,14/* off */, 4/* len */));
assertEquals(0x00000000, getBits(b,15/* off */, 4/* len */));
}
/**
* byte[2] (16-bits)
*
* @todo test slices from arrays with more than 4 bytes
*/
public void test_getBitsFromByteArray_06() {
final byte[] b = new byte[2];
// four bits on starting at bit 11.
BytesUtil.setBit(b, 11/* offset */, true);
BytesUtil.setBit(b, 12/* offset */, true);
BytesUtil.setBit(b, 13/* offset */, true);
BytesUtil.setBit(b, 14/* offset */, true);
/*
* Test with a window extending from bit zero with a variety of bit
* lengths ranging from an end bit index one less than the first ONE bit
* through an end bit index beyond the last ONE bit.
*/
assertEquals(0x00000000, getBits(b, 0/* off */, 11/* len */));
assertEquals(0x00000001, getBits(b, 0/* off */, 12/* len */));
assertEquals(0x00000003, getBits(b, 0/* off */, 13/* len */));
assertEquals(0x00000007, getBits(b, 0/* off */, 14/* len */));
assertEquals(0x0000000f, getBits(b, 0/* off */, 15/* len */));
assertEquals(0x0000001e, getBits(b, 0/* off */, 16/* len */));
/*
* Now that we have reached the last legal length verify that length 17
* is rejected.
*/
try {
getBits(b, 0/* off */, 17/* len */);
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
/*
* Now increase the offset while decreasing the length and step through
* a few more slices. These all see the FOUR (4) ON bits plus one
* trailing ZERO (0) bit.
*/
assertEquals(0x0000001e, getBits(b, 1/* off */, 15/* len */));
assertEquals(0x0000001e, getBits(b, 2/* off */, 14/* len */));
assertEquals(0x0000001e, getBits(b, 3/* off */, 13/* len */));
assertEquals(0x0000001e, getBits(b, 4/* off */, 12/* len */));
assertEquals(0x0000001e, getBits(b, 5/* off */, 11/* len */));
assertEquals(0x0000001e, getBits(b, 6/* off */, 10/* len */));
assertEquals(0x0000001e, getBits(b, 7/* off */, 9/* len */));
assertEquals(0x0000001e, getBits(b, 8/* off */, 8/* len */));
assertEquals(0x0000001e, getBits(b, 9/* off */, 7/* len */));
assertEquals(0x0000001e, getBits(b,10/* off */, 6/* len */));
assertEquals(0x0000001e, getBits(b,11/* off */, 5/* len */));
/*
* Continue to increase the offset while decreasing the length, but now
* we will start to loose the ONE bits on both sides as the window keeps
* sliding and shrinking.
*/
assertEquals(0x0000000e, getBits(b,12/* off */, 4/* len */));
assertEquals(0x00000006, getBits(b,13/* off */, 3/* len */));
assertEquals(0x00000002, getBits(b,14/* off */, 2/* len */));
assertEquals(0x00000000, getBits(b,15/* off */, 1/* len */));
/*
* This is also illegal (the starting offset is too large).
*/
try {
getBits(b,16/* off */, 1/* len */);
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
}
private int getBits(final byte[] a,final int off,final int len) {
final int ret = BytesUtil.getBits(a, off, len);
if (log.isInfoEnabled()) {
final StringBuilder sb = new StringBuilder();
final Formatter f = new Formatter(sb);
f.format("[%" + (a.length * 8) + "s] =(%2d:%2d)=> [%32s]",
BytesUtil.toBitString(a), off, len, Integer.toBinaryString(ret));
log.info(sb.toString());
}
return ret;
}
/**
* A stress test for compatibility with {@link InputBitStream}. An array is
* filled with random bits and the behavior of {@link InputBitStream} and
* {@link BytesUtil#getBits(byte[], int, int)} is compared on a number of
* randomly selected bit slices.
*
* @throws IOException
*/
public void test_stress_InputBitStream_compatible() throws IOException {
final Random r = new Random();
// #of iterations
final long limit = 1000000;
// Note: length is guaranteed to be LT int32 bits so [int] index is Ok.
// Note: + 4 since we will do [bitlen - 32] below. 4*8==32.
final int len = r.nextInt(Bytes.kilobyte32 * 8) + 4;
final int bitlen = len << 3;
// Fill array with random data.
final byte[] b = new byte[len];
r.nextBytes(b);
// wrap with InputBitStream.
final InputBitStream ibs = new InputBitStream(b);
for (int i = 0; i < limit; i++) {
// start of the bit slice.
final int sliceBitOff = r.nextInt(bitlen - 32);
final int bitsremaining = bitlen - sliceBitOff;
// allow any slice of between 1 and 32 bits length.
final int sliceBitLen = r.nextInt(Math.min(32, bitsremaining)) + 1;
assert sliceBitLen >= 1 && sliceBitLen <= 32;
// position the stream.
ibs.position(sliceBitOff);
final int v1 = ibs.readInt(sliceBitLen);
final int v2 = BytesUtil.getBits(b, sliceBitOff, sliceBitLen);
if (v1 != v2) {
fail("Expected=" + v1 + ", actual=" + v2 + ", trial=" + i
+ ", bitSlice(off=" + sliceBitOff + ", len="
+ sliceBitLen + ")" + ", arrayLen=" + b.length);
}
}
}
}