239 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
package wavefront
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"strconv"
 | 
						|
	"time"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	ErrEOF              = errors.New("EOF")
 | 
						|
	ErrInvalidTimestamp = errors.New("Invalid timestamp")
 | 
						|
)
 | 
						|
 | 
						|
// Interface for parsing line elements.
 | 
						|
type ElementParser interface {
 | 
						|
	parse(p *PointParser, pt *Point) error
 | 
						|
}
 | 
						|
 | 
						|
type NameParser struct{}
 | 
						|
type ValueParser struct{}
 | 
						|
type TimestampParser struct {
 | 
						|
	optional bool
 | 
						|
}
 | 
						|
type WhiteSpaceParser struct {
 | 
						|
	nextOptional bool
 | 
						|
}
 | 
						|
type TagParser struct{}
 | 
						|
type LoopedParser struct {
 | 
						|
	wrappedParser ElementParser
 | 
						|
	wsPaser       *WhiteSpaceParser
 | 
						|
}
 | 
						|
type LiteralParser struct {
 | 
						|
	literal string
 | 
						|
}
 | 
						|
 | 
						|
func (ep *NameParser) parse(p *PointParser, pt *Point) error {
 | 
						|
	//Valid characters are: a-z, A-Z, 0-9, hyphen ("-"), underscore ("_"), dot (".").
 | 
						|
	// Forward slash ("/") and comma (",") are allowed if metricName is enclosed in double quotes.
 | 
						|
	name, err := parseLiteral(p)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	pt.Name = name
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (ep *ValueParser) parse(p *PointParser, pt *Point) error {
 | 
						|
	tok, lit := p.scan()
 | 
						|
	if tok == EOF {
 | 
						|
		return fmt.Errorf("found %q, expected number", lit)
 | 
						|
	}
 | 
						|
 | 
						|
	p.writeBuf.Reset()
 | 
						|
	if tok == MINUS_SIGN {
 | 
						|
		p.writeBuf.WriteString(lit)
 | 
						|
		tok, lit = p.scan()
 | 
						|
	}
 | 
						|
 | 
						|
	for tok != EOF && (tok == LETTER || tok == NUMBER || tok == DOT) {
 | 
						|
		p.writeBuf.WriteString(lit)
 | 
						|
		tok, lit = p.scan()
 | 
						|
	}
 | 
						|
	p.unscan()
 | 
						|
 | 
						|
	pt.Value = p.writeBuf.String()
 | 
						|
	_, err := strconv.ParseFloat(pt.Value, 64)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("invalid metric value %s", pt.Value)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (ep *TimestampParser) parse(p *PointParser, pt *Point) error {
 | 
						|
	tok, lit := p.scan()
 | 
						|
	if tok == EOF {
 | 
						|
		if ep.optional {
 | 
						|
			p.unscanTokens(2)
 | 
						|
			return setTimestamp(pt, 0, 1)
 | 
						|
		}
 | 
						|
		return fmt.Errorf("found %q, expected number", lit)
 | 
						|
	}
 | 
						|
 | 
						|
	if tok != NUMBER {
 | 
						|
		if ep.optional {
 | 
						|
			p.unscanTokens(2)
 | 
						|
			return setTimestamp(pt, 0, 1)
 | 
						|
		}
 | 
						|
		return ErrInvalidTimestamp
 | 
						|
	}
 | 
						|
 | 
						|
	p.writeBuf.Reset()
 | 
						|
	for tok != EOF && tok == NUMBER {
 | 
						|
		p.writeBuf.WriteString(lit)
 | 
						|
		tok, lit = p.scan()
 | 
						|
	}
 | 
						|
	p.unscan()
 | 
						|
 | 
						|
	tsStr := p.writeBuf.String()
 | 
						|
	ts, err := strconv.ParseInt(tsStr, 10, 64)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	return setTimestamp(pt, ts, len(tsStr))
 | 
						|
}
 | 
						|
 | 
						|
func setTimestamp(pt *Point, ts int64, numDigits int) error {
 | 
						|
 | 
						|
	if numDigits == 19 {
 | 
						|
		// nanoseconds
 | 
						|
		ts = ts / 1e9
 | 
						|
	} else if numDigits == 16 {
 | 
						|
		// microseconds
 | 
						|
		ts = ts / 1e6
 | 
						|
	} else if numDigits == 13 {
 | 
						|
		// milliseconds
 | 
						|
		ts = ts / 1e3
 | 
						|
	} else if numDigits != 10 {
 | 
						|
		// must be in seconds, return error if not 0
 | 
						|
		if ts == 0 {
 | 
						|
			ts = getCurrentTime()
 | 
						|
		} else {
 | 
						|
			return ErrInvalidTimestamp
 | 
						|
		}
 | 
						|
	}
 | 
						|
	pt.Timestamp = ts
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (ep *LoopedParser) parse(p *PointParser, pt *Point) error {
 | 
						|
	for {
 | 
						|
		err := ep.wrappedParser.parse(p, pt)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		err = ep.wsPaser.parse(p, pt)
 | 
						|
		if err == ErrEOF {
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (ep *TagParser) parse(p *PointParser, pt *Point) error {
 | 
						|
	k, err := parseLiteral(p)
 | 
						|
	if err != nil {
 | 
						|
		if k == "" {
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	next, lit := p.scan()
 | 
						|
	if next != EQUALS {
 | 
						|
		return fmt.Errorf("found %q, expected equals", lit)
 | 
						|
	}
 | 
						|
 | 
						|
	v, err := parseLiteral(p)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if len(pt.Tags) == 0 {
 | 
						|
		pt.Tags = make(map[string]string)
 | 
						|
	}
 | 
						|
	pt.Tags[k] = v
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (ep *WhiteSpaceParser) parse(p *PointParser, pt *Point) error {
 | 
						|
	tok := WS
 | 
						|
	for tok != EOF && tok == WS {
 | 
						|
		tok, _ = p.scan()
 | 
						|
	}
 | 
						|
 | 
						|
	if tok == EOF {
 | 
						|
		if !ep.nextOptional {
 | 
						|
			return ErrEOF
 | 
						|
		}
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	p.unscan()
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (ep *LiteralParser) parse(p *PointParser, pt *Point) error {
 | 
						|
	l, err := parseLiteral(p)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if l != ep.literal {
 | 
						|
		return fmt.Errorf("found %s, expected %s", l, ep.literal)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func parseQuotedLiteral(p *PointParser) (string, error) {
 | 
						|
	p.writeBuf.Reset()
 | 
						|
 | 
						|
	escaped := false
 | 
						|
	tok, lit := p.scan()
 | 
						|
	for tok != EOF && (tok != QUOTES || (tok == QUOTES && escaped)) {
 | 
						|
		// let everything through
 | 
						|
		escaped = tok == BACKSLASH
 | 
						|
		p.writeBuf.WriteString(lit)
 | 
						|
		tok, lit = p.scan()
 | 
						|
	}
 | 
						|
	if tok == EOF {
 | 
						|
		return "", fmt.Errorf("found %q, expected quotes", lit)
 | 
						|
	}
 | 
						|
	return p.writeBuf.String(), nil
 | 
						|
}
 | 
						|
 | 
						|
func parseLiteral(p *PointParser) (string, error) {
 | 
						|
	tok, lit := p.scan()
 | 
						|
	if tok == EOF {
 | 
						|
		return "", fmt.Errorf("found %q, expected literal", lit)
 | 
						|
	}
 | 
						|
 | 
						|
	if tok == QUOTES {
 | 
						|
		return parseQuotedLiteral(p)
 | 
						|
	}
 | 
						|
 | 
						|
	p.writeBuf.Reset()
 | 
						|
	for tok != EOF && tok > literal_beg && tok < literal_end {
 | 
						|
		p.writeBuf.WriteString(lit)
 | 
						|
		tok, lit = p.scan()
 | 
						|
	}
 | 
						|
	if tok == QUOTES {
 | 
						|
		return "", errors.New("found quote inside unquoted literal")
 | 
						|
	}
 | 
						|
	p.unscan()
 | 
						|
	return p.writeBuf.String(), nil
 | 
						|
}
 | 
						|
 | 
						|
func getCurrentTime() int64 {
 | 
						|
	return time.Now().UnixNano() / 1e9
 | 
						|
}
 |