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