001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.util;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertTrue;
023import static org.junit.Assert.fail;
024
025import java.io.IOException;
026import java.nio.ByteBuffer;
027import java.util.concurrent.ThreadLocalRandom;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.nio.ByteBuff;
030import org.apache.hadoop.hbase.nio.MultiByteBuff;
031import org.apache.hadoop.hbase.nio.SingleByteBuff;
032import org.apache.hadoop.hbase.testclassification.MiscTests;
033import org.apache.hadoop.hbase.testclassification.SmallTests;
034import org.junit.ClassRule;
035import org.junit.Test;
036import org.junit.experimental.categories.Category;
037
038@Category({ MiscTests.class, SmallTests.class })
039public class TestByteBufferArray {
040
041  @ClassRule
042  public static final HBaseClassTestRule CLASS_RULE =
043    HBaseClassTestRule.forClass(TestByteBufferArray.class);
044
045  private static final ByteBufferAllocator ALLOC = (size) -> ByteBuffer.allocateDirect((int) size);
046
047  @Test
048  public void testAsSubBufferWhenEndOffsetLandInLastBuffer() throws Exception {
049    int capacity = 4 * 1024 * 1024;
050    ByteBufferArray array = new ByteBufferArray(capacity, ALLOC);
051    ByteBuff subBuf = ByteBuff.wrap(array.asSubByteBuffers(0, capacity));
052    subBuf.position(capacity - 1);// Position to the last byte
053    assertTrue(subBuf.hasRemaining());
054    // Read last byte
055    subBuf.get();
056    assertFalse(subBuf.hasRemaining());
057  }
058
059  @Test
060  public void testByteBufferCreation() throws Exception {
061    int capacity = 470 * 1021 * 1023;
062    ByteBufferArray array = new ByteBufferArray(capacity, ALLOC);
063    assertEquals(118, array.buffers.length);
064    for (int i = 0; i < array.buffers.length; i++) {
065      assertEquals(ByteBufferArray.DEFAULT_BUFFER_SIZE, array.buffers[i].capacity());
066    }
067  }
068
069  @Test
070  public void testByteBufferCreation1() throws Exception {
071    long cap = 7 * 1024L * 1024L;
072    int bufferSize = ByteBufferArray.getBufferSize(cap), bufferCount = 25;
073    ByteBufferArray array = new ByteBufferArray(bufferSize, bufferCount, 16, cap, ALLOC);
074    for (int i = 0; i < array.buffers.length; i++) {
075      assertEquals(458752, array.buffers[i].capacity());
076    }
077  }
078
079  private static void fill(ByteBuff buf, byte val) {
080    for (int i = buf.position(); i < buf.limit(); i++) {
081      buf.put(i, val);
082    }
083  }
084
085  private ByteBuff createByteBuff(int len) {
086    assert len >= 0;
087    int pos = len == 0 ? 0 : ThreadLocalRandom.current().nextInt(len);
088    ByteBuff b = ByteBuff.wrap(ByteBuffer.allocate(2 * len));
089    b.position(pos).limit(pos + len);
090    return b;
091  }
092
093  private interface Call {
094    void run() throws IOException;
095  }
096
097  @SuppressWarnings("TryFailThrowable")
098  private void expectedAssert(Call r) throws IOException {
099    try {
100      r.run();
101      fail();
102    } catch (AssertionError e) {
103      // Ignore
104    }
105  }
106
107  @Test
108  public void testArrayIO() throws IOException {
109    int cap = 9 * 1024 * 1024, bufferSize = ByteBufferArray.getBufferSize(cap);
110    ByteBufferArray array = new ByteBufferArray(cap, ALLOC);
111    testReadAndWrite(array, 0, 512, (byte) 2);
112    testReadAndWrite(array, cap - 512, 512, (byte) 3);
113    testReadAndWrite(array, 4 * 1024 * 1024, 5 * 1024 * 1024, (byte) 4);
114    testReadAndWrite(array, 256, 256, (byte) 5);
115    testReadAndWrite(array, 257, 513, (byte) 6);
116    testReadAndWrite(array, 0, cap, (byte) 7);
117    testReadAndWrite(array, cap, 0, (byte) 8);
118    testReadAndWrite(array, cap - 1, 1, (byte) 9);
119    testReadAndWrite(array, cap - 2, 2, (byte) 10);
120
121    expectedAssert(() -> testReadAndWrite(array, cap - 2, 3, (byte) 11));
122    expectedAssert(() -> testReadAndWrite(array, cap + 1, 0, (byte) 12));
123    expectedAssert(() -> testReadAndWrite(array, 0, cap + 1, (byte) 12));
124    expectedAssert(() -> testReadAndWrite(array, -1, 0, (byte) 13));
125    expectedAssert(() -> testReadAndWrite(array, 0, -23, (byte) 14));
126    expectedAssert(() -> testReadAndWrite(array, 0, 0, (byte) 15));
127    expectedAssert(() -> testReadAndWrite(array, 4096, cap - 4096 + 1, (byte) 16));
128
129    testAsSubByteBuff(array, 0, cap, true);
130    testAsSubByteBuff(array, 0, 0, false);
131    testAsSubByteBuff(array, 0, 1, false);
132    testAsSubByteBuff(array, 0, bufferSize - 1, false);
133    testAsSubByteBuff(array, 0, bufferSize, false);
134    testAsSubByteBuff(array, 0, bufferSize + 1, true);
135    testAsSubByteBuff(array, 0, 2 * bufferSize, true);
136    testAsSubByteBuff(array, 0, 5 * bufferSize, true);
137    testAsSubByteBuff(array, cap - bufferSize - 1, bufferSize, true);
138    testAsSubByteBuff(array, cap - bufferSize, bufferSize, false);
139    testAsSubByteBuff(array, cap - bufferSize, 0, false);
140    testAsSubByteBuff(array, cap - bufferSize, 1, false);
141    testAsSubByteBuff(array, cap - bufferSize, bufferSize - 1, false);
142    testAsSubByteBuff(array, cap - 2 * bufferSize, 2 * bufferSize, true);
143    testAsSubByteBuff(array, cap - 2 * bufferSize, bufferSize + 1, true);
144    testAsSubByteBuff(array, cap - 2 * bufferSize, bufferSize - 1, false);
145    testAsSubByteBuff(array, cap - 2 * bufferSize, 0, false);
146
147    expectedAssert(() -> testAsSubByteBuff(array, 0, cap + 1, false));
148    expectedAssert(() -> testAsSubByteBuff(array, 0, -1, false));
149    expectedAssert(() -> testAsSubByteBuff(array, -1, -1, false));
150    expectedAssert(() -> testAsSubByteBuff(array, cap - bufferSize, bufferSize + 1, false));
151    expectedAssert(() -> testAsSubByteBuff(array, 2 * bufferSize, cap - 2 * bufferSize + 1, false));
152  }
153
154  private void testReadAndWrite(ByteBufferArray array, int off, int dataSize, byte val) {
155    ByteBuff src = createByteBuff(dataSize);
156    int pos = src.position(), lim = src.limit();
157    fill(src, val);
158    assertEquals(src.remaining(), dataSize);
159    try {
160      assertEquals(dataSize, array.write(off, src));
161      assertEquals(0, src.remaining());
162    } finally {
163      src.position(pos).limit(lim);
164    }
165
166    ByteBuff dst = createByteBuff(dataSize);
167    pos = dst.position();
168    lim = dst.limit();
169    try {
170      assertEquals(dataSize, array.read(off, dst));
171      assertEquals(0, dst.remaining());
172    } finally {
173      dst.position(pos).limit(lim);
174    }
175    assertByteBuffEquals(src, dst);
176  }
177
178  private void testAsSubByteBuff(ByteBufferArray array, int off, int len, boolean isMulti) {
179    ByteBuff ret = ByteBuff.wrap(array.asSubByteBuffers(off, len));
180    if (isMulti) {
181      assertTrue(ret instanceof MultiByteBuff);
182    } else {
183      assertTrue(ret instanceof SingleByteBuff);
184    }
185    assertTrue(!ret.hasArray());
186    assertEquals(len, ret.remaining());
187
188    ByteBuff tmp = createByteBuff(len);
189    int pos = tmp.position(), lim = tmp.limit();
190    try {
191      assertEquals(len, array.read(off, tmp));
192      assertEquals(0, tmp.remaining());
193    } finally {
194      tmp.position(pos).limit(lim);
195    }
196
197    assertByteBuffEquals(ret, tmp);
198  }
199
200  private void assertByteBuffEquals(ByteBuff a, ByteBuff b) {
201    assertEquals(a.remaining(), b.remaining());
202    for (int i = a.position(), j = b.position(); i < a.limit(); i++, j++) {
203      assertEquals(a.get(i), b.get(j));
204    }
205  }
206}