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