4
4
package trace // import "go.opentelemetry.io/otel/trace"
5
5
6
6
import (
7
- "bytes"
8
- "encoding/hex"
9
7
"encoding/json"
10
8
)
11
9
@@ -41,18 +39,44 @@ var (
41
39
// IsValid checks whether the trace TraceID is valid. A valid trace ID does
42
40
// not consist of zeros only.
43
41
func (t TraceID ) IsValid () bool {
44
- return ! bytes . Equal ( t [:], nilTraceID [:])
42
+ return t != nilTraceID
45
43
}
46
44
47
45
// MarshalJSON implements a custom marshal function to encode TraceID
48
46
// as a hex string.
49
47
func (t TraceID ) MarshalJSON () ([]byte , error ) {
50
- return json .Marshal (t .String ())
48
+ b := [32 + 2 ]byte {0 : '"' , 33 : '"' }
49
+ h := t .hexBytes ()
50
+ copy (b [1 :], h [:])
51
+ return b [:], nil
51
52
}
52
53
53
54
// String returns the hex string representation form of a TraceID.
54
55
func (t TraceID ) String () string {
55
- return hex .EncodeToString (t [:])
56
+ h := t .hexBytes ()
57
+ return string (h [:])
58
+ }
59
+
60
+ // hexBytes returns the hex string representation form of a TraceID.
61
+ func (t TraceID ) hexBytes () [32 ]byte {
62
+ return [32 ]byte {
63
+ hexLU [t [0x0 ]>> 4 ], hexLU [t [0x0 ]& 0xf ],
64
+ hexLU [t [0x1 ]>> 4 ], hexLU [t [0x1 ]& 0xf ],
65
+ hexLU [t [0x2 ]>> 4 ], hexLU [t [0x2 ]& 0xf ],
66
+ hexLU [t [0x3 ]>> 4 ], hexLU [t [0x3 ]& 0xf ],
67
+ hexLU [t [0x4 ]>> 4 ], hexLU [t [0x4 ]& 0xf ],
68
+ hexLU [t [0x5 ]>> 4 ], hexLU [t [0x5 ]& 0xf ],
69
+ hexLU [t [0x6 ]>> 4 ], hexLU [t [0x6 ]& 0xf ],
70
+ hexLU [t [0x7 ]>> 4 ], hexLU [t [0x7 ]& 0xf ],
71
+ hexLU [t [0x8 ]>> 4 ], hexLU [t [0x8 ]& 0xf ],
72
+ hexLU [t [0x9 ]>> 4 ], hexLU [t [0x9 ]& 0xf ],
73
+ hexLU [t [0xa ]>> 4 ], hexLU [t [0xa ]& 0xf ],
74
+ hexLU [t [0xb ]>> 4 ], hexLU [t [0xb ]& 0xf ],
75
+ hexLU [t [0xc ]>> 4 ], hexLU [t [0xc ]& 0xf ],
76
+ hexLU [t [0xd ]>> 4 ], hexLU [t [0xd ]& 0xf ],
77
+ hexLU [t [0xe ]>> 4 ], hexLU [t [0xe ]& 0xf ],
78
+ hexLU [t [0xf ]>> 4 ], hexLU [t [0xf ]& 0xf ],
79
+ }
56
80
}
57
81
58
82
// SpanID is a unique identity of a span in a trace.
@@ -66,78 +90,88 @@ var (
66
90
// IsValid checks whether the SpanID is valid. A valid SpanID does not consist
67
91
// of zeros only.
68
92
func (s SpanID ) IsValid () bool {
69
- return ! bytes . Equal ( s [:], nilSpanID [:])
93
+ return s != nilSpanID
70
94
}
71
95
72
96
// MarshalJSON implements a custom marshal function to encode SpanID
73
97
// as a hex string.
74
98
func (s SpanID ) MarshalJSON () ([]byte , error ) {
75
- return json .Marshal (s .String ())
99
+ b := [16 + 2 ]byte {0 : '"' , 17 : '"' }
100
+ h := s .hexBytes ()
101
+ copy (b [1 :], h [:])
102
+ return b [:], nil
76
103
}
77
104
78
105
// String returns the hex string representation form of a SpanID.
79
106
func (s SpanID ) String () string {
80
- return hex .EncodeToString (s [:])
107
+ b := s .hexBytes ()
108
+ return string (b [:])
109
+ }
110
+
111
+ func (s SpanID ) hexBytes () [16 ]byte {
112
+ return [16 ]byte {
113
+ hexLU [s [0 ]>> 4 ], hexLU [s [0 ]& 0xf ],
114
+ hexLU [s [1 ]>> 4 ], hexLU [s [1 ]& 0xf ],
115
+ hexLU [s [2 ]>> 4 ], hexLU [s [2 ]& 0xf ],
116
+ hexLU [s [3 ]>> 4 ], hexLU [s [3 ]& 0xf ],
117
+ hexLU [s [4 ]>> 4 ], hexLU [s [4 ]& 0xf ],
118
+ hexLU [s [5 ]>> 4 ], hexLU [s [5 ]& 0xf ],
119
+ hexLU [s [6 ]>> 4 ], hexLU [s [6 ]& 0xf ],
120
+ hexLU [s [7 ]>> 4 ], hexLU [s [7 ]& 0xf ],
121
+ }
81
122
}
82
123
83
124
// TraceIDFromHex returns a TraceID from a hex string if it is compliant with
84
125
// the W3C trace-context specification. See more at
85
126
// https://www.w3.org/TR/trace-context/#trace-id
86
127
// nolint:revive // revive complains about stutter of `trace.TraceIDFromHex`.
87
128
func TraceIDFromHex (h string ) (TraceID , error ) {
88
- t := TraceID {}
89
129
if len (h ) != 32 {
90
- return t , errInvalidTraceIDLength
130
+ return [ 16 ] byte {} , errInvalidTraceIDLength
91
131
}
92
-
93
- if err := decodeHex (h , t [:]); err != nil {
94
- return t , err
132
+ var b [16 ]byte
133
+ invalidMark := byte (0 )
134
+ for i := 0 ; i < len (h ); i += 4 {
135
+ b [i / 2 ] = (hexRev [h [i ]] << 4 ) | hexRev [h [i + 1 ]]
136
+ b [i / 2 + 1 ] = (hexRev [h [i + 2 ]] << 4 ) | hexRev [h [i + 3 ]]
137
+ invalidMark |= hexRev [h [i ]] | hexRev [h [i + 1 ]] | hexRev [h [i + 2 ]] | hexRev [h [i + 3 ]]
95
138
}
96
-
97
- if ! t .IsValid () {
98
- return t , errNilTraceID
139
+ // If the upper 4 bits of any byte are not zero, there was an invalid hex
140
+ // character since invalid hex characters are 0xff in hexLU.
141
+ if invalidMark & 0xf0 != 0 {
142
+ return [16 ]byte {}, errInvalidHexID
143
+ }
144
+ // If we didn't set any bits, then h was all zeros.
145
+ if invalidMark == 0 {
146
+ return [16 ]byte {}, errNilTraceID
99
147
}
100
- return t , nil
148
+ return b , nil
101
149
}
102
150
103
151
// SpanIDFromHex returns a SpanID from a hex string if it is compliant
104
152
// with the w3c trace-context specification.
105
153
// See more at https://www.w3.org/TR/trace-context/#parent-id
106
154
func SpanIDFromHex (h string ) (SpanID , error ) {
107
- s := SpanID {}
108
155
if len (h ) != 16 {
109
- return s , errInvalidSpanIDLength
110
- }
111
-
112
- if err := decodeHex (h , s [:]); err != nil {
113
- return s , err
156
+ return [8 ]byte {}, errInvalidSpanIDLength
114
157
}
115
-
116
- if ! s .IsValid () {
117
- return s , errNilSpanID
158
+ var b [8 ]byte
159
+ invalidMark := byte (0 )
160
+ for i := 0 ; i < len (h ); i += 4 {
161
+ b [i / 2 ] = (hexRev [h [i ]] << 4 ) | hexRev [h [i + 1 ]]
162
+ b [i / 2 + 1 ] = (hexRev [h [i + 2 ]] << 4 ) | hexRev [h [i + 3 ]]
163
+ invalidMark |= hexRev [h [i ]] | hexRev [h [i + 1 ]] | hexRev [h [i + 2 ]] | hexRev [h [i + 3 ]]
118
164
}
119
- return s , nil
120
- }
121
-
122
- func decodeHex (h string , b []byte ) error {
123
- for _ , r := range h {
124
- switch {
125
- case 'a' <= r && r <= 'f' :
126
- continue
127
- case '0' <= r && r <= '9' :
128
- continue
129
- default :
130
- return errInvalidHexID
131
- }
165
+ // If the upper 4 bits of any byte are not zero, there was an invalid hex
166
+ // character since invalid hex characters are 0xff in hexLU.
167
+ if invalidMark & 0xf0 != 0 {
168
+ return [8 ]byte {}, errInvalidHexID
132
169
}
133
-
134
- decoded , err := hex .DecodeString (h )
135
- if err != nil {
136
- return err
170
+ // If we didn't set any bits, then h was all zeros.
171
+ if invalidMark == 0 {
172
+ return [8 ]byte {}, errNilSpanID
137
173
}
138
-
139
- copy (b , decoded )
140
- return nil
174
+ return b , nil
141
175
}
142
176
143
177
// TraceFlags contains flags that can be set on a SpanContext.
@@ -160,12 +194,20 @@ func (tf TraceFlags) WithSampled(sampled bool) TraceFlags { // nolint:revive //
160
194
// MarshalJSON implements a custom marshal function to encode TraceFlags
161
195
// as a hex string.
162
196
func (tf TraceFlags ) MarshalJSON () ([]byte , error ) {
163
- return json .Marshal (tf .String ())
197
+ b := [2 + 2 ]byte {0 : '"' , 3 : '"' }
198
+ h := tf .hexBytes ()
199
+ copy (b [1 :], h [:])
200
+ return b [:], nil
164
201
}
165
202
166
203
// String returns the hex string representation form of TraceFlags.
167
204
func (tf TraceFlags ) String () string {
168
- return hex .EncodeToString ([]byte {byte (tf )}[:])
205
+ h := tf .hexBytes ()
206
+ return string (h [:])
207
+ }
208
+
209
+ func (tf TraceFlags ) hexBytes () [2 ]byte {
210
+ return [2 ]byte {hexLU [tf >> 4 ], hexLU [tf & 0xf ]}
169
211
}
170
212
171
213
// SpanContextConfig contains mutable fields usable for constructing
0 commit comments