111f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath/* 211f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * Copyright (C) 2014 The Android Open Source Project 311f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * 411f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License"); 511f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * you may not use this file except in compliance with the License. 611f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * You may obtain a copy of the License at 711f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * 811f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * http://www.apache.org/licenses/LICENSE-2.0 911f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * 1011f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * Unless required by applicable law or agreed to in writing, software 1111f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, 1211f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1311f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * See the License for the specific language governing permissions and 1411f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * limitations under the License. 1511f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath */ 1611f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath 1711f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamathpackage libcore.util; 1811f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath 1911f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath/** 2011f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * Hexadecimal encoding where each byte is represented by two hexadecimal digits. 2111f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath */ 2211f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamathpublic class HexEncoding { 2311f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath 2411f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath /** Hidden constructor to prevent instantiation. */ 2511f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath private HexEncoding() {} 2611f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath 278c704b4fa9f072cc074897b99ea8f71a02757f33Narayan Kamath private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray(); 2811f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath 2911f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath /** 3011f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * Encodes the provided data as a sequence of hexadecimal characters. 3111f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath */ 3211f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath public static char[] encode(byte[] data) { 3311f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath return encode(data, 0, data.length); 3411f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath } 3511f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath 3611f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath /** 3711f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * Encodes the provided data as a sequence of hexadecimal characters. 3811f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath */ 3911f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath public static char[] encode(byte[] data, int offset, int len) { 4011f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath char[] result = new char[len * 2]; 4111f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath for (int i = 0; i < len; i++) { 4211f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath byte b = data[offset + i]; 4311f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath int resultIndex = 2 * i; 4411f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath result[resultIndex] = (HEX_DIGITS[(b >>> 4) & 0x0f]); 4511f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath result[resultIndex + 1] = (HEX_DIGITS[b & 0x0f]); 4611f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath } 4711f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath 4811f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath return result; 4911f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath } 5011f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath 5111f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath /** 5211f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar} 5311f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * is {@code true} odd-length inputs are allowed and the first character is interpreted 5411f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * as the lower bits of the first result byte. 5511f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * 5611f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath * Throws an {@code IllegalArgumentException} if the input is malformed. 5711f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath */ 5811f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath public static byte[] decode(char[] encoded, boolean allowSingleChar) throws IllegalArgumentException { 5911f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath int resultLengthBytes = (encoded.length + 1) / 2; 6011f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath byte[] result = new byte[resultLengthBytes]; 6111f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath 6211f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath int resultOffset = 0; 6311f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath int i = 0; 6411f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath if (allowSingleChar) { 6511f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath if ((encoded.length % 2) != 0) { 6611f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath // Odd number of digits -- the first digit is the lower 4 bits of the first result byte. 6711f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath result[resultOffset++] = (byte) toDigit(encoded, i); 6811f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath i++; 6911f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath } 7011f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath } else { 7111f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath if ((encoded.length % 2) != 0) { 7211f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath throw new IllegalArgumentException("Invalid input length: " + encoded.length); 7311f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath } 7411f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath } 7511f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath 7611f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath for (int len = encoded.length; i < len; i += 2) { 7711f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath result[resultOffset++] = (byte) ((toDigit(encoded, i) << 4) | toDigit(encoded, i + 1)); 7811f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath } 7911f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath 8011f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath return result; 8111f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath } 8211f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath 8311f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath private static int toDigit(char[] str, int offset) throws IllegalArgumentException { 8411f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath // NOTE: that this isn't really a code point in the traditional sense, since we're 8511f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath // just rejecting surrogate pairs outright. 8611f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath int pseudoCodePoint = str[offset]; 8711f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath 8811f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') { 8911f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath return pseudoCodePoint - '0'; 9011f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') { 9111f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath return 10 + (pseudoCodePoint - 'a'); 9211f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') { 9311f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath return 10 + (pseudoCodePoint - 'A'); 9411f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath } 9511f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath 9611f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath throw new IllegalArgumentException("Illegal char: " + str[offset] + 9711f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath " at offset " + offset); 9811f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath } 9911f82d1a94ebe1becb7e7d09dd3343ce117bdd46Narayan Kamath} 100