/
randomness.go
116 lines (101 loc) · 3.93 KB
/
randomness.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package sampling // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling"
import (
"encoding/binary"
"errors"
"strconv"
"go.opentelemetry.io/collector/pdata/pcommon"
)
// numRandomnessValues equals MaxAdjustedCount--this variable has been
// introduced to improve readability. Recall that MaxAdjustedCount is
// 2**56 which is one greater than the maximum RValue
// ("ffffffffffffff", i.e., "100000000000000").
const numRandomnessValues = MaxAdjustedCount
// ErrRValueSize is returned by RValueToRandomess in case of
// unexpected size.
var ErrRValueSize = errors.New("r-value must have 14 hex digits")
// leastHalfTraceIDThresholdMask is the mask to use on the
// least-significant half of the TraceID, i.e., bytes 8-15.
// Because this is a 56 bit mask, the result after masking is
// the unsigned value of bytes 9 through 15.
//
// This helps extract 56 bits of randomness from the second half of
// the TraceID, as specified in https://www.w3.org/TR/trace-context-2/#randomness-of-trace-id
const leastHalfTraceIDThresholdMask = MaxAdjustedCount - 1
// AllProbabilitiesRandomness is sampled at all probabilities.
var AllProbabilitiesRandomness = Randomness{unsigned: numRandomnessValues - 1}
// Randomness may be derived from R-value or TraceID.
//
// Randomness contains 56 bits of randomness, derived in one of two ways, see:
// https://www.w3.org/TR/trace-context-2/#randomness-of-trace-id
type Randomness struct {
// unsigned is in the range [0, MaxAdjustedCount-1]
unsigned uint64
}
// TraceIDToRandomness returns randomness from a TraceID (assumes
// the traceparent random flag was set).
func TraceIDToRandomness(id pcommon.TraceID) Randomness {
// To get the 56 bits we want, take the second half of the trace ID,
leastHalf := binary.BigEndian.Uint64(id[8:])
return Randomness{
// Then apply the mask to get the least-significant 56 bits / 7 bytes.
// Equivalently stated: zero the most-significant 8 bits.
unsigned: leastHalf & leastHalfTraceIDThresholdMask,
}
}
// RValueToRandomness parses NumHexDigits hex bytes into a Randomness.
func RValueToRandomness(s string) (Randomness, error) {
if len(s) != NumHexDigits {
return Randomness{}, ErrRValueSize
}
unsigned, err := strconv.ParseUint(s, hexBase, 64)
if err != nil {
return Randomness{}, err
}
return Randomness{
unsigned: unsigned,
}, nil
}
// RValue formats the r-value encoding.
func (rnd Randomness) RValue() string {
// The important part here is to format a full 14-byte hex
// string, including leading zeros. We could accomplish the
// same with custom code or with fmt.Sprintf directives, but
// here we let strconv.FormatUint fill in leading zeros, as
// follows:
//
// Format (numRandomnessValues+Randomness) as a hex string
// Strip the leading hex digit, which is a "1" by design
//
// For example, a randomness that requires two leading zeros
// (all in hexadecimal):
//
// randomness is 7 bytes: aabbccddeeff
// numRandomnessValues is 2^56: 100000000000000
// randomness+numRandomnessValues: 100aabbccddeeff
// strip the leading "1": 00aabbccddeeff
//
// If the value is out-of-range, the empty string will be
// returned.
if rnd.unsigned >= numRandomnessValues {
return ""
}
return strconv.FormatUint(numRandomnessValues+rnd.unsigned, hexBase)[1:]
}
// Unsigned returns the unsigned representation of the random value.
// Items of data SHOULD be sampled when:
//
// Threshold.Unsigned() <= // Randomness.Unsigned().
func (rnd Randomness) Unsigned() uint64 {
return rnd.unsigned
}
// UnsignedToRandomness constructs a randomness using 56 random bits
// of unsigned number. If the input is out of range, an invalid value
// will be returned with an error.
func UnsignedToRandomness(x uint64) (Randomness, error) {
if x >= MaxAdjustedCount {
return AllProbabilitiesRandomness, ErrRValueSize
}
return Randomness{unsigned: x}, nil
}