/** * Copyright The Apache Software Foundation * * 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.nio; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import org.apache.hadoop.hbase.testclassification.MiscTests; import org.apache.hadoop.hbase.testclassification.SmallTests; import org.apache.hadoop.hbase.util.ByteBufferUtils; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.ObjectIntPair; import org.junit.Test; import org.junit.experimental.categories.Category; @Category({ MiscTests.class, SmallTests.class }) public class TestMultiByteBuff { @Test public void testWritesAndReads() { // Absolute reads ByteBuffer bb1 = ByteBuffer.allocate(15); ByteBuffer bb2 = ByteBuffer.allocate(15); int i1 = 4; bb1.putInt(i1); long l1 = 45L, l2 = 100L, l3 = 12345L; bb1.putLong(l1); short s1 = 2; bb1.putShort(s1); byte[] b = Bytes.toBytes(l2); bb1.put(b, 0, 1); bb2.put(b, 1, 7); bb2.putLong(l3); MultiByteBuff mbb = new MultiByteBuff(bb1, bb2); assertEquals(l1, mbb.getLong(4)); assertEquals(l2, mbb.getLong(14)); assertEquals(l3, mbb.getLong(22)); assertEquals(i1, mbb.getInt(0)); assertEquals(s1, mbb.getShort(12)); // Relative reads assertEquals(i1, mbb.getInt()); assertEquals(l1, mbb.getLong()); assertEquals(s1, mbb.getShort()); assertEquals(l2, mbb.getLong()); assertEquals(l3, mbb.getLong()); // Absolute writes bb1 = ByteBuffer.allocate(15); bb2 = ByteBuffer.allocate(15); mbb = new MultiByteBuff(bb1, bb2); byte b1 = 5, b2 = 31; mbb.put(b1); mbb.putLong(l1); mbb.putInt(i1); mbb.putLong(l2); mbb.put(b2); mbb.position(mbb.position() + 2); try { mbb.putLong(l3); fail("'Should have thrown BufferOverflowException"); } catch (BufferOverflowException e) { } mbb.position(mbb.position() - 2); mbb.putLong(l3); mbb.rewind(); assertEquals(b1, mbb.get()); assertEquals(l1, mbb.getLong()); assertEquals(i1, mbb.getInt()); assertEquals(l2, mbb.getLong()); assertEquals(b2, mbb.get()); assertEquals(l3, mbb.getLong()); mbb.put(21, b1); mbb.position(21); assertEquals(b1, mbb.get()); mbb.put(b); assertEquals(l2, mbb.getLong(22)); } @Test public void testPutPrimitives() { ByteBuffer bb = ByteBuffer.allocate(10); SingleByteBuff s = new SingleByteBuff(bb); s.putLong(-4465109508325701663l); bb.rewind(); long long1 = bb.getLong(); assertEquals(long1, -4465109508325701663l); s.position(8); } @Test public void testArrayBasedMethods() { byte[] b = new byte[15]; ByteBuffer bb1 = ByteBuffer.wrap(b, 1, 10).slice(); ByteBuffer bb2 = ByteBuffer.allocate(15); ByteBuff mbb1 = new MultiByteBuff(bb1, bb2); assertFalse(mbb1.hasArray()); try { mbb1.array(); fail(); } catch (UnsupportedOperationException e) { } try { mbb1.arrayOffset(); fail(); } catch (UnsupportedOperationException e) { } mbb1 = new SingleByteBuff(bb1); assertTrue(mbb1.hasArray()); assertEquals(1, mbb1.arrayOffset()); assertEquals(b, mbb1.array()); mbb1 = new SingleByteBuff(ByteBuffer.allocateDirect(10)); assertFalse(mbb1.hasArray()); try { mbb1.array(); fail(); } catch (UnsupportedOperationException e) { } try { mbb1.arrayOffset(); fail(); } catch (UnsupportedOperationException e) { } } @Test public void testMarkAndResetWithMBB() { ByteBuffer bb1 = ByteBuffer.allocateDirect(15); ByteBuffer bb2 = ByteBuffer.allocateDirect(15); bb1.putInt(4); long l1 = 45L, l2 = 100L, l3 = 12345L; bb1.putLong(l1); bb1.putShort((short) 2); byte[] b = Bytes.toBytes(l2); bb1.put(b, 0, 1); bb2.put(b, 1, 7); bb2.putLong(l3); ByteBuff multi = new MultiByteBuff(bb1, bb2); assertEquals(4, multi.getInt()); assertEquals(l1, multi.getLong()); multi.mark(); assertEquals((short) 2, multi.getShort()); multi.reset(); assertEquals((short) 2, multi.getShort()); multi.mark(); assertEquals(l2, multi.getLong()); multi.reset(); assertEquals(l2, multi.getLong()); multi.mark(); assertEquals(l3, multi.getLong()); multi.reset(); assertEquals(l3, multi.getLong()); // Try absolute gets with mark and reset multi.mark(); assertEquals(l2, multi.getLong(14)); multi.reset(); assertEquals(l3, multi.getLong(22)); // Just reset to see what happens multi.reset(); assertEquals(l2, multi.getLong(14)); multi.mark(); assertEquals(l3, multi.getLong(22)); multi.reset(); } @Test public void testSkipNBytes() { ByteBuffer bb1 = ByteBuffer.allocate(15); ByteBuffer bb2 = ByteBuffer.allocate(15); bb1.putInt(4); long l1 = 45L, l2 = 100L, l3 = 12345L; bb1.putLong(l1); bb1.putShort((short) 2); byte[] b = Bytes.toBytes(l2); bb1.put(b, 0, 1); bb2.put(b, 1, 7); bb2.putLong(l3); MultiByteBuff multi = new MultiByteBuff(bb1, bb2); assertEquals(4, multi.getInt()); assertEquals(l1, multi.getLong()); multi.skip(10); assertEquals(l3, multi.getLong()); } @Test public void testMoveBack() { ByteBuffer bb1 = ByteBuffer.allocate(15); ByteBuffer bb2 = ByteBuffer.allocate(15); bb1.putInt(4); long l1 = 45L, l2 = 100L, l3 = 12345L; bb1.putLong(l1); bb1.putShort((short) 2); byte[] b = Bytes.toBytes(l2); bb1.put(b, 0, 1); bb2.put(b, 1, 7); bb2.putLong(l3); MultiByteBuff multi = new MultiByteBuff(bb1, bb2); assertEquals(4, multi.getInt()); assertEquals(l1, multi.getLong()); multi.skip(10); multi.moveBack(4); multi.moveBack(6); multi.moveBack(8); assertEquals(l1, multi.getLong()); } @Test public void testSubBuffer() { ByteBuffer bb1 = ByteBuffer.allocateDirect(10); ByteBuffer bb2 = ByteBuffer.allocateDirect(10); MultiByteBuff multi = new MultiByteBuff(bb1, bb2); long l1 = 1234L, l2 = 100L; multi.putLong(l1); multi.putLong(l2); multi.rewind(); ByteBuffer sub = multi.asSubByteBuffer(Bytes.SIZEOF_LONG); assertTrue(bb1 == sub); assertEquals(l1, ByteBufferUtils.toLong(sub, sub.position())); multi.skip(Bytes.SIZEOF_LONG); sub = multi.asSubByteBuffer(Bytes.SIZEOF_LONG); assertFalse(bb1 == sub); assertFalse(bb2 == sub); assertEquals(l2, ByteBufferUtils.toLong(sub, sub.position())); multi.rewind(); ObjectIntPair<ByteBuffer> p = new ObjectIntPair<>(); multi.asSubByteBuffer(8, Bytes.SIZEOF_LONG, p); assertFalse(bb1 == p.getFirst()); assertFalse(bb2 == p.getFirst()); assertEquals(0, p.getSecond()); assertEquals(l2, ByteBufferUtils.toLong(sub, p.getSecond())); } @Test public void testSliceDuplicateMethods() throws Exception { ByteBuffer bb1 = ByteBuffer.allocateDirect(10); ByteBuffer bb2 = ByteBuffer.allocateDirect(15); MultiByteBuff multi = new MultiByteBuff(bb1, bb2); long l1 = 1234L, l2 = 100L; multi.put((byte) 2); multi.putLong(l1); multi.putLong(l2); multi.putInt(45); multi.position(1); multi.limit(multi.position() + (2 * Bytes.SIZEOF_LONG)); MultiByteBuff sliced = multi.slice(); assertEquals(0, sliced.position()); assertEquals((2 * Bytes.SIZEOF_LONG), sliced.limit()); assertEquals(l1, sliced.getLong()); assertEquals(l2, sliced.getLong()); MultiByteBuff dup = multi.duplicate(); assertEquals(1, dup.position()); assertEquals(dup.position() + (2 * Bytes.SIZEOF_LONG), dup.limit()); assertEquals(l1, dup.getLong()); assertEquals(l2, dup.getLong()); } @Test public void testGetWithPosOnMultiBuffers() throws IOException { byte[] b = new byte[4]; byte[] b1 = new byte[4]; ByteBuffer bb1 = ByteBuffer.wrap(b); ByteBuffer bb2 = ByteBuffer.wrap(b1); MultiByteBuff mbb1 = new MultiByteBuff(bb1, bb2); mbb1.position(2); mbb1.putInt(4); int res = mbb1.getInt(2); byte[] bres = new byte[4]; bres[0] = mbb1.get(2); bres[1] = mbb1.get(3); bres[2] = mbb1.get(4); bres[3] = mbb1.get(5); int expected = Bytes.toInt(bres); assertEquals(res, expected); } @Test public void testGetIntStrictlyForwardWithPosOnMultiBuffers() throws IOException { byte[] b = new byte[4]; byte[] b1 = new byte[8]; ByteBuffer bb1 = ByteBuffer.wrap(b); ByteBuffer bb2 = ByteBuffer.wrap(b1); MultiByteBuff mbb1 = new MultiByteBuff(bb1, bb2); mbb1.position(2); mbb1.putInt(4); mbb1.position(7); mbb1.put((byte) 2); mbb1.putInt(3); mbb1.rewind(); mbb1.getIntAfterPosition(4); byte res = mbb1.get(7); assertEquals((byte) 2, res); mbb1.position(7); int intRes = mbb1.getIntAfterPosition(1); assertEquals(3, intRes); } @Test public void testPositonalCopyToByteArray() throws Exception { byte[] b = new byte[4]; byte[] b1 = new byte[8]; ByteBuffer bb1 = ByteBuffer.wrap(b); ByteBuffer bb2 = ByteBuffer.wrap(b1); MultiByteBuff mbb1 = new MultiByteBuff(bb1, bb2); mbb1.position(2); mbb1.putInt(4); mbb1.position(7); mbb1.put((byte) 2); mbb1.putInt(3); byte[] dst = new byte[4]; mbb1.get(2, dst, 0, 4); assertEquals(4, Bytes.toInt(dst)); assertEquals(12, mbb1.position()); mbb1.position(1); dst = new byte[4]; mbb1.get(8, dst, 0, 4); assertEquals(3, Bytes.toInt(dst)); assertEquals(1, mbb1.position()); mbb1.position(12); dst = new byte[1]; mbb1.get(7, dst, 0, 1); assertEquals(2, dst[0]); assertEquals(12, mbb1.position()); } @Test public void testToBytes() throws Exception { byte[] b = new byte[4]; byte[] b1 = new byte[8]; for (int i = 0; i < b.length; i++) { b[i] = (byte) i; } for (int i = 0; i < b1.length; i++) { b1[i] = (byte) (b1.length + i); } ByteBuffer bb1 = ByteBuffer.wrap(b); ByteBuffer bb2 = ByteBuffer.wrap(b1); MultiByteBuff mbb1 = new MultiByteBuff(bb1, bb2); // Test 1 Offset hitting exclusive second element byte[] actual = mbb1.toBytes(6, 4); assertTrue(Bytes.equals(actual, 0, actual.length, b1, 2, 4)); // Test 2 offset hitting exclusive second element // but continuing to the end of the second one actual = mbb1.toBytes(5, 7); assertTrue(Bytes.equals(actual, 0, actual.length, b1, 1, 7)); // Test 3 with offset hitting in first element, // continuing to next actual = mbb1.toBytes(2, 7); byte[] expected = new byte[7]; System.arraycopy(b, 2, expected, 0, 2); System.arraycopy(b1, 0, expected, 2, 5); assertTrue(Bytes.equals(actual, expected)); // Test 4 hitting only in first exclusively actual = mbb1.toBytes(1, 3); assertTrue(Bytes.equals(actual, 0, actual.length, b, 1, 3)); } @Test public void testHasRemaining() { ByteBuffer b1 = ByteBuffer.allocate(8); ByteBuffer b2 = ByteBuffer.allocate(8); ByteBuffer b3 = ByteBuffer.allocate(8); MultiByteBuff mbb1 = new MultiByteBuff(b1, b2, b3); assertTrue(mbb1.hasRemaining()); mbb1.limit(20); // Limit in mid of last of BB mbb1.position(15); mbb1.get();// We are at the end of second BB assertTrue(mbb1.hasRemaining()); mbb1.position(20); assertFalse(mbb1.hasRemaining()); mbb1.limit(12); // Limit in mid of second BB mbb1.position(11); assertTrue(mbb1.hasRemaining()); mbb1.get(); // Now we have reached the limit assertFalse(mbb1.hasRemaining()); mbb1.limit(16);// Limit at begin of the last BB mbb1.position(15); assertTrue(mbb1.hasRemaining()); mbb1.get(); // Now we have reached the limit assertFalse(mbb1.hasRemaining()); } }