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  }