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 final class BigendianEncoding { 23 static final int LONG_BYTES = Long.SIZE / Byte.SIZE; 24 static final int BYTE_BASE16 = 2; 25 static final int LONG_BASE16 = BYTE_BASE16 * LONG_BYTES; 26 private static final String ALPHABET = "0123456789abcdef"; 27 private static final int ASCII_CHARACTERS = 128; 28 private static final char[] ENCODING = buildEncodingArray(); 29 private static final byte[] DECODING = buildDecodingArray(); 30 buildEncodingArray()31 private static char[] buildEncodingArray() { 32 char[] encoding = new char[512]; 33 for (int i = 0; i < 256; ++i) { 34 encoding[i] = ALPHABET.charAt(i >>> 4); 35 encoding[i | 0x100] = ALPHABET.charAt(i & 0xF); 36 } 37 return encoding; 38 } 39 buildDecodingArray()40 private static byte[] buildDecodingArray() { 41 byte[] decoding = new byte[ASCII_CHARACTERS]; 42 Arrays.fill(decoding, (byte) -1); 43 for (int i = 0; i < ALPHABET.length(); i++) { 44 char c = ALPHABET.charAt(i); 45 decoding[c] = (byte) i; 46 } 47 return decoding; 48 } 49 50 /** 51 * Returns the {@code long} value whose big-endian representation is stored in the first 8 bytes 52 * of {@code bytes} starting from the {@code offset}. 53 * 54 * @param bytes the byte array representation of the {@code long}. 55 * @param offset the starting offset in the byte array. 56 * @return the {@code long} value whose big-endian representation is given. 57 * @throws IllegalArgumentException if {@code bytes} has fewer than 8 elements. 58 */ longFromByteArray(byte[] bytes, int offset)59 static long longFromByteArray(byte[] bytes, int offset) { 60 Utils.checkArgument(bytes.length >= offset + LONG_BYTES, "array too small"); 61 return (bytes[offset] & 0xFFL) << 56 62 | (bytes[offset + 1] & 0xFFL) << 48 63 | (bytes[offset + 2] & 0xFFL) << 40 64 | (bytes[offset + 3] & 0xFFL) << 32 65 | (bytes[offset + 4] & 0xFFL) << 24 66 | (bytes[offset + 5] & 0xFFL) << 16 67 | (bytes[offset + 6] & 0xFFL) << 8 68 | (bytes[offset + 7] & 0xFFL); 69 } 70 71 /** 72 * Stores the big-endian representation of {@code value} in the {@code dest} starting from the 73 * {@code destOffset}. 74 * 75 * @param value the value to be converted. 76 * @param dest the destination byte array. 77 * @param destOffset the starting offset in the destination byte array. 78 */ longToByteArray(long value, byte[] dest, int destOffset)79 static void longToByteArray(long value, byte[] dest, int destOffset) { 80 Utils.checkArgument(dest.length >= destOffset + LONG_BYTES, "array too small"); 81 dest[destOffset + 7] = (byte) (value & 0xFFL); 82 dest[destOffset + 6] = (byte) (value >> 8 & 0xFFL); 83 dest[destOffset + 5] = (byte) (value >> 16 & 0xFFL); 84 dest[destOffset + 4] = (byte) (value >> 24 & 0xFFL); 85 dest[destOffset + 3] = (byte) (value >> 32 & 0xFFL); 86 dest[destOffset + 2] = (byte) (value >> 40 & 0xFFL); 87 dest[destOffset + 1] = (byte) (value >> 48 & 0xFFL); 88 dest[destOffset] = (byte) (value >> 56 & 0xFFL); 89 } 90 91 /** 92 * Returns the {@code long} value whose base16 representation is stored in the first 16 chars of 93 * {@code chars} starting from the {@code offset}. 94 * 95 * @param chars the base16 representation of the {@code long}. 96 * @param offset the starting offset in the {@code CharSequence}. 97 */ longFromBase16String(CharSequence chars, int offset)98 static long longFromBase16String(CharSequence chars, int offset) { 99 Utils.checkArgument(chars.length() >= offset + LONG_BASE16, "chars too small"); 100 return (decodeByte(chars.charAt(offset), chars.charAt(offset + 1)) & 0xFFL) << 56 101 | (decodeByte(chars.charAt(offset + 2), chars.charAt(offset + 3)) & 0xFFL) << 48 102 | (decodeByte(chars.charAt(offset + 4), chars.charAt(offset + 5)) & 0xFFL) << 40 103 | (decodeByte(chars.charAt(offset + 6), chars.charAt(offset + 7)) & 0xFFL) << 32 104 | (decodeByte(chars.charAt(offset + 8), chars.charAt(offset + 9)) & 0xFFL) << 24 105 | (decodeByte(chars.charAt(offset + 10), chars.charAt(offset + 11)) & 0xFFL) << 16 106 | (decodeByte(chars.charAt(offset + 12), chars.charAt(offset + 13)) & 0xFFL) << 8 107 | (decodeByte(chars.charAt(offset + 14), chars.charAt(offset + 15)) & 0xFFL); 108 } 109 110 /** 111 * Appends the base16 encoding of the specified {@code value} to the {@code dest}. 112 * 113 * @param value the value to be converted. 114 * @param dest the destination char array. 115 * @param destOffset the starting offset in the destination char array. 116 */ longToBase16String(long value, char[] dest, int destOffset)117 static void longToBase16String(long value, char[] dest, int destOffset) { 118 byteToBase16((byte) (value >> 56 & 0xFFL), dest, destOffset); 119 byteToBase16((byte) (value >> 48 & 0xFFL), dest, destOffset + BYTE_BASE16); 120 byteToBase16((byte) (value >> 40 & 0xFFL), dest, destOffset + 2 * BYTE_BASE16); 121 byteToBase16((byte) (value >> 32 & 0xFFL), dest, destOffset + 3 * BYTE_BASE16); 122 byteToBase16((byte) (value >> 24 & 0xFFL), dest, destOffset + 4 * BYTE_BASE16); 123 byteToBase16((byte) (value >> 16 & 0xFFL), dest, destOffset + 5 * BYTE_BASE16); 124 byteToBase16((byte) (value >> 8 & 0xFFL), dest, destOffset + 6 * BYTE_BASE16); 125 byteToBase16((byte) (value & 0xFFL), dest, destOffset + 7 * BYTE_BASE16); 126 } 127 128 /** 129 * Encodes the specified byte, and returns the encoded {@code String}. 130 * 131 * @param value the value to be converted. 132 * @param dest the destination char array. 133 * @param destOffset the starting offset in the destination char array. 134 */ byteToBase16String(byte value, char[] dest, int destOffset)135 static void byteToBase16String(byte value, char[] dest, int destOffset) { 136 byteToBase16(value, dest, destOffset); 137 } 138 139 /** 140 * Decodes the specified two character sequence, and returns the resulting {@code byte}. 141 * 142 * @param chars the character sequence to be decoded. 143 * @param offset the starting offset in the {@code CharSequence}. 144 * @return the resulting {@code byte} 145 * @throws IllegalArgumentException if the input is not a valid encoded string according to this 146 * encoding. 147 */ byteFromBase16String(CharSequence chars, int offset)148 static byte byteFromBase16String(CharSequence chars, int offset) { 149 Utils.checkArgument(chars.length() >= offset + 2, "chars too small"); 150 return decodeByte(chars.charAt(offset), chars.charAt(offset + 1)); 151 } 152 decodeByte(char hi, char lo)153 private static byte decodeByte(char hi, char lo) { 154 Utils.checkArgument(lo < ASCII_CHARACTERS && DECODING[lo] != -1, "invalid character " + lo); 155 Utils.checkArgument(hi < ASCII_CHARACTERS && DECODING[hi] != -1, "invalid character " + hi); 156 int decoded = DECODING[hi] << 4 | DECODING[lo]; 157 return (byte) decoded; 158 } 159 160 private static void byteToBase16(byte value, char[] dest, int destOffset) { 161 int b = value & 0xFF; 162 dest[destOffset] = ENCODING[b]; 163 dest[destOffset + 1] = ENCODING[b | 0x100]; 164 } 165 166 private BigendianEncoding() {} 167 } 168