blob: c3ca0ed7923828ec2a075138e175bbfe6684a3c9 [file] [log] [blame]
Joey Armstronga6af1522023-01-17 16:06:16 -05001// Copyright The OpenTelemetry Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package metric
16
17//go:generate stringer -type=NumberKind
18
19import (
20 "fmt"
21 "math"
22 "sync/atomic"
23
24 "go.opentelemetry.io/otel/internal"
25)
26
27// NumberKind describes the data type of the Number.
28type NumberKind int8
29
30const (
31 // Int64NumberKind means that the Number stores int64.
32 Int64NumberKind NumberKind = iota
33 // Float64NumberKind means that the Number stores float64.
34 Float64NumberKind
35)
36
37// Zero returns a zero value for a given NumberKind
38func (k NumberKind) Zero() Number {
39 switch k {
40 case Int64NumberKind:
41 return NewInt64Number(0)
42 case Float64NumberKind:
43 return NewFloat64Number(0.)
44 default:
45 return Number(0)
46 }
47}
48
49// Minimum returns the minimum representable value
50// for a given NumberKind
51func (k NumberKind) Minimum() Number {
52 switch k {
53 case Int64NumberKind:
54 return NewInt64Number(math.MinInt64)
55 case Float64NumberKind:
56 return NewFloat64Number(-1. * math.MaxFloat64)
57 default:
58 return Number(0)
59 }
60}
61
62// Maximum returns the maximum representable value
63// for a given NumberKind
64func (k NumberKind) Maximum() Number {
65 switch k {
66 case Int64NumberKind:
67 return NewInt64Number(math.MaxInt64)
68 case Float64NumberKind:
69 return NewFloat64Number(math.MaxFloat64)
70 default:
71 return Number(0)
72 }
73}
74
75// Number represents either an integral or a floating point value. It
76// needs to be accompanied with a source of NumberKind that describes
77// the actual type of the value stored within Number.
78type Number uint64
79
80// - constructors
81
82// NewNumberFromRaw creates a new Number from a raw value.
83func NewNumberFromRaw(r uint64) Number {
84 return Number(r)
85}
86
87// NewInt64Number creates an integral Number.
88func NewInt64Number(i int64) Number {
89 return NewNumberFromRaw(internal.Int64ToRaw(i))
90}
91
92// NewFloat64Number creates a floating point Number.
93func NewFloat64Number(f float64) Number {
94 return NewNumberFromRaw(internal.Float64ToRaw(f))
95}
96
97// NewNumberSignChange returns a number with the same magnitude and
98// the opposite sign. `kind` must describe the kind of number in `nn`.
99//
100// Does not change Uint64NumberKind values.
101func NewNumberSignChange(kind NumberKind, nn Number) Number {
102 switch kind {
103 case Int64NumberKind:
104 return NewInt64Number(-nn.AsInt64())
105 case Float64NumberKind:
106 return NewFloat64Number(-nn.AsFloat64())
107 }
108 return nn
109}
110
111// - as x
112
113// AsNumber gets the Number.
114func (n *Number) AsNumber() Number {
115 return *n
116}
117
118// AsRaw gets the uninterpreted raw value. Might be useful for some
119// atomic operations.
120func (n *Number) AsRaw() uint64 {
121 return uint64(*n)
122}
123
124// AsInt64 assumes that the value contains an int64 and returns it as
125// such.
126func (n *Number) AsInt64() int64 {
127 return internal.RawToInt64(n.AsRaw())
128}
129
130// AsFloat64 assumes that the measurement value contains a float64 and
131// returns it as such.
132func (n *Number) AsFloat64() float64 {
133 return internal.RawToFloat64(n.AsRaw())
134}
135
136// - as x atomic
137
138// AsNumberAtomic gets the Number atomically.
139func (n *Number) AsNumberAtomic() Number {
140 return NewNumberFromRaw(n.AsRawAtomic())
141}
142
143// AsRawAtomic gets the uninterpreted raw value atomically. Might be
144// useful for some atomic operations.
145func (n *Number) AsRawAtomic() uint64 {
146 return atomic.LoadUint64(n.AsRawPtr())
147}
148
149// AsInt64Atomic assumes that the number contains an int64 and returns
150// it as such atomically.
151func (n *Number) AsInt64Atomic() int64 {
152 return atomic.LoadInt64(n.AsInt64Ptr())
153}
154
155// AsFloat64Atomic assumes that the measurement value contains a
156// float64 and returns it as such atomically.
157func (n *Number) AsFloat64Atomic() float64 {
158 return internal.RawToFloat64(n.AsRawAtomic())
159}
160
161// - as x ptr
162
163// AsRawPtr gets the pointer to the raw, uninterpreted raw
164// value. Might be useful for some atomic operations.
165func (n *Number) AsRawPtr() *uint64 {
166 return (*uint64)(n)
167}
168
169// AsInt64Ptr assumes that the number contains an int64 and returns a
170// pointer to it.
171func (n *Number) AsInt64Ptr() *int64 {
172 return internal.RawPtrToInt64Ptr(n.AsRawPtr())
173}
174
175// AsFloat64Ptr assumes that the number contains a float64 and returns a
176// pointer to it.
177func (n *Number) AsFloat64Ptr() *float64 {
178 return internal.RawPtrToFloat64Ptr(n.AsRawPtr())
179}
180
181// - coerce
182
183// CoerceToInt64 casts the number to int64. May result in
184// data/precision loss.
185func (n *Number) CoerceToInt64(kind NumberKind) int64 {
186 switch kind {
187 case Int64NumberKind:
188 return n.AsInt64()
189 case Float64NumberKind:
190 return int64(n.AsFloat64())
191 default:
192 // you get what you deserve
193 return 0
194 }
195}
196
197// CoerceToFloat64 casts the number to float64. May result in
198// data/precision loss.
199func (n *Number) CoerceToFloat64(kind NumberKind) float64 {
200 switch kind {
201 case Int64NumberKind:
202 return float64(n.AsInt64())
203 case Float64NumberKind:
204 return n.AsFloat64()
205 default:
206 // you get what you deserve
207 return 0
208 }
209}
210
211// - set
212
213// SetNumber sets the number to the passed number. Both should be of
214// the same kind.
215func (n *Number) SetNumber(nn Number) {
216 *n.AsRawPtr() = nn.AsRaw()
217}
218
219// SetRaw sets the number to the passed raw value. Both number and the
220// raw number should represent the same kind.
221func (n *Number) SetRaw(r uint64) {
222 *n.AsRawPtr() = r
223}
224
225// SetInt64 assumes that the number contains an int64 and sets it to
226// the passed value.
227func (n *Number) SetInt64(i int64) {
228 *n.AsInt64Ptr() = i
229}
230
231// SetFloat64 assumes that the number contains a float64 and sets it
232// to the passed value.
233func (n *Number) SetFloat64(f float64) {
234 *n.AsFloat64Ptr() = f
235}
236
237// - set atomic
238
239// SetNumberAtomic sets the number to the passed number
240// atomically. Both should be of the same kind.
241func (n *Number) SetNumberAtomic(nn Number) {
242 atomic.StoreUint64(n.AsRawPtr(), nn.AsRaw())
243}
244
245// SetRawAtomic sets the number to the passed raw value
246// atomically. Both number and the raw number should represent the
247// same kind.
248func (n *Number) SetRawAtomic(r uint64) {
249 atomic.StoreUint64(n.AsRawPtr(), r)
250}
251
252// SetInt64Atomic assumes that the number contains an int64 and sets
253// it to the passed value atomically.
254func (n *Number) SetInt64Atomic(i int64) {
255 atomic.StoreInt64(n.AsInt64Ptr(), i)
256}
257
258// SetFloat64Atomic assumes that the number contains a float64 and
259// sets it to the passed value atomically.
260func (n *Number) SetFloat64Atomic(f float64) {
261 atomic.StoreUint64(n.AsRawPtr(), internal.Float64ToRaw(f))
262}
263
264// - swap
265
266// SwapNumber sets the number to the passed number and returns the old
267// number. Both this number and the passed number should be of the
268// same kind.
269func (n *Number) SwapNumber(nn Number) Number {
270 old := *n
271 n.SetNumber(nn)
272 return old
273}
274
275// SwapRaw sets the number to the passed raw value and returns the old
276// raw value. Both number and the raw number should represent the same
277// kind.
278func (n *Number) SwapRaw(r uint64) uint64 {
279 old := n.AsRaw()
280 n.SetRaw(r)
281 return old
282}
283
284// SwapInt64 assumes that the number contains an int64, sets it to the
285// passed value and returns the old int64 value.
286func (n *Number) SwapInt64(i int64) int64 {
287 old := n.AsInt64()
288 n.SetInt64(i)
289 return old
290}
291
292// SwapFloat64 assumes that the number contains an float64, sets it to
293// the passed value and returns the old float64 value.
294func (n *Number) SwapFloat64(f float64) float64 {
295 old := n.AsFloat64()
296 n.SetFloat64(f)
297 return old
298}
299
300// - swap atomic
301
302// SwapNumberAtomic sets the number to the passed number and returns
303// the old number atomically. Both this number and the passed number
304// should be of the same kind.
305func (n *Number) SwapNumberAtomic(nn Number) Number {
306 return NewNumberFromRaw(atomic.SwapUint64(n.AsRawPtr(), nn.AsRaw()))
307}
308
309// SwapRawAtomic sets the number to the passed raw value and returns
310// the old raw value atomically. Both number and the raw number should
311// represent the same kind.
312func (n *Number) SwapRawAtomic(r uint64) uint64 {
313 return atomic.SwapUint64(n.AsRawPtr(), r)
314}
315
316// SwapInt64Atomic assumes that the number contains an int64, sets it
317// to the passed value and returns the old int64 value atomically.
318func (n *Number) SwapInt64Atomic(i int64) int64 {
319 return atomic.SwapInt64(n.AsInt64Ptr(), i)
320}
321
322// SwapFloat64Atomic assumes that the number contains an float64, sets
323// it to the passed value and returns the old float64 value
324// atomically.
325func (n *Number) SwapFloat64Atomic(f float64) float64 {
326 return internal.RawToFloat64(atomic.SwapUint64(n.AsRawPtr(), internal.Float64ToRaw(f)))
327}
328
329// - add
330
331// AddNumber assumes that this and the passed number are of the passed
332// kind and adds the passed number to this number.
333func (n *Number) AddNumber(kind NumberKind, nn Number) {
334 switch kind {
335 case Int64NumberKind:
336 n.AddInt64(nn.AsInt64())
337 case Float64NumberKind:
338 n.AddFloat64(nn.AsFloat64())
339 }
340}
341
342// AddRaw assumes that this number and the passed raw value are of the
343// passed kind and adds the passed raw value to this number.
344func (n *Number) AddRaw(kind NumberKind, r uint64) {
345 n.AddNumber(kind, NewNumberFromRaw(r))
346}
347
348// AddInt64 assumes that the number contains an int64 and adds the
349// passed int64 to it.
350func (n *Number) AddInt64(i int64) {
351 *n.AsInt64Ptr() += i
352}
353
354// AddFloat64 assumes that the number contains a float64 and adds the
355// passed float64 to it.
356func (n *Number) AddFloat64(f float64) {
357 *n.AsFloat64Ptr() += f
358}
359
360// - add atomic
361
362// AddNumberAtomic assumes that this and the passed number are of the
363// passed kind and adds the passed number to this number atomically.
364func (n *Number) AddNumberAtomic(kind NumberKind, nn Number) {
365 switch kind {
366 case Int64NumberKind:
367 n.AddInt64Atomic(nn.AsInt64())
368 case Float64NumberKind:
369 n.AddFloat64Atomic(nn.AsFloat64())
370 }
371}
372
373// AddRawAtomic assumes that this number and the passed raw value are
374// of the passed kind and adds the passed raw value to this number
375// atomically.
376func (n *Number) AddRawAtomic(kind NumberKind, r uint64) {
377 n.AddNumberAtomic(kind, NewNumberFromRaw(r))
378}
379
380// AddInt64Atomic assumes that the number contains an int64 and adds
381// the passed int64 to it atomically.
382func (n *Number) AddInt64Atomic(i int64) {
383 atomic.AddInt64(n.AsInt64Ptr(), i)
384}
385
386// AddFloat64Atomic assumes that the number contains a float64 and
387// adds the passed float64 to it atomically.
388func (n *Number) AddFloat64Atomic(f float64) {
389 for {
390 o := n.AsFloat64Atomic()
391 if n.CompareAndSwapFloat64(o, o+f) {
392 break
393 }
394 }
395}
396
397// - compare and swap (atomic only)
398
399// CompareAndSwapNumber does the atomic CAS operation on this
400// number. This number and passed old and new numbers should be of the
401// same kind.
402func (n *Number) CompareAndSwapNumber(on, nn Number) bool {
403 return atomic.CompareAndSwapUint64(n.AsRawPtr(), on.AsRaw(), nn.AsRaw())
404}
405
406// CompareAndSwapRaw does the atomic CAS operation on this
407// number. This number and passed old and new raw values should be of
408// the same kind.
409func (n *Number) CompareAndSwapRaw(or, nr uint64) bool {
410 return atomic.CompareAndSwapUint64(n.AsRawPtr(), or, nr)
411}
412
413// CompareAndSwapInt64 assumes that this number contains an int64 and
414// does the atomic CAS operation on it.
415func (n *Number) CompareAndSwapInt64(oi, ni int64) bool {
416 return atomic.CompareAndSwapInt64(n.AsInt64Ptr(), oi, ni)
417}
418
419// CompareAndSwapFloat64 assumes that this number contains a float64 and
420// does the atomic CAS operation on it.
421func (n *Number) CompareAndSwapFloat64(of, nf float64) bool {
422 return atomic.CompareAndSwapUint64(n.AsRawPtr(), internal.Float64ToRaw(of), internal.Float64ToRaw(nf))
423}
424
425// - compare
426
427// CompareNumber compares two Numbers given their kind. Both numbers
428// should have the same kind. This returns:
429// 0 if the numbers are equal
430// -1 if the subject `n` is less than the argument `nn`
431// +1 if the subject `n` is greater than the argument `nn`
432func (n *Number) CompareNumber(kind NumberKind, nn Number) int {
433 switch kind {
434 case Int64NumberKind:
435 return n.CompareInt64(nn.AsInt64())
436 case Float64NumberKind:
437 return n.CompareFloat64(nn.AsFloat64())
438 default:
439 // you get what you deserve
440 return 0
441 }
442}
443
444// CompareRaw compares two numbers, where one is input as a raw
445// uint64, interpreting both values as a `kind` of number.
446func (n *Number) CompareRaw(kind NumberKind, r uint64) int {
447 return n.CompareNumber(kind, NewNumberFromRaw(r))
448}
449
450// CompareInt64 assumes that the Number contains an int64 and performs
451// a comparison between the value and the other value. It returns the
452// typical result of the compare function: -1 if the value is less
453// than the other, 0 if both are equal, 1 if the value is greater than
454// the other.
455func (n *Number) CompareInt64(i int64) int {
456 this := n.AsInt64()
457 if this < i {
458 return -1
459 } else if this > i {
460 return 1
461 }
462 return 0
463}
464
465// CompareFloat64 assumes that the Number contains a float64 and
466// performs a comparison between the value and the other value. It
467// returns the typical result of the compare function: -1 if the value
468// is less than the other, 0 if both are equal, 1 if the value is
469// greater than the other.
470//
471// Do not compare NaN values.
472func (n *Number) CompareFloat64(f float64) int {
473 this := n.AsFloat64()
474 if this < f {
475 return -1
476 } else if this > f {
477 return 1
478 }
479 return 0
480}
481
482// - relations to zero
483
484// IsPositive returns true if the actual value is greater than zero.
485func (n *Number) IsPositive(kind NumberKind) bool {
486 return n.compareWithZero(kind) > 0
487}
488
489// IsNegative returns true if the actual value is less than zero.
490func (n *Number) IsNegative(kind NumberKind) bool {
491 return n.compareWithZero(kind) < 0
492}
493
494// IsZero returns true if the actual value is equal to zero.
495func (n *Number) IsZero(kind NumberKind) bool {
496 return n.compareWithZero(kind) == 0
497}
498
499// - misc
500
501// Emit returns a string representation of the raw value of the
502// Number. A %d is used for integral values, %f for floating point
503// values.
504func (n *Number) Emit(kind NumberKind) string {
505 switch kind {
506 case Int64NumberKind:
507 return fmt.Sprintf("%d", n.AsInt64())
508 case Float64NumberKind:
509 return fmt.Sprintf("%f", n.AsFloat64())
510 default:
511 return ""
512 }
513}
514
515// AsInterface returns the number as an interface{}, typically used
516// for NumberKind-correct JSON conversion.
517func (n *Number) AsInterface(kind NumberKind) interface{} {
518 switch kind {
519 case Int64NumberKind:
520 return n.AsInt64()
521 case Float64NumberKind:
522 return n.AsFloat64()
523 default:
524 return math.NaN()
525 }
526}
527
528// - private stuff
529
530func (n *Number) compareWithZero(kind NumberKind) int {
531 switch kind {
532 case Int64NumberKind:
533 return n.CompareInt64(0)
534 case Float64NumberKind:
535 return n.CompareFloat64(0.)
536 default:
537 // you get what you deserve
538 return 0
539 }
540}