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 }