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.Random;
028
029import org.apache.hadoop.hbase.HBaseClassTestRule;
030import org.apache.hadoop.hbase.nio.ByteBuff;
031import org.apache.hadoop.hbase.nio.MultiByteBuff;
032import org.apache.hadoop.hbase.nio.SingleByteBuff;
033import org.apache.hadoop.hbase.testclassification.MiscTests;
034import org.apache.hadoop.hbase.testclassification.SmallTests;
035import org.junit.ClassRule;
036import org.junit.Test;
037import org.junit.experimental.categories.Category;
038
039@Category({ MiscTests.class, SmallTests.class })
040public class TestByteBufferArray {
041
042  private static final Random RANDOM = new Random(System.currentTimeMillis());
043
044  @ClassRule
045  public static final HBaseClassTestRule CLASS_RULE =
046      HBaseClassTestRule.forClass(TestByteBufferArray.class);
047
048  private static final ByteBufferAllocator ALLOC = (size) -> ByteBuffer.allocateDirect((int) size);
049
050  @Test
051  public void testAsSubBufferWhenEndOffsetLandInLastBuffer() throws Exception {
052    int capacity = 4 * 1024 * 1024;
053    ByteBufferArray array = new ByteBufferArray(capacity, ALLOC);
054    ByteBuff subBuf = ByteBuff.wrap(array.asSubByteBuffers(0, capacity));
055    subBuf.position(capacity - 1);// Position to the last byte
056    assertTrue(subBuf.hasRemaining());
057    // Read last byte
058    subBuf.get();
059    assertFalse(subBuf.hasRemaining());
060  }
061
062  @Test
063  public void testByteBufferCreation() throws Exception {
064    int capacity = 470 * 1021 * 1023;
065    ByteBufferArray array = new ByteBufferArray(capacity, ALLOC);
066    assertEquals(118, array.buffers.length);
067    for (int i = 0; i < array.buffers.length; i++) {
068      assertEquals(ByteBufferArray.DEFAULT_BUFFER_SIZE, array.buffers[i].capacity());
069    }
070  }
071
072  @Test
073  public void testByteBufferCreation1() throws Exception {
074    long cap = 7 * 1024L * 1024L;
075    int bufferSize = ByteBufferArray.getBufferSize(cap), bufferCount = 25;
076    ByteBufferArray array = new ByteBufferArray(bufferSize, bufferCount, 16, cap, ALLOC);
077    for (int i = 0; i < array.buffers.length; i++) {
078      assertEquals(458752, array.buffers[i].capacity());
079    }
080  }
081
082  private static void fill(ByteBuff buf, byte val) {
083    for (int i = buf.position(); i < buf.limit(); i++) {
084      buf.put(i, val);
085    }
086  }
087
088  private ByteBuff createByteBuff(int len) {
089    assert len >= 0;
090    int pos = len == 0 ? 0 : RANDOM.nextInt(len);
091    ByteBuff b = ByteBuff.wrap(ByteBuffer.allocate(2 * len));
092    b.position(pos).limit(pos + len);
093    return b;
094  }
095
096  private interface Call {
097    void run() throws IOException;
098  }
099
100  @SuppressWarnings("TryFailThrowable")
101  private void expectedAssert(Call r) throws IOException {
102    try {
103      r.run();
104      fail();
105    } catch (AssertionError e) {
106      // Ignore
107    }
108  }
109
110
111  @Test
112  public void testArrayIO() throws IOException {
113    int cap = 9 * 1024 * 1024, bufferSize = ByteBufferArray.getBufferSize(cap);
114    ByteBufferArray array = new ByteBufferArray(cap, ALLOC);
115    testReadAndWrite(array, 0, 512, (byte) 2);
116    testReadAndWrite(array, cap - 512, 512, (byte) 3);
117    testReadAndWrite(array, 4 * 1024 * 1024, 5 * 1024 * 1024, (byte) 4);
118    testReadAndWrite(array, 256, 256, (byte) 5);
119    testReadAndWrite(array, 257, 513, (byte) 6);
120    testReadAndWrite(array, 0, cap, (byte) 7);
121    testReadAndWrite(array, cap, 0, (byte) 8);
122    testReadAndWrite(array, cap - 1, 1, (byte) 9);
123    testReadAndWrite(array, cap - 2, 2, (byte) 10);
124
125    expectedAssert(() -> testReadAndWrite(array, cap - 2, 3, (byte) 11));
126    expectedAssert(() -> testReadAndWrite(array, cap + 1, 0, (byte) 12));
127    expectedAssert(() -> testReadAndWrite(array, 0, cap + 1, (byte) 12));
128    expectedAssert(() -> testReadAndWrite(array, -1, 0, (byte) 13));
129    expectedAssert(() -> testReadAndWrite(array, 0, -23, (byte) 14));
130    expectedAssert(() -> testReadAndWrite(array, 0, 0, (byte) 15));
131    expectedAssert(() -> testReadAndWrite(array, 4096, cap - 4096 + 1, (byte) 16));
132
133    testAsSubByteBuff(array, 0, cap, true);
134    testAsSubByteBuff(array, 0, 0, false);
135    testAsSubByteBuff(array, 0, 1, false);
136    testAsSubByteBuff(array, 0, bufferSize - 1, false);
137    testAsSubByteBuff(array, 0, bufferSize, false);
138    testAsSubByteBuff(array, 0, bufferSize + 1, true);
139    testAsSubByteBuff(array, 0, 2 * bufferSize, true);
140    testAsSubByteBuff(array, 0, 5 * bufferSize, true);
141    testAsSubByteBuff(array, cap - bufferSize - 1, bufferSize, true);
142    testAsSubByteBuff(array, cap - bufferSize, bufferSize, false);
143    testAsSubByteBuff(array, cap - bufferSize, 0, false);
144    testAsSubByteBuff(array, cap - bufferSize, 1, false);
145    testAsSubByteBuff(array, cap - bufferSize, bufferSize - 1, false);
146    testAsSubByteBuff(array, cap - 2 * bufferSize, 2 * bufferSize, true);
147    testAsSubByteBuff(array, cap - 2 * bufferSize, bufferSize + 1, true);
148    testAsSubByteBuff(array, cap - 2 * bufferSize, bufferSize - 1, false);
149    testAsSubByteBuff(array, cap - 2 * bufferSize, 0, false);
150
151    expectedAssert(() -> testAsSubByteBuff(array, 0, cap + 1, false));
152    expectedAssert(() -> testAsSubByteBuff(array, 0, -1, false));
153    expectedAssert(() -> testAsSubByteBuff(array, -1, -1, false));
154    expectedAssert(() -> testAsSubByteBuff(array, cap - bufferSize, bufferSize + 1, false));
155    expectedAssert(() -> testAsSubByteBuff(array, 2 * bufferSize, cap - 2 * bufferSize + 1, false));
156  }
157
158  private void testReadAndWrite(ByteBufferArray array, int off, int dataSize, byte val) {
159    ByteBuff src = createByteBuff(dataSize);
160    int pos = src.position(), lim = src.limit();
161    fill(src, val);
162    assertEquals(src.remaining(), dataSize);
163    try {
164      assertEquals(dataSize, array.write(off, src));
165      assertEquals(0, src.remaining());
166    } finally {
167      src.position(pos).limit(lim);
168    }
169
170    ByteBuff dst = createByteBuff(dataSize);
171    pos = dst.position();
172    lim = dst.limit();
173    try {
174      assertEquals(dataSize, array.read(off, dst));
175      assertEquals(0, dst.remaining());
176    } finally {
177      dst.position(pos).limit(lim);
178    }
179    assertByteBuffEquals(src, dst);
180  }
181
182  private void testAsSubByteBuff(ByteBufferArray array, int off, int len, boolean isMulti) {
183    ByteBuff ret = ByteBuff.wrap(array.asSubByteBuffers(off, len));
184    if (isMulti) {
185      assertTrue(ret instanceof MultiByteBuff);
186    } else {
187      assertTrue(ret instanceof SingleByteBuff);
188    }
189    assertTrue(!ret.hasArray());
190    assertEquals(len, ret.remaining());
191
192    ByteBuff tmp = createByteBuff(len);
193    int pos = tmp.position(), lim = tmp.limit();
194    try {
195      assertEquals(len, array.read(off, tmp));
196      assertEquals(0, tmp.remaining());
197    } finally {
198      tmp.position(pos).limit(lim);
199    }
200
201    assertByteBuffEquals(ret, tmp);
202  }
203
204  private void assertByteBuffEquals(ByteBuff a, ByteBuff b) {
205    assertEquals(a.remaining(), b.remaining());
206    for (int i = a.position(), j = b.position(); i < a.limit(); i++, j++) {
207      assertEquals(a.get(i), b.get(j));
208    }
209  }
210}