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