• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package libcore.util;
18 
19 /**
20  * Hexadecimal encoding where each byte is represented by two hexadecimal digits.
21  * @hide
22  */
23 public class HexEncoding {
24 
25     /** Hidden constructor to prevent instantiation. */
HexEncoding()26     private HexEncoding() {}
27 
28     private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
29 
30     /**
31      * Encodes the provided data as a sequence of hexadecimal characters.
32      */
encode(byte[] data)33     public static char[] encode(byte[] data) {
34         return encode(data, 0, data.length);
35     }
36 
37     /**
38      * Encodes the provided data as a sequence of hexadecimal characters.
39      */
encode(byte[] data, int offset, int len)40     public static char[] encode(byte[] data, int offset, int len) {
41         char[] result = new char[len * 2];
42         for (int i = 0; i < len; i++) {
43             byte b = data[offset + i];
44             int resultIndex = 2 * i;
45             result[resultIndex] = (HEX_DIGITS[(b >>> 4) & 0x0f]);
46             result[resultIndex + 1] = (HEX_DIGITS[b & 0x0f]);
47         }
48 
49         return result;
50     }
51 
52     /**
53      * Encodes the provided data as a sequence of hexadecimal characters.
54      */
encodeToString(byte[] data)55     public static String encodeToString(byte[] data) {
56         return new String(encode(data));
57     }
58 
59     /**
60      * Decodes the provided hexadecimal string into a byte array.  Odd-length inputs
61      * are not allowed.
62      *
63      * Throws an {@code IllegalArgumentException} if the input is malformed.
64      */
decode(String encoded)65     public static byte[] decode(String encoded) throws IllegalArgumentException {
66         return decode(encoded.toCharArray());
67     }
68 
69     /**
70      * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar}
71      * is {@code true} odd-length inputs are allowed and the first character is interpreted
72      * as the lower bits of the first result byte.
73      *
74      * Throws an {@code IllegalArgumentException} if the input is malformed.
75      */
decode(String encoded, boolean allowSingleChar)76     public static byte[] decode(String encoded, boolean allowSingleChar) throws IllegalArgumentException {
77         return decode(encoded.toCharArray(), allowSingleChar);
78     }
79 
80     /**
81      * Decodes the provided hexadecimal string into a byte array.  Odd-length inputs
82      * are not allowed.
83      *
84      * Throws an {@code IllegalArgumentException} if the input is malformed.
85      */
decode(char[] encoded)86     public static byte[] decode(char[] encoded) throws IllegalArgumentException {
87         return decode(encoded, false);
88     }
89 
90     /**
91      * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar}
92      * is {@code true} odd-length inputs are allowed and the first character is interpreted
93      * as the lower bits of the first result byte.
94      *
95      * Throws an {@code IllegalArgumentException} if the input is malformed.
96      */
decode(char[] encoded, boolean allowSingleChar)97     public static byte[] decode(char[] encoded, boolean allowSingleChar) throws IllegalArgumentException {
98         int resultLengthBytes = (encoded.length + 1) / 2;
99         byte[] result = new byte[resultLengthBytes];
100 
101         int resultOffset = 0;
102         int i = 0;
103         if (allowSingleChar) {
104             if ((encoded.length % 2) != 0) {
105                 // Odd number of digits -- the first digit is the lower 4 bits of the first result byte.
106                 result[resultOffset++] = (byte) toDigit(encoded, i);
107                 i++;
108             }
109         } else {
110             if ((encoded.length % 2) != 0) {
111                 throw new IllegalArgumentException("Invalid input length: " + encoded.length);
112             }
113         }
114 
115         for (int len = encoded.length; i < len; i += 2) {
116             result[resultOffset++] = (byte) ((toDigit(encoded, i) << 4) | toDigit(encoded, i + 1));
117         }
118 
119         return result;
120     }
121 
toDigit(char[] str, int offset)122     private static int toDigit(char[] str, int offset) throws IllegalArgumentException {
123         // NOTE: that this isn't really a code point in the traditional sense, since we're
124         // just rejecting surrogate pairs outright.
125         int pseudoCodePoint = str[offset];
126 
127         if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') {
128             return pseudoCodePoint - '0';
129         } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') {
130             return 10 + (pseudoCodePoint - 'a');
131         } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') {
132             return 10 + (pseudoCodePoint - 'A');
133         }
134 
135         throw new IllegalArgumentException("Illegal char: " + str[offset] +
136                 " at offset " + offset);
137     }
138 }
139