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