1 /* 2 * Copyright 2016-17, 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.common.Internal; 20 import io.opencensus.internal.Utils; 21 import java.util.Arrays; 22 import java.util.Random; 23 import javax.annotation.Nullable; 24 import javax.annotation.concurrent.Immutable; 25 26 /** 27 * A class that represents a trace identifier. A valid trace identifier is a 16-byte array with at 28 * least one non-zero byte. 29 * 30 * @since 0.5 31 */ 32 @Immutable 33 public final class TraceId implements Comparable<TraceId> { 34 /** 35 * The size in bytes of the {@code TraceId}. 36 * 37 * @since 0.5 38 */ 39 public static final int SIZE = 16; 40 41 private static final int HEX_SIZE = 32; 42 43 /** 44 * The invalid {@code TraceId}. All bytes are '\0'. 45 * 46 * @since 0.5 47 */ 48 public static final TraceId INVALID = new TraceId(new byte[SIZE]); 49 50 // The internal representation of the TraceId. 51 private final byte[] bytes; 52 TraceId(byte[] bytes)53 private TraceId(byte[] bytes) { 54 this.bytes = bytes; 55 } 56 57 /** 58 * Returns a {@code TraceId} built from a byte representation. 59 * 60 * <p>Equivalent with: 61 * 62 * <pre>{@code 63 * TraceId.fromBytes(buffer, 0); 64 * }</pre> 65 * 66 * @param buffer the representation of the {@code TraceId}. 67 * @return a {@code TraceId} whose representation is given by the {@code buffer} parameter. 68 * @throws NullPointerException if {@code buffer} is null. 69 * @throws IllegalArgumentException if {@code buffer.length} is not {@link TraceId#SIZE}. 70 * @since 0.5 71 */ fromBytes(byte[] buffer)72 public static TraceId fromBytes(byte[] buffer) { 73 Utils.checkNotNull(buffer, "buffer"); 74 Utils.checkArgument( 75 buffer.length == SIZE, "Invalid size: expected %s, got %s", SIZE, buffer.length); 76 byte[] bytesCopied = Arrays.copyOf(buffer, SIZE); 77 return new TraceId(bytesCopied); 78 } 79 80 /** 81 * Returns a {@code TraceId} whose representation is copied from the {@code src} beginning at the 82 * {@code srcOffset} offset. 83 * 84 * @param src the buffer where the representation of the {@code TraceId} is copied. 85 * @param srcOffset the offset in the buffer where the representation of the {@code TraceId} 86 * begins. 87 * @return a {@code TraceId} whose representation is copied from the buffer. 88 * @throws NullPointerException if {@code src} is null. 89 * @throws IndexOutOfBoundsException if {@code srcOffset+TraceId.SIZE} is greater than {@code 90 * src.length}. 91 * @since 0.5 92 */ fromBytes(byte[] src, int srcOffset)93 public static TraceId fromBytes(byte[] src, int srcOffset) { 94 byte[] bytes = new byte[SIZE]; 95 System.arraycopy(src, srcOffset, bytes, 0, SIZE); 96 return new TraceId(bytes); 97 } 98 99 /** 100 * Returns a {@code TraceId} built from a lowercase base16 representation. 101 * 102 * @param src the lowercase base16 representation. 103 * @return a {@code TraceId} built from a lowercase base16 representation. 104 * @throws NullPointerException if {@code src} is null. 105 * @throws IllegalArgumentException if {@code src.length} is not {@code 2 * TraceId.SIZE} OR if 106 * the {@code str} has invalid characters. 107 * @since 0.11 108 */ fromLowerBase16(CharSequence src)109 public static TraceId fromLowerBase16(CharSequence src) { 110 Utils.checkArgument( 111 src.length() == HEX_SIZE, "Invalid size: expected %s, got %s", HEX_SIZE, src.length()); 112 return new TraceId(LowerCaseBase16Encoding.decodeToBytes(src)); 113 } 114 115 /** 116 * Generates a new random {@code TraceId}. 117 * 118 * @param random the random number generator. 119 * @return a new valid {@code TraceId}. 120 * @since 0.5 121 */ generateRandomId(Random random)122 public static TraceId generateRandomId(Random random) { 123 byte[] bytes = new byte[SIZE]; 124 do { 125 random.nextBytes(bytes); 126 } while (Arrays.equals(bytes, INVALID.bytes)); 127 return new TraceId(bytes); 128 } 129 130 /** 131 * Returns the 16-bytes array representation of the {@code TraceId}. 132 * 133 * @return the 16-bytes array representation of the {@code TraceId}. 134 * @since 0.5 135 */ getBytes()136 public byte[] getBytes() { 137 return Arrays.copyOf(bytes, SIZE); 138 } 139 140 /** 141 * Copies the byte array representations of the {@code TraceId} into the {@code dest} beginning at 142 * the {@code destOffset} offset. 143 * 144 * <p>Equivalent with (but faster because it avoids any new allocations): 145 * 146 * <pre>{@code 147 * System.arraycopy(getBytes(), 0, dest, destOffset, TraceId.SIZE); 148 * }</pre> 149 * 150 * @param dest the destination buffer. 151 * @param destOffset the starting offset in the destination buffer. 152 * @throws NullPointerException if {@code dest} is null. 153 * @throws IndexOutOfBoundsException if {@code destOffset+TraceId.SIZE} is greater than {@code 154 * dest.length}. 155 * @since 0.5 156 */ copyBytesTo(byte[] dest, int destOffset)157 public void copyBytesTo(byte[] dest, int destOffset) { 158 System.arraycopy(bytes, 0, dest, destOffset, SIZE); 159 } 160 161 /** 162 * Returns whether the {@code TraceId} is valid. A valid trace identifier is a 16-byte array with 163 * at least one non-zero byte. 164 * 165 * @return {@code true} if the {@code TraceId} is valid. 166 * @since 0.5 167 */ isValid()168 public boolean isValid() { 169 return !Arrays.equals(bytes, INVALID.bytes); 170 } 171 172 /** 173 * Returns the lowercase base16 encoding of this {@code TraceId}. 174 * 175 * @return the lowercase base16 encoding of this {@code TraceId}. 176 * @since 0.11 177 */ toLowerBase16()178 public String toLowerBase16() { 179 return LowerCaseBase16Encoding.encodeToString(bytes); 180 } 181 182 /** 183 * Returns the lower 8 bytes of the trace-id as a long value, assuming little-endian order. This 184 * is used in ProbabilitySampler. 185 * 186 * <p>This method is marked as internal and subject to change. 187 * 188 * @return the lower 8 bytes of the trace-id as a long value, assuming little-endian order. 189 */ 190 @Internal getLowerLong()191 public long getLowerLong() { 192 long result = 0; 193 for (int i = 0; i < Long.SIZE / Byte.SIZE; i++) { 194 result <<= Byte.SIZE; 195 result |= (bytes[i] & 0xff); 196 } 197 if (result < 0) { 198 return -result; 199 } 200 return result; 201 } 202 203 @Override equals(@ullable Object obj)204 public boolean equals(@Nullable Object obj) { 205 if (obj == this) { 206 return true; 207 } 208 209 if (!(obj instanceof TraceId)) { 210 return false; 211 } 212 213 TraceId that = (TraceId) obj; 214 return Arrays.equals(bytes, that.bytes); 215 } 216 217 @Override hashCode()218 public int hashCode() { 219 return Arrays.hashCode(bytes); 220 } 221 222 @Override toString()223 public String toString() { 224 return "TraceId{traceId=" + toLowerBase16() + "}"; 225 } 226 227 @Override compareTo(TraceId that)228 public int compareTo(TraceId that) { 229 for (int i = 0; i < SIZE; i++) { 230 if (bytes[i] != that.bytes[i]) { 231 return bytes[i] < that.bytes[i] ? -1 : 1; 232 } 233 } 234 return 0; 235 } 236 } 237