1 /* 2 * Copyright 2018, OpenCensus Authors 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 io.opencensus.trace; 18 19 import io.opencensus.internal.Utils; 20 import java.util.Arrays; 21 22 /** Internal copy of the Guava implementation of the {@code BaseEncoding.base16().lowerCase()}. */ 23 final class LowerCaseBase16Encoding { 24 private static final String ALPHABET = "0123456789abcdef"; 25 private static final int ASCII_CHARACTERS = 128; 26 private static final char[] ENCODING = buildEncodingArray(); 27 private static final byte[] DECODING = buildDecodingArray(); 28 buildEncodingArray()29 private static char[] buildEncodingArray() { 30 char[] encoding = new char[512]; 31 for (int i = 0; i < 256; ++i) { 32 encoding[i] = ALPHABET.charAt(i >>> 4); 33 encoding[i | 0x100] = ALPHABET.charAt(i & 0xF); 34 } 35 return encoding; 36 } 37 buildDecodingArray()38 private static byte[] buildDecodingArray() { 39 byte[] decoding = new byte[ASCII_CHARACTERS]; 40 Arrays.fill(decoding, (byte) -1); 41 for (int i = 0; i < ALPHABET.length(); i++) { 42 char c = ALPHABET.charAt(i); 43 decoding[c] = (byte) i; 44 } 45 return decoding; 46 } 47 48 /** 49 * Encodes the specified byte array, and returns the encoded {@code String}. 50 * 51 * @param bytes byte array to be encoded. 52 * @return the encoded {@code String}. 53 */ encodeToString(byte[] bytes)54 static String encodeToString(byte[] bytes) { 55 StringBuilder stringBuilder = new StringBuilder(bytes.length * 2); 56 for (byte byteVal : bytes) { 57 int b = byteVal & 0xFF; 58 stringBuilder.append(ENCODING[b]); 59 stringBuilder.append(ENCODING[b | 0x100]); 60 } 61 return stringBuilder.toString(); 62 } 63 64 /** 65 * Decodes the specified character sequence, and returns the resulting {@code byte[]}. 66 * 67 * @param chars the character sequence to be decoded. 68 * @return the resulting {@code byte[]} 69 * @throws IllegalArgumentException if the input is not a valid encoded string according to this 70 * encoding. 71 */ decodeToBytes(CharSequence chars)72 static byte[] decodeToBytes(CharSequence chars) { 73 Utils.checkArgument(chars.length() % 2 == 0, "Invalid input length " + chars.length()); 74 int bytesWritten = 0; 75 byte[] bytes = new byte[chars.length() / 2]; 76 for (int i = 0; i < chars.length(); i += 2) { 77 bytes[bytesWritten++] = decodeByte(chars.charAt(i), chars.charAt(i + 1)); 78 } 79 return bytes; 80 } 81 decodeByte(char hi, char lo)82 private static byte decodeByte(char hi, char lo) { 83 Utils.checkArgument(lo < ASCII_CHARACTERS && DECODING[lo] != -1, "Invalid character " + lo); 84 Utils.checkArgument(hi < ASCII_CHARACTERS && DECODING[hi] != -1, "Invalid character " + hi); 85 int decoded = DECODING[hi] << 4 | DECODING[lo]; 86 return (byte) decoded; 87 } 88 89 // Private constructor to disallow instances. 90 private LowerCaseBase16Encoding() {} 91 } 92