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    MultiByteBuff 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    MultiByteBuff 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}