// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "math" "strconv" "unsafe" jsoniter "github.com/json-iterator/go" ) func marshalJSONIsEmpty(ptr unsafe.Pointer) bool { return false } // MarshalTimestamp marshals a point timestamp using the passed jsoniter stream. func MarshalTimestamp(t int64, stream *jsoniter.Stream) { // Write out the timestamp as a float divided by 1000. // This is ~3x faster than converting to a float. if t < 0 { stream.WriteRaw(`-`) t = -t } stream.WriteInt64(t / 1000) fraction := t % 1000 if fraction != 0 { stream.WriteRaw(`.`) if fraction < 100 { stream.WriteRaw(`0`) } if fraction < 10 { stream.WriteRaw(`0`) } stream.WriteInt64(fraction) } } // MarshalValue marshals a point value using the passed jsoniter stream. func MarshalValue(v float64, stream *jsoniter.Stream) { stream.WriteRaw(`"`) // Taken from https://github.com/json-iterator/go/blob/master/stream_float.go#L71 as a workaround // to https://github.com/json-iterator/go/issues/365 (jsoniter, to follow json standard, doesn't allow inf/nan). buf := stream.Buffer() abs := math.Abs(v) fmt := byte('f') // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. if abs != 0 { if abs < 1e-6 || abs >= 1e21 { fmt = 'e' } } buf = strconv.AppendFloat(buf, v, fmt, -1, 64) stream.SetBuffer(buf) stream.WriteRaw(`"`) } // MarshalHistogramBucket writes something like: [ 3, "-0.25", "0.25", "3"] // See MarshalHistogram to understand what the numbers mean func MarshalHistogramBucket(b HistogramBucket, stream *jsoniter.Stream) { stream.WriteArrayStart() stream.WriteInt32(b.Boundaries) stream.WriteMore() MarshalValue(float64(b.Lower), stream) stream.WriteMore() MarshalValue(float64(b.Upper), stream) stream.WriteMore() MarshalValue(float64(b.Count), stream) stream.WriteArrayEnd() } // MarshalHistogram writes something like: // // { // "count": "42", // "sum": "34593.34", // "buckets": [ // [ 3, "-0.25", "0.25", "3"], // [ 0, "0.25", "0.5", "12"], // [ 0, "0.5", "1", "21"], // [ 0, "2", "4", "6"] // ] // } // // The 1st element in each bucket array determines if the boundaries are // inclusive (AKA closed) or exclusive (AKA open): // // 0: lower exclusive, upper inclusive // 1: lower inclusive, upper exclusive // 2: both exclusive // 3: both inclusive // // The 2nd and 3rd elements are the lower and upper boundary. The 4th element is // the bucket count. func MarshalHistogram(h SampleHistogram, stream *jsoniter.Stream) { stream.WriteObjectStart() stream.WriteObjectField(`count`) MarshalValue(float64(h.Count), stream) stream.WriteMore() stream.WriteObjectField(`sum`) MarshalValue(float64(h.Sum), stream) bucketFound := false for _, bucket := range h.Buckets { if bucket.Count == 0 { continue // No need to expose empty buckets in JSON. } stream.WriteMore() if !bucketFound { stream.WriteObjectField(`buckets`) stream.WriteArrayStart() } bucketFound = true MarshalHistogramBucket(*bucket, stream) } if bucketFound { stream.WriteArrayEnd() } stream.WriteObjectEnd() }