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. */ 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 */ 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 */ 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 * Encodes the provided data as a sequence of hexadecimal characters. 53 */ 54 public static String encodeToString(byte[] data) { 55 return new String(encode(data)); 56 } 57 58 /** 59 * Decodes the provided hexadecimal string into a byte array. Odd-length inputs 60 * are not allowed. 61 * 62 * Throws an {@code IllegalArgumentException} if the input is malformed. 63 */ 64 public static byte[] decode(String encoded) throws IllegalArgumentException { 65 return decode(encoded.toCharArray()); 66 } 67 68 /** 69 * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar} 70 * is {@code true} odd-length inputs are allowed and the first character is interpreted 71 * as the lower bits of the first result byte. 72 * 73 * Throws an {@code IllegalArgumentException} if the input is malformed. 74 */ 75 public static byte[] decode(String encoded, boolean allowSingleChar) throws IllegalArgumentException { 76 return decode(encoded.toCharArray(), allowSingleChar); 77 } 78 79 /** 80 * Decodes the provided hexadecimal string into a byte array. Odd-length inputs 81 * are not allowed. 82 * 83 * Throws an {@code IllegalArgumentException} if the input is malformed. 84 */ 85 public static byte[] decode(char[] encoded) throws IllegalArgumentException { 86 return decode(encoded, false); 87 } 88 89 /** 90 * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar} 91 * is {@code true} odd-length inputs are allowed and the first character is interpreted 92 * as the lower bits of the first result byte. 93 * 94 * Throws an {@code IllegalArgumentException} if the input is malformed. 95 */ 96 public static byte[] decode(char[] encoded, boolean allowSingleChar) throws IllegalArgumentException { 97 int resultLengthBytes = (encoded.length + 1) / 2; 98 byte[] result = new byte[resultLengthBytes]; 99 100 int resultOffset = 0; 101 int i = 0; 102 if (allowSingleChar) { 103 if ((encoded.length % 2) != 0) { 104 // Odd number of digits -- the first digit is the lower 4 bits of the first result byte. 105 result[resultOffset++] = (byte) toDigit(encoded, i); 106 i++; 107 } 108 } else { 109 if ((encoded.length % 2) != 0) { 110 throw new IllegalArgumentException("Invalid input length: " + encoded.length); 111 } 112 } 113 114 for (int len = encoded.length; i < len; i += 2) { 115 result[resultOffset++] = (byte) ((toDigit(encoded, i) << 4) | toDigit(encoded, i + 1)); 116 } 117 118 return result; 119 } 120 121 private static int toDigit(char[] str, int offset) throws IllegalArgumentException { 122 // NOTE: that this isn't really a code point in the traditional sense, since we're 123 // just rejecting surrogate pairs outright. 124 int pseudoCodePoint = str[offset]; 125 126 if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') { 127 return pseudoCodePoint - '0'; 128 } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') { 129 return 10 + (pseudoCodePoint - 'a'); 130 } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') { 131 return 10 + (pseudoCodePoint - 'A'); 132 } 133 134 throw new IllegalArgumentException("Illegal char: " + str[offset] + 135 " at offset " + offset); 136 } 137 } 138