...
1 package evaluation
2
3 import (
4 "crypto/sha1"
5 "encoding/hex"
6
7 "github.com/launchdarkly/go-server-sdk-evaluation/v2/internal"
8
9 "github.com/launchdarkly/go-sdk-common/v3/ldattr"
10 "github.com/launchdarkly/go-sdk-common/v3/ldcontext"
11 "github.com/launchdarkly/go-sdk-common/v3/ldvalue"
12 )
13
14 const (
15 longScale = float32(0xFFFFFFFFFFFFFFF)
16
17 initialHashInputBufferSize = 100
18 )
19
20 type bucketingFailureReason int
21
22 const (
23 bucketingFailureInvalidAttrRef bucketingFailureReason = iota + 1
24 bucketingFailureContextLacksDesiredKind
25 bucketingFailureAttributeNotFound
26 bucketingFailureAttributeValueWrongType
27 )
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 func (es *evaluationScope) computeBucketValue(
45 isExperiment bool,
46 seed ldvalue.OptionalInt,
47 contextKind ldcontext.Kind,
48 key string,
49 attr ldattr.Ref,
50 salt string,
51 ) (float32, bucketingFailureReason, error) {
52 hashInput := internal.LocalBuffer{Data: make([]byte, 0, initialHashInputBufferSize)}
53
54
55
56
57
58 if seed.IsDefined() {
59 hashInput.AppendInt(seed.IntValue())
60 } else {
61 hashInput.AppendString(key)
62 hashInput.AppendByte('.')
63 hashInput.AppendString(salt)
64 }
65 hashInput.AppendByte('.')
66
67 if isExperiment || !attr.IsDefined() {
68 attr = ldattr.NewLiteralRef(ldattr.KeyAttr)
69 } else if attr.Err() != nil {
70 return 0, bucketingFailureInvalidAttrRef, badAttrRefError(attr.String())
71 }
72 selectedContext := es.context.IndividualContextByKind(contextKind)
73 if !selectedContext.IsDefined() {
74 return 0, bucketingFailureContextLacksDesiredKind, nil
75 }
76 uValue := selectedContext.GetValueForRef(attr)
77 if uValue.IsNull() {
78 return 0, bucketingFailureAttributeNotFound, nil
79 }
80 switch {
81 case uValue.IsString():
82 hashInput.AppendString(uValue.StringValue())
83 case uValue.IsInt():
84 hashInput.AppendInt(uValue.IntValue())
85 default:
86
87
88 return 0, bucketingFailureAttributeValueWrongType, nil
89 }
90
91 if es.owner.enableSecondaryKey && !isExperiment {
92 if secondary := selectedContext.Secondary(); secondary.IsDefined() {
93
94 hashInput.AppendByte('.')
95 hashInput.AppendString(secondary.StringValue())
96 }
97 }
98
99 hashOutputBytes := sha1.Sum(hashInput.Data)
100 hexEncodedChars := make([]byte, 64)
101 hex.Encode(hexEncodedChars, hashOutputBytes[:])
102 hash := hexEncodedChars[:15]
103
104 intVal, _ := internal.ParseHexUint64(hash)
105
106 bucket := float32(intVal) / longScale
107
108 return bucket, 0, nil
109 }
110
View as plain text