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