1   /**
2    * Copyright 2009 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.util;
21  
22  import java.io.ByteArrayInputStream;
23  import java.io.ByteArrayOutputStream;
24  import java.io.DataInputStream;
25  import java.io.DataOutputStream;
26  import java.io.IOException;
27  import java.math.BigDecimal;
28  import java.math.BigInteger;
29  import java.util.Arrays;
30  import java.util.Random;
31  
32  import junit.framework.TestCase;
33  import org.apache.hadoop.hbase.SmallTests;
34  import org.junit.experimental.categories.Category;
35  
36  @Category(SmallTests.class)
37  public class TestBytes extends TestCase {
38    public void testNullHashCode() {
39      byte [] b = null;
40      Exception ee = null;
41      try {
42        Bytes.hashCode(b);
43      } catch (Exception e) {
44        ee = e;
45      }
46      assertNotNull(ee);
47    }
48  
49    public void testSplit() throws Exception {
50      byte [] lowest = Bytes.toBytes("AAA");
51      byte [] middle = Bytes.toBytes("CCC");
52      byte [] highest = Bytes.toBytes("EEE");
53      byte [][] parts = Bytes.split(lowest, highest, 1);
54      for (int i = 0; i < parts.length; i++) {
55        System.out.println(Bytes.toString(parts[i]));
56      }
57      assertEquals(3, parts.length);
58      assertTrue(Bytes.equals(parts[1], middle));
59      // Now divide into three parts.  Change highest so split is even.
60      highest = Bytes.toBytes("DDD");
61      parts = Bytes.split(lowest, highest, 2);
62      for (int i = 0; i < parts.length; i++) {
63        System.out.println(Bytes.toString(parts[i]));
64      }
65      assertEquals(4, parts.length);
66      // Assert that 3rd part is 'CCC'.
67      assertTrue(Bytes.equals(parts[2], middle));
68    }
69  
70    public void testSplit2() throws Exception {
71      // More split tests.
72      byte [] lowest = Bytes.toBytes("http://A");
73      byte [] highest = Bytes.toBytes("http://z");
74      byte [] middle = Bytes.toBytes("http://]");
75      byte [][] parts = Bytes.split(lowest, highest, 1);
76      for (int i = 0; i < parts.length; i++) {
77        System.out.println(Bytes.toString(parts[i]));
78      }
79      assertEquals(3, parts.length);
80      assertTrue(Bytes.equals(parts[1], middle));
81    }
82  
83    public void testSplit3() throws Exception {
84      // Test invalid split cases
85      byte [] low = { 1, 1, 1 };
86      byte [] high = { 1, 1, 3 };
87  
88      // If swapped, should throw IAE
89      try {
90        Bytes.split(high, low, 1);
91        assertTrue("Should not be able to split if low > high", false);
92      } catch(IllegalArgumentException iae) {
93        // Correct
94      }
95  
96      // Single split should work
97      byte [][] parts = Bytes.split(low, high, 1);
98      for (int i = 0; i < parts.length; i++) {
99        System.out.println("" + i + " -> " + Bytes.toStringBinary(parts[i]));
100     }
101     assertTrue("Returned split should have 3 parts but has " + parts.length, parts.length == 3);
102 
103     // If split more than once, this should fail
104     parts = Bytes.split(low, high, 2);
105     assertTrue("Returned split but should have failed", parts == null);
106 
107     // Split 0 times should throw IAE
108     try {
109       parts = Bytes.split(low, high, 0);
110       assertTrue("Should not be able to split 0 times", false);
111     } catch(IllegalArgumentException iae) {
112       // Correct
113     }
114   }
115 
116   public void testToInt() throws Exception {
117     int [] ints = {-1, 123, Integer.MIN_VALUE, Integer.MAX_VALUE};
118     for (int i = 0; i < ints.length; i++) {
119       byte [] b = Bytes.toBytes(ints[i]);
120       assertEquals(ints[i], Bytes.toInt(b));
121       byte [] b2 = bytesWithOffset(b);
122       assertEquals(ints[i], Bytes.toInt(b2, 1));
123       assertEquals(ints[i], Bytes.toInt(b2, 1, Bytes.SIZEOF_INT));
124     }
125   }
126 
127   public void testToLong() throws Exception {
128     long [] longs = {-1l, 123l, Long.MIN_VALUE, Long.MAX_VALUE};
129     for (int i = 0; i < longs.length; i++) {
130       byte [] b = Bytes.toBytes(longs[i]);
131       assertEquals(longs[i], Bytes.toLong(b));
132       byte [] b2 = bytesWithOffset(b);
133       assertEquals(longs[i], Bytes.toLong(b2, 1));
134       assertEquals(longs[i], Bytes.toLong(b2, 1, Bytes.SIZEOF_LONG));
135     }
136   }
137 
138   public void testToFloat() throws Exception {
139     float [] floats = {-1f, 123.123f, Float.MAX_VALUE};
140     for (int i = 0; i < floats.length; i++) {
141       byte [] b = Bytes.toBytes(floats[i]);
142       assertEquals(floats[i], Bytes.toFloat(b));
143       byte [] b2 = bytesWithOffset(b);
144       assertEquals(floats[i], Bytes.toFloat(b2, 1));
145     }
146   }
147 
148   public void testToDouble() throws Exception {
149     double [] doubles = {Double.MIN_VALUE, Double.MAX_VALUE};
150     for (int i = 0; i < doubles.length; i++) {
151       byte [] b = Bytes.toBytes(doubles[i]);
152       assertEquals(doubles[i], Bytes.toDouble(b));
153       byte [] b2 = bytesWithOffset(b);
154       assertEquals(doubles[i], Bytes.toDouble(b2, 1));
155     }
156   }
157 
158   public void testToBigDecimal() throws Exception {
159     BigDecimal [] decimals = {new BigDecimal("-1"), new BigDecimal("123.123"),
160       new BigDecimal("123123123123")};
161     for (int i = 0; i < decimals.length; i++) {
162       byte [] b = Bytes.toBytes(decimals[i]);
163       assertEquals(decimals[i], Bytes.toBigDecimal(b));
164       byte [] b2 = bytesWithOffset(b);
165       assertEquals(decimals[i], Bytes.toBigDecimal(b2, 1, b.length));
166     }
167   }
168 
169   private byte [] bytesWithOffset(byte [] src) {
170     // add one byte in front to test offset
171     byte [] result = new byte[src.length + 1];
172     result[0] = (byte) 0xAA;
173     System.arraycopy(src, 0, result, 1, src.length);
174     return result;
175   }
176 
177   public void testBinarySearch() throws Exception {
178     byte [][] arr = {
179         {1},
180         {3},
181         {5},
182         {7},
183         {9},
184         {11},
185         {13},
186         {15},
187     };
188     byte [] key1 = {3,1};
189     byte [] key2 = {4,9};
190     byte [] key2_2 = {4};
191     byte [] key3 = {5,11};
192     byte [] key4 = {0};
193     byte [] key5 = {2};
194 
195     assertEquals(1, Bytes.binarySearch(arr, key1, 0, 1,
196       Bytes.BYTES_RAWCOMPARATOR));
197     assertEquals(0, Bytes.binarySearch(arr, key1, 1, 1,
198       Bytes.BYTES_RAWCOMPARATOR));
199     assertEquals(-(2+1), Arrays.binarySearch(arr, key2_2,
200       Bytes.BYTES_COMPARATOR));
201     assertEquals(-(2+1), Bytes.binarySearch(arr, key2, 0, 1,
202       Bytes.BYTES_RAWCOMPARATOR));
203     assertEquals(4, Bytes.binarySearch(arr, key2, 1, 1,
204       Bytes.BYTES_RAWCOMPARATOR));
205     assertEquals(2, Bytes.binarySearch(arr, key3, 0, 1,
206       Bytes.BYTES_RAWCOMPARATOR));
207     assertEquals(5, Bytes.binarySearch(arr, key3, 1, 1,
208       Bytes.BYTES_RAWCOMPARATOR));
209     assertEquals(-1,
210       Bytes.binarySearch(arr, key4, 0, 1, Bytes.BYTES_RAWCOMPARATOR));
211     assertEquals(-2,
212       Bytes.binarySearch(arr, key5, 0, 1, Bytes.BYTES_RAWCOMPARATOR));
213 
214     // Search for values to the left and to the right of each item in the array.
215     for (int i = 0; i < arr.length; ++i) {
216       assertEquals(-(i + 1), Bytes.binarySearch(arr,
217           new byte[] { (byte) (arr[i][0] - 1) }, 0, 1,
218           Bytes.BYTES_RAWCOMPARATOR));
219       assertEquals(-(i + 2), Bytes.binarySearch(arr,
220           new byte[] { (byte) (arr[i][0] + 1) }, 0, 1,
221           Bytes.BYTES_RAWCOMPARATOR));
222     }
223   }
224 
225   public void testToStringBytesBinaryReversible() {  
226     //  let's run test with 1000 randomly generated byte arrays
227     Random rand = new Random(System.currentTimeMillis());
228     byte[] randomBytes = new byte[1000];
229     for (int i = 0; i < 1000; i++) {
230       rand.nextBytes(randomBytes);
231       verifyReversibleForBytes(randomBytes); 
232     }
233     
234         
235     //  some specific cases
236     verifyReversibleForBytes(new  byte[] {});
237     verifyReversibleForBytes(new  byte[] {'\\', 'x', 'A', 'D'});
238     verifyReversibleForBytes(new  byte[] {'\\', 'x', 'A', 'D', '\\'});
239   }
240 
241   private void verifyReversibleForBytes(byte[] originalBytes) {  
242     String convertedString = Bytes.toStringBinary(originalBytes);
243     byte[] convertedBytes = Bytes.toBytesBinary(convertedString);
244     if (Bytes.compareTo(originalBytes, convertedBytes) != 0) {
245       fail("Not reversible for\nbyte[]: " + Arrays.toString(originalBytes) +
246           ",\nStringBinary: " + convertedString);
247     }
248   }
249 
250   public void testStartsWith() {
251     assertTrue(Bytes.startsWith(Bytes.toBytes("hello"), Bytes.toBytes("h")));
252     assertTrue(Bytes.startsWith(Bytes.toBytes("hello"), Bytes.toBytes("")));
253     assertTrue(Bytes.startsWith(Bytes.toBytes("hello"), Bytes.toBytes("hello")));
254     assertFalse(Bytes.startsWith(Bytes.toBytes("hello"), Bytes.toBytes("helloworld")));
255     assertFalse(Bytes.startsWith(Bytes.toBytes(""), Bytes.toBytes("hello")));
256   }
257 
258   public void testIncrementBytes() throws IOException {
259 
260     assertTrue(checkTestIncrementBytes(10, 1));
261     assertTrue(checkTestIncrementBytes(12, 123435445));
262     assertTrue(checkTestIncrementBytes(124634654, 1));
263     assertTrue(checkTestIncrementBytes(10005460, 5005645));
264     assertTrue(checkTestIncrementBytes(1, -1));
265     assertTrue(checkTestIncrementBytes(10, -1));
266     assertTrue(checkTestIncrementBytes(10, -5));
267     assertTrue(checkTestIncrementBytes(1005435000, -5));
268     assertTrue(checkTestIncrementBytes(10, -43657655));
269     assertTrue(checkTestIncrementBytes(-1, 1));
270     assertTrue(checkTestIncrementBytes(-26, 5034520));
271     assertTrue(checkTestIncrementBytes(-10657200, 5));
272     assertTrue(checkTestIncrementBytes(-12343250, 45376475));
273     assertTrue(checkTestIncrementBytes(-10, -5));
274     assertTrue(checkTestIncrementBytes(-12343250, -5));
275     assertTrue(checkTestIncrementBytes(-12, -34565445));
276     assertTrue(checkTestIncrementBytes(-1546543452, -34565445));
277   }
278 
279   private static boolean checkTestIncrementBytes(long val, long amount)
280   throws IOException {
281     byte[] value = Bytes.toBytes(val);
282     byte [] testValue = {-1, -1, -1, -1, -1, -1, -1, -1};
283     if (value[0] > 0) {
284       testValue = new byte[Bytes.SIZEOF_LONG];
285     }
286     System.arraycopy(value, 0, testValue, testValue.length - value.length,
287         value.length);
288 
289     long incrementResult = Bytes.toLong(Bytes.incrementBytes(value, amount));
290 
291     return (Bytes.toLong(testValue) + amount) == incrementResult;
292   }
293 
294   public void testFixedSizeString() throws IOException {
295     ByteArrayOutputStream baos = new ByteArrayOutputStream();
296     DataOutputStream dos = new DataOutputStream(baos);
297     Bytes.writeStringFixedSize(dos, "Hello", 5);
298     Bytes.writeStringFixedSize(dos, "World", 18);
299     Bytes.writeStringFixedSize(dos, "", 9);
300 
301     try {
302       // Use a long dash which is three bytes in UTF-8. If encoding happens
303       // using ISO-8859-1, this will fail.
304       Bytes.writeStringFixedSize(dos, "Too\u2013Long", 9);
305       fail("Exception expected");
306     } catch (IOException ex) {
307       assertEquals(
308           "Trying to write 10 bytes (Too\\xE2\\x80\\x93Long) into a field of " +
309           "length 9", ex.getMessage());
310     }
311 
312     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
313     DataInputStream dis = new DataInputStream(bais);
314     assertEquals("Hello", Bytes.readStringFixedSize(dis, 5));
315     assertEquals("World", Bytes.readStringFixedSize(dis, 18));
316     assertEquals("", Bytes.readStringFixedSize(dis, 9));
317   }
318 
319   public void testToBytesBinaryTrailingBackslashes() throws Exception {
320     try {
321       Bytes.toBytesBinary("abc\\x00\\x01\\");
322     } catch (StringIndexOutOfBoundsException ex) {
323       fail("Illegal string access: " + ex.getMessage());
324     }
325   }
326 
327   @org.junit.Rule
328   public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
329     new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
330 }
331