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