github.com/thanos-io/thanos@v0.32.5/internal/cortex/cortexpb/compat.go (about) 1 // Copyright (c) The Cortex Authors. 2 // Licensed under the Apache License 2.0. 3 4 package cortexpb 5 6 import ( 7 stdjson "encoding/json" 8 "fmt" 9 "math" 10 "sort" 11 "strconv" 12 "strings" 13 "time" 14 "unsafe" 15 16 jsoniter "github.com/json-iterator/go" 17 "github.com/prometheus/common/model" 18 "github.com/prometheus/prometheus/model/labels" 19 "github.com/thanos-io/thanos/internal/cortex/util" 20 ) 21 22 // FromLabelAdaptersToLabels casts []LabelAdapter to labels.Labels. 23 // It uses unsafe, but as LabelAdapter == labels.Label this should be safe. 24 // This allows us to use labels.Labels directly in protos. 25 // 26 // Note: while resulting labels.Labels is supposedly sorted, this function 27 // doesn't enforce that. If input is not sorted, output will be wrong. 28 func FromLabelAdaptersToLabels(ls []LabelAdapter) labels.Labels { 29 return *(*labels.Labels)(unsafe.Pointer(&ls)) 30 } 31 32 // FromLabelsToLabelAdapters casts labels.Labels to []LabelAdapter. 33 // It uses unsafe, but as LabelAdapter == labels.Label this should be safe. 34 // This allows us to use labels.Labels directly in protos. 35 func FromLabelsToLabelAdapters(ls labels.Labels) []LabelAdapter { 36 return *(*[]LabelAdapter)(unsafe.Pointer(&ls)) 37 } 38 39 // FromLabelAdaptersToMetric converts []LabelAdapter to a model.Metric. 40 // Don't do this on any performance sensitive paths. 41 func FromLabelAdaptersToMetric(ls []LabelAdapter) model.Metric { 42 return util.LabelsToMetric(FromLabelAdaptersToLabels(ls)) 43 } 44 45 // FromMetricsToLabelAdapters converts model.Metric to []LabelAdapter. 46 // Don't do this on any performance sensitive paths. 47 // The result is sorted. 48 func FromMetricsToLabelAdapters(metric model.Metric) []LabelAdapter { 49 result := make([]LabelAdapter, 0, len(metric)) 50 for k, v := range metric { 51 result = append(result, LabelAdapter{ 52 Name: string(k), 53 Value: string(v), 54 }) 55 } 56 sort.Sort(byLabel(result)) // The labels should be sorted upon initialisation. 57 return result 58 } 59 60 type byLabel []LabelAdapter 61 62 func (s byLabel) Len() int { return len(s) } 63 func (s byLabel) Less(i, j int) bool { return strings.Compare(s[i].Name, s[j].Name) < 0 } 64 func (s byLabel) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 65 66 // isTesting is only set from tests to get special behaviour to verify that custom sample encode and decode is used, 67 // both when using jsonitor or standard json package. 68 var isTesting = false 69 70 // MarshalJSON implements json.Marshaler. 71 func (s Sample) MarshalJSON() ([]byte, error) { 72 if isTesting && math.IsNaN(s.Value) { 73 return nil, fmt.Errorf("test sample") 74 } 75 76 t, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(model.Time(s.TimestampMs)) 77 if err != nil { 78 return nil, err 79 } 80 v, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(model.SampleValue(s.Value)) 81 if err != nil { 82 return nil, err 83 } 84 return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil 85 } 86 87 // UnmarshalJSON implements json.Unmarshaler. 88 func (s *Sample) UnmarshalJSON(b []byte) error { 89 var t model.Time 90 var v model.SampleValue 91 vs := [...]stdjson.Unmarshaler{&t, &v} 92 if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(b, &vs); err != nil { 93 return err 94 } 95 s.TimestampMs = int64(t) 96 s.Value = float64(v) 97 98 if isTesting && math.IsNaN(float64(v)) { 99 return fmt.Errorf("test sample") 100 } 101 return nil 102 } 103 104 func SampleJsoniterEncode(ptr unsafe.Pointer, stream *jsoniter.Stream) { 105 sample := (*Sample)(ptr) 106 107 if isTesting && math.IsNaN(sample.Value) { 108 stream.Error = fmt.Errorf("test sample") 109 return 110 } 111 112 stream.WriteArrayStart() 113 stream.WriteFloat64(float64(sample.TimestampMs) / float64(time.Second/time.Millisecond)) 114 stream.WriteMore() 115 stream.WriteString(model.SampleValue(sample.Value).String()) 116 stream.WriteArrayEnd() 117 } 118 119 func SampleJsoniterDecode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { 120 if !iter.ReadArray() { 121 iter.ReportError("cortexpb.Sample", "expected [") 122 return 123 } 124 125 t := model.Time(iter.ReadFloat64() * float64(time.Second/time.Millisecond)) 126 127 if !iter.ReadArray() { 128 iter.ReportError("cortexpb.Sample", "expected ,") 129 return 130 } 131 132 bs := iter.ReadStringAsSlice() 133 ss := *(*string)(unsafe.Pointer(&bs)) 134 v, err := strconv.ParseFloat(ss, 64) 135 if err != nil { 136 iter.ReportError("cortexpb.Sample", err.Error()) 137 return 138 } 139 140 if isTesting && math.IsNaN(v) { 141 iter.Error = fmt.Errorf("test sample") 142 return 143 } 144 145 if iter.ReadArray() { 146 iter.ReportError("cortexpb.Sample", "expected ]") 147 } 148 149 *(*Sample)(ptr) = Sample{ 150 TimestampMs: int64(t), 151 Value: v, 152 } 153 } 154 155 func init() { 156 jsoniter.RegisterTypeEncoderFunc("cortexpb.Sample", SampleJsoniterEncode, func(unsafe.Pointer) bool { return false }) 157 jsoniter.RegisterTypeDecoderFunc("cortexpb.Sample", SampleJsoniterDecode) 158 }