129 lines
2.9 KiB
Go
129 lines
2.9 KiB
Go
// Copyright 2017 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.
|
|
|
|
//go:generate go get github.com/cznic/golex
|
|
//go:generate golex -o=lex.l.go lex.l
|
|
|
|
// Package textparse contains an efficient parser for the Prometheus text format.
|
|
package textparse
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"sort"
|
|
"unsafe"
|
|
|
|
"github.com/prometheus/prometheus/pkg/labels"
|
|
)
|
|
|
|
type lexer struct {
|
|
b []byte
|
|
i int
|
|
vstart int
|
|
tstart int
|
|
|
|
err error
|
|
val float64
|
|
ts *int64
|
|
offsets []int
|
|
mstart, mend int
|
|
nextMstart int
|
|
}
|
|
|
|
const eof = 0
|
|
|
|
func (l *lexer) next() byte {
|
|
l.i++
|
|
if l.i >= len(l.b) {
|
|
l.err = io.EOF
|
|
return eof
|
|
}
|
|
c := l.b[l.i]
|
|
return c
|
|
}
|
|
|
|
func (l *lexer) Error(es string) {
|
|
l.err = errors.New(es)
|
|
}
|
|
|
|
// Parser parses samples from a byte slice of samples in the official
|
|
// Prometheus text exposition format.
|
|
type Parser struct {
|
|
l *lexer
|
|
err error
|
|
val float64
|
|
}
|
|
|
|
// New returns a new parser of the byte slice.
|
|
func New(b []byte) *Parser {
|
|
return &Parser{l: &lexer{b: b}}
|
|
}
|
|
|
|
// Next advances the parser to the next sample. It returns false if no
|
|
// more samples were read or an error occurred.
|
|
func (p *Parser) Next() bool {
|
|
switch p.l.Lex() {
|
|
case -1, eof:
|
|
return false
|
|
case 1:
|
|
return true
|
|
}
|
|
panic("unexpected")
|
|
}
|
|
|
|
// At returns the bytes of the metric, the timestamp if set, and the value
|
|
// of the current sample.
|
|
func (p *Parser) At() ([]byte, *int64, float64) {
|
|
return p.l.b[p.l.mstart:p.l.mend], p.l.ts, p.l.val
|
|
}
|
|
|
|
// Err returns the current error.
|
|
func (p *Parser) Err() error {
|
|
if p.err != nil {
|
|
return p.err
|
|
}
|
|
if p.l.err == io.EOF {
|
|
return nil
|
|
}
|
|
return p.l.err
|
|
}
|
|
|
|
// Metric writes the labels of the current sample into the passed labels.
|
|
// It returns the string from which the metric was parsed.
|
|
func (p *Parser) Metric(l *labels.Labels) string {
|
|
// Allocate the full immutable string immediately, so we just
|
|
// have to create references on it below.
|
|
s := string(p.l.b[p.l.mstart:p.l.mend])
|
|
|
|
*l = append(*l, labels.Label{
|
|
Name: labels.MetricName,
|
|
Value: s[:p.l.offsets[0]-p.l.mstart],
|
|
})
|
|
|
|
for i := 1; i < len(p.l.offsets); i += 3 {
|
|
a := p.l.offsets[i] - p.l.mstart
|
|
b := p.l.offsets[i+1] - p.l.mstart
|
|
c := p.l.offsets[i+2] - p.l.mstart
|
|
|
|
*l = append(*l, labels.Label{Name: s[a:b], Value: s[b+2 : c]})
|
|
}
|
|
|
|
sort.Sort((*l)[1:])
|
|
|
|
return s
|
|
}
|
|
|
|
func yoloString(b []byte) string {
|
|
return *((*string)(unsafe.Pointer(&b)))
|
|
}
|