/** * 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.cassandra.utils; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.CharacterCodingException; import java.util.Arrays; import java.util.concurrent.ThreadLocalRandom; import org.junit.Assert; import org.junit.Test; import org.apache.cassandra.io.util.DataOutputBuffer; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; public class ByteBufferUtilTest { private static final String s = "cassandra"; private ByteBuffer fromStringWithPosition(String s, int pos, boolean direct) { int l = s.length(); ByteBuffer bb; if (direct) { bb = ByteBuffer.allocateDirect(l + pos); } else { ByteBuffer tmp = ByteBuffer.allocate(l + pos + 3); tmp.position(3); bb = tmp.slice(); // make bb have a non null arrayOffset } bb.position(pos); bb.mark(); bb.put(s.getBytes()); bb.reset(); assert bb.position() == pos; return bb; } @Test public void testString() throws Exception { assert s.equals(ByteBufferUtil.string(ByteBufferUtil.bytes(s))); int pos = 10; ByteBuffer bb = fromStringWithPosition(s, 10, false); assert s.equals(ByteBufferUtil.string(bb, 10, s.length())); bb = fromStringWithPosition(s, 10, true); assert s.equals(ByteBufferUtil.string(bb, 10, s.length())); } @Test public void testGetArray() { byte[] t = s.getBytes(); ByteBuffer bb = ByteBufferUtil.bytes(s); assertArrayEquals(t, ByteBufferUtil.getArray(bb)); bb = fromStringWithPosition(s, 10, false); assertArrayEquals(t, ByteBufferUtil.getArray(bb)); bb = fromStringWithPosition(s, 10, true); assertArrayEquals(t, ByteBufferUtil.getArray(bb)); } @Test public void testLastIndexOf() { ByteBuffer bb = ByteBufferUtil.bytes(s); checkLastIndexOf(bb); bb = fromStringWithPosition(s, 10, false); checkLastIndexOf(bb); bb = fromStringWithPosition(s, 10, true); checkLastIndexOf(bb); } private void checkLastIndexOf(ByteBuffer bb) { assert bb.position() + 8 == ByteBufferUtil.lastIndexOf(bb, (byte) 'a', bb.position() + 8); assert bb.position() + 4 == ByteBufferUtil.lastIndexOf(bb, (byte) 'a', bb.position() + 7); assert bb.position() + 3 == ByteBufferUtil.lastIndexOf(bb, (byte) 's', bb.position() + 8); assert -1 == ByteBufferUtil.lastIndexOf(bb, (byte) 'o', bb.position() + 8); assert -1 == ByteBufferUtil.lastIndexOf(bb, (byte) 'd', bb.position() + 5); } @Test public void testClone() { ByteBuffer bb = ByteBufferUtil.bytes(s); ByteBuffer clone1 = ByteBufferUtil.clone(bb); assert bb != clone1; assert bb.equals(clone1); assert bb.array() != clone1.array(); bb = fromStringWithPosition(s, 10, false); ByteBuffer clone2 = ByteBufferUtil.clone(bb); assert bb != clone2; assert bb.equals(clone2); assert clone1.equals(clone2); assert bb.array() != clone2.array(); bb = fromStringWithPosition(s, 10, true); ByteBuffer clone3 = ByteBufferUtil.clone(bb); assert bb != clone3; assert bb.equals(clone3); assert clone1.equals(clone3); } @Test public void testArrayCopy() { ByteBuffer bb = ByteBufferUtil.bytes(s); checkArrayCopy(bb); bb = fromStringWithPosition(s, 10, false); checkArrayCopy(bb); bb = fromStringWithPosition(s, 10, true); checkArrayCopy(bb); } private void checkArrayCopy(ByteBuffer bb) { byte[] bytes = new byte[s.length()]; ByteBufferUtil.arrayCopy(bb, bb.position(), bytes, 0, s.length()); assertArrayEquals(s.getBytes(), bytes); bytes = new byte[5]; ByteBufferUtil.arrayCopy(bb, bb.position() + 3, bytes, 1, 4); assertArrayEquals(Arrays.copyOfRange(s.getBytes(), 3, 7), Arrays.copyOfRange(bytes, 1, 5)); } @Test public void testReadWrite() throws IOException { ByteBuffer bb = ByteBufferUtil.bytes(s); checkReadWrite(bb); bb = fromStringWithPosition(s, 10, false); checkReadWrite(bb); bb = fromStringWithPosition(s, 10, true); checkReadWrite(bb); } private void checkReadWrite(ByteBuffer bb) throws IOException { DataOutputBuffer out = new DataOutputBuffer(); ByteBufferUtil.writeWithLength(bb, out); ByteBufferUtil.writeWithShortLength(bb, out); DataInputStream in = new DataInputStream(new ByteArrayInputStream(out.toByteArray())); assert bb.equals(ByteBufferUtil.readWithLength(in)); assert bb.equals(ByteBufferUtil.readWithShortLength(in)); } @Test public void testInputStream() throws IOException { ByteBuffer bb = ByteBuffer.allocate(13); bb.putInt(255); bb.put((byte) -3); bb.putLong(42L); bb.clear(); DataInputStream in = new DataInputStream(ByteBufferUtil.inputStream(bb)); assert in.readInt() == 255; assert in.readByte() == (byte)-3; assert in.readLong() == 42L; } @Test public void testIntBytesConversions() { // positive, negative, 1 and 2 byte cases, including a few edges that would foul things up unless you're careful // about masking away sign extension. int[] ints = new int[] { -20, -127, -128, 0, 1, 127, 128, 65534, 65535, -65534, -65535 }; for (int i : ints) { ByteBuffer ba = ByteBufferUtil.bytes(i); int actual = ByteBufferUtil.toInt(ba); assertEquals(i, actual); } } @Test(expected=CharacterCodingException.class) public void testDecode() throws IOException { ByteBuffer bytes = ByteBuffer.wrap(new byte[]{(byte)0xff, (byte)0xfe}); ByteBufferUtil.string(bytes); } @Test public void testHexBytesConversion() { for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) { ByteBuffer bb = ByteBuffer.allocate(1); bb.put((byte) i); bb.clear(); String s = ByteBufferUtil.bytesToHex(bb); ByteBuffer bb2 = ByteBufferUtil.hexToBytes(s); assertEquals(bb, bb2); } // check that non-zero buffer positions work, // i.e. that conversion accounts for the buffer offset and limit correctly ByteBuffer bb = ByteBuffer.allocate(4); for (int i = 0; i < 4; i++) { bb.put((byte) i); } // use a chunk out of the middle of the buffer bb.position(1); bb.limit(3); assertEquals(2, bb.remaining()); String s = ByteBufferUtil.bytesToHex(bb); ByteBuffer bb2 = ByteBufferUtil.hexToBytes(s); assertEquals(bb, bb2); assertEquals("0102", s); } @Test public void testStartsAndEndsWith() { byte[] bytes = new byte[512]; ThreadLocalRandom random = ThreadLocalRandom.current(); random.nextBytes(bytes); ByteBuffer a = ByteBuffer.wrap(bytes); ByteBuffer b = a.duplicate(); // let's take random slices of a and match for (int i = 0; i < 512; i++) { // prefix from the original offset b.position(0).limit(a.remaining() - random.nextInt(0, a.remaining() - 1)); Assert.assertTrue(ByteBufferUtil.startsWith(a, b)); Assert.assertTrue(ByteBufferUtil.startsWith(a, b.slice())); // prefix from random position inside of array int pos = random.nextInt(1, a.remaining() - 5); a.position(pos); b.limit(bytes.length - 1).position(pos); Assert.assertTrue(ByteBufferUtil.startsWith(a, b)); a.position(0); // endsWith at random position b.limit(a.remaining()).position(random.nextInt(0, a.remaining() - 1)); Assert.assertTrue(ByteBufferUtil.endsWith(a, b)); Assert.assertTrue(ByteBufferUtil.endsWith(a, b.slice())); } a.limit(bytes.length - 1).position(0); b.limit(bytes.length - 1).position(1); Assert.assertFalse(ByteBufferUtil.startsWith(a, b)); Assert.assertFalse(ByteBufferUtil.startsWith(a, b.slice())); Assert.assertTrue(ByteBufferUtil.endsWith(a, b)); Assert.assertTrue(ByteBufferUtil.endsWith(a, b.slice())); a.position(5); Assert.assertFalse(ByteBufferUtil.startsWith(a, b)); Assert.assertFalse(ByteBufferUtil.endsWith(a, b)); } }