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