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.assertArrayEquals;
021import static org.junit.Assert.assertEquals;
022import static org.junit.Assert.assertFalse;
023import static org.junit.Assert.assertNotNull;
024import static org.junit.Assert.assertNotSame;
025import static org.junit.Assert.assertTrue;
026import static org.junit.Assert.fail;
027
028import java.io.ByteArrayInputStream;
029import java.io.ByteArrayOutputStream;
030import java.io.DataInputStream;
031import java.io.DataOutputStream;
032import java.io.IOException;
033import java.lang.reflect.Field;
034import java.lang.reflect.Modifier;
035import java.math.BigDecimal;
036import java.nio.ByteBuffer;
037import java.util.ArrayList;
038import java.util.Arrays;
039import java.util.List;
040import java.util.Random;
041import java.util.concurrent.ThreadLocalRandom;
042import org.apache.hadoop.hbase.HBaseClassTestRule;
043import org.apache.hadoop.hbase.testclassification.MediumTests;
044import org.apache.hadoop.hbase.testclassification.MiscTests;
045import org.apache.hadoop.hbase.unsafe.HBasePlatformDependent;
046import org.apache.hadoop.io.WritableUtils;
047import org.junit.Assert;
048import org.junit.ClassRule;
049import org.junit.Test;
050import org.junit.experimental.categories.Category;
051
052@Category({ MiscTests.class, MediumTests.class })
053public class TestBytes {
054  @ClassRule
055  public static final HBaseClassTestRule CLASS_RULE = HBaseClassTestRule.forClass(TestBytes.class);
056
057  private static void setUnsafe(boolean value) throws Exception {
058    Field field = Bytes.class.getDeclaredField("UNSAFE_UNALIGNED");
059    field.setAccessible(true);
060    Field modifiersField = Field.class.getDeclaredField("modifiers");
061    modifiersField.setAccessible(true);
062    int oldModifiers = field.getModifiers();
063    modifiersField.setInt(field, oldModifiers & ~Modifier.FINAL);
064    try {
065      field.set(null, value);
066    } finally {
067      modifiersField.setInt(field, oldModifiers);
068    }
069    assertEquals(Bytes.UNSAFE_UNALIGNED, value);
070  }
071
072  @Test
073  public void testShort() throws Exception {
074    testShort(false);
075  }
076
077  @Test
078  public void testShortUnsafe() throws Exception {
079    testShort(true);
080  }
081
082  private static void testShort(boolean unsafe) throws Exception {
083    setUnsafe(unsafe);
084    try {
085      for (short n : Arrays.asList(Short.MIN_VALUE, (short) -100, (short) -1, (short) 0, (short) 1,
086        (short) 300, Short.MAX_VALUE)) {
087        byte[] bytes = Bytes.toBytes(n);
088        assertEquals(Bytes.toShort(bytes, 0, bytes.length), n);
089      }
090    } finally {
091      setUnsafe(HBasePlatformDependent.unaligned());
092    }
093  }
094
095  @Test
096  public void testNullHashCode() {
097    byte[] b = null;
098    Exception ee = null;
099    try {
100      Bytes.hashCode(b);
101    } catch (Exception e) {
102      ee = e;
103    }
104    assertNotNull(ee);
105  }
106
107  @Test
108  public void testAdd() {
109    byte[] a = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
110    byte[] b = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
111    byte[] c = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 };
112    byte[] result1 = Bytes.add(a, b, c);
113    byte[] result2 = Bytes.add(new byte[][] { a, b, c });
114    assertEquals(0, Bytes.compareTo(result1, result2));
115  }
116
117  @Test
118  public void testSplit() {
119    byte[] lowest = Bytes.toBytes("AAA");
120    byte[] middle = Bytes.toBytes("CCC");
121    byte[] highest = Bytes.toBytes("EEE");
122    byte[][] parts = Bytes.split(lowest, highest, 1);
123    for (byte[] bytes : parts) {
124      System.out.println(Bytes.toString(bytes));
125    }
126    assertEquals(3, parts.length);
127    assertTrue(Bytes.equals(parts[1], middle));
128    // Now divide into three parts. Change highest so split is even.
129    highest = Bytes.toBytes("DDD");
130    parts = Bytes.split(lowest, highest, 2);
131    for (byte[] part : parts) {
132      System.out.println(Bytes.toString(part));
133    }
134    assertEquals(4, parts.length);
135    // Assert that 3rd part is 'CCC'.
136    assertTrue(Bytes.equals(parts[2], middle));
137  }
138
139  @Test
140  public void testSplit2() {
141    // More split tests.
142    byte[] lowest = Bytes.toBytes("http://A");
143    byte[] highest = Bytes.toBytes("http://z");
144    byte[] middle = Bytes.toBytes("http://]");
145    byte[][] parts = Bytes.split(lowest, highest, 1);
146    for (byte[] part : parts) {
147      System.out.println(Bytes.toString(part));
148    }
149    assertEquals(3, parts.length);
150    assertTrue(Bytes.equals(parts[1], middle));
151  }
152
153  @Test
154  public void testSplit3() {
155    // Test invalid split cases
156    byte[] low = { 1, 1, 1 };
157    byte[] high = { 1, 1, 3 };
158
159    // If swapped, should throw IAE
160    try {
161      Bytes.split(high, low, 1);
162      fail("Should not be able to split if low > high");
163    } catch (IllegalArgumentException iae) {
164      // Correct
165    }
166
167    // Single split should work
168    byte[][] parts = Bytes.split(low, high, 1);
169    for (int i = 0; i < parts.length; i++) {
170      System.out.println("" + i + " -> " + Bytes.toStringBinary(parts[i]));
171    }
172    assertEquals("Returned split should have 3 parts but has " + parts.length, 3, parts.length);
173
174    // If split more than once, use additional byte to split
175    parts = Bytes.split(low, high, 2);
176    assertNotNull("Split with an additional byte", parts);
177    assertEquals(parts.length, low.length + 1);
178
179    // Split 0 times should throw IAE
180    try {
181      Bytes.split(low, high, 0);
182      fail("Should not be able to split 0 times");
183    } catch (IllegalArgumentException iae) {
184      // Correct
185    }
186  }
187
188  @Test
189  public void testToInt() {
190    int[] ints = { -1, 123, Integer.MIN_VALUE, Integer.MAX_VALUE };
191    for (int anInt : ints) {
192      byte[] b = Bytes.toBytes(anInt);
193      assertEquals(anInt, Bytes.toInt(b));
194      byte[] b2 = bytesWithOffset(b);
195      assertEquals(anInt, Bytes.toInt(b2, 1));
196      assertEquals(anInt, Bytes.toInt(b2, 1, Bytes.SIZEOF_INT));
197    }
198  }
199
200  @Test
201  public void testToLong() {
202    long[] longs = { -1L, 123L, Long.MIN_VALUE, Long.MAX_VALUE };
203    for (long aLong : longs) {
204      byte[] b = Bytes.toBytes(aLong);
205      assertEquals(aLong, Bytes.toLong(b));
206      byte[] b2 = bytesWithOffset(b);
207      assertEquals(aLong, Bytes.toLong(b2, 1));
208      assertEquals(aLong, Bytes.toLong(b2, 1, Bytes.SIZEOF_LONG));
209    }
210  }
211
212  @Test
213  public void testToFloat() {
214    float[] floats = { -1f, 123.123f, Float.MAX_VALUE };
215    for (float aFloat : floats) {
216      byte[] b = Bytes.toBytes(aFloat);
217      assertEquals(aFloat, Bytes.toFloat(b), 0.0f);
218      byte[] b2 = bytesWithOffset(b);
219      assertEquals(aFloat, Bytes.toFloat(b2, 1), 0.0f);
220    }
221  }
222
223  @Test
224  public void testToDouble() {
225    double[] doubles = { Double.MIN_VALUE, Double.MAX_VALUE };
226    for (double aDouble : doubles) {
227      byte[] b = Bytes.toBytes(aDouble);
228      assertEquals(aDouble, Bytes.toDouble(b), 0.0);
229      byte[] b2 = bytesWithOffset(b);
230      assertEquals(aDouble, Bytes.toDouble(b2, 1), 0.0);
231    }
232  }
233
234  @Test
235  public void testToBigDecimal() {
236    BigDecimal[] decimals =
237      { new BigDecimal("-1"), new BigDecimal("123.123"), new BigDecimal("123123123123") };
238    for (BigDecimal decimal : decimals) {
239      byte[] b = Bytes.toBytes(decimal);
240      assertEquals(decimal, Bytes.toBigDecimal(b));
241      byte[] b2 = bytesWithOffset(b);
242      assertEquals(decimal, Bytes.toBigDecimal(b2, 1, b.length));
243    }
244  }
245
246  private byte[] bytesWithOffset(byte[] src) {
247    // add one byte in front to test offset
248    byte[] result = new byte[src.length + 1];
249    result[0] = (byte) 0xAA;
250    System.arraycopy(src, 0, result, 1, src.length);
251    return result;
252  }
253
254  @Test
255  public void testToBytesForByteBuffer() {
256    byte[] array = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
257    ByteBuffer target = ByteBuffer.wrap(array);
258    target.position(2);
259    target.limit(7);
260
261    byte[] actual = Bytes.toBytes(target);
262    byte[] expected = { 0, 1, 2, 3, 4, 5, 6 };
263    assertArrayEquals(expected, actual);
264    assertEquals(2, target.position());
265    assertEquals(7, target.limit());
266
267    ByteBuffer target2 = target.slice();
268    assertEquals(0, target2.position());
269    assertEquals(5, target2.limit());
270
271    byte[] actual2 = Bytes.toBytes(target2);
272    byte[] expected2 = { 2, 3, 4, 5, 6 };
273    assertArrayEquals(expected2, actual2);
274    assertEquals(0, target2.position());
275    assertEquals(5, target2.limit());
276  }
277
278  @Test
279  public void testGetBytesForByteBuffer() {
280    byte[] array = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
281    ByteBuffer target = ByteBuffer.wrap(array);
282    target.position(2);
283    target.limit(7);
284
285    byte[] actual = Bytes.getBytes(target);
286    byte[] expected = { 2, 3, 4, 5, 6 };
287    assertArrayEquals(expected, actual);
288    assertEquals(2, target.position());
289    assertEquals(7, target.limit());
290  }
291
292  @Test
293  public void testReadAsVLong() throws Exception {
294    long[] longs = { -1L, 123L, Long.MIN_VALUE, Long.MAX_VALUE };
295    for (long aLong : longs) {
296      ByteArrayOutputStream baos = new ByteArrayOutputStream();
297      DataOutputStream output = new DataOutputStream(baos);
298      WritableUtils.writeVLong(output, aLong);
299      byte[] long_bytes_no_offset = baos.toByteArray();
300      assertEquals(aLong, Bytes.readAsVLong(long_bytes_no_offset, 0));
301      byte[] long_bytes_with_offset = bytesWithOffset(long_bytes_no_offset);
302      assertEquals(aLong, Bytes.readAsVLong(long_bytes_with_offset, 1));
303    }
304  }
305
306  @Test
307  public void testToStringBinaryForBytes() {
308    byte[] array = { '0', '9', 'a', 'z', 'A', 'Z', '@', 1 };
309    String actual = Bytes.toStringBinary(array);
310    String expected = "09azAZ@\\x01";
311    assertEquals(expected, actual);
312
313    String actual2 = Bytes.toStringBinary(array, 2, 3);
314    String expected2 = "azA";
315    assertEquals(expected2, actual2);
316  }
317
318  @Test
319  public void testToStringBinaryForArrayBasedByteBuffer() {
320    byte[] array = { '0', '9', 'a', 'z', 'A', 'Z', '@', 1 };
321    ByteBuffer target = ByteBuffer.wrap(array);
322    String actual = Bytes.toStringBinary(target);
323    String expected = "09azAZ@\\x01";
324    assertEquals(expected, actual);
325  }
326
327  @Test
328  public void testToStringBinaryForReadOnlyByteBuffer() {
329    byte[] array = { '0', '9', 'a', 'z', 'A', 'Z', '@', 1 };
330    ByteBuffer target = ByteBuffer.wrap(array).asReadOnlyBuffer();
331    String actual = Bytes.toStringBinary(target);
332    String expected = "09azAZ@\\x01";
333    assertEquals(expected, actual);
334  }
335
336  @Test
337  public void testBinarySearch() {
338    byte[][] arr = { { 1 }, { 3 }, { 5 }, { 7 }, { 9 }, { 11 }, { 13 }, { 15 }, };
339    byte[] key1 = { 3, 1 };
340    byte[] key2 = { 4, 9 };
341    byte[] key2_2 = { 4 };
342    byte[] key3 = { 5, 11 };
343    byte[] key4 = { 0 };
344    byte[] key5 = { 2 };
345
346    assertEquals(1, Bytes.binarySearch(arr, key1, 0, 1));
347    assertEquals(0, Bytes.binarySearch(arr, key1, 1, 1));
348    assertEquals(-(2 + 1), Arrays.binarySearch(arr, key2_2, Bytes.BYTES_COMPARATOR));
349    assertEquals(-(2 + 1), Bytes.binarySearch(arr, key2, 0, 1));
350    assertEquals(4, Bytes.binarySearch(arr, key2, 1, 1));
351    assertEquals(2, Bytes.binarySearch(arr, key3, 0, 1));
352    assertEquals(5, Bytes.binarySearch(arr, key3, 1, 1));
353    assertEquals(-1, Bytes.binarySearch(arr, key4, 0, 1));
354    assertEquals(-2, Bytes.binarySearch(arr, key5, 0, 1));
355
356    // Search for values to the left and to the right of each item in the array.
357    for (int i = 0; i < arr.length; ++i) {
358      assertEquals(-(i + 1), Bytes.binarySearch(arr, new byte[] { (byte) (arr[i][0] - 1) }, 0, 1));
359      assertEquals(-(i + 2), Bytes.binarySearch(arr, new byte[] { (byte) (arr[i][0] + 1) }, 0, 1));
360    }
361  }
362
363  @Test
364  public void testToStringBytesBinaryReversible() {
365    byte[] randomBytes = new byte[1000];
366    for (int i = 0; i < 1000; i++) {
367      Bytes.random(randomBytes);
368      verifyReversibleForBytes(randomBytes);
369    }
370    // some specific cases
371    verifyReversibleForBytes(new byte[] {});
372    verifyReversibleForBytes(new byte[] { '\\', 'x', 'A', 'D' });
373    verifyReversibleForBytes(new byte[] { '\\', 'x', 'A', 'D', '\\' });
374  }
375
376  private void verifyReversibleForBytes(byte[] originalBytes) {
377    String convertedString = Bytes.toStringBinary(originalBytes);
378    byte[] convertedBytes = Bytes.toBytesBinary(convertedString);
379    if (Bytes.compareTo(originalBytes, convertedBytes) != 0) {
380      fail("Not reversible for\nbyte[]: " + Arrays.toString(originalBytes) + ",\nStringBinary: "
381        + convertedString);
382    }
383  }
384
385  @Test
386  public void testStartsWith() {
387    assertTrue(Bytes.startsWith(Bytes.toBytes("hello"), Bytes.toBytes("h")));
388    assertTrue(Bytes.startsWith(Bytes.toBytes("hello"), Bytes.toBytes("")));
389    assertTrue(Bytes.startsWith(Bytes.toBytes("hello"), Bytes.toBytes("hello")));
390    assertFalse(Bytes.startsWith(Bytes.toBytes("hello"), Bytes.toBytes("helloworld")));
391    assertFalse(Bytes.startsWith(Bytes.toBytes(""), Bytes.toBytes("hello")));
392  }
393
394  @Test
395  public void testIncrementBytes() {
396    assertTrue(checkTestIncrementBytes(10, 1));
397    assertTrue(checkTestIncrementBytes(12, 123435445));
398    assertTrue(checkTestIncrementBytes(124634654, 1));
399    assertTrue(checkTestIncrementBytes(10005460, 5005645));
400    assertTrue(checkTestIncrementBytes(1, -1));
401    assertTrue(checkTestIncrementBytes(10, -1));
402    assertTrue(checkTestIncrementBytes(10, -5));
403    assertTrue(checkTestIncrementBytes(1005435000, -5));
404    assertTrue(checkTestIncrementBytes(10, -43657655));
405    assertTrue(checkTestIncrementBytes(-1, 1));
406    assertTrue(checkTestIncrementBytes(-26, 5034520));
407    assertTrue(checkTestIncrementBytes(-10657200, 5));
408    assertTrue(checkTestIncrementBytes(-12343250, 45376475));
409    assertTrue(checkTestIncrementBytes(-10, -5));
410    assertTrue(checkTestIncrementBytes(-12343250, -5));
411    assertTrue(checkTestIncrementBytes(-12, -34565445));
412    assertTrue(checkTestIncrementBytes(-1546543452, -34565445));
413  }
414
415  private static boolean checkTestIncrementBytes(long val, long amount) {
416    byte[] value = Bytes.toBytes(val);
417    byte[] testValue = { -1, -1, -1, -1, -1, -1, -1, -1 };
418    if (value[0] > 0) {
419      testValue = new byte[Bytes.SIZEOF_LONG];
420    }
421    System.arraycopy(value, 0, testValue, testValue.length - value.length, value.length);
422
423    long incrementResult = Bytes.toLong(Bytes.incrementBytes(value, amount));
424
425    return (Bytes.toLong(testValue) + amount) == incrementResult;
426  }
427
428  @Test
429  public void testFixedSizeString() throws IOException {
430    ByteArrayOutputStream baos = new ByteArrayOutputStream();
431    DataOutputStream dos = new DataOutputStream(baos);
432    Bytes.writeStringFixedSize(dos, "Hello", 5);
433    Bytes.writeStringFixedSize(dos, "World", 18);
434    Bytes.writeStringFixedSize(dos, "", 9);
435
436    try {
437      // Use a long dash which is three bytes in UTF-8. If encoding happens
438      // using ISO-8859-1, this will fail.
439      Bytes.writeStringFixedSize(dos, "Too\u2013Long", 9);
440      fail("Exception expected");
441    } catch (IOException ex) {
442      assertEquals(
443        "Trying to write 10 bytes (Too\\xE2\\x80\\x93Long) into a field of " + "length 9",
444        ex.getMessage());
445    }
446
447    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
448    DataInputStream dis = new DataInputStream(bais);
449    assertEquals("Hello", Bytes.readStringFixedSize(dis, 5));
450    assertEquals("World", Bytes.readStringFixedSize(dis, 18));
451    assertEquals("", Bytes.readStringFixedSize(dis, 9));
452  }
453
454  @Test
455  public void testCopy() {
456    byte[] bytes = Bytes.toBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
457    byte[] copy = Bytes.copy(bytes);
458    assertNotSame(bytes, copy);
459    assertTrue(Bytes.equals(bytes, copy));
460  }
461
462  @Test
463  public void testToBytesBinaryTrailingBackslashes() {
464    try {
465      Bytes.toBytesBinary("abc\\x00\\x01\\");
466    } catch (StringIndexOutOfBoundsException ex) {
467      fail("Illegal string access: " + ex.getMessage());
468    }
469  }
470
471  @Test
472  public void testToStringBinary_toBytesBinary_Reversable() {
473    String bytes = Bytes.toStringBinary(Bytes.toBytes(2.17));
474    assertEquals(2.17, Bytes.toDouble(Bytes.toBytesBinary(bytes)), 0);
475  }
476
477  @Test
478  public void testUnsignedBinarySearch() {
479    byte[] bytes = new byte[] { 0, 5, 123, 127, -128, -100, -1 };
480    Assert.assertEquals(1, Bytes.unsignedBinarySearch(bytes, 0, bytes.length, (byte) 5));
481    Assert.assertEquals(3, Bytes.unsignedBinarySearch(bytes, 0, bytes.length, (byte) 127));
482    Assert.assertEquals(4, Bytes.unsignedBinarySearch(bytes, 0, bytes.length, (byte) -128));
483    Assert.assertEquals(5, Bytes.unsignedBinarySearch(bytes, 0, bytes.length, (byte) -100));
484    Assert.assertEquals(6, Bytes.unsignedBinarySearch(bytes, 0, bytes.length, (byte) -1));
485    Assert.assertEquals(-1 - 1, Bytes.unsignedBinarySearch(bytes, 0, bytes.length, (byte) 2));
486    Assert.assertEquals(-6 - 1, Bytes.unsignedBinarySearch(bytes, 0, bytes.length, (byte) -5));
487  }
488
489  @Test
490  public void testUnsignedIncrement() {
491    byte[] a = Bytes.toBytes(0);
492    int a2 = Bytes.toInt(Bytes.unsignedCopyAndIncrement(a), 0);
493    Assert.assertEquals(1, a2);
494
495    byte[] b = Bytes.toBytes(-1);
496    byte[] actual = Bytes.unsignedCopyAndIncrement(b);
497    Assert.assertNotSame(b, actual);
498    byte[] expected = new byte[] { 1, 0, 0, 0, 0 };
499    assertArrayEquals(expected, actual);
500
501    byte[] c = Bytes.toBytes(255);// should wrap to the next significant byte
502    int c2 = Bytes.toInt(Bytes.unsignedCopyAndIncrement(c), 0);
503    Assert.assertEquals(256, c2);
504  }
505
506  @Test
507  public void testIndexOf() {
508    byte[] array = Bytes.toBytes("hello");
509    assertEquals(1, Bytes.indexOf(array, (byte) 'e'));
510    assertEquals(4, Bytes.indexOf(array, (byte) 'o'));
511    assertEquals(-1, Bytes.indexOf(array, (byte) 'a'));
512    assertEquals(0, Bytes.indexOf(array, Bytes.toBytes("hel")));
513    assertEquals(2, Bytes.indexOf(array, Bytes.toBytes("ll")));
514    assertEquals(-1, Bytes.indexOf(array, Bytes.toBytes("hll")));
515  }
516
517  @Test
518  public void testContains() {
519    byte[] array = Bytes.toBytes("hello world");
520    assertTrue(Bytes.contains(array, (byte) 'e'));
521    assertTrue(Bytes.contains(array, (byte) 'd'));
522    assertFalse(Bytes.contains(array, (byte) 'a'));
523    assertTrue(Bytes.contains(array, Bytes.toBytes("world")));
524    assertTrue(Bytes.contains(array, Bytes.toBytes("ello")));
525    assertFalse(Bytes.contains(array, Bytes.toBytes("owo")));
526  }
527
528  @Test
529  public void testZero() {
530    byte[] array = Bytes.toBytes("hello");
531    Bytes.zero(array);
532    for (byte b : array) {
533      assertEquals(0, b);
534    }
535    array = Bytes.toBytes("hello world");
536    Bytes.zero(array, 2, 7);
537    assertFalse(array[0] == 0);
538    assertFalse(array[1] == 0);
539    for (int i = 2; i < 9; i++) {
540      assertEquals(0, array[i]);
541    }
542    for (int i = 9; i < array.length; i++) {
543      assertFalse(array[i] == 0);
544    }
545  }
546
547  @Test
548  public void testPutBuffer() {
549    byte[] b = new byte[100];
550    for (byte i = 0; i < 100; i++) {
551      Bytes.putByteBuffer(b, i, ByteBuffer.wrap(new byte[] { i }));
552    }
553    for (byte i = 0; i < 100; i++) {
554      Assert.assertEquals(i, b[i]);
555    }
556  }
557
558  @Test
559  public void testToFromHex() {
560    List<String> testStrings = new ArrayList<>(8);
561    testStrings.addAll(Arrays.asList("", "00", "A0", "ff", "FFffFFFFFFFFFF", "12",
562      "0123456789abcdef", "283462839463924623984692834692346ABCDFEDDCA0"));
563    for (String testString : testStrings) {
564      byte[] byteData = Bytes.fromHex(testString);
565      Assert.assertEquals(testString.length() / 2, byteData.length);
566      String result = Bytes.toHex(byteData);
567      Assert.assertTrue(testString.equalsIgnoreCase(result));
568    }
569
570    List<byte[]> testByteData = new ArrayList<>(5);
571    testByteData.addAll(Arrays.asList(new byte[0], new byte[1], new byte[10],
572      new byte[] { 1, 2, 3, 4, 5 }, new byte[] { (byte) 0xFF }));
573    Random rand = ThreadLocalRandom.current();
574    for (int i = 0; i < 20; i++) {
575      byte[] bytes = new byte[rand.nextInt(100)];
576      Bytes.random(bytes);
577      testByteData.add(bytes);
578    }
579
580    for (byte[] testData : testByteData) {
581      String hexString = Bytes.toHex(testData);
582      Assert.assertEquals(testData.length * 2, hexString.length());
583      byte[] result = Bytes.fromHex(hexString);
584      assertArrayEquals(testData, result);
585    }
586  }
587}