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