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 */ 22 public class HexEncoding { 23 24 /** Hidden constructor to prevent instantiation. */ HexEncoding()25 private HexEncoding() {} 26 27 private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray(); 28 29 /** 30 * Encodes the provided data as a sequence of hexadecimal characters. 31 */ encode(byte[] data)32 public static char[] encode(byte[] data) { 33 return encode(data, 0, data.length); 34 } 35 36 /** 37 * Encodes the provided data as a sequence of hexadecimal characters. 38 */ encode(byte[] data, int offset, int len)39 public static char[] encode(byte[] data, int offset, int len) { 40 char[] result = new char[len * 2]; 41 for (int i = 0; i < len; i++) { 42 byte b = data[offset + i]; 43 int resultIndex = 2 * i; 44 result[resultIndex] = (HEX_DIGITS[(b >>> 4) & 0x0f]); 45 result[resultIndex + 1] = (HEX_DIGITS[b & 0x0f]); 46 } 47 48 return result; 49 } 50 51 /** 52 * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar} 53 * is {@code true} odd-length inputs are allowed and the first character is interpreted 54 * as the lower bits of the first result byte. 55 * 56 * Throws an {@code IllegalArgumentException} if the input is malformed. 57 */ decode(char[] encoded, boolean allowSingleChar)58 public static byte[] decode(char[] encoded, boolean allowSingleChar) throws IllegalArgumentException { 59 int resultLengthBytes = (encoded.length + 1) / 2; 60 byte[] result = new byte[resultLengthBytes]; 61 62 int resultOffset = 0; 63 int i = 0; 64 if (allowSingleChar) { 65 if ((encoded.length % 2) != 0) { 66 // Odd number of digits -- the first digit is the lower 4 bits of the first result byte. 67 result[resultOffset++] = (byte) toDigit(encoded, i); 68 i++; 69 } 70 } else { 71 if ((encoded.length % 2) != 0) { 72 throw new IllegalArgumentException("Invalid input length: " + encoded.length); 73 } 74 } 75 76 for (int len = encoded.length; i < len; i += 2) { 77 result[resultOffset++] = (byte) ((toDigit(encoded, i) << 4) | toDigit(encoded, i + 1)); 78 } 79 80 return result; 81 } 82 toDigit(char[] str, int offset)83 private static int toDigit(char[] str, int offset) throws IllegalArgumentException { 84 // NOTE: that this isn't really a code point in the traditional sense, since we're 85 // just rejecting surrogate pairs outright. 86 int pseudoCodePoint = str[offset]; 87 88 if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') { 89 return pseudoCodePoint - '0'; 90 } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') { 91 return 10 + (pseudoCodePoint - 'a'); 92 } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') { 93 return 10 + (pseudoCodePoint - 'A'); 94 } 95 96 throw new IllegalArgumentException("Illegal char: " + str[offset] + 97 " at offset " + offset); 98 } 99 } 100