| // Copyright The OpenTelemetry Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package metric |
| |
| //go:generate stringer -type=NumberKind |
| |
| import ( |
| "fmt" |
| "math" |
| "sync/atomic" |
| |
| "go.opentelemetry.io/otel/internal" |
| ) |
| |
| // NumberKind describes the data type of the Number. |
| type NumberKind int8 |
| |
| const ( |
| // Int64NumberKind means that the Number stores int64. |
| Int64NumberKind NumberKind = iota |
| // Float64NumberKind means that the Number stores float64. |
| Float64NumberKind |
| ) |
| |
| // Zero returns a zero value for a given NumberKind |
| func (k NumberKind) Zero() Number { |
| switch k { |
| case Int64NumberKind: |
| return NewInt64Number(0) |
| case Float64NumberKind: |
| return NewFloat64Number(0.) |
| default: |
| return Number(0) |
| } |
| } |
| |
| // Minimum returns the minimum representable value |
| // for a given NumberKind |
| func (k NumberKind) Minimum() Number { |
| switch k { |
| case Int64NumberKind: |
| return NewInt64Number(math.MinInt64) |
| case Float64NumberKind: |
| return NewFloat64Number(-1. * math.MaxFloat64) |
| default: |
| return Number(0) |
| } |
| } |
| |
| // Maximum returns the maximum representable value |
| // for a given NumberKind |
| func (k NumberKind) Maximum() Number { |
| switch k { |
| case Int64NumberKind: |
| return NewInt64Number(math.MaxInt64) |
| case Float64NumberKind: |
| return NewFloat64Number(math.MaxFloat64) |
| default: |
| return Number(0) |
| } |
| } |
| |
| // Number represents either an integral or a floating point value. It |
| // needs to be accompanied with a source of NumberKind that describes |
| // the actual type of the value stored within Number. |
| type Number uint64 |
| |
| // - constructors |
| |
| // NewNumberFromRaw creates a new Number from a raw value. |
| func NewNumberFromRaw(r uint64) Number { |
| return Number(r) |
| } |
| |
| // NewInt64Number creates an integral Number. |
| func NewInt64Number(i int64) Number { |
| return NewNumberFromRaw(internal.Int64ToRaw(i)) |
| } |
| |
| // NewFloat64Number creates a floating point Number. |
| func NewFloat64Number(f float64) Number { |
| return NewNumberFromRaw(internal.Float64ToRaw(f)) |
| } |
| |
| // NewNumberSignChange returns a number with the same magnitude and |
| // the opposite sign. `kind` must describe the kind of number in `nn`. |
| // |
| // Does not change Uint64NumberKind values. |
| func NewNumberSignChange(kind NumberKind, nn Number) Number { |
| switch kind { |
| case Int64NumberKind: |
| return NewInt64Number(-nn.AsInt64()) |
| case Float64NumberKind: |
| return NewFloat64Number(-nn.AsFloat64()) |
| } |
| return nn |
| } |
| |
| // - as x |
| |
| // AsNumber gets the Number. |
| func (n *Number) AsNumber() Number { |
| return *n |
| } |
| |
| // AsRaw gets the uninterpreted raw value. Might be useful for some |
| // atomic operations. |
| func (n *Number) AsRaw() uint64 { |
| return uint64(*n) |
| } |
| |
| // AsInt64 assumes that the value contains an int64 and returns it as |
| // such. |
| func (n *Number) AsInt64() int64 { |
| return internal.RawToInt64(n.AsRaw()) |
| } |
| |
| // AsFloat64 assumes that the measurement value contains a float64 and |
| // returns it as such. |
| func (n *Number) AsFloat64() float64 { |
| return internal.RawToFloat64(n.AsRaw()) |
| } |
| |
| // - as x atomic |
| |
| // AsNumberAtomic gets the Number atomically. |
| func (n *Number) AsNumberAtomic() Number { |
| return NewNumberFromRaw(n.AsRawAtomic()) |
| } |
| |
| // AsRawAtomic gets the uninterpreted raw value atomically. Might be |
| // useful for some atomic operations. |
| func (n *Number) AsRawAtomic() uint64 { |
| return atomic.LoadUint64(n.AsRawPtr()) |
| } |
| |
| // AsInt64Atomic assumes that the number contains an int64 and returns |
| // it as such atomically. |
| func (n *Number) AsInt64Atomic() int64 { |
| return atomic.LoadInt64(n.AsInt64Ptr()) |
| } |
| |
| // AsFloat64Atomic assumes that the measurement value contains a |
| // float64 and returns it as such atomically. |
| func (n *Number) AsFloat64Atomic() float64 { |
| return internal.RawToFloat64(n.AsRawAtomic()) |
| } |
| |
| // - as x ptr |
| |
| // AsRawPtr gets the pointer to the raw, uninterpreted raw |
| // value. Might be useful for some atomic operations. |
| func (n *Number) AsRawPtr() *uint64 { |
| return (*uint64)(n) |
| } |
| |
| // AsInt64Ptr assumes that the number contains an int64 and returns a |
| // pointer to it. |
| func (n *Number) AsInt64Ptr() *int64 { |
| return internal.RawPtrToInt64Ptr(n.AsRawPtr()) |
| } |
| |
| // AsFloat64Ptr assumes that the number contains a float64 and returns a |
| // pointer to it. |
| func (n *Number) AsFloat64Ptr() *float64 { |
| return internal.RawPtrToFloat64Ptr(n.AsRawPtr()) |
| } |
| |
| // - coerce |
| |
| // CoerceToInt64 casts the number to int64. May result in |
| // data/precision loss. |
| func (n *Number) CoerceToInt64(kind NumberKind) int64 { |
| switch kind { |
| case Int64NumberKind: |
| return n.AsInt64() |
| case Float64NumberKind: |
| return int64(n.AsFloat64()) |
| default: |
| // you get what you deserve |
| return 0 |
| } |
| } |
| |
| // CoerceToFloat64 casts the number to float64. May result in |
| // data/precision loss. |
| func (n *Number) CoerceToFloat64(kind NumberKind) float64 { |
| switch kind { |
| case Int64NumberKind: |
| return float64(n.AsInt64()) |
| case Float64NumberKind: |
| return n.AsFloat64() |
| default: |
| // you get what you deserve |
| return 0 |
| } |
| } |
| |
| // - set |
| |
| // SetNumber sets the number to the passed number. Both should be of |
| // the same kind. |
| func (n *Number) SetNumber(nn Number) { |
| *n.AsRawPtr() = nn.AsRaw() |
| } |
| |
| // SetRaw sets the number to the passed raw value. Both number and the |
| // raw number should represent the same kind. |
| func (n *Number) SetRaw(r uint64) { |
| *n.AsRawPtr() = r |
| } |
| |
| // SetInt64 assumes that the number contains an int64 and sets it to |
| // the passed value. |
| func (n *Number) SetInt64(i int64) { |
| *n.AsInt64Ptr() = i |
| } |
| |
| // SetFloat64 assumes that the number contains a float64 and sets it |
| // to the passed value. |
| func (n *Number) SetFloat64(f float64) { |
| *n.AsFloat64Ptr() = f |
| } |
| |
| // - set atomic |
| |
| // SetNumberAtomic sets the number to the passed number |
| // atomically. Both should be of the same kind. |
| func (n *Number) SetNumberAtomic(nn Number) { |
| atomic.StoreUint64(n.AsRawPtr(), nn.AsRaw()) |
| } |
| |
| // SetRawAtomic sets the number to the passed raw value |
| // atomically. Both number and the raw number should represent the |
| // same kind. |
| func (n *Number) SetRawAtomic(r uint64) { |
| atomic.StoreUint64(n.AsRawPtr(), r) |
| } |
| |
| // SetInt64Atomic assumes that the number contains an int64 and sets |
| // it to the passed value atomically. |
| func (n *Number) SetInt64Atomic(i int64) { |
| atomic.StoreInt64(n.AsInt64Ptr(), i) |
| } |
| |
| // SetFloat64Atomic assumes that the number contains a float64 and |
| // sets it to the passed value atomically. |
| func (n *Number) SetFloat64Atomic(f float64) { |
| atomic.StoreUint64(n.AsRawPtr(), internal.Float64ToRaw(f)) |
| } |
| |
| // - swap |
| |
| // SwapNumber sets the number to the passed number and returns the old |
| // number. Both this number and the passed number should be of the |
| // same kind. |
| func (n *Number) SwapNumber(nn Number) Number { |
| old := *n |
| n.SetNumber(nn) |
| return old |
| } |
| |
| // SwapRaw sets the number to the passed raw value and returns the old |
| // raw value. Both number and the raw number should represent the same |
| // kind. |
| func (n *Number) SwapRaw(r uint64) uint64 { |
| old := n.AsRaw() |
| n.SetRaw(r) |
| return old |
| } |
| |
| // SwapInt64 assumes that the number contains an int64, sets it to the |
| // passed value and returns the old int64 value. |
| func (n *Number) SwapInt64(i int64) int64 { |
| old := n.AsInt64() |
| n.SetInt64(i) |
| return old |
| } |
| |
| // SwapFloat64 assumes that the number contains an float64, sets it to |
| // the passed value and returns the old float64 value. |
| func (n *Number) SwapFloat64(f float64) float64 { |
| old := n.AsFloat64() |
| n.SetFloat64(f) |
| return old |
| } |
| |
| // - swap atomic |
| |
| // SwapNumberAtomic sets the number to the passed number and returns |
| // the old number atomically. Both this number and the passed number |
| // should be of the same kind. |
| func (n *Number) SwapNumberAtomic(nn Number) Number { |
| return NewNumberFromRaw(atomic.SwapUint64(n.AsRawPtr(), nn.AsRaw())) |
| } |
| |
| // SwapRawAtomic sets the number to the passed raw value and returns |
| // the old raw value atomically. Both number and the raw number should |
| // represent the same kind. |
| func (n *Number) SwapRawAtomic(r uint64) uint64 { |
| return atomic.SwapUint64(n.AsRawPtr(), r) |
| } |
| |
| // SwapInt64Atomic assumes that the number contains an int64, sets it |
| // to the passed value and returns the old int64 value atomically. |
| func (n *Number) SwapInt64Atomic(i int64) int64 { |
| return atomic.SwapInt64(n.AsInt64Ptr(), i) |
| } |
| |
| // SwapFloat64Atomic assumes that the number contains an float64, sets |
| // it to the passed value and returns the old float64 value |
| // atomically. |
| func (n *Number) SwapFloat64Atomic(f float64) float64 { |
| return internal.RawToFloat64(atomic.SwapUint64(n.AsRawPtr(), internal.Float64ToRaw(f))) |
| } |
| |
| // - add |
| |
| // AddNumber assumes that this and the passed number are of the passed |
| // kind and adds the passed number to this number. |
| func (n *Number) AddNumber(kind NumberKind, nn Number) { |
| switch kind { |
| case Int64NumberKind: |
| n.AddInt64(nn.AsInt64()) |
| case Float64NumberKind: |
| n.AddFloat64(nn.AsFloat64()) |
| } |
| } |
| |
| // AddRaw assumes that this number and the passed raw value are of the |
| // passed kind and adds the passed raw value to this number. |
| func (n *Number) AddRaw(kind NumberKind, r uint64) { |
| n.AddNumber(kind, NewNumberFromRaw(r)) |
| } |
| |
| // AddInt64 assumes that the number contains an int64 and adds the |
| // passed int64 to it. |
| func (n *Number) AddInt64(i int64) { |
| *n.AsInt64Ptr() += i |
| } |
| |
| // AddFloat64 assumes that the number contains a float64 and adds the |
| // passed float64 to it. |
| func (n *Number) AddFloat64(f float64) { |
| *n.AsFloat64Ptr() += f |
| } |
| |
| // - add atomic |
| |
| // AddNumberAtomic assumes that this and the passed number are of the |
| // passed kind and adds the passed number to this number atomically. |
| func (n *Number) AddNumberAtomic(kind NumberKind, nn Number) { |
| switch kind { |
| case Int64NumberKind: |
| n.AddInt64Atomic(nn.AsInt64()) |
| case Float64NumberKind: |
| n.AddFloat64Atomic(nn.AsFloat64()) |
| } |
| } |
| |
| // AddRawAtomic assumes that this number and the passed raw value are |
| // of the passed kind and adds the passed raw value to this number |
| // atomically. |
| func (n *Number) AddRawAtomic(kind NumberKind, r uint64) { |
| n.AddNumberAtomic(kind, NewNumberFromRaw(r)) |
| } |
| |
| // AddInt64Atomic assumes that the number contains an int64 and adds |
| // the passed int64 to it atomically. |
| func (n *Number) AddInt64Atomic(i int64) { |
| atomic.AddInt64(n.AsInt64Ptr(), i) |
| } |
| |
| // AddFloat64Atomic assumes that the number contains a float64 and |
| // adds the passed float64 to it atomically. |
| func (n *Number) AddFloat64Atomic(f float64) { |
| for { |
| o := n.AsFloat64Atomic() |
| if n.CompareAndSwapFloat64(o, o+f) { |
| break |
| } |
| } |
| } |
| |
| // - compare and swap (atomic only) |
| |
| // CompareAndSwapNumber does the atomic CAS operation on this |
| // number. This number and passed old and new numbers should be of the |
| // same kind. |
| func (n *Number) CompareAndSwapNumber(on, nn Number) bool { |
| return atomic.CompareAndSwapUint64(n.AsRawPtr(), on.AsRaw(), nn.AsRaw()) |
| } |
| |
| // CompareAndSwapRaw does the atomic CAS operation on this |
| // number. This number and passed old and new raw values should be of |
| // the same kind. |
| func (n *Number) CompareAndSwapRaw(or, nr uint64) bool { |
| return atomic.CompareAndSwapUint64(n.AsRawPtr(), or, nr) |
| } |
| |
| // CompareAndSwapInt64 assumes that this number contains an int64 and |
| // does the atomic CAS operation on it. |
| func (n *Number) CompareAndSwapInt64(oi, ni int64) bool { |
| return atomic.CompareAndSwapInt64(n.AsInt64Ptr(), oi, ni) |
| } |
| |
| // CompareAndSwapFloat64 assumes that this number contains a float64 and |
| // does the atomic CAS operation on it. |
| func (n *Number) CompareAndSwapFloat64(of, nf float64) bool { |
| return atomic.CompareAndSwapUint64(n.AsRawPtr(), internal.Float64ToRaw(of), internal.Float64ToRaw(nf)) |
| } |
| |
| // - compare |
| |
| // CompareNumber compares two Numbers given their kind. Both numbers |
| // should have the same kind. This returns: |
| // 0 if the numbers are equal |
| // -1 if the subject `n` is less than the argument `nn` |
| // +1 if the subject `n` is greater than the argument `nn` |
| func (n *Number) CompareNumber(kind NumberKind, nn Number) int { |
| switch kind { |
| case Int64NumberKind: |
| return n.CompareInt64(nn.AsInt64()) |
| case Float64NumberKind: |
| return n.CompareFloat64(nn.AsFloat64()) |
| default: |
| // you get what you deserve |
| return 0 |
| } |
| } |
| |
| // CompareRaw compares two numbers, where one is input as a raw |
| // uint64, interpreting both values as a `kind` of number. |
| func (n *Number) CompareRaw(kind NumberKind, r uint64) int { |
| return n.CompareNumber(kind, NewNumberFromRaw(r)) |
| } |
| |
| // CompareInt64 assumes that the Number contains an int64 and performs |
| // a comparison between the value and the other value. It returns the |
| // typical result of the compare function: -1 if the value is less |
| // than the other, 0 if both are equal, 1 if the value is greater than |
| // the other. |
| func (n *Number) CompareInt64(i int64) int { |
| this := n.AsInt64() |
| if this < i { |
| return -1 |
| } else if this > i { |
| return 1 |
| } |
| return 0 |
| } |
| |
| // CompareFloat64 assumes that the Number contains a float64 and |
| // performs a comparison between the value and the other value. It |
| // returns the typical result of the compare function: -1 if the value |
| // is less than the other, 0 if both are equal, 1 if the value is |
| // greater than the other. |
| // |
| // Do not compare NaN values. |
| func (n *Number) CompareFloat64(f float64) int { |
| this := n.AsFloat64() |
| if this < f { |
| return -1 |
| } else if this > f { |
| return 1 |
| } |
| return 0 |
| } |
| |
| // - relations to zero |
| |
| // IsPositive returns true if the actual value is greater than zero. |
| func (n *Number) IsPositive(kind NumberKind) bool { |
| return n.compareWithZero(kind) > 0 |
| } |
| |
| // IsNegative returns true if the actual value is less than zero. |
| func (n *Number) IsNegative(kind NumberKind) bool { |
| return n.compareWithZero(kind) < 0 |
| } |
| |
| // IsZero returns true if the actual value is equal to zero. |
| func (n *Number) IsZero(kind NumberKind) bool { |
| return n.compareWithZero(kind) == 0 |
| } |
| |
| // - misc |
| |
| // Emit returns a string representation of the raw value of the |
| // Number. A %d is used for integral values, %f for floating point |
| // values. |
| func (n *Number) Emit(kind NumberKind) string { |
| switch kind { |
| case Int64NumberKind: |
| return fmt.Sprintf("%d", n.AsInt64()) |
| case Float64NumberKind: |
| return fmt.Sprintf("%f", n.AsFloat64()) |
| default: |
| return "" |
| } |
| } |
| |
| // AsInterface returns the number as an interface{}, typically used |
| // for NumberKind-correct JSON conversion. |
| func (n *Number) AsInterface(kind NumberKind) interface{} { |
| switch kind { |
| case Int64NumberKind: |
| return n.AsInt64() |
| case Float64NumberKind: |
| return n.AsFloat64() |
| default: |
| return math.NaN() |
| } |
| } |
| |
| // - private stuff |
| |
| func (n *Number) compareWithZero(kind NumberKind) int { |
| switch kind { |
| case Int64NumberKind: |
| return n.CompareInt64(0) |
| case Float64NumberKind: |
| return n.CompareFloat64(0.) |
| default: |
| // you get what you deserve |
| return 0 |
| } |
| } |