github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/chunk/json_helpers.go (about)

     1  package chunk
     2  
     3  import (
     4  	"sort"
     5  	"unsafe"
     6  
     7  	jsoniter "github.com/json-iterator/go"
     8  	"github.com/prometheus/common/model"
     9  	"github.com/prometheus/prometheus/model/labels"
    10  )
    11  
    12  func init() {
    13  	jsoniter.RegisterTypeDecoderFunc("labels.Labels", decodeLabels)
    14  	jsoniter.RegisterTypeEncoderFunc("labels.Labels", encodeLabels, labelsIsEmpty)
    15  	jsoniter.RegisterTypeDecoderFunc("model.Time", decodeModelTime)
    16  	jsoniter.RegisterTypeEncoderFunc("model.Time", encodeModelTime, modelTimeIsEmpty)
    17  }
    18  
    19  // Override Prometheus' labels.Labels decoder which goes via a map
    20  func decodeLabels(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
    21  	labelsPtr := (*labels.Labels)(ptr)
    22  	*labelsPtr = make(labels.Labels, 0, 10)
    23  	iter.ReadMapCB(func(iter *jsoniter.Iterator, key string) bool {
    24  		value := iter.ReadString()
    25  		*labelsPtr = append(*labelsPtr, labels.Label{Name: key, Value: value})
    26  		return true
    27  	})
    28  	// Labels are always sorted, but earlier Cortex using a map would
    29  	// output in any order so we have to sort on read in
    30  	sort.Sort(*labelsPtr)
    31  }
    32  
    33  // Override Prometheus' labels.Labels encoder which goes via a map
    34  func encodeLabels(ptr unsafe.Pointer, stream *jsoniter.Stream) {
    35  	labelsPtr := (*labels.Labels)(ptr)
    36  	stream.WriteObjectStart()
    37  	for i, v := range *labelsPtr {
    38  		if i != 0 {
    39  			stream.WriteMore()
    40  		}
    41  		stream.WriteString(v.Name)
    42  		stream.WriteRaw(`:`)
    43  		stream.WriteString(v.Value)
    44  	}
    45  	stream.WriteObjectEnd()
    46  }
    47  
    48  func labelsIsEmpty(ptr unsafe.Pointer) bool {
    49  	labelsPtr := (*labels.Labels)(ptr)
    50  	return len(*labelsPtr) == 0
    51  }
    52  
    53  // Decode via jsoniter's float64 routine is faster than getting the string data and decoding as two integers
    54  func decodeModelTime(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
    55  	pt := (*model.Time)(ptr)
    56  	f := iter.ReadFloat64()
    57  	*pt = model.Time(int64(f * 1000))
    58  }
    59  
    60  // Write out the timestamp as an int divided by 1000. This is ~3x faster than converting to a float.
    61  // Adapted from https://github.com/prometheus/prometheus/blob/cc39021b2bb6f829c7a626e4bdce2f338d1b76db/web/api/v1/api.go#L829
    62  func encodeModelTime(ptr unsafe.Pointer, stream *jsoniter.Stream) {
    63  	pt := (*model.Time)(ptr)
    64  	t := int64(*pt)
    65  	if t < 0 {
    66  		stream.WriteRaw(`-`)
    67  		t = -t
    68  	}
    69  	stream.WriteInt64(t / 1000)
    70  	fraction := t % 1000
    71  	if fraction != 0 {
    72  		stream.WriteRaw(`.`)
    73  		if fraction < 100 {
    74  			stream.WriteRaw(`0`)
    75  		}
    76  		if fraction < 10 {
    77  			stream.WriteRaw(`0`)
    78  		}
    79  		stream.WriteInt64(fraction)
    80  	}
    81  }
    82  
    83  func modelTimeIsEmpty(ptr unsafe.Pointer) bool {
    84  	return false
    85  }