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.nio; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNotEquals; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import java.io.IOException; 027import java.nio.BufferOverflowException; 028import java.nio.ByteBuffer; 029import org.apache.hadoop.hbase.HBaseClassTestRule; 030import org.apache.hadoop.hbase.testclassification.MiscTests; 031import org.apache.hadoop.hbase.testclassification.SmallTests; 032import org.apache.hadoop.hbase.util.ByteBufferUtils; 033import org.apache.hadoop.hbase.util.Bytes; 034import org.apache.hadoop.hbase.util.ObjectIntPair; 035import org.junit.ClassRule; 036import org.junit.Test; 037import org.junit.experimental.categories.Category; 038 039@Category({ MiscTests.class, SmallTests.class }) 040public class TestMultiByteBuff { 041 042 @ClassRule 043 public static final HBaseClassTestRule CLASS_RULE = 044 HBaseClassTestRule.forClass(TestMultiByteBuff.class); 045 046 /** 047 * Test right answer though we span many sub-buffers. 048 */ 049 @Test 050 public void testGetShort() { 051 ByteBuffer bb1 = ByteBuffer.allocate(1); 052 bb1.put((byte)1); 053 ByteBuffer bb2 = ByteBuffer.allocate(1); 054 bb2.put((byte)0); 055 ByteBuffer bb3 = ByteBuffer.allocate(1); 056 bb3.put((byte)2); 057 ByteBuffer bb4 = ByteBuffer.allocate(1); 058 bb4.put((byte)3); 059 MultiByteBuff mbb = new MultiByteBuff(bb1, bb2, bb3, bb4); 060 assertEquals(256, mbb.getShortAfterPosition(0)); 061 assertEquals(2, mbb.getShortAfterPosition(1)); 062 assertEquals(515, mbb.getShortAfterPosition(2)); 063 } 064 065 @Test 066 public void testWritesAndReads() { 067 // Absolute reads 068 ByteBuffer bb1 = ByteBuffer.allocate(15); 069 ByteBuffer bb2 = ByteBuffer.allocate(15); 070 int i1 = 4; 071 bb1.putInt(i1); 072 long l1 = 45L, l2 = 100L, l3 = 12345L; 073 bb1.putLong(l1); 074 short s1 = 2; 075 bb1.putShort(s1); 076 byte[] b = Bytes.toBytes(l2); 077 bb1.put(b, 0, 1); 078 bb2.put(b, 1, 7); 079 bb2.putLong(l3); 080 MultiByteBuff mbb = new MultiByteBuff(bb1, bb2); 081 assertEquals(l1, mbb.getLong(4)); 082 assertEquals(l2, mbb.getLong(14)); 083 assertEquals(l3, mbb.getLong(22)); 084 assertEquals(i1, mbb.getInt(0)); 085 assertEquals(s1, mbb.getShort(12)); 086 // Relative reads 087 assertEquals(i1, mbb.getInt()); 088 assertEquals(l1, mbb.getLong()); 089 assertEquals(s1, mbb.getShort()); 090 assertEquals(l2, mbb.getLong()); 091 assertEquals(l3, mbb.getLong()); 092 // Absolute writes 093 bb1 = ByteBuffer.allocate(15); 094 bb2 = ByteBuffer.allocate(15); 095 mbb = new MultiByteBuff(bb1, bb2); 096 byte b1 = 5, b2 = 31; 097 mbb.put(b1); 098 mbb.putLong(l1); 099 mbb.putInt(i1); 100 mbb.putLong(l2); 101 mbb.put(b2); 102 mbb.position(mbb.position() + 2); 103 try { 104 mbb.putLong(l3); 105 fail("'Should have thrown BufferOverflowException"); 106 } catch (BufferOverflowException e) { 107 } 108 mbb.position(mbb.position() - 2); 109 mbb.putLong(l3); 110 mbb.rewind(); 111 assertEquals(b1, mbb.get()); 112 assertEquals(l1, mbb.getLong()); 113 assertEquals(i1, mbb.getInt()); 114 assertEquals(l2, mbb.getLong()); 115 assertEquals(b2, mbb.get()); 116 assertEquals(l3, mbb.getLong()); 117 mbb.put(21, b1); 118 mbb.position(21); 119 assertEquals(b1, mbb.get()); 120 mbb.put(b); 121 assertEquals(l2, mbb.getLong(22)); 122 } 123 124 @Test 125 public void testPutPrimitives() { 126 ByteBuffer bb = ByteBuffer.allocate(10); 127 SingleByteBuff s = new SingleByteBuff(bb); 128 s.putLong(-4465109508325701663L); 129 bb.rewind(); 130 long long1 = bb.getLong(); 131 assertEquals(-4465109508325701663L, long1); 132 s.position(8); 133 } 134 135 @Test 136 public void testArrayBasedMethods() { 137 byte[] b = new byte[15]; 138 ByteBuffer bb1 = ByteBuffer.wrap(b, 1, 10).slice(); 139 ByteBuffer bb2 = ByteBuffer.allocate(15); 140 ByteBuff mbb1 = new MultiByteBuff(bb1, bb2); 141 assertFalse(mbb1.hasArray()); 142 try { 143 mbb1.array(); 144 fail(); 145 } catch (UnsupportedOperationException e) { 146 } 147 try { 148 mbb1.arrayOffset(); 149 fail(); 150 } catch (UnsupportedOperationException e) { 151 } 152 mbb1 = new SingleByteBuff(bb1); 153 assertTrue(mbb1.hasArray()); 154 assertEquals(1, mbb1.arrayOffset()); 155 assertEquals(b, mbb1.array()); 156 mbb1 = new SingleByteBuff(ByteBuffer.allocateDirect(10)); 157 assertFalse(mbb1.hasArray()); 158 try { 159 mbb1.array(); 160 fail(); 161 } catch (UnsupportedOperationException e) { 162 } 163 try { 164 mbb1.arrayOffset(); 165 fail(); 166 } catch (UnsupportedOperationException e) { 167 } 168 } 169 170 @Test 171 public void testMarkAndResetWithMBB() { 172 ByteBuffer bb1 = ByteBuffer.allocateDirect(15); 173 ByteBuffer bb2 = ByteBuffer.allocateDirect(15); 174 bb1.putInt(4); 175 long l1 = 45L, l2 = 100L, l3 = 12345L; 176 bb1.putLong(l1); 177 bb1.putShort((short) 2); 178 byte[] b = Bytes.toBytes(l2); 179 bb1.put(b, 0, 1); 180 bb2.put(b, 1, 7); 181 bb2.putLong(l3); 182 ByteBuff multi = new MultiByteBuff(bb1, bb2); 183 assertEquals(4, multi.getInt()); 184 assertEquals(l1, multi.getLong()); 185 multi.mark(); 186 assertEquals((short) 2, multi.getShort()); 187 multi.reset(); 188 assertEquals((short) 2, multi.getShort()); 189 multi.mark(); 190 assertEquals(l2, multi.getLong()); 191 multi.reset(); 192 assertEquals(l2, multi.getLong()); 193 multi.mark(); 194 assertEquals(l3, multi.getLong()); 195 multi.reset(); 196 assertEquals(l3, multi.getLong()); 197 // Try absolute gets with mark and reset 198 multi.mark(); 199 assertEquals(l2, multi.getLong(14)); 200 multi.reset(); 201 assertEquals(l3, multi.getLong(22)); 202 // Just reset to see what happens 203 multi.reset(); 204 assertEquals(l2, multi.getLong(14)); 205 multi.mark(); 206 assertEquals(l3, multi.getLong(22)); 207 multi.reset(); 208 } 209 210 @Test 211 public void testSkipNBytes() { 212 ByteBuffer bb1 = ByteBuffer.allocate(15); 213 ByteBuffer bb2 = ByteBuffer.allocate(15); 214 bb1.putInt(4); 215 long l1 = 45L, l2 = 100L, l3 = 12345L; 216 bb1.putLong(l1); 217 bb1.putShort((short) 2); 218 byte[] b = Bytes.toBytes(l2); 219 bb1.put(b, 0, 1); 220 bb2.put(b, 1, 7); 221 bb2.putLong(l3); 222 MultiByteBuff multi = new MultiByteBuff(bb1, bb2); 223 assertEquals(4, multi.getInt()); 224 assertEquals(l1, multi.getLong()); 225 multi.skip(10); 226 assertEquals(l3, multi.getLong()); 227 } 228 229 @Test 230 public void testMoveBack() { 231 ByteBuffer bb1 = ByteBuffer.allocate(15); 232 ByteBuffer bb2 = ByteBuffer.allocate(15); 233 bb1.putInt(4); 234 long l1 = 45L, l2 = 100L, l3 = 12345L; 235 bb1.putLong(l1); 236 bb1.putShort((short) 2); 237 byte[] b = Bytes.toBytes(l2); 238 bb1.put(b, 0, 1); 239 bb2.put(b, 1, 7); 240 bb2.putLong(l3); 241 MultiByteBuff multi = new MultiByteBuff(bb1, bb2); 242 assertEquals(4, multi.getInt()); 243 assertEquals(l1, multi.getLong()); 244 multi.skip(10); 245 multi.moveBack(4); 246 multi.moveBack(6); 247 multi.moveBack(8); 248 assertEquals(l1, multi.getLong()); 249 } 250 251 @Test 252 public void testSubBuffer() { 253 ByteBuffer bb1 = ByteBuffer.allocateDirect(10); 254 ByteBuffer bb2 = ByteBuffer.allocateDirect(10); 255 MultiByteBuff multi = new MultiByteBuff(bb1, bb2); 256 long l1 = 1234L, l2 = 100L; 257 multi.putLong(l1); 258 multi.putLong(l2); 259 multi.rewind(); 260 ByteBuffer sub = multi.asSubByteBuffer(Bytes.SIZEOF_LONG); 261 assertEquals(bb1, sub); 262 assertEquals(l1, ByteBufferUtils.toLong(sub, sub.position())); 263 multi.skip(Bytes.SIZEOF_LONG); 264 sub = multi.asSubByteBuffer(Bytes.SIZEOF_LONG); 265 assertNotEquals(bb1, sub); 266 assertNotEquals(bb2, sub); 267 assertEquals(l2, ByteBufferUtils.toLong(sub, sub.position())); 268 multi.rewind(); 269 ObjectIntPair<ByteBuffer> p = new ObjectIntPair<>(); 270 multi.asSubByteBuffer(8, Bytes.SIZEOF_LONG, p); 271 assertNotEquals(bb1, p.getFirst()); 272 assertNotEquals(bb2, p.getFirst()); 273 assertEquals(0, p.getSecond()); 274 assertEquals(l2, ByteBufferUtils.toLong(sub, p.getSecond())); 275 } 276 277 @Test 278 public void testSliceDuplicateMethods() throws Exception { 279 ByteBuffer bb1 = ByteBuffer.allocateDirect(10); 280 ByteBuffer bb2 = ByteBuffer.allocateDirect(15); 281 MultiByteBuff multi = new MultiByteBuff(bb1, bb2); 282 long l1 = 1234L, l2 = 100L; 283 multi.put((byte) 2); 284 multi.putLong(l1); 285 multi.putLong(l2); 286 multi.putInt(45); 287 multi.position(1); 288 multi.limit(multi.position() + (2 * Bytes.SIZEOF_LONG)); 289 ByteBuff sliced = multi.slice(); 290 assertEquals(0, sliced.position()); 291 assertEquals((2 * Bytes.SIZEOF_LONG), sliced.limit()); 292 assertEquals(l1, sliced.getLong()); 293 assertEquals(l2, sliced.getLong()); 294 ByteBuff dup = multi.duplicate(); 295 assertEquals(1, dup.position()); 296 assertEquals(dup.position() + (2 * Bytes.SIZEOF_LONG), dup.limit()); 297 assertEquals(l1, dup.getLong()); 298 assertEquals(l2, dup.getLong()); 299 } 300 301 @Test 302 public void testGetWithPosOnMultiBuffers() throws IOException { 303 byte[] b = new byte[4]; 304 byte[] b1 = new byte[4]; 305 ByteBuffer bb1 = ByteBuffer.wrap(b); 306 ByteBuffer bb2 = ByteBuffer.wrap(b1); 307 MultiByteBuff mbb1 = new MultiByteBuff(bb1, bb2); 308 mbb1.position(2); 309 mbb1.putInt(4); 310 int res = mbb1.getInt(2); 311 byte[] bres = new byte[4]; 312 bres[0] = mbb1.get(2); 313 bres[1] = mbb1.get(3); 314 bres[2] = mbb1.get(4); 315 bres[3] = mbb1.get(5); 316 int expected = Bytes.toInt(bres); 317 assertEquals(expected, res); 318 } 319 320 @Test 321 public void testGetIntStrictlyForwardWithPosOnMultiBuffers() throws IOException { 322 byte[] b = new byte[4]; 323 byte[] b1 = new byte[8]; 324 ByteBuffer bb1 = ByteBuffer.wrap(b); 325 ByteBuffer bb2 = ByteBuffer.wrap(b1); 326 MultiByteBuff mbb1 = new MultiByteBuff(bb1, bb2); 327 mbb1.position(2); 328 mbb1.putInt(4); 329 mbb1.position(7); 330 mbb1.put((byte) 2); 331 mbb1.putInt(3); 332 mbb1.rewind(); 333 mbb1.getIntAfterPosition(4); 334 byte res = mbb1.get(7); 335 assertEquals((byte) 2, res); 336 mbb1.position(7); 337 int intRes = mbb1.getIntAfterPosition(1); 338 assertEquals(3, intRes); 339 } 340 341 @Test 342 public void testPositonalCopyToByteArray() throws Exception { 343 byte[] b = new byte[4]; 344 byte[] b1 = new byte[8]; 345 ByteBuffer bb1 = ByteBuffer.wrap(b); 346 ByteBuffer bb2 = ByteBuffer.wrap(b1); 347 MultiByteBuff mbb1 = new MultiByteBuff(bb1, bb2); 348 mbb1.position(2); 349 mbb1.putInt(4); 350 mbb1.position(7); 351 mbb1.put((byte) 2); 352 mbb1.putInt(3); 353 byte[] dst = new byte[4]; 354 mbb1.get(2, dst, 0, 4); 355 assertEquals(4, Bytes.toInt(dst)); 356 assertEquals(12, mbb1.position()); 357 mbb1.position(1); 358 dst = new byte[4]; 359 mbb1.get(8, dst, 0, 4); 360 assertEquals(3, Bytes.toInt(dst)); 361 assertEquals(1, mbb1.position()); 362 mbb1.position(12); 363 dst = new byte[1]; 364 mbb1.get(7, dst, 0, 1); 365 assertEquals(2, dst[0]); 366 assertEquals(12, mbb1.position()); 367 } 368 369 @Test 370 public void testToBytes() throws Exception { 371 byte[] b = new byte[4]; 372 byte[] b1 = new byte[8]; 373 for (int i = 0; i < b.length; i++) { 374 b[i] = (byte) i; 375 } 376 for (int i = 0; i < b1.length; i++) { 377 b1[i] = (byte) (b1.length + i); 378 } 379 ByteBuffer bb1 = ByteBuffer.wrap(b); 380 ByteBuffer bb2 = ByteBuffer.wrap(b1); 381 MultiByteBuff mbb1 = new MultiByteBuff(bb1, bb2); 382 383 // Test 1 Offset hitting exclusive second element 384 byte[] actual = mbb1.toBytes(6, 4); 385 assertTrue(Bytes.equals(actual, 0, actual.length, 386 b1, 2, 4)); 387 // Test 2 offset hitting exclusive second element 388 // but continuing to the end of the second one 389 actual = mbb1.toBytes(5, 7); 390 assertTrue(Bytes.equals(actual, 0, actual.length, 391 b1, 1, 7)); 392 // Test 3 with offset hitting in first element, 393 // continuing to next 394 actual = mbb1.toBytes(2, 7); 395 byte[] expected = new byte[7]; 396 System.arraycopy(b, 2, expected, 0, 2); 397 System.arraycopy(b1, 0, expected, 2, 5); 398 assertTrue(Bytes.equals(actual, expected)); 399 // Test 4 hitting only in first exclusively 400 actual = mbb1.toBytes(1, 3); 401 assertTrue(Bytes.equals(actual, 0, actual.length, 402 b, 1, 3)); 403 } 404 405 @Test 406 public void testHasRemaining() { 407 ByteBuffer b1 = ByteBuffer.allocate(8); 408 ByteBuffer b2 = ByteBuffer.allocate(8); 409 ByteBuffer b3 = ByteBuffer.allocate(8); 410 MultiByteBuff mbb1 = new MultiByteBuff(b1, b2, b3); 411 assertTrue(mbb1.hasRemaining()); 412 mbb1.limit(20); // Limit in mid of last of BB 413 mbb1.position(15); 414 mbb1.get();// We are at the end of second BB 415 assertTrue(mbb1.hasRemaining()); 416 mbb1.position(20); 417 assertFalse(mbb1.hasRemaining()); 418 mbb1.limit(12); // Limit in mid of second BB 419 mbb1.position(11); 420 assertTrue(mbb1.hasRemaining()); 421 mbb1.get(); // Now we have reached the limit 422 assertFalse(mbb1.hasRemaining()); 423 mbb1.limit(16);// Limit at begin of the last BB 424 mbb1.position(15); 425 assertTrue(mbb1.hasRemaining()); 426 mbb1.get(); // Now we have reached the limit 427 assertFalse(mbb1.hasRemaining()); 428 } 429 430 @Test 431 public void testGetPrimitivesWithSmallIndividualBBs() { 432 short s = 45; 433 int i = 2345; 434 long l = 75681526L; 435 ByteBuffer bb = ByteBuffer.allocate(14); 436 bb.putShort(s); 437 bb.putInt(i); 438 bb.putLong(l); 439 440 ByteBuffer bb1 = ((ByteBuffer) bb.duplicate().position(0).limit(1)).slice(); 441 ByteBuffer bb2 = ((ByteBuffer) bb.duplicate().position(1).limit(3)).slice(); 442 ByteBuffer bb3 = ((ByteBuffer) bb.duplicate().position(3).limit(5)).slice(); 443 ByteBuffer bb4 = ((ByteBuffer) bb.duplicate().position(5).limit(11)).slice(); 444 ByteBuffer bb5 = ((ByteBuffer) bb.duplicate().position(11).limit(12)).slice(); 445 ByteBuffer bb6 = ((ByteBuffer) bb.duplicate().position(12).limit(14)).slice(); 446 MultiByteBuff mbb = new MultiByteBuff(bb1, bb2, bb3, bb4, bb5, bb6); 447 assertEquals(s, mbb.getShortAfterPosition(0)); 448 assertEquals(i, mbb.getIntAfterPosition(2)); 449 assertEquals(l, mbb.getLongAfterPosition(6)); 450 451 assertEquals(s, mbb.getShort(0)); 452 assertEquals(i, mbb.getInt(2)); 453 assertEquals(l, mbb.getLong(6)); 454 455 mbb.position(0); 456 assertEquals(s, mbb.getShort()); 457 assertEquals(i, mbb.getInt()); 458 assertEquals(l, mbb.getLong()); 459 } 460 461 @Test 462 public void testGetByteBufferWithOffsetAndPos() { 463 byte[] a = Bytes.toBytes("abcd"); 464 byte[] b = Bytes.toBytes("efghijkl"); 465 ByteBuffer aa = ByteBuffer.wrap(a); 466 ByteBuffer bb = ByteBuffer.wrap(b); 467 MultiByteBuff mbb = new MultiByteBuff(aa, bb); 468 ByteBuffer out = ByteBuffer.allocate(12); 469 mbb.get(out, 0, 1); 470 assertEquals(out.position(), 1); 471 assertTrue(Bytes.equals(Bytes.toBytes("a"), 0, 1, out.array(), 0, 1)); 472 473 mbb.get(out, 1, 4); 474 assertEquals(out.position(), 5); 475 assertTrue(Bytes.equals(Bytes.toBytes("abcde"), 0, 5, out.array(), 0, 5)); 476 477 mbb.get(out, 10, 1); 478 assertEquals(out.position(), 6); 479 assertTrue(Bytes.equals(Bytes.toBytes("abcdek"), 0, 6, out.array(), 0, 6)); 480 481 mbb.get(out, 0, 6); 482 assertEquals(out.position(), 12); 483 assertTrue(Bytes.equals(Bytes.toBytes("abcdekabcdef"), 0, 12, out.array(), 0, 12)); 484 } 485}