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