1 package issue411
2
3 import (
4 "encoding/base64"
5 "encoding/binary"
6 "fmt"
7 "strconv"
8 )
9
10
11 type TraceID struct {
12 Low uint64 `json:"lo"`
13 High uint64 `json:"hi"`
14 }
15
16
17 type SpanID uint64
18
19
20
21
22 func NewTraceID(high, low uint64) TraceID {
23 return TraceID{High: high, Low: low}
24 }
25
26 func (t TraceID) String() string {
27 if t.High == 0 {
28 return fmt.Sprintf("%x", t.Low)
29 }
30 return fmt.Sprintf("%x%016x", t.High, t.Low)
31 }
32
33
34 func TraceIDFromString(s string) (TraceID, error) {
35 var hi, lo uint64
36 var err error
37 if len(s) > 32 {
38 return TraceID{}, fmt.Errorf("TraceID cannot be longer than 32 hex characters: %s", s)
39 } else if len(s) > 16 {
40 hiLen := len(s) - 16
41 if hi, err = strconv.ParseUint(s[0:hiLen], 16, 64); err != nil {
42 return TraceID{}, err
43 }
44 if lo, err = strconv.ParseUint(s[hiLen:], 16, 64); err != nil {
45 return TraceID{}, err
46 }
47 } else {
48 if lo, err = strconv.ParseUint(s, 16, 64); err != nil {
49 return TraceID{}, err
50 }
51 }
52 return TraceID{High: hi, Low: lo}, nil
53 }
54
55
56 func (t TraceID) MarshalText() ([]byte, error) {
57 return nil, fmt.Errorf("unsupported method TraceID.MarshalText; please use github.com/gogo/protobuf/jsonpb for marshalling")
58 }
59
60
61 func (t *TraceID) UnmarshalText(text []byte) error {
62 return fmt.Errorf("unsupported method TraceID.UnmarshalText; please use github.com/gogo/protobuf/jsonpb for marshalling")
63 }
64
65
66 func (t *TraceID) Size() int {
67 return 16
68 }
69
70
71 func (t TraceID) Marshal() ([]byte, error) {
72 b := make([]byte, t.Size())
73 _, err := t.MarshalTo(b)
74 return b, err
75 }
76
77
78 func (t *TraceID) MarshalTo(data []byte) (n int, err error) {
79 var b [16]byte
80 binary.BigEndian.PutUint64(b[:8], uint64(t.High))
81 binary.BigEndian.PutUint64(b[8:], uint64(t.Low))
82 return marshalBytes(data, b[:])
83 }
84
85
86 func (t *TraceID) Unmarshal(data []byte) error {
87 if len(data) < 16 {
88 return fmt.Errorf("buffer is too short")
89 }
90 t.High = binary.BigEndian.Uint64(data[:8])
91 t.Low = binary.BigEndian.Uint64(data[8:])
92 return nil
93 }
94
95 func marshalBytes(dst []byte, src []byte) (n int, err error) {
96 if len(dst) < len(src) {
97 return 0, fmt.Errorf("buffer is too short")
98 }
99 return copy(dst, src), nil
100 }
101
102
103
104
105 func (t TraceID) MarshalJSON() ([]byte, error) {
106 var b [16]byte
107 _, err := t.MarshalTo(b[:])
108 if err != nil {
109 return []byte{}, err
110 }
111 s := make([]byte, 24+2)
112 base64.StdEncoding.Encode(s[1:25], b[:])
113 s[0], s[25] = '"', '"'
114 return s, nil
115 }
116
117
118
119 func (t *TraceID) UnmarshalJSON(data []byte) error {
120 s := string(data)
121 if l := len(s); l > 2 && s[0] == '"' && s[l-1] == '"' {
122 s = s[1 : l-1]
123 }
124 b, err := base64.StdEncoding.DecodeString(s)
125 if err != nil {
126 return fmt.Errorf("cannot unmarshal TraceID from string '%s': %v", string(data), err)
127 }
128 return t.Unmarshal(b)
129 }
130
131
132
133
134 func NewSpanID(v uint64) SpanID {
135 return SpanID(v)
136 }
137
138 func (s SpanID) String() string {
139 return fmt.Sprintf("%x", uint64(s))
140 }
141
142
143 func SpanIDFromString(s string) (SpanID, error) {
144 if len(s) > 16 {
145 return SpanID(0), fmt.Errorf("SpanID cannot be longer than 16 hex characters: %s", s)
146 }
147 id, err := strconv.ParseUint(s, 16, 64)
148 if err != nil {
149 return SpanID(0), err
150 }
151 return SpanID(id), nil
152 }
153
154
155 func (s SpanID) MarshalText() ([]byte, error) {
156 return nil, fmt.Errorf("unsupported method SpanID.MarshalText; please use github.com/gogo/protobuf/jsonpb for marshalling")
157 }
158
159
160 func (s *SpanID) UnmarshalText(text []byte) error {
161 return fmt.Errorf("unsupported method SpanID.UnmarshalText; please use github.com/gogo/protobuf/jsonpb for marshalling")
162 }
163
164
165 func (s *SpanID) Size() int {
166 return 8
167 }
168
169
170 func (s SpanID) Marshal() ([]byte, error) {
171 b := make([]byte, s.Size())
172 _, err := s.MarshalTo(b)
173 return b, err
174 }
175
176
177 func (s *SpanID) MarshalTo(data []byte) (n int, err error) {
178 var b [8]byte
179 binary.BigEndian.PutUint64(b[:], uint64(*s))
180 return marshalBytes(data, b[:])
181 }
182
183
184 func (s *SpanID) Unmarshal(data []byte) error {
185 if len(data) < 8 {
186 return fmt.Errorf("buffer is too short")
187 }
188 *s = NewSpanID(binary.BigEndian.Uint64(data))
189 return nil
190 }
191
192
193
194
195 func (s SpanID) MarshalJSON() ([]byte, error) {
196 var b [8]byte
197 _, err := s.MarshalTo(b[:])
198 if err != nil {
199 return []byte{}, err
200 }
201 v := make([]byte, 12+2)
202 base64.StdEncoding.Encode(v[1:13], b[:])
203 v[0], v[13] = '"', '"'
204 return v, nil
205 }
206
207
208
209
210
211
212 func (s *SpanID) UnmarshalJSON(data []byte) error {
213 str := string(data)
214 if l := len(str); l > 2 && str[0] == '"' && str[l-1] == '"' {
215 str = str[1 : l-1]
216 }
217 b, err := base64.StdEncoding.DecodeString(str)
218 if err != nil {
219 return fmt.Errorf("cannot unmarshal SpanID from string '%s': %v", string(data), err)
220 }
221 return s.Unmarshal(b)
222 }
223
View as plain text