Added integration support for DataDog APM Tracing

This commit is contained in:
Alex Antonov 2018-06-28 11:40:04 -05:00 committed by Traefiker Bot
parent ba8c9295ac
commit 3192307d59
61 changed files with 9999 additions and 5 deletions

26
Gopkg.lock generated
View file

@ -1005,6 +1005,12 @@
revision = "5f041e8faa004a95c88a202771f4cc3e991971e6"
version = "v2.0.1"
[[projects]]
name = "github.com/philhofer/fwd"
packages = ["."]
revision = "bb6d471dc95d4fe11e432687f8b70ff496cf3136"
version = "v1.0.0"
[[projects]]
name = "github.com/pierrec/lz4"
packages = ["."]
@ -1139,6 +1145,12 @@
]
revision = "37e84520dcf74488f67654f9c775b9752c232dc1"
[[projects]]
name = "github.com/tinylib/msgp"
packages = ["msgp"]
revision = "b2b6a672cf1e5b90748f79b8b81fc8c5cf0571a1"
version = "1.0.2"
[[projects]]
branch = "master"
name = "github.com/tuvistavie/securerandom"
@ -1430,6 +1442,18 @@
revision = "41344da2231b913fa3d983840a57a6b1b7b631a1"
version = "v1.12.0"
[[projects]]
name = "gopkg.in/DataDog/dd-trace-go.v1"
packages = [
"ddtrace",
"ddtrace/ext",
"ddtrace/internal",
"ddtrace/opentracer",
"ddtrace/tracer"
]
revision = "d052956664af54dbcff2712d10c67c76fbfc299f"
version = "v1.0.0"
[[projects]]
name = "gopkg.in/fsnotify.v1"
packages = ["."]
@ -1687,6 +1711,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "6090df9fbed29043b8602fafc92694ee7f3783d1c97a2cb080dc44913f928d1e"
inputs-digest = "63fb25f0e549ec7942fda4d11c25e04bdf756dcc44d31e897b103f2270dc42d9"
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -256,3 +256,7 @@
non-go = true
go-tests = true
unused-packages = true
[[constraint]]
name = "gopkg.in/DataDog/dd-trace-go.v1"
version = "1.0.0"

View file

@ -98,3 +98,41 @@ Træfik supports two backends: Jaeger and Zipkin.
#
id128Bit = true
```
## DataDog
```toml
# Tracing definition
[tracing]
# Backend name used to send tracing data
#
# Default: "jaeger"
#
backend = "datadog"
# Service name used in DataDog backend
#
# Default: "traefik"
#
serviceName = "traefik"
[tracing.datadog]
# Local Agent Host Port instructs reporter to send spans to datadog-tracing-agent at this address
#
# Default: "127.0.0.1:8126"
#
localAgentHostPort = "127.0.0.1:8126"
# Enable DataDog debug
#
# Default: false
#
debug = false
# Apply shared tag in a form of Key:Value to all the traces
#
# Default: ""
#
globalTag = ""
```

View file

@ -0,0 +1,45 @@
package datadog
import (
"io"
"strings"
"github.com/containous/traefik/log"
"github.com/opentracing/opentracing-go"
ddtracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer"
datadog "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)
// Name sets the name of this tracer
const Name = "datadog"
// Config provides configuration settings for a datadog tracer
type Config struct {
LocalAgentHostPort string `description:"Set datadog-agent's host:port that the reporter will used. Defaults to localhost:8126" export:"false"`
GlobalTag string `description:"Key:Value tag to be set on all the spans." export:"true"`
Debug bool `description:"Enable DataDog debug." export:"true"`
}
// Setup sets up the tracer
func (c *Config) Setup(serviceName string) (opentracing.Tracer, io.Closer, error) {
tag := strings.SplitN(c.GlobalTag, ":", 2)
value := ""
if len(tag) == 2 {
value = tag[1]
}
tracer := ddtracer.New(
datadog.WithAgentAddr(c.LocalAgentHostPort),
datadog.WithServiceName(serviceName),
datadog.WithGlobalTag(tag[0], value),
datadog.WithDebugMode(c.Debug),
)
// Without this, child spans are getting the NOOP tracer
opentracing.SetGlobalTracer(tracer)
log.Debug("DataDog tracer configured")
return tracer, nil, nil
}

View file

@ -6,6 +6,7 @@ import (
"net/http"
"github.com/containous/traefik/log"
"github.com/containous/traefik/middlewares/tracing/datadog"
"github.com/containous/traefik/middlewares/tracing/jaeger"
"github.com/containous/traefik/middlewares/tracing/zipkin"
"github.com/opentracing/opentracing-go"
@ -14,10 +15,11 @@ import (
// Tracing middleware
type Tracing struct {
Backend string `description:"Selects the tracking backend ('jaeger','zipkin')." export:"true"`
ServiceName string `description:"Set the name for this service" export:"true"`
Jaeger *jaeger.Config `description:"Settings for jaeger"`
Zipkin *zipkin.Config `description:"Settings for zipkin"`
Backend string `description:"Selects the tracking backend ('jaeger','zipkin', 'datadog')." export:"true"`
ServiceName string `description:"Set the name for this service" export:"true"`
Jaeger *jaeger.Config `description:"Settings for jaeger"`
Zipkin *zipkin.Config `description:"Settings for zipkin"`
DataDog *datadog.Config `description:"Settings for DataDog"`
tracer opentracing.Tracer
closer io.Closer
@ -52,6 +54,8 @@ func (t *Tracing) Setup() {
t.tracer, t.closer, err = t.Jaeger.Setup(t.ServiceName)
case zipkin.Name:
t.tracer, t.closer, err = t.Zipkin.Setup(t.ServiceName)
case datadog.Name:
t.tracer, t.closer, err = t.DataDog.Setup(t.ServiceName)
default:
log.Warnf("Unknown tracer %q", t.Backend)
return

7
vendor/github.com/philhofer/fwd/LICENSE.md generated vendored Normal file
View file

@ -0,0 +1,7 @@
Copyright (c) 2014-2015, Philip Hofer
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

383
vendor/github.com/philhofer/fwd/reader.go generated vendored Normal file
View file

@ -0,0 +1,383 @@
// The `fwd` package provides a buffered reader
// and writer. Each has methods that help improve
// the encoding/decoding performance of some binary
// protocols.
//
// The `fwd.Writer` and `fwd.Reader` type provide similar
// functionality to their counterparts in `bufio`, plus
// a few extra utility methods that simplify read-ahead
// and write-ahead. I wrote this package to improve serialization
// performance for http://github.com/tinylib/msgp,
// where it provided about a 2x speedup over `bufio` for certain
// workloads. However, care must be taken to understand the semantics of the
// extra methods provided by this package, as they allow
// the user to access and manipulate the buffer memory
// directly.
//
// The extra methods for `fwd.Reader` are `Peek`, `Skip`
// and `Next`. `(*fwd.Reader).Peek`, unlike `(*bufio.Reader).Peek`,
// will re-allocate the read buffer in order to accommodate arbitrarily
// large read-ahead. `(*fwd.Reader).Skip` skips the next `n` bytes
// in the stream, and uses the `io.Seeker` interface if the underlying
// stream implements it. `(*fwd.Reader).Next` returns a slice pointing
// to the next `n` bytes in the read buffer (like `Peek`), but also
// increments the read position. This allows users to process streams
// in arbitrary block sizes without having to manage appropriately-sized
// slices. Additionally, obviating the need to copy the data from the
// buffer to another location in memory can improve performance dramatically
// in CPU-bound applications.
//
// `fwd.Writer` only has one extra method, which is `(*fwd.Writer).Next`, which
// returns a slice pointing to the next `n` bytes of the writer, and increments
// the write position by the length of the returned slice. This allows users
// to write directly to the end of the buffer.
//
package fwd
import "io"
const (
// DefaultReaderSize is the default size of the read buffer
DefaultReaderSize = 2048
// minimum read buffer; straight from bufio
minReaderSize = 16
)
// NewReader returns a new *Reader that reads from 'r'
func NewReader(r io.Reader) *Reader {
return NewReaderSize(r, DefaultReaderSize)
}
// NewReaderSize returns a new *Reader that
// reads from 'r' and has a buffer size 'n'
func NewReaderSize(r io.Reader, n int) *Reader {
rd := &Reader{
r: r,
data: make([]byte, 0, max(minReaderSize, n)),
}
if s, ok := r.(io.Seeker); ok {
rd.rs = s
}
return rd
}
// Reader is a buffered look-ahead reader
type Reader struct {
r io.Reader // underlying reader
// data[n:len(data)] is buffered data; data[len(data):cap(data)] is free buffer space
data []byte // data
n int // read offset
state error // last read error
// if the reader past to NewReader was
// also an io.Seeker, this is non-nil
rs io.Seeker
}
// Reset resets the underlying reader
// and the read buffer.
func (r *Reader) Reset(rd io.Reader) {
r.r = rd
r.data = r.data[0:0]
r.n = 0
r.state = nil
if s, ok := rd.(io.Seeker); ok {
r.rs = s
} else {
r.rs = nil
}
}
// more() does one read on the underlying reader
func (r *Reader) more() {
// move data backwards so that
// the read offset is 0; this way
// we can supply the maximum number of
// bytes to the reader
if r.n != 0 {
if r.n < len(r.data) {
r.data = r.data[:copy(r.data[0:], r.data[r.n:])]
} else {
r.data = r.data[:0]
}
r.n = 0
}
var a int
a, r.state = r.r.Read(r.data[len(r.data):cap(r.data)])
if a == 0 && r.state == nil {
r.state = io.ErrNoProgress
return
} else if a > 0 && r.state == io.EOF {
// discard the io.EOF if we read more than 0 bytes.
// the next call to Read should return io.EOF again.
r.state = nil
}
r.data = r.data[:len(r.data)+a]
}
// pop error
func (r *Reader) err() (e error) {
e, r.state = r.state, nil
return
}
// pop error; EOF -> io.ErrUnexpectedEOF
func (r *Reader) noEOF() (e error) {
e, r.state = r.state, nil
if e == io.EOF {
e = io.ErrUnexpectedEOF
}
return
}
// buffered bytes
func (r *Reader) buffered() int { return len(r.data) - r.n }
// Buffered returns the number of bytes currently in the buffer
func (r *Reader) Buffered() int { return len(r.data) - r.n }
// BufferSize returns the total size of the buffer
func (r *Reader) BufferSize() int { return cap(r.data) }
// Peek returns the next 'n' buffered bytes,
// reading from the underlying reader if necessary.
// It will only return a slice shorter than 'n' bytes
// if it also returns an error. Peek does not advance
// the reader. EOF errors are *not* returned as
// io.ErrUnexpectedEOF.
func (r *Reader) Peek(n int) ([]byte, error) {
// in the degenerate case,
// we may need to realloc
// (the caller asked for more
// bytes than the size of the buffer)
if cap(r.data) < n {
old := r.data[r.n:]
r.data = make([]byte, n+r.buffered())
r.data = r.data[:copy(r.data, old)]
r.n = 0
}
// keep filling until
// we hit an error or
// read enough bytes
for r.buffered() < n && r.state == nil {
r.more()
}
// we must have hit an error
if r.buffered() < n {
return r.data[r.n:], r.err()
}
return r.data[r.n : r.n+n], nil
}
// Skip moves the reader forward 'n' bytes.
// Returns the number of bytes skipped and any
// errors encountered. It is analogous to Seek(n, 1).
// If the underlying reader implements io.Seeker, then
// that method will be used to skip forward.
//
// If the reader encounters
// an EOF before skipping 'n' bytes, it
// returns io.ErrUnexpectedEOF. If the
// underlying reader implements io.Seeker, then
// those rules apply instead. (Many implementations
// will not return `io.EOF` until the next call
// to Read.)
func (r *Reader) Skip(n int) (int, error) {
// fast path
if r.buffered() >= n {
r.n += n
return n, nil
}
// use seeker implementation
// if we can
if r.rs != nil {
return r.skipSeek(n)
}
// loop on filling
// and then erasing
o := n
for r.buffered() < n && r.state == nil {
r.more()
// we can skip forward
// up to r.buffered() bytes
step := min(r.buffered(), n)
r.n += step
n -= step
}
// at this point, n should be
// 0 if everything went smoothly
return o - n, r.noEOF()
}
// Next returns the next 'n' bytes in the stream.
// Unlike Peek, Next advances the reader position.
// The returned bytes point to the same
// data as the buffer, so the slice is
// only valid until the next reader method call.
// An EOF is considered an unexpected error.
// If an the returned slice is less than the
// length asked for, an error will be returned,
// and the reader position will not be incremented.
func (r *Reader) Next(n int) ([]byte, error) {
// in case the buffer is too small
if cap(r.data) < n {
old := r.data[r.n:]
r.data = make([]byte, n+r.buffered())
r.data = r.data[:copy(r.data, old)]
r.n = 0
}
// fill at least 'n' bytes
for r.buffered() < n && r.state == nil {
r.more()
}
if r.buffered() < n {
return r.data[r.n:], r.noEOF()
}
out := r.data[r.n : r.n+n]
r.n += n
return out, nil
}
// skipSeek uses the io.Seeker to seek forward.
// only call this function when n > r.buffered()
func (r *Reader) skipSeek(n int) (int, error) {
o := r.buffered()
// first, clear buffer
n -= o
r.n = 0
r.data = r.data[:0]
// then seek forward remaning bytes
i, err := r.rs.Seek(int64(n), 1)
return int(i) + o, err
}
// Read implements `io.Reader`
func (r *Reader) Read(b []byte) (int, error) {
// if we have data in the buffer, just
// return that.
if r.buffered() != 0 {
x := copy(b, r.data[r.n:])
r.n += x
return x, nil
}
var n int
// we have no buffered data; determine
// whether or not to buffer or call
// the underlying reader directly
if len(b) >= cap(r.data) {
n, r.state = r.r.Read(b)
} else {
r.more()
n = copy(b, r.data)
r.n = n
}
if n == 0 {
return 0, r.err()
}
return n, nil
}
// ReadFull attempts to read len(b) bytes into
// 'b'. It returns the number of bytes read into
// 'b', and an error if it does not return len(b).
// EOF is considered an unexpected error.
func (r *Reader) ReadFull(b []byte) (int, error) {
var n int // read into b
var nn int // scratch
l := len(b)
// either read buffered data,
// or read directly for the underlying
// buffer, or fetch more buffered data.
for n < l && r.state == nil {
if r.buffered() != 0 {
nn = copy(b[n:], r.data[r.n:])
n += nn
r.n += nn
} else if l-n > cap(r.data) {
nn, r.state = r.r.Read(b[n:])
n += nn
} else {
r.more()
}
}
if n < l {
return n, r.noEOF()
}
return n, nil
}
// ReadByte implements `io.ByteReader`
func (r *Reader) ReadByte() (byte, error) {
for r.buffered() < 1 && r.state == nil {
r.more()
}
if r.buffered() < 1 {
return 0, r.err()
}
b := r.data[r.n]
r.n++
return b, nil
}
// WriteTo implements `io.WriterTo`
func (r *Reader) WriteTo(w io.Writer) (int64, error) {
var (
i int64
ii int
err error
)
// first, clear buffer
if r.buffered() > 0 {
ii, err = w.Write(r.data[r.n:])
i += int64(ii)
if err != nil {
return i, err
}
r.data = r.data[0:0]
r.n = 0
}
for r.state == nil {
// here we just do
// 1:1 reads and writes
r.more()
if r.buffered() > 0 {
ii, err = w.Write(r.data)
i += int64(ii)
if err != nil {
return i, err
}
r.data = r.data[0:0]
r.n = 0
}
}
if r.state != io.EOF {
return i, r.err()
}
return i, nil
}
func min(a int, b int) int {
if a < b {
return a
}
return b
}
func max(a int, b int) int {
if a < b {
return b
}
return a
}

224
vendor/github.com/philhofer/fwd/writer.go generated vendored Normal file
View file

@ -0,0 +1,224 @@
package fwd
import "io"
const (
// DefaultWriterSize is the
// default write buffer size.
DefaultWriterSize = 2048
minWriterSize = minReaderSize
)
// Writer is a buffered writer
type Writer struct {
w io.Writer // writer
buf []byte // 0:len(buf) is bufered data
}
// NewWriter returns a new writer
// that writes to 'w' and has a buffer
// that is `DefaultWriterSize` bytes.
func NewWriter(w io.Writer) *Writer {
if wr, ok := w.(*Writer); ok {
return wr
}
return &Writer{
w: w,
buf: make([]byte, 0, DefaultWriterSize),
}
}
// NewWriterSize returns a new writer
// that writes to 'w' and has a buffer
// that is 'size' bytes.
func NewWriterSize(w io.Writer, size int) *Writer {
if wr, ok := w.(*Writer); ok && cap(wr.buf) >= size {
return wr
}
return &Writer{
w: w,
buf: make([]byte, 0, max(size, minWriterSize)),
}
}
// Buffered returns the number of buffered bytes
// in the reader.
func (w *Writer) Buffered() int { return len(w.buf) }
// BufferSize returns the maximum size of the buffer.
func (w *Writer) BufferSize() int { return cap(w.buf) }
// Flush flushes any buffered bytes
// to the underlying writer.
func (w *Writer) Flush() error {
l := len(w.buf)
if l > 0 {
n, err := w.w.Write(w.buf)
// if we didn't write the whole
// thing, copy the unwritten
// bytes to the beginnning of the
// buffer.
if n < l && n > 0 {
w.pushback(n)
if err == nil {
err = io.ErrShortWrite
}
}
if err != nil {
return err
}
w.buf = w.buf[:0]
return nil
}
return nil
}
// Write implements `io.Writer`
func (w *Writer) Write(p []byte) (int, error) {
c, l, ln := cap(w.buf), len(w.buf), len(p)
avail := c - l
// requires flush
if avail < ln {
if err := w.Flush(); err != nil {
return 0, err
}
l = len(w.buf)
}
// too big to fit in buffer;
// write directly to w.w
if c < ln {
return w.w.Write(p)
}
// grow buf slice; copy; return
w.buf = w.buf[:l+ln]
return copy(w.buf[l:], p), nil
}
// WriteString is analogous to Write, but it takes a string.
func (w *Writer) WriteString(s string) (int, error) {
c, l, ln := cap(w.buf), len(w.buf), len(s)
avail := c - l
// requires flush
if avail < ln {
if err := w.Flush(); err != nil {
return 0, err
}
l = len(w.buf)
}
// too big to fit in buffer;
// write directly to w.w
//
// yes, this is unsafe. *but*
// io.Writer is not allowed
// to mutate its input or
// maintain a reference to it,
// per the spec in package io.
//
// plus, if the string is really
// too big to fit in the buffer, then
// creating a copy to write it is
// expensive (and, strictly speaking,
// unnecessary)
if c < ln {
return w.w.Write(unsafestr(s))
}
// grow buf slice; copy; return
w.buf = w.buf[:l+ln]
return copy(w.buf[l:], s), nil
}
// WriteByte implements `io.ByteWriter`
func (w *Writer) WriteByte(b byte) error {
if len(w.buf) == cap(w.buf) {
if err := w.Flush(); err != nil {
return err
}
}
w.buf = append(w.buf, b)
return nil
}
// Next returns the next 'n' free bytes
// in the write buffer, flushing the writer
// as necessary. Next will return `io.ErrShortBuffer`
// if 'n' is greater than the size of the write buffer.
// Calls to 'next' increment the write position by
// the size of the returned buffer.
func (w *Writer) Next(n int) ([]byte, error) {
c, l := cap(w.buf), len(w.buf)
if n > c {
return nil, io.ErrShortBuffer
}
avail := c - l
if avail < n {
if err := w.Flush(); err != nil {
return nil, err
}
l = len(w.buf)
}
w.buf = w.buf[:l+n]
return w.buf[l:], nil
}
// take the bytes from w.buf[n:len(w.buf)]
// and put them at the beginning of w.buf,
// and resize to the length of the copied segment.
func (w *Writer) pushback(n int) {
w.buf = w.buf[:copy(w.buf, w.buf[n:])]
}
// ReadFrom implements `io.ReaderFrom`
func (w *Writer) ReadFrom(r io.Reader) (int64, error) {
// anticipatory flush
if err := w.Flush(); err != nil {
return 0, err
}
w.buf = w.buf[0:cap(w.buf)] // expand buffer
var nn int64 // written
var err error // error
var x int // read
// 1:1 reads and writes
for err == nil {
x, err = r.Read(w.buf)
if x > 0 {
n, werr := w.w.Write(w.buf[:x])
nn += int64(n)
if err != nil {
if n < x && n > 0 {
w.pushback(n - x)
}
return nn, werr
}
if n < x {
w.pushback(n - x)
return nn, io.ErrShortWrite
}
} else if err == nil {
err = io.ErrNoProgress
break
}
}
if err != io.EOF {
return nn, err
}
// we only clear here
// because we are sure
// the writes have
// succeeded. otherwise,
// we retain the data in case
// future writes succeed.
w.buf = w.buf[0:0]
return nn, nil
}

5
vendor/github.com/philhofer/fwd/writer_appengine.go generated vendored Normal file
View file

@ -0,0 +1,5 @@
// +build appengine
package fwd
func unsafestr(s string) []byte { return []byte(s) }

18
vendor/github.com/philhofer/fwd/writer_unsafe.go generated vendored Normal file
View file

@ -0,0 +1,18 @@
// +build !appengine
package fwd
import (
"reflect"
"unsafe"
)
// unsafe cast string as []byte
func unsafestr(b string) []byte {
l := len(b)
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Len: l,
Cap: l,
Data: (*reflect.StringHeader)(unsafe.Pointer(&b)).Data,
}))
}

8
vendor/github.com/tinylib/msgp/LICENSE generated vendored Normal file
View file

@ -0,0 +1,8 @@
Copyright (c) 2014 Philip Hofer
Portions Copyright (c) 2009 The Go Authors (license at http://golang.org) where indicated
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

24
vendor/github.com/tinylib/msgp/msgp/advise_linux.go generated vendored Normal file
View file

@ -0,0 +1,24 @@
// +build linux,!appengine
package msgp
import (
"os"
"syscall"
)
func adviseRead(mem []byte) {
syscall.Madvise(mem, syscall.MADV_SEQUENTIAL|syscall.MADV_WILLNEED)
}
func adviseWrite(mem []byte) {
syscall.Madvise(mem, syscall.MADV_SEQUENTIAL)
}
func fallocate(f *os.File, sz int64) error {
err := syscall.Fallocate(int(f.Fd()), 0, 0, sz)
if err == syscall.ENOTSUP {
return f.Truncate(sz)
}
return err
}

17
vendor/github.com/tinylib/msgp/msgp/advise_other.go generated vendored Normal file
View file

@ -0,0 +1,17 @@
// +build !linux appengine
package msgp
import (
"os"
)
// TODO: darwin, BSD support
func adviseRead(mem []byte) {}
func adviseWrite(mem []byte) {}
func fallocate(f *os.File, sz int64) error {
return f.Truncate(sz)
}

15
vendor/github.com/tinylib/msgp/msgp/appengine.go generated vendored Normal file
View file

@ -0,0 +1,15 @@
// +build appengine
package msgp
// let's just assume appengine
// uses 64-bit hardware...
const smallint = false
func UnsafeString(b []byte) string {
return string(b)
}
func UnsafeBytes(s string) []byte {
return []byte(s)
}

39
vendor/github.com/tinylib/msgp/msgp/circular.go generated vendored Normal file
View file

@ -0,0 +1,39 @@
package msgp
type timer interface {
StartTimer()
StopTimer()
}
// EndlessReader is an io.Reader
// that loops over the same data
// endlessly. It is used for benchmarking.
type EndlessReader struct {
tb timer
data []byte
offset int
}
// NewEndlessReader returns a new endless reader
func NewEndlessReader(b []byte, tb timer) *EndlessReader {
return &EndlessReader{tb: tb, data: b, offset: 0}
}
// Read implements io.Reader. In practice, it
// always returns (len(p), nil), although it
// fills the supplied slice while the benchmark
// timer is stopped.
func (c *EndlessReader) Read(p []byte) (int, error) {
c.tb.StopTimer()
var n int
l := len(p)
m := len(c.data)
for n < l {
nn := copy(p[n:], c.data[c.offset:])
n += nn
c.offset += nn
c.offset %= m
}
c.tb.StartTimer()
return n, nil
}

142
vendor/github.com/tinylib/msgp/msgp/defs.go generated vendored Normal file
View file

@ -0,0 +1,142 @@
// This package is the support library for the msgp code generator (http://github.com/tinylib/msgp).
//
// This package defines the utilites used by the msgp code generator for encoding and decoding MessagePack
// from []byte and io.Reader/io.Writer types. Much of this package is devoted to helping the msgp code
// generator implement the Marshaler/Unmarshaler and Encodable/Decodable interfaces.
//
// This package defines four "families" of functions:
// - AppendXxxx() appends an object to a []byte in MessagePack encoding.
// - ReadXxxxBytes() reads an object from a []byte and returns the remaining bytes.
// - (*Writer).WriteXxxx() writes an object to the buffered *Writer type.
// - (*Reader).ReadXxxx() reads an object from a buffered *Reader type.
//
// Once a type has satisfied the `Encodable` and `Decodable` interfaces,
// it can be written and read from arbitrary `io.Writer`s and `io.Reader`s using
// msgp.Encode(io.Writer, msgp.Encodable)
// and
// msgp.Decode(io.Reader, msgp.Decodable)
//
// There are also methods for converting MessagePack to JSON without
// an explicit de-serialization step.
//
// For additional tips, tricks, and gotchas, please visit
// the wiki at http://github.com/tinylib/msgp
package msgp
const last4 = 0x0f
const first4 = 0xf0
const last5 = 0x1f
const first3 = 0xe0
const last7 = 0x7f
func isfixint(b byte) bool {
return b>>7 == 0
}
func isnfixint(b byte) bool {
return b&first3 == mnfixint
}
func isfixmap(b byte) bool {
return b&first4 == mfixmap
}
func isfixarray(b byte) bool {
return b&first4 == mfixarray
}
func isfixstr(b byte) bool {
return b&first3 == mfixstr
}
func wfixint(u uint8) byte {
return u & last7
}
func rfixint(b byte) uint8 {
return b
}
func wnfixint(i int8) byte {
return byte(i) | mnfixint
}
func rnfixint(b byte) int8 {
return int8(b)
}
func rfixmap(b byte) uint8 {
return b & last4
}
func wfixmap(u uint8) byte {
return mfixmap | (u & last4)
}
func rfixstr(b byte) uint8 {
return b & last5
}
func wfixstr(u uint8) byte {
return (u & last5) | mfixstr
}
func rfixarray(b byte) uint8 {
return (b & last4)
}
func wfixarray(u uint8) byte {
return (u & last4) | mfixarray
}
// These are all the byte
// prefixes defined by the
// msgpack standard
const (
// 0XXXXXXX
mfixint uint8 = 0x00
// 111XXXXX
mnfixint uint8 = 0xe0
// 1000XXXX
mfixmap uint8 = 0x80
// 1001XXXX
mfixarray uint8 = 0x90
// 101XXXXX
mfixstr uint8 = 0xa0
mnil uint8 = 0xc0
mfalse uint8 = 0xc2
mtrue uint8 = 0xc3
mbin8 uint8 = 0xc4
mbin16 uint8 = 0xc5
mbin32 uint8 = 0xc6
mext8 uint8 = 0xc7
mext16 uint8 = 0xc8
mext32 uint8 = 0xc9
mfloat32 uint8 = 0xca
mfloat64 uint8 = 0xcb
muint8 uint8 = 0xcc
muint16 uint8 = 0xcd
muint32 uint8 = 0xce
muint64 uint8 = 0xcf
mint8 uint8 = 0xd0
mint16 uint8 = 0xd1
mint32 uint8 = 0xd2
mint64 uint8 = 0xd3
mfixext1 uint8 = 0xd4
mfixext2 uint8 = 0xd5
mfixext4 uint8 = 0xd6
mfixext8 uint8 = 0xd7
mfixext16 uint8 = 0xd8
mstr8 uint8 = 0xd9
mstr16 uint8 = 0xda
mstr32 uint8 = 0xdb
marray16 uint8 = 0xdc
marray32 uint8 = 0xdd
mmap16 uint8 = 0xde
mmap32 uint8 = 0xdf
)

241
vendor/github.com/tinylib/msgp/msgp/edit.go generated vendored Normal file
View file

@ -0,0 +1,241 @@
package msgp
import (
"math"
)
// Locate returns a []byte pointing to the field
// in a messagepack map with the provided key. (The returned []byte
// points to a sub-slice of 'raw'; Locate does no allocations.) If the
// key doesn't exist in the map, a zero-length []byte will be returned.
func Locate(key string, raw []byte) []byte {
s, n := locate(raw, key)
return raw[s:n]
}
// Replace takes a key ("key") in a messagepack map ("raw")
// and replaces its value with the one provided and returns
// the new []byte. The returned []byte may point to the same
// memory as "raw". Replace makes no effort to evaluate the validity
// of the contents of 'val'. It may use up to the full capacity of 'raw.'
// Replace returns 'nil' if the field doesn't exist or if the object in 'raw'
// is not a map.
func Replace(key string, raw []byte, val []byte) []byte {
start, end := locate(raw, key)
if start == end {
return nil
}
return replace(raw, start, end, val, true)
}
// CopyReplace works similarly to Replace except that the returned
// byte slice does not point to the same memory as 'raw'. CopyReplace
// returns 'nil' if the field doesn't exist or 'raw' isn't a map.
func CopyReplace(key string, raw []byte, val []byte) []byte {
start, end := locate(raw, key)
if start == end {
return nil
}
return replace(raw, start, end, val, false)
}
// Remove removes a key-value pair from 'raw'. It returns
// 'raw' unchanged if the key didn't exist.
func Remove(key string, raw []byte) []byte {
start, end := locateKV(raw, key)
if start == end {
return raw
}
raw = raw[:start+copy(raw[start:], raw[end:])]
return resizeMap(raw, -1)
}
// HasKey returns whether the map in 'raw' has
// a field with key 'key'
func HasKey(key string, raw []byte) bool {
sz, bts, err := ReadMapHeaderBytes(raw)
if err != nil {
return false
}
var field []byte
for i := uint32(0); i < sz; i++ {
field, bts, err = ReadStringZC(bts)
if err != nil {
return false
}
if UnsafeString(field) == key {
return true
}
}
return false
}
func replace(raw []byte, start int, end int, val []byte, inplace bool) []byte {
ll := end - start // length of segment to replace
lv := len(val)
if inplace {
extra := lv - ll
// fastest case: we're doing
// a 1:1 replacement
if extra == 0 {
copy(raw[start:], val)
return raw
} else if extra < 0 {
// 'val' smaller than replaced value
// copy in place and shift back
x := copy(raw[start:], val)
y := copy(raw[start+x:], raw[end:])
return raw[:start+x+y]
} else if extra < cap(raw)-len(raw) {
// 'val' less than (cap-len) extra bytes
// copy in place and shift forward
raw = raw[0 : len(raw)+extra]
// shift end forward
copy(raw[end+extra:], raw[end:])
copy(raw[start:], val)
return raw
}
}
// we have to allocate new space
out := make([]byte, len(raw)+len(val)-ll)
x := copy(out, raw[:start])
y := copy(out[x:], val)
copy(out[x+y:], raw[end:])
return out
}
// locate does a naive O(n) search for the map key; returns start, end
// (returns 0,0 on error)
func locate(raw []byte, key string) (start int, end int) {
var (
sz uint32
bts []byte
field []byte
err error
)
sz, bts, err = ReadMapHeaderBytes(raw)
if err != nil {
return
}
// loop and locate field
for i := uint32(0); i < sz; i++ {
field, bts, err = ReadStringZC(bts)
if err != nil {
return 0, 0
}
if UnsafeString(field) == key {
// start location
l := len(raw)
start = l - len(bts)
bts, err = Skip(bts)
if err != nil {
return 0, 0
}
end = l - len(bts)
return
}
bts, err = Skip(bts)
if err != nil {
return 0, 0
}
}
return 0, 0
}
// locate key AND value
func locateKV(raw []byte, key string) (start int, end int) {
var (
sz uint32
bts []byte
field []byte
err error
)
sz, bts, err = ReadMapHeaderBytes(raw)
if err != nil {
return 0, 0
}
for i := uint32(0); i < sz; i++ {
tmp := len(bts)
field, bts, err = ReadStringZC(bts)
if err != nil {
return 0, 0
}
if UnsafeString(field) == key {
start = len(raw) - tmp
bts, err = Skip(bts)
if err != nil {
return 0, 0
}
end = len(raw) - len(bts)
return
}
bts, err = Skip(bts)
if err != nil {
return 0, 0
}
}
return 0, 0
}
// delta is delta on map size
func resizeMap(raw []byte, delta int64) []byte {
var sz int64
switch raw[0] {
case mmap16:
sz = int64(big.Uint16(raw[1:]))
if sz+delta <= math.MaxUint16 {
big.PutUint16(raw[1:], uint16(sz+delta))
return raw
}
if cap(raw)-len(raw) >= 2 {
raw = raw[0 : len(raw)+2]
copy(raw[5:], raw[3:])
big.PutUint32(raw[1:], uint32(sz+delta))
return raw
}
n := make([]byte, 0, len(raw)+5)
n = AppendMapHeader(n, uint32(sz+delta))
return append(n, raw[3:]...)
case mmap32:
sz = int64(big.Uint32(raw[1:]))
big.PutUint32(raw[1:], uint32(sz+delta))
return raw
default:
sz = int64(rfixmap(raw[0]))
if sz+delta < 16 {
raw[0] = wfixmap(uint8(sz + delta))
return raw
} else if sz+delta <= math.MaxUint16 {
if cap(raw)-len(raw) >= 2 {
raw = raw[0 : len(raw)+2]
copy(raw[3:], raw[1:])
raw[0] = mmap16
big.PutUint16(raw[1:], uint16(sz+delta))
return raw
}
n := make([]byte, 0, len(raw)+5)
n = AppendMapHeader(n, uint32(sz+delta))
return append(n, raw[1:]...)
}
if cap(raw)-len(raw) >= 4 {
raw = raw[0 : len(raw)+4]
copy(raw[5:], raw[1:])
raw[0] = mmap32
big.PutUint32(raw[1:], uint32(sz+delta))
return raw
}
n := make([]byte, 0, len(raw)+5)
n = AppendMapHeader(n, uint32(sz+delta))
return append(n, raw[1:]...)
}
}

99
vendor/github.com/tinylib/msgp/msgp/elsize.go generated vendored Normal file
View file

@ -0,0 +1,99 @@
package msgp
// size of every object on the wire,
// plus type information. gives us
// constant-time type information
// for traversing composite objects.
//
var sizes = [256]bytespec{
mnil: {size: 1, extra: constsize, typ: NilType},
mfalse: {size: 1, extra: constsize, typ: BoolType},
mtrue: {size: 1, extra: constsize, typ: BoolType},
mbin8: {size: 2, extra: extra8, typ: BinType},
mbin16: {size: 3, extra: extra16, typ: BinType},
mbin32: {size: 5, extra: extra32, typ: BinType},
mext8: {size: 3, extra: extra8, typ: ExtensionType},
mext16: {size: 4, extra: extra16, typ: ExtensionType},
mext32: {size: 6, extra: extra32, typ: ExtensionType},
mfloat32: {size: 5, extra: constsize, typ: Float32Type},
mfloat64: {size: 9, extra: constsize, typ: Float64Type},
muint8: {size: 2, extra: constsize, typ: UintType},
muint16: {size: 3, extra: constsize, typ: UintType},
muint32: {size: 5, extra: constsize, typ: UintType},
muint64: {size: 9, extra: constsize, typ: UintType},
mint8: {size: 2, extra: constsize, typ: IntType},
mint16: {size: 3, extra: constsize, typ: IntType},
mint32: {size: 5, extra: constsize, typ: IntType},
mint64: {size: 9, extra: constsize, typ: IntType},
mfixext1: {size: 3, extra: constsize, typ: ExtensionType},
mfixext2: {size: 4, extra: constsize, typ: ExtensionType},
mfixext4: {size: 6, extra: constsize, typ: ExtensionType},
mfixext8: {size: 10, extra: constsize, typ: ExtensionType},
mfixext16: {size: 18, extra: constsize, typ: ExtensionType},
mstr8: {size: 2, extra: extra8, typ: StrType},
mstr16: {size: 3, extra: extra16, typ: StrType},
mstr32: {size: 5, extra: extra32, typ: StrType},
marray16: {size: 3, extra: array16v, typ: ArrayType},
marray32: {size: 5, extra: array32v, typ: ArrayType},
mmap16: {size: 3, extra: map16v, typ: MapType},
mmap32: {size: 5, extra: map32v, typ: MapType},
}
func init() {
// set up fixed fields
// fixint
for i := mfixint; i < 0x80; i++ {
sizes[i] = bytespec{size: 1, extra: constsize, typ: IntType}
}
// nfixint
for i := uint16(mnfixint); i < 0x100; i++ {
sizes[uint8(i)] = bytespec{size: 1, extra: constsize, typ: IntType}
}
// fixstr gets constsize,
// since the prefix yields the size
for i := mfixstr; i < 0xc0; i++ {
sizes[i] = bytespec{size: 1 + rfixstr(i), extra: constsize, typ: StrType}
}
// fixmap
for i := mfixmap; i < 0x90; i++ {
sizes[i] = bytespec{size: 1, extra: varmode(2 * rfixmap(i)), typ: MapType}
}
// fixarray
for i := mfixarray; i < 0xa0; i++ {
sizes[i] = bytespec{size: 1, extra: varmode(rfixarray(i)), typ: ArrayType}
}
}
// a valid bytespsec has
// non-zero 'size' and
// non-zero 'typ'
type bytespec struct {
size uint8 // prefix size information
extra varmode // extra size information
typ Type // type
_ byte // makes bytespec 4 bytes (yes, this matters)
}
// size mode
// if positive, # elements for composites
type varmode int8
const (
constsize varmode = 0 // constant size (size bytes + uint8(varmode) objects)
extra8 = -1 // has uint8(p[1]) extra bytes
extra16 = -2 // has be16(p[1:]) extra bytes
extra32 = -3 // has be32(p[1:]) extra bytes
map16v = -4 // use map16
map32v = -5 // use map32
array16v = -6 // use array16
array32v = -7 // use array32
)
func getType(v byte) Type {
return sizes[v].typ
}

142
vendor/github.com/tinylib/msgp/msgp/errors.go generated vendored Normal file
View file

@ -0,0 +1,142 @@
package msgp
import (
"fmt"
"reflect"
)
var (
// ErrShortBytes is returned when the
// slice being decoded is too short to
// contain the contents of the message
ErrShortBytes error = errShort{}
// this error is only returned
// if we reach code that should
// be unreachable
fatal error = errFatal{}
)
// Error is the interface satisfied
// by all of the errors that originate
// from this package.
type Error interface {
error
// Resumable returns whether
// or not the error means that
// the stream of data is malformed
// and the information is unrecoverable.
Resumable() bool
}
type errShort struct{}
func (e errShort) Error() string { return "msgp: too few bytes left to read object" }
func (e errShort) Resumable() bool { return false }
type errFatal struct{}
func (f errFatal) Error() string { return "msgp: fatal decoding error (unreachable code)" }
func (f errFatal) Resumable() bool { return false }
// ArrayError is an error returned
// when decoding a fix-sized array
// of the wrong size
type ArrayError struct {
Wanted uint32
Got uint32
}
// Error implements the error interface
func (a ArrayError) Error() string {
return fmt.Sprintf("msgp: wanted array of size %d; got %d", a.Wanted, a.Got)
}
// Resumable is always 'true' for ArrayErrors
func (a ArrayError) Resumable() bool { return true }
// IntOverflow is returned when a call
// would downcast an integer to a type
// with too few bits to hold its value.
type IntOverflow struct {
Value int64 // the value of the integer
FailedBitsize int // the bit size that the int64 could not fit into
}
// Error implements the error interface
func (i IntOverflow) Error() string {
return fmt.Sprintf("msgp: %d overflows int%d", i.Value, i.FailedBitsize)
}
// Resumable is always 'true' for overflows
func (i IntOverflow) Resumable() bool { return true }
// UintOverflow is returned when a call
// would downcast an unsigned integer to a type
// with too few bits to hold its value
type UintOverflow struct {
Value uint64 // value of the uint
FailedBitsize int // the bit size that couldn't fit the value
}
// Error implements the error interface
func (u UintOverflow) Error() string {
return fmt.Sprintf("msgp: %d overflows uint%d", u.Value, u.FailedBitsize)
}
// Resumable is always 'true' for overflows
func (u UintOverflow) Resumable() bool { return true }
// A TypeError is returned when a particular
// decoding method is unsuitable for decoding
// a particular MessagePack value.
type TypeError struct {
Method Type // Type expected by method
Encoded Type // Type actually encoded
}
// Error implements the error interface
func (t TypeError) Error() string {
return fmt.Sprintf("msgp: attempted to decode type %q with method for %q", t.Encoded, t.Method)
}
// Resumable returns 'true' for TypeErrors
func (t TypeError) Resumable() bool { return true }
// returns either InvalidPrefixError or
// TypeError depending on whether or not
// the prefix is recognized
func badPrefix(want Type, lead byte) error {
t := sizes[lead].typ
if t == InvalidType {
return InvalidPrefixError(lead)
}
return TypeError{Method: want, Encoded: t}
}
// InvalidPrefixError is returned when a bad encoding
// uses a prefix that is not recognized in the MessagePack standard.
// This kind of error is unrecoverable.
type InvalidPrefixError byte
// Error implements the error interface
func (i InvalidPrefixError) Error() string {
return fmt.Sprintf("msgp: unrecognized type prefix 0x%x", byte(i))
}
// Resumable returns 'false' for InvalidPrefixErrors
func (i InvalidPrefixError) Resumable() bool { return false }
// ErrUnsupportedType is returned
// when a bad argument is supplied
// to a function that takes `interface{}`.
type ErrUnsupportedType struct {
T reflect.Type
}
// Error implements error
func (e *ErrUnsupportedType) Error() string { return fmt.Sprintf("msgp: type %q not supported", e.T) }
// Resumable returns 'true' for ErrUnsupportedType
func (e *ErrUnsupportedType) Resumable() bool { return true }

548
vendor/github.com/tinylib/msgp/msgp/extension.go generated vendored Normal file
View file

@ -0,0 +1,548 @@
package msgp
import (
"fmt"
"math"
)
const (
// Complex64Extension is the extension number used for complex64
Complex64Extension = 3
// Complex128Extension is the extension number used for complex128
Complex128Extension = 4
// TimeExtension is the extension number used for time.Time
TimeExtension = 5
)
// our extensions live here
var extensionReg = make(map[int8]func() Extension)
// RegisterExtension registers extensions so that they
// can be initialized and returned by methods that
// decode `interface{}` values. This should only
// be called during initialization. f() should return
// a newly-initialized zero value of the extension. Keep in
// mind that extensions 3, 4, and 5 are reserved for
// complex64, complex128, and time.Time, respectively,
// and that MessagePack reserves extension types from -127 to -1.
//
// For example, if you wanted to register a user-defined struct:
//
// msgp.RegisterExtension(10, func() msgp.Extension { &MyExtension{} })
//
// RegisterExtension will panic if you call it multiple times
// with the same 'typ' argument, or if you use a reserved
// type (3, 4, or 5).
func RegisterExtension(typ int8, f func() Extension) {
switch typ {
case Complex64Extension, Complex128Extension, TimeExtension:
panic(fmt.Sprint("msgp: forbidden extension type:", typ))
}
if _, ok := extensionReg[typ]; ok {
panic(fmt.Sprint("msgp: RegisterExtension() called with typ", typ, "more than once"))
}
extensionReg[typ] = f
}
// ExtensionTypeError is an error type returned
// when there is a mis-match between an extension type
// and the type encoded on the wire
type ExtensionTypeError struct {
Got int8
Want int8
}
// Error implements the error interface
func (e ExtensionTypeError) Error() string {
return fmt.Sprintf("msgp: error decoding extension: wanted type %d; got type %d", e.Want, e.Got)
}
// Resumable returns 'true' for ExtensionTypeErrors
func (e ExtensionTypeError) Resumable() bool { return true }
func errExt(got int8, wanted int8) error {
return ExtensionTypeError{Got: got, Want: wanted}
}
// Extension is the interface fulfilled
// by types that want to define their
// own binary encoding.
type Extension interface {
// ExtensionType should return
// a int8 that identifies the concrete
// type of the extension. (Types <0 are
// officially reserved by the MessagePack
// specifications.)
ExtensionType() int8
// Len should return the length
// of the data to be encoded
Len() int
// MarshalBinaryTo should copy
// the data into the supplied slice,
// assuming that the slice has length Len()
MarshalBinaryTo([]byte) error
UnmarshalBinary([]byte) error
}
// RawExtension implements the Extension interface
type RawExtension struct {
Data []byte
Type int8
}
// ExtensionType implements Extension.ExtensionType, and returns r.Type
func (r *RawExtension) ExtensionType() int8 { return r.Type }
// Len implements Extension.Len, and returns len(r.Data)
func (r *RawExtension) Len() int { return len(r.Data) }
// MarshalBinaryTo implements Extension.MarshalBinaryTo,
// and returns a copy of r.Data
func (r *RawExtension) MarshalBinaryTo(d []byte) error {
copy(d, r.Data)
return nil
}
// UnmarshalBinary implements Extension.UnmarshalBinary,
// and sets r.Data to the contents of the provided slice
func (r *RawExtension) UnmarshalBinary(b []byte) error {
if cap(r.Data) >= len(b) {
r.Data = r.Data[0:len(b)]
} else {
r.Data = make([]byte, len(b))
}
copy(r.Data, b)
return nil
}
// WriteExtension writes an extension type to the writer
func (mw *Writer) WriteExtension(e Extension) error {
l := e.Len()
var err error
switch l {
case 0:
o, err := mw.require(3)
if err != nil {
return err
}
mw.buf[o] = mext8
mw.buf[o+1] = 0
mw.buf[o+2] = byte(e.ExtensionType())
case 1:
o, err := mw.require(2)
if err != nil {
return err
}
mw.buf[o] = mfixext1
mw.buf[o+1] = byte(e.ExtensionType())
case 2:
o, err := mw.require(2)
if err != nil {
return err
}
mw.buf[o] = mfixext2
mw.buf[o+1] = byte(e.ExtensionType())
case 4:
o, err := mw.require(2)
if err != nil {
return err
}
mw.buf[o] = mfixext4
mw.buf[o+1] = byte(e.ExtensionType())
case 8:
o, err := mw.require(2)
if err != nil {
return err
}
mw.buf[o] = mfixext8
mw.buf[o+1] = byte(e.ExtensionType())
case 16:
o, err := mw.require(2)
if err != nil {
return err
}
mw.buf[o] = mfixext16
mw.buf[o+1] = byte(e.ExtensionType())
default:
switch {
case l < math.MaxUint8:
o, err := mw.require(3)
if err != nil {
return err
}
mw.buf[o] = mext8
mw.buf[o+1] = byte(uint8(l))
mw.buf[o+2] = byte(e.ExtensionType())
case l < math.MaxUint16:
o, err := mw.require(4)
if err != nil {
return err
}
mw.buf[o] = mext16
big.PutUint16(mw.buf[o+1:], uint16(l))
mw.buf[o+3] = byte(e.ExtensionType())
default:
o, err := mw.require(6)
if err != nil {
return err
}
mw.buf[o] = mext32
big.PutUint32(mw.buf[o+1:], uint32(l))
mw.buf[o+5] = byte(e.ExtensionType())
}
}
// we can only write directly to the
// buffer if we're sure that it
// fits the object
if l <= mw.bufsize() {
o, err := mw.require(l)
if err != nil {
return err
}
return e.MarshalBinaryTo(mw.buf[o:])
}
// here we create a new buffer
// just large enough for the body
// and save it as the write buffer
err = mw.flush()
if err != nil {
return err
}
buf := make([]byte, l)
err = e.MarshalBinaryTo(buf)
if err != nil {
return err
}
mw.buf = buf
mw.wloc = l
return nil
}
// peek at the extension type, assuming the next
// kind to be read is Extension
func (m *Reader) peekExtensionType() (int8, error) {
p, err := m.R.Peek(2)
if err != nil {
return 0, err
}
spec := sizes[p[0]]
if spec.typ != ExtensionType {
return 0, badPrefix(ExtensionType, p[0])
}
if spec.extra == constsize {
return int8(p[1]), nil
}
size := spec.size
p, err = m.R.Peek(int(size))
if err != nil {
return 0, err
}
return int8(p[size-1]), nil
}
// peekExtension peeks at the extension encoding type
// (must guarantee at least 1 byte in 'b')
func peekExtension(b []byte) (int8, error) {
spec := sizes[b[0]]
size := spec.size
if spec.typ != ExtensionType {
return 0, badPrefix(ExtensionType, b[0])
}
if len(b) < int(size) {
return 0, ErrShortBytes
}
// for fixed extensions,
// the type information is in
// the second byte
if spec.extra == constsize {
return int8(b[1]), nil
}
// otherwise, it's in the last
// part of the prefix
return int8(b[size-1]), nil
}
// ReadExtension reads the next object from the reader
// as an extension. ReadExtension will fail if the next
// object in the stream is not an extension, or if
// e.Type() is not the same as the wire type.
func (m *Reader) ReadExtension(e Extension) (err error) {
var p []byte
p, err = m.R.Peek(2)
if err != nil {
return
}
lead := p[0]
var read int
var off int
switch lead {
case mfixext1:
if int8(p[1]) != e.ExtensionType() {
err = errExt(int8(p[1]), e.ExtensionType())
return
}
p, err = m.R.Peek(3)
if err != nil {
return
}
err = e.UnmarshalBinary(p[2:])
if err == nil {
_, err = m.R.Skip(3)
}
return
case mfixext2:
if int8(p[1]) != e.ExtensionType() {
err = errExt(int8(p[1]), e.ExtensionType())
return
}
p, err = m.R.Peek(4)
if err != nil {
return
}
err = e.UnmarshalBinary(p[2:])
if err == nil {
_, err = m.R.Skip(4)
}
return
case mfixext4:
if int8(p[1]) != e.ExtensionType() {
err = errExt(int8(p[1]), e.ExtensionType())
return
}
p, err = m.R.Peek(6)
if err != nil {
return
}
err = e.UnmarshalBinary(p[2:])
if err == nil {
_, err = m.R.Skip(6)
}
return
case mfixext8:
if int8(p[1]) != e.ExtensionType() {
err = errExt(int8(p[1]), e.ExtensionType())
return
}
p, err = m.R.Peek(10)
if err != nil {
return
}
err = e.UnmarshalBinary(p[2:])
if err == nil {
_, err = m.R.Skip(10)
}
return
case mfixext16:
if int8(p[1]) != e.ExtensionType() {
err = errExt(int8(p[1]), e.ExtensionType())
return
}
p, err = m.R.Peek(18)
if err != nil {
return
}
err = e.UnmarshalBinary(p[2:])
if err == nil {
_, err = m.R.Skip(18)
}
return
case mext8:
p, err = m.R.Peek(3)
if err != nil {
return
}
if int8(p[2]) != e.ExtensionType() {
err = errExt(int8(p[2]), e.ExtensionType())
return
}
read = int(uint8(p[1]))
off = 3
case mext16:
p, err = m.R.Peek(4)
if err != nil {
return
}
if int8(p[3]) != e.ExtensionType() {
err = errExt(int8(p[3]), e.ExtensionType())
return
}
read = int(big.Uint16(p[1:]))
off = 4
case mext32:
p, err = m.R.Peek(6)
if err != nil {
return
}
if int8(p[5]) != e.ExtensionType() {
err = errExt(int8(p[5]), e.ExtensionType())
return
}
read = int(big.Uint32(p[1:]))
off = 6
default:
err = badPrefix(ExtensionType, lead)
return
}
p, err = m.R.Peek(read + off)
if err != nil {
return
}
err = e.UnmarshalBinary(p[off:])
if err == nil {
_, err = m.R.Skip(read + off)
}
return
}
// AppendExtension appends a MessagePack extension to the provided slice
func AppendExtension(b []byte, e Extension) ([]byte, error) {
l := e.Len()
var o []byte
var n int
switch l {
case 0:
o, n = ensure(b, 3)
o[n] = mext8
o[n+1] = 0
o[n+2] = byte(e.ExtensionType())
return o[:n+3], nil
case 1:
o, n = ensure(b, 3)
o[n] = mfixext1
o[n+1] = byte(e.ExtensionType())
n += 2
case 2:
o, n = ensure(b, 4)
o[n] = mfixext2
o[n+1] = byte(e.ExtensionType())
n += 2
case 4:
o, n = ensure(b, 6)
o[n] = mfixext4
o[n+1] = byte(e.ExtensionType())
n += 2
case 8:
o, n = ensure(b, 10)
o[n] = mfixext8
o[n+1] = byte(e.ExtensionType())
n += 2
case 16:
o, n = ensure(b, 18)
o[n] = mfixext16
o[n+1] = byte(e.ExtensionType())
n += 2
}
switch {
case l < math.MaxUint8:
o, n = ensure(b, l+3)
o[n] = mext8
o[n+1] = byte(uint8(l))
o[n+2] = byte(e.ExtensionType())
n += 3
case l < math.MaxUint16:
o, n = ensure(b, l+4)
o[n] = mext16
big.PutUint16(o[n+1:], uint16(l))
o[n+3] = byte(e.ExtensionType())
n += 4
default:
o, n = ensure(b, l+6)
o[n] = mext32
big.PutUint32(o[n+1:], uint32(l))
o[n+5] = byte(e.ExtensionType())
n += 6
}
return o, e.MarshalBinaryTo(o[n:])
}
// ReadExtensionBytes reads an extension from 'b' into 'e'
// and returns any remaining bytes.
// Possible errors:
// - ErrShortBytes ('b' not long enough)
// - ExtensionTypeErorr{} (wire type not the same as e.Type())
// - TypeErorr{} (next object not an extension)
// - InvalidPrefixError
// - An umarshal error returned from e.UnmarshalBinary
func ReadExtensionBytes(b []byte, e Extension) ([]byte, error) {
l := len(b)
if l < 3 {
return b, ErrShortBytes
}
lead := b[0]
var (
sz int // size of 'data'
off int // offset of 'data'
typ int8
)
switch lead {
case mfixext1:
typ = int8(b[1])
sz = 1
off = 2
case mfixext2:
typ = int8(b[1])
sz = 2
off = 2
case mfixext4:
typ = int8(b[1])
sz = 4
off = 2
case mfixext8:
typ = int8(b[1])
sz = 8
off = 2
case mfixext16:
typ = int8(b[1])
sz = 16
off = 2
case mext8:
sz = int(uint8(b[1]))
typ = int8(b[2])
off = 3
if sz == 0 {
return b[3:], e.UnmarshalBinary(b[3:3])
}
case mext16:
if l < 4 {
return b, ErrShortBytes
}
sz = int(big.Uint16(b[1:]))
typ = int8(b[3])
off = 4
case mext32:
if l < 6 {
return b, ErrShortBytes
}
sz = int(big.Uint32(b[1:]))
typ = int8(b[5])
off = 6
default:
return b, badPrefix(ExtensionType, lead)
}
if typ != e.ExtensionType() {
return b, errExt(typ, e.ExtensionType())
}
// the data of the extension starts
// at 'off' and is 'sz' bytes long
if len(b[off:]) < sz {
return b, ErrShortBytes
}
tot := off + sz
return b[tot:], e.UnmarshalBinary(b[off:tot])
}

92
vendor/github.com/tinylib/msgp/msgp/file.go generated vendored Normal file
View file

@ -0,0 +1,92 @@
// +build linux darwin dragonfly freebsd netbsd openbsd
// +build !appengine
package msgp
import (
"os"
"syscall"
)
// ReadFile reads a file into 'dst' using
// a read-only memory mapping. Consequently,
// the file must be mmap-able, and the
// Unmarshaler should never write to
// the source memory. (Methods generated
// by the msgp tool obey that constraint, but
// user-defined implementations may not.)
//
// Reading and writing through file mappings
// is only efficient for large files; small
// files are best read and written using
// the ordinary streaming interfaces.
//
func ReadFile(dst Unmarshaler, file *os.File) error {
stat, err := file.Stat()
if err != nil {
return err
}
data, err := syscall.Mmap(int(file.Fd()), 0, int(stat.Size()), syscall.PROT_READ, syscall.MAP_SHARED)
if err != nil {
return err
}
adviseRead(data)
_, err = dst.UnmarshalMsg(data)
uerr := syscall.Munmap(data)
if err == nil {
err = uerr
}
return err
}
// MarshalSizer is the combination
// of the Marshaler and Sizer
// interfaces.
type MarshalSizer interface {
Marshaler
Sizer
}
// WriteFile writes a file from 'src' using
// memory mapping. It overwrites the entire
// contents of the previous file.
// The mapping size is calculated
// using the `Msgsize()` method
// of 'src', so it must produce a result
// equal to or greater than the actual encoded
// size of the object. Otherwise,
// a fault (SIGBUS) will occur.
//
// Reading and writing through file mappings
// is only efficient for large files; small
// files are best read and written using
// the ordinary streaming interfaces.
//
// NOTE: The performance of this call
// is highly OS- and filesystem-dependent.
// Users should take care to test that this
// performs as expected in a production environment.
// (Linux users should run a kernel and filesystem
// that support fallocate(2) for the best results.)
func WriteFile(src MarshalSizer, file *os.File) error {
sz := src.Msgsize()
err := fallocate(file, int64(sz))
if err != nil {
return err
}
data, err := syscall.Mmap(int(file.Fd()), 0, sz, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
if err != nil {
return err
}
adviseWrite(data)
chunk := data[:0]
chunk, err = src.MarshalMsg(chunk)
if err != nil {
return err
}
uerr := syscall.Munmap(data)
if uerr != nil {
return uerr
}
return file.Truncate(int64(len(chunk)))
}

47
vendor/github.com/tinylib/msgp/msgp/file_port.go generated vendored Normal file
View file

@ -0,0 +1,47 @@
// +build windows appengine
package msgp
import (
"io/ioutil"
"os"
)
// MarshalSizer is the combination
// of the Marshaler and Sizer
// interfaces.
type MarshalSizer interface {
Marshaler
Sizer
}
func ReadFile(dst Unmarshaler, file *os.File) error {
if u, ok := dst.(Decodable); ok {
return u.DecodeMsg(NewReader(file))
}
data, err := ioutil.ReadAll(file)
if err != nil {
return err
}
_, err = dst.UnmarshalMsg(data)
return err
}
func WriteFile(src MarshalSizer, file *os.File) error {
if e, ok := src.(Encodable); ok {
w := NewWriter(file)
err := e.EncodeMsg(w)
if err == nil {
err = w.Flush()
}
return err
}
raw, err := src.MarshalMsg(nil)
if err != nil {
return err
}
_, err = file.Write(raw)
return err
}

174
vendor/github.com/tinylib/msgp/msgp/integers.go generated vendored Normal file
View file

@ -0,0 +1,174 @@
package msgp
/* ----------------------------------
integer encoding utilities
(inline-able)
TODO(tinylib): there are faster,
albeit non-portable solutions
to the code below. implement
byteswap?
---------------------------------- */
func putMint64(b []byte, i int64) {
b[0] = mint64
b[1] = byte(i >> 56)
b[2] = byte(i >> 48)
b[3] = byte(i >> 40)
b[4] = byte(i >> 32)
b[5] = byte(i >> 24)
b[6] = byte(i >> 16)
b[7] = byte(i >> 8)
b[8] = byte(i)
}
func getMint64(b []byte) int64 {
return (int64(b[1]) << 56) | (int64(b[2]) << 48) |
(int64(b[3]) << 40) | (int64(b[4]) << 32) |
(int64(b[5]) << 24) | (int64(b[6]) << 16) |
(int64(b[7]) << 8) | (int64(b[8]))
}
func putMint32(b []byte, i int32) {
b[0] = mint32
b[1] = byte(i >> 24)
b[2] = byte(i >> 16)
b[3] = byte(i >> 8)
b[4] = byte(i)
}
func getMint32(b []byte) int32 {
return (int32(b[1]) << 24) | (int32(b[2]) << 16) | (int32(b[3]) << 8) | (int32(b[4]))
}
func putMint16(b []byte, i int16) {
b[0] = mint16
b[1] = byte(i >> 8)
b[2] = byte(i)
}
func getMint16(b []byte) (i int16) {
return (int16(b[1]) << 8) | int16(b[2])
}
func putMint8(b []byte, i int8) {
b[0] = mint8
b[1] = byte(i)
}
func getMint8(b []byte) (i int8) {
return int8(b[1])
}
func putMuint64(b []byte, u uint64) {
b[0] = muint64
b[1] = byte(u >> 56)
b[2] = byte(u >> 48)
b[3] = byte(u >> 40)
b[4] = byte(u >> 32)
b[5] = byte(u >> 24)
b[6] = byte(u >> 16)
b[7] = byte(u >> 8)
b[8] = byte(u)
}
func getMuint64(b []byte) uint64 {
return (uint64(b[1]) << 56) | (uint64(b[2]) << 48) |
(uint64(b[3]) << 40) | (uint64(b[4]) << 32) |
(uint64(b[5]) << 24) | (uint64(b[6]) << 16) |
(uint64(b[7]) << 8) | (uint64(b[8]))
}
func putMuint32(b []byte, u uint32) {
b[0] = muint32
b[1] = byte(u >> 24)
b[2] = byte(u >> 16)
b[3] = byte(u >> 8)
b[4] = byte(u)
}
func getMuint32(b []byte) uint32 {
return (uint32(b[1]) << 24) | (uint32(b[2]) << 16) | (uint32(b[3]) << 8) | (uint32(b[4]))
}
func putMuint16(b []byte, u uint16) {
b[0] = muint16
b[1] = byte(u >> 8)
b[2] = byte(u)
}
func getMuint16(b []byte) uint16 {
return (uint16(b[1]) << 8) | uint16(b[2])
}
func putMuint8(b []byte, u uint8) {
b[0] = muint8
b[1] = byte(u)
}
func getMuint8(b []byte) uint8 {
return uint8(b[1])
}
func getUnix(b []byte) (sec int64, nsec int32) {
sec = (int64(b[0]) << 56) | (int64(b[1]) << 48) |
(int64(b[2]) << 40) | (int64(b[3]) << 32) |
(int64(b[4]) << 24) | (int64(b[5]) << 16) |
(int64(b[6]) << 8) | (int64(b[7]))
nsec = (int32(b[8]) << 24) | (int32(b[9]) << 16) | (int32(b[10]) << 8) | (int32(b[11]))
return
}
func putUnix(b []byte, sec int64, nsec int32) {
b[0] = byte(sec >> 56)
b[1] = byte(sec >> 48)
b[2] = byte(sec >> 40)
b[3] = byte(sec >> 32)
b[4] = byte(sec >> 24)
b[5] = byte(sec >> 16)
b[6] = byte(sec >> 8)
b[7] = byte(sec)
b[8] = byte(nsec >> 24)
b[9] = byte(nsec >> 16)
b[10] = byte(nsec >> 8)
b[11] = byte(nsec)
}
/* -----------------------------
prefix utilities
----------------------------- */
// write prefix and uint8
func prefixu8(b []byte, pre byte, sz uint8) {
b[0] = pre
b[1] = byte(sz)
}
// write prefix and big-endian uint16
func prefixu16(b []byte, pre byte, sz uint16) {
b[0] = pre
b[1] = byte(sz >> 8)
b[2] = byte(sz)
}
// write prefix and big-endian uint32
func prefixu32(b []byte, pre byte, sz uint32) {
b[0] = pre
b[1] = byte(sz >> 24)
b[2] = byte(sz >> 16)
b[3] = byte(sz >> 8)
b[4] = byte(sz)
}
func prefixu64(b []byte, pre byte, sz uint64) {
b[0] = pre
b[1] = byte(sz >> 56)
b[2] = byte(sz >> 48)
b[3] = byte(sz >> 40)
b[4] = byte(sz >> 32)
b[5] = byte(sz >> 24)
b[6] = byte(sz >> 16)
b[7] = byte(sz >> 8)
b[8] = byte(sz)
}

542
vendor/github.com/tinylib/msgp/msgp/json.go generated vendored Normal file
View file

@ -0,0 +1,542 @@
package msgp
import (
"bufio"
"encoding/base64"
"encoding/json"
"io"
"strconv"
"unicode/utf8"
)
var (
null = []byte("null")
hex = []byte("0123456789abcdef")
)
var defuns [_maxtype]func(jsWriter, *Reader) (int, error)
// note: there is an initialization loop if
// this isn't set up during init()
func init() {
// since none of these functions are inline-able,
// there is not much of a penalty to the indirect
// call. however, this is best expressed as a jump-table...
defuns = [_maxtype]func(jsWriter, *Reader) (int, error){
StrType: rwString,
BinType: rwBytes,
MapType: rwMap,
ArrayType: rwArray,
Float64Type: rwFloat64,
Float32Type: rwFloat32,
BoolType: rwBool,
IntType: rwInt,
UintType: rwUint,
NilType: rwNil,
ExtensionType: rwExtension,
Complex64Type: rwExtension,
Complex128Type: rwExtension,
TimeType: rwTime,
}
}
// this is the interface
// used to write json
type jsWriter interface {
io.Writer
io.ByteWriter
WriteString(string) (int, error)
}
// CopyToJSON reads MessagePack from 'src' and copies it
// as JSON to 'dst' until EOF.
func CopyToJSON(dst io.Writer, src io.Reader) (n int64, err error) {
r := NewReader(src)
n, err = r.WriteToJSON(dst)
freeR(r)
return
}
// WriteToJSON translates MessagePack from 'r' and writes it as
// JSON to 'w' until the underlying reader returns io.EOF. It returns
// the number of bytes written, and an error if it stopped before EOF.
func (r *Reader) WriteToJSON(w io.Writer) (n int64, err error) {
var j jsWriter
var bf *bufio.Writer
if jsw, ok := w.(jsWriter); ok {
j = jsw
} else {
bf = bufio.NewWriter(w)
j = bf
}
var nn int
for err == nil {
nn, err = rwNext(j, r)
n += int64(nn)
}
if err != io.EOF {
if bf != nil {
bf.Flush()
}
return
}
err = nil
if bf != nil {
err = bf.Flush()
}
return
}
func rwNext(w jsWriter, src *Reader) (int, error) {
t, err := src.NextType()
if err != nil {
return 0, err
}
return defuns[t](w, src)
}
func rwMap(dst jsWriter, src *Reader) (n int, err error) {
var comma bool
var sz uint32
var field []byte
sz, err = src.ReadMapHeader()
if err != nil {
return
}
if sz == 0 {
return dst.WriteString("{}")
}
err = dst.WriteByte('{')
if err != nil {
return
}
n++
var nn int
for i := uint32(0); i < sz; i++ {
if comma {
err = dst.WriteByte(',')
if err != nil {
return
}
n++
}
field, err = src.ReadMapKeyPtr()
if err != nil {
return
}
nn, err = rwquoted(dst, field)
n += nn
if err != nil {
return
}
err = dst.WriteByte(':')
if err != nil {
return
}
n++
nn, err = rwNext(dst, src)
n += nn
if err != nil {
return
}
if !comma {
comma = true
}
}
err = dst.WriteByte('}')
if err != nil {
return
}
n++
return
}
func rwArray(dst jsWriter, src *Reader) (n int, err error) {
err = dst.WriteByte('[')
if err != nil {
return
}
var sz uint32
var nn int
sz, err = src.ReadArrayHeader()
if err != nil {
return
}
comma := false
for i := uint32(0); i < sz; i++ {
if comma {
err = dst.WriteByte(',')
if err != nil {
return
}
n++
}
nn, err = rwNext(dst, src)
n += nn
if err != nil {
return
}
comma = true
}
err = dst.WriteByte(']')
if err != nil {
return
}
n++
return
}
func rwNil(dst jsWriter, src *Reader) (int, error) {
err := src.ReadNil()
if err != nil {
return 0, err
}
return dst.Write(null)
}
func rwFloat32(dst jsWriter, src *Reader) (int, error) {
f, err := src.ReadFloat32()
if err != nil {
return 0, err
}
src.scratch = strconv.AppendFloat(src.scratch[:0], float64(f), 'f', -1, 64)
return dst.Write(src.scratch)
}
func rwFloat64(dst jsWriter, src *Reader) (int, error) {
f, err := src.ReadFloat64()
if err != nil {
return 0, err
}
src.scratch = strconv.AppendFloat(src.scratch[:0], f, 'f', -1, 32)
return dst.Write(src.scratch)
}
func rwInt(dst jsWriter, src *Reader) (int, error) {
i, err := src.ReadInt64()
if err != nil {
return 0, err
}
src.scratch = strconv.AppendInt(src.scratch[:0], i, 10)
return dst.Write(src.scratch)
}
func rwUint(dst jsWriter, src *Reader) (int, error) {
u, err := src.ReadUint64()
if err != nil {
return 0, err
}
src.scratch = strconv.AppendUint(src.scratch[:0], u, 10)
return dst.Write(src.scratch)
}
func rwBool(dst jsWriter, src *Reader) (int, error) {
b, err := src.ReadBool()
if err != nil {
return 0, err
}
if b {
return dst.WriteString("true")
}
return dst.WriteString("false")
}
func rwTime(dst jsWriter, src *Reader) (int, error) {
t, err := src.ReadTime()
if err != nil {
return 0, err
}
bts, err := t.MarshalJSON()
if err != nil {
return 0, err
}
return dst.Write(bts)
}
func rwExtension(dst jsWriter, src *Reader) (n int, err error) {
et, err := src.peekExtensionType()
if err != nil {
return 0, err
}
// registered extensions can override
// the JSON encoding
if j, ok := extensionReg[et]; ok {
var bts []byte
e := j()
err = src.ReadExtension(e)
if err != nil {
return
}
bts, err = json.Marshal(e)
if err != nil {
return
}
return dst.Write(bts)
}
e := RawExtension{}
e.Type = et
err = src.ReadExtension(&e)
if err != nil {
return
}
var nn int
err = dst.WriteByte('{')
if err != nil {
return
}
n++
nn, err = dst.WriteString(`"type:"`)
n += nn
if err != nil {
return
}
src.scratch = strconv.AppendInt(src.scratch[0:0], int64(e.Type), 10)
nn, err = dst.Write(src.scratch)
n += nn
if err != nil {
return
}
nn, err = dst.WriteString(`,"data":"`)
n += nn
if err != nil {
return
}
enc := base64.NewEncoder(base64.StdEncoding, dst)
nn, err = enc.Write(e.Data)
n += nn
if err != nil {
return
}
err = enc.Close()
if err != nil {
return
}
nn, err = dst.WriteString(`"}`)
n += nn
return
}
func rwString(dst jsWriter, src *Reader) (n int, err error) {
var p []byte
p, err = src.R.Peek(1)
if err != nil {
return
}
lead := p[0]
var read int
if isfixstr(lead) {
read = int(rfixstr(lead))
src.R.Skip(1)
goto write
}
switch lead {
case mstr8:
p, err = src.R.Next(2)
if err != nil {
return
}
read = int(uint8(p[1]))
case mstr16:
p, err = src.R.Next(3)
if err != nil {
return
}
read = int(big.Uint16(p[1:]))
case mstr32:
p, err = src.R.Next(5)
if err != nil {
return
}
read = int(big.Uint32(p[1:]))
default:
err = badPrefix(StrType, lead)
return
}
write:
p, err = src.R.Next(read)
if err != nil {
return
}
n, err = rwquoted(dst, p)
return
}
func rwBytes(dst jsWriter, src *Reader) (n int, err error) {
var nn int
err = dst.WriteByte('"')
if err != nil {
return
}
n++
src.scratch, err = src.ReadBytes(src.scratch[:0])
if err != nil {
return
}
enc := base64.NewEncoder(base64.StdEncoding, dst)
nn, err = enc.Write(src.scratch)
n += nn
if err != nil {
return
}
err = enc.Close()
if err != nil {
return
}
err = dst.WriteByte('"')
if err != nil {
return
}
n++
return
}
// Below (c) The Go Authors, 2009-2014
// Subject to the BSD-style license found at http://golang.org
//
// see: encoding/json/encode.go:(*encodeState).stringbytes()
func rwquoted(dst jsWriter, s []byte) (n int, err error) {
var nn int
err = dst.WriteByte('"')
if err != nil {
return
}
n++
start := 0
for i := 0; i < len(s); {
if b := s[i]; b < utf8.RuneSelf {
if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
i++
continue
}
if start < i {
nn, err = dst.Write(s[start:i])
n += nn
if err != nil {
return
}
}
switch b {
case '\\', '"':
err = dst.WriteByte('\\')
if err != nil {
return
}
n++
err = dst.WriteByte(b)
if err != nil {
return
}
n++
case '\n':
err = dst.WriteByte('\\')
if err != nil {
return
}
n++
err = dst.WriteByte('n')
if err != nil {
return
}
n++
case '\r':
err = dst.WriteByte('\\')
if err != nil {
return
}
n++
err = dst.WriteByte('r')
if err != nil {
return
}
n++
default:
nn, err = dst.WriteString(`\u00`)
n += nn
if err != nil {
return
}
err = dst.WriteByte(hex[b>>4])
if err != nil {
return
}
n++
err = dst.WriteByte(hex[b&0xF])
if err != nil {
return
}
n++
}
i++
start = i
continue
}
c, size := utf8.DecodeRune(s[i:])
if c == utf8.RuneError && size == 1 {
if start < i {
nn, err = dst.Write(s[start:i])
n += nn
if err != nil {
return
}
nn, err = dst.WriteString(`\ufffd`)
n += nn
if err != nil {
return
}
i += size
start = i
continue
}
}
if c == '\u2028' || c == '\u2029' {
if start < i {
nn, err = dst.Write(s[start:i])
n += nn
if err != nil {
return
}
nn, err = dst.WriteString(`\u202`)
n += nn
if err != nil {
return
}
err = dst.WriteByte(hex[c&0xF])
if err != nil {
return
}
n++
}
}
i += size
}
if start < len(s) {
nn, err = dst.Write(s[start:])
n += nn
if err != nil {
return
}
}
err = dst.WriteByte('"')
if err != nil {
return
}
n++
return
}

363
vendor/github.com/tinylib/msgp/msgp/json_bytes.go generated vendored Normal file
View file

@ -0,0 +1,363 @@
package msgp
import (
"bufio"
"encoding/base64"
"encoding/json"
"io"
"strconv"
"time"
)
var unfuns [_maxtype]func(jsWriter, []byte, []byte) ([]byte, []byte, error)
func init() {
// NOTE(pmh): this is best expressed as a jump table,
// but gc doesn't do that yet. revisit post-go1.5.
unfuns = [_maxtype]func(jsWriter, []byte, []byte) ([]byte, []byte, error){
StrType: rwStringBytes,
BinType: rwBytesBytes,
MapType: rwMapBytes,
ArrayType: rwArrayBytes,
Float64Type: rwFloat64Bytes,
Float32Type: rwFloat32Bytes,
BoolType: rwBoolBytes,
IntType: rwIntBytes,
UintType: rwUintBytes,
NilType: rwNullBytes,
ExtensionType: rwExtensionBytes,
Complex64Type: rwExtensionBytes,
Complex128Type: rwExtensionBytes,
TimeType: rwTimeBytes,
}
}
// UnmarshalAsJSON takes raw messagepack and writes
// it as JSON to 'w'. If an error is returned, the
// bytes not translated will also be returned. If
// no errors are encountered, the length of the returned
// slice will be zero.
func UnmarshalAsJSON(w io.Writer, msg []byte) ([]byte, error) {
var (
scratch []byte
cast bool
dst jsWriter
err error
)
if jsw, ok := w.(jsWriter); ok {
dst = jsw
cast = true
} else {
dst = bufio.NewWriterSize(w, 512)
}
for len(msg) > 0 && err == nil {
msg, scratch, err = writeNext(dst, msg, scratch)
}
if !cast && err == nil {
err = dst.(*bufio.Writer).Flush()
}
return msg, err
}
func writeNext(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
if len(msg) < 1 {
return msg, scratch, ErrShortBytes
}
t := getType(msg[0])
if t == InvalidType {
return msg, scratch, InvalidPrefixError(msg[0])
}
if t == ExtensionType {
et, err := peekExtension(msg)
if err != nil {
return nil, scratch, err
}
if et == TimeExtension {
t = TimeType
}
}
return unfuns[t](w, msg, scratch)
}
func rwArrayBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
sz, msg, err := ReadArrayHeaderBytes(msg)
if err != nil {
return msg, scratch, err
}
err = w.WriteByte('[')
if err != nil {
return msg, scratch, err
}
for i := uint32(0); i < sz; i++ {
if i != 0 {
err = w.WriteByte(',')
if err != nil {
return msg, scratch, err
}
}
msg, scratch, err = writeNext(w, msg, scratch)
if err != nil {
return msg, scratch, err
}
}
err = w.WriteByte(']')
return msg, scratch, err
}
func rwMapBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
sz, msg, err := ReadMapHeaderBytes(msg)
if err != nil {
return msg, scratch, err
}
err = w.WriteByte('{')
if err != nil {
return msg, scratch, err
}
for i := uint32(0); i < sz; i++ {
if i != 0 {
err = w.WriteByte(',')
if err != nil {
return msg, scratch, err
}
}
msg, scratch, err = rwMapKeyBytes(w, msg, scratch)
if err != nil {
return msg, scratch, err
}
err = w.WriteByte(':')
if err != nil {
return msg, scratch, err
}
msg, scratch, err = writeNext(w, msg, scratch)
if err != nil {
return msg, scratch, err
}
}
err = w.WriteByte('}')
return msg, scratch, err
}
func rwMapKeyBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
msg, scratch, err := rwStringBytes(w, msg, scratch)
if err != nil {
if tperr, ok := err.(TypeError); ok && tperr.Encoded == BinType {
return rwBytesBytes(w, msg, scratch)
}
}
return msg, scratch, err
}
func rwStringBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
str, msg, err := ReadStringZC(msg)
if err != nil {
return msg, scratch, err
}
_, err = rwquoted(w, str)
return msg, scratch, err
}
func rwBytesBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
bts, msg, err := ReadBytesZC(msg)
if err != nil {
return msg, scratch, err
}
l := base64.StdEncoding.EncodedLen(len(bts))
if cap(scratch) >= l {
scratch = scratch[0:l]
} else {
scratch = make([]byte, l)
}
base64.StdEncoding.Encode(scratch, bts)
err = w.WriteByte('"')
if err != nil {
return msg, scratch, err
}
_, err = w.Write(scratch)
if err != nil {
return msg, scratch, err
}
err = w.WriteByte('"')
return msg, scratch, err
}
func rwNullBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
msg, err := ReadNilBytes(msg)
if err != nil {
return msg, scratch, err
}
_, err = w.Write(null)
return msg, scratch, err
}
func rwBoolBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
b, msg, err := ReadBoolBytes(msg)
if err != nil {
return msg, scratch, err
}
if b {
_, err = w.WriteString("true")
return msg, scratch, err
}
_, err = w.WriteString("false")
return msg, scratch, err
}
func rwIntBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
i, msg, err := ReadInt64Bytes(msg)
if err != nil {
return msg, scratch, err
}
scratch = strconv.AppendInt(scratch[0:0], i, 10)
_, err = w.Write(scratch)
return msg, scratch, err
}
func rwUintBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
u, msg, err := ReadUint64Bytes(msg)
if err != nil {
return msg, scratch, err
}
scratch = strconv.AppendUint(scratch[0:0], u, 10)
_, err = w.Write(scratch)
return msg, scratch, err
}
func rwFloatBytes(w jsWriter, msg []byte, f64 bool, scratch []byte) ([]byte, []byte, error) {
var f float64
var err error
var sz int
if f64 {
sz = 64
f, msg, err = ReadFloat64Bytes(msg)
} else {
sz = 32
var v float32
v, msg, err = ReadFloat32Bytes(msg)
f = float64(v)
}
if err != nil {
return msg, scratch, err
}
scratch = strconv.AppendFloat(scratch, f, 'f', -1, sz)
_, err = w.Write(scratch)
return msg, scratch, err
}
func rwFloat32Bytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
var f float32
var err error
f, msg, err = ReadFloat32Bytes(msg)
if err != nil {
return msg, scratch, err
}
scratch = strconv.AppendFloat(scratch[:0], float64(f), 'f', -1, 32)
_, err = w.Write(scratch)
return msg, scratch, err
}
func rwFloat64Bytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
var f float64
var err error
f, msg, err = ReadFloat64Bytes(msg)
if err != nil {
return msg, scratch, err
}
scratch = strconv.AppendFloat(scratch[:0], f, 'f', -1, 64)
_, err = w.Write(scratch)
return msg, scratch, err
}
func rwTimeBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
var t time.Time
var err error
t, msg, err = ReadTimeBytes(msg)
if err != nil {
return msg, scratch, err
}
bts, err := t.MarshalJSON()
if err != nil {
return msg, scratch, err
}
_, err = w.Write(bts)
return msg, scratch, err
}
func rwExtensionBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) {
var err error
var et int8
et, err = peekExtension(msg)
if err != nil {
return msg, scratch, err
}
// if it's time.Time
if et == TimeExtension {
var tm time.Time
tm, msg, err = ReadTimeBytes(msg)
if err != nil {
return msg, scratch, err
}
bts, err := tm.MarshalJSON()
if err != nil {
return msg, scratch, err
}
_, err = w.Write(bts)
return msg, scratch, err
}
// if the extension is registered,
// use its canonical JSON form
if f, ok := extensionReg[et]; ok {
e := f()
msg, err = ReadExtensionBytes(msg, e)
if err != nil {
return msg, scratch, err
}
bts, err := json.Marshal(e)
if err != nil {
return msg, scratch, err
}
_, err = w.Write(bts)
return msg, scratch, err
}
// otherwise, write `{"type": <num>, "data": "<base64data>"}`
r := RawExtension{}
r.Type = et
msg, err = ReadExtensionBytes(msg, &r)
if err != nil {
return msg, scratch, err
}
scratch, err = writeExt(w, r, scratch)
return msg, scratch, err
}
func writeExt(w jsWriter, r RawExtension, scratch []byte) ([]byte, error) {
_, err := w.WriteString(`{"type":`)
if err != nil {
return scratch, err
}
scratch = strconv.AppendInt(scratch[0:0], int64(r.Type), 10)
_, err = w.Write(scratch)
if err != nil {
return scratch, err
}
_, err = w.WriteString(`,"data":"`)
if err != nil {
return scratch, err
}
l := base64.StdEncoding.EncodedLen(len(r.Data))
if cap(scratch) >= l {
scratch = scratch[0:l]
} else {
scratch = make([]byte, l)
}
base64.StdEncoding.Encode(scratch, r.Data)
_, err = w.Write(scratch)
if err != nil {
return scratch, err
}
_, err = w.WriteString(`"}`)
return scratch, err
}

267
vendor/github.com/tinylib/msgp/msgp/number.go generated vendored Normal file
View file

@ -0,0 +1,267 @@
package msgp
import (
"math"
"strconv"
)
// The portable parts of the Number implementation
// Number can be
// an int64, uint64, float32,
// or float64 internally.
// It can decode itself
// from any of the native
// messagepack number types.
// The zero-value of Number
// is Int(0). Using the equality
// operator with Number compares
// both the type and the value
// of the number.
type Number struct {
// internally, this
// is just a tagged union.
// the raw bits of the number
// are stored the same way regardless.
bits uint64
typ Type
}
// AsInt sets the number to an int64.
func (n *Number) AsInt(i int64) {
// we always store int(0)
// as {0, InvalidType} in
// order to preserve
// the behavior of the == operator
if i == 0 {
n.typ = InvalidType
n.bits = 0
return
}
n.typ = IntType
n.bits = uint64(i)
}
// AsUint sets the number to a uint64.
func (n *Number) AsUint(u uint64) {
n.typ = UintType
n.bits = u
}
// AsFloat32 sets the value of the number
// to a float32.
func (n *Number) AsFloat32(f float32) {
n.typ = Float32Type
n.bits = uint64(math.Float32bits(f))
}
// AsFloat64 sets the value of the
// number to a float64.
func (n *Number) AsFloat64(f float64) {
n.typ = Float64Type
n.bits = math.Float64bits(f)
}
// Int casts the number as an int64, and
// returns whether or not that was the
// underlying type.
func (n *Number) Int() (int64, bool) {
return int64(n.bits), n.typ == IntType || n.typ == InvalidType
}
// Uint casts the number as a uint64, and returns
// whether or not that was the underlying type.
func (n *Number) Uint() (uint64, bool) {
return n.bits, n.typ == UintType
}
// Float casts the number to a float64, and
// returns whether or not that was the underlying
// type (either a float64 or a float32).
func (n *Number) Float() (float64, bool) {
switch n.typ {
case Float32Type:
return float64(math.Float32frombits(uint32(n.bits))), true
case Float64Type:
return math.Float64frombits(n.bits), true
default:
return 0.0, false
}
}
// Type will return one of:
// Float64Type, Float32Type, UintType, or IntType.
func (n *Number) Type() Type {
if n.typ == InvalidType {
return IntType
}
return n.typ
}
// DecodeMsg implements msgp.Decodable
func (n *Number) DecodeMsg(r *Reader) error {
typ, err := r.NextType()
if err != nil {
return err
}
switch typ {
case Float32Type:
f, err := r.ReadFloat32()
if err != nil {
return err
}
n.AsFloat32(f)
return nil
case Float64Type:
f, err := r.ReadFloat64()
if err != nil {
return err
}
n.AsFloat64(f)
return nil
case IntType:
i, err := r.ReadInt64()
if err != nil {
return err
}
n.AsInt(i)
return nil
case UintType:
u, err := r.ReadUint64()
if err != nil {
return err
}
n.AsUint(u)
return nil
default:
return TypeError{Encoded: typ, Method: IntType}
}
}
// UnmarshalMsg implements msgp.Unmarshaler
func (n *Number) UnmarshalMsg(b []byte) ([]byte, error) {
typ := NextType(b)
switch typ {
case IntType:
i, o, err := ReadInt64Bytes(b)
if err != nil {
return b, err
}
n.AsInt(i)
return o, nil
case UintType:
u, o, err := ReadUint64Bytes(b)
if err != nil {
return b, err
}
n.AsUint(u)
return o, nil
case Float64Type:
f, o, err := ReadFloat64Bytes(b)
if err != nil {
return b, err
}
n.AsFloat64(f)
return o, nil
case Float32Type:
f, o, err := ReadFloat32Bytes(b)
if err != nil {
return b, err
}
n.AsFloat32(f)
return o, nil
default:
return b, TypeError{Method: IntType, Encoded: typ}
}
}
// MarshalMsg implements msgp.Marshaler
func (n *Number) MarshalMsg(b []byte) ([]byte, error) {
switch n.typ {
case IntType:
return AppendInt64(b, int64(n.bits)), nil
case UintType:
return AppendUint64(b, uint64(n.bits)), nil
case Float64Type:
return AppendFloat64(b, math.Float64frombits(n.bits)), nil
case Float32Type:
return AppendFloat32(b, math.Float32frombits(uint32(n.bits))), nil
default:
return AppendInt64(b, 0), nil
}
}
// EncodeMsg implements msgp.Encodable
func (n *Number) EncodeMsg(w *Writer) error {
switch n.typ {
case IntType:
return w.WriteInt64(int64(n.bits))
case UintType:
return w.WriteUint64(n.bits)
case Float64Type:
return w.WriteFloat64(math.Float64frombits(n.bits))
case Float32Type:
return w.WriteFloat32(math.Float32frombits(uint32(n.bits)))
default:
return w.WriteInt64(0)
}
}
// Msgsize implements msgp.Sizer
func (n *Number) Msgsize() int {
switch n.typ {
case Float32Type:
return Float32Size
case Float64Type:
return Float64Size
case IntType:
return Int64Size
case UintType:
return Uint64Size
default:
return 1 // fixint(0)
}
}
// MarshalJSON implements json.Marshaler
func (n *Number) MarshalJSON() ([]byte, error) {
t := n.Type()
if t == InvalidType {
return []byte{'0'}, nil
}
out := make([]byte, 0, 32)
switch t {
case Float32Type, Float64Type:
f, _ := n.Float()
return strconv.AppendFloat(out, f, 'f', -1, 64), nil
case IntType:
i, _ := n.Int()
return strconv.AppendInt(out, i, 10), nil
case UintType:
u, _ := n.Uint()
return strconv.AppendUint(out, u, 10), nil
default:
panic("(*Number).typ is invalid")
}
}
// String implements fmt.Stringer
func (n *Number) String() string {
switch n.typ {
case InvalidType:
return "0"
case Float32Type, Float64Type:
f, _ := n.Float()
return strconv.FormatFloat(f, 'f', -1, 64)
case IntType:
i, _ := n.Int()
return strconv.FormatInt(i, 10)
case UintType:
u, _ := n.Uint()
return strconv.FormatUint(u, 10)
default:
panic("(*Number).typ is invalid")
}
}

1265
vendor/github.com/tinylib/msgp/msgp/read.go generated vendored Normal file

File diff suppressed because it is too large Load diff

1089
vendor/github.com/tinylib/msgp/msgp/read_bytes.go generated vendored Normal file

File diff suppressed because it is too large Load diff

38
vendor/github.com/tinylib/msgp/msgp/size.go generated vendored Normal file
View file

@ -0,0 +1,38 @@
package msgp
// The sizes provided
// are the worst-case
// encoded sizes for
// each type. For variable-
// length types ([]byte, string),
// the total encoded size is
// the prefix size plus the
// length of the object.
const (
Int64Size = 9
IntSize = Int64Size
UintSize = Int64Size
Int8Size = 2
Int16Size = 3
Int32Size = 5
Uint8Size = 2
ByteSize = Uint8Size
Uint16Size = 3
Uint32Size = 5
Uint64Size = Int64Size
Float64Size = 9
Float32Size = 5
Complex64Size = 10
Complex128Size = 18
TimeSize = 15
BoolSize = 1
NilSize = 1
MapHeaderSize = 5
ArrayHeaderSize = 5
BytesPrefixSize = 5
StringPrefixSize = 5
ExtensionPrefixSize = 6
)

40
vendor/github.com/tinylib/msgp/msgp/unsafe.go generated vendored Normal file
View file

@ -0,0 +1,40 @@
// +build !appengine
package msgp
import (
"reflect"
"unsafe"
)
// NOTE:
// all of the definition in this file
// should be repeated in appengine.go,
// but without using unsafe
const (
// spec says int and uint are always
// the same size, but that int/uint
// size may not be machine word size
smallint = unsafe.Sizeof(int(0)) == 4
)
// UnsafeString returns the byte slice as a volatile string
// THIS SHOULD ONLY BE USED BY THE CODE GENERATOR.
// THIS IS EVIL CODE.
// YOU HAVE BEEN WARNED.
func UnsafeString(b []byte) string {
return *(*string)(unsafe.Pointer(&reflect.StringHeader{Data: uintptr(unsafe.Pointer(&b[0])), Len: len(b)}))
}
// UnsafeBytes returns the string as a byte slice
// THIS SHOULD ONLY BE USED BY THE CODE GENERATOR.
// THIS IS EVIL CODE.
// YOU HAVE BEEN WARNED.
func UnsafeBytes(s string) []byte {
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Len: len(s),
Cap: len(s),
Data: (*(*reflect.StringHeader)(unsafe.Pointer(&s))).Data,
}))
}

845
vendor/github.com/tinylib/msgp/msgp/write.go generated vendored Normal file
View file

@ -0,0 +1,845 @@
package msgp
import (
"errors"
"fmt"
"io"
"math"
"reflect"
"sync"
"time"
)
// Sizer is an interface implemented
// by types that can estimate their
// size when MessagePack encoded.
// This interface is optional, but
// encoding/marshaling implementations
// may use this as a way to pre-allocate
// memory for serialization.
type Sizer interface {
Msgsize() int
}
var (
// Nowhere is an io.Writer to nowhere
Nowhere io.Writer = nwhere{}
btsType = reflect.TypeOf(([]byte)(nil))
writerPool = sync.Pool{
New: func() interface{} {
return &Writer{buf: make([]byte, 2048)}
},
}
)
func popWriter(w io.Writer) *Writer {
wr := writerPool.Get().(*Writer)
wr.Reset(w)
return wr
}
func pushWriter(wr *Writer) {
wr.w = nil
wr.wloc = 0
writerPool.Put(wr)
}
// freeW frees a writer for use
// by other processes. It is not necessary
// to call freeW on a writer. However, maintaining
// a reference to a *Writer after calling freeW on
// it will cause undefined behavior.
func freeW(w *Writer) { pushWriter(w) }
// Require ensures that cap(old)-len(old) >= extra.
func Require(old []byte, extra int) []byte {
l := len(old)
c := cap(old)
r := l + extra
if c >= r {
return old
} else if l == 0 {
return make([]byte, 0, extra)
}
// the new size is the greater
// of double the old capacity
// and the sum of the old length
// and the number of new bytes
// necessary.
c <<= 1
if c < r {
c = r
}
n := make([]byte, l, c)
copy(n, old)
return n
}
// nowhere writer
type nwhere struct{}
func (n nwhere) Write(p []byte) (int, error) { return len(p), nil }
// Marshaler is the interface implemented
// by types that know how to marshal themselves
// as MessagePack. MarshalMsg appends the marshalled
// form of the object to the provided
// byte slice, returning the extended
// slice and any errors encountered.
type Marshaler interface {
MarshalMsg([]byte) ([]byte, error)
}
// Encodable is the interface implemented
// by types that know how to write themselves
// as MessagePack using a *msgp.Writer.
type Encodable interface {
EncodeMsg(*Writer) error
}
// Writer is a buffered writer
// that can be used to write
// MessagePack objects to an io.Writer.
// You must call *Writer.Flush() in order
// to flush all of the buffered data
// to the underlying writer.
type Writer struct {
w io.Writer
buf []byte
wloc int
}
// NewWriter returns a new *Writer.
func NewWriter(w io.Writer) *Writer {
if wr, ok := w.(*Writer); ok {
return wr
}
return popWriter(w)
}
// NewWriterSize returns a writer with a custom buffer size.
func NewWriterSize(w io.Writer, sz int) *Writer {
// we must be able to require() 18
// contiguous bytes, so that is the
// practical minimum buffer size
if sz < 18 {
sz = 18
}
return &Writer{
w: w,
buf: make([]byte, sz),
}
}
// Encode encodes an Encodable to an io.Writer.
func Encode(w io.Writer, e Encodable) error {
wr := NewWriter(w)
err := e.EncodeMsg(wr)
if err == nil {
err = wr.Flush()
}
freeW(wr)
return err
}
func (mw *Writer) flush() error {
if mw.wloc == 0 {
return nil
}
n, err := mw.w.Write(mw.buf[:mw.wloc])
if err != nil {
if n > 0 {
mw.wloc = copy(mw.buf, mw.buf[n:mw.wloc])
}
return err
}
mw.wloc = 0
return nil
}
// Flush flushes all of the buffered
// data to the underlying writer.
func (mw *Writer) Flush() error { return mw.flush() }
// Buffered returns the number bytes in the write buffer
func (mw *Writer) Buffered() int { return len(mw.buf) - mw.wloc }
func (mw *Writer) avail() int { return len(mw.buf) - mw.wloc }
func (mw *Writer) bufsize() int { return len(mw.buf) }
// NOTE: this should only be called with
// a number that is guaranteed to be less than
// len(mw.buf). typically, it is called with a constant.
//
// NOTE: this is a hot code path
func (mw *Writer) require(n int) (int, error) {
c := len(mw.buf)
wl := mw.wloc
if c-wl < n {
if err := mw.flush(); err != nil {
return 0, err
}
wl = mw.wloc
}
mw.wloc += n
return wl, nil
}
func (mw *Writer) Append(b ...byte) error {
if mw.avail() < len(b) {
err := mw.flush()
if err != nil {
return err
}
}
mw.wloc += copy(mw.buf[mw.wloc:], b)
return nil
}
// push one byte onto the buffer
//
// NOTE: this is a hot code path
func (mw *Writer) push(b byte) error {
if mw.wloc == len(mw.buf) {
if err := mw.flush(); err != nil {
return err
}
}
mw.buf[mw.wloc] = b
mw.wloc++
return nil
}
func (mw *Writer) prefix8(b byte, u uint8) error {
const need = 2
if len(mw.buf)-mw.wloc < need {
if err := mw.flush(); err != nil {
return err
}
}
prefixu8(mw.buf[mw.wloc:], b, u)
mw.wloc += need
return nil
}
func (mw *Writer) prefix16(b byte, u uint16) error {
const need = 3
if len(mw.buf)-mw.wloc < need {
if err := mw.flush(); err != nil {
return err
}
}
prefixu16(mw.buf[mw.wloc:], b, u)
mw.wloc += need
return nil
}
func (mw *Writer) prefix32(b byte, u uint32) error {
const need = 5
if len(mw.buf)-mw.wloc < need {
if err := mw.flush(); err != nil {
return err
}
}
prefixu32(mw.buf[mw.wloc:], b, u)
mw.wloc += need
return nil
}
func (mw *Writer) prefix64(b byte, u uint64) error {
const need = 9
if len(mw.buf)-mw.wloc < need {
if err := mw.flush(); err != nil {
return err
}
}
prefixu64(mw.buf[mw.wloc:], b, u)
mw.wloc += need
return nil
}
// Write implements io.Writer, and writes
// data directly to the buffer.
func (mw *Writer) Write(p []byte) (int, error) {
l := len(p)
if mw.avail() < l {
if err := mw.flush(); err != nil {
return 0, err
}
if l > len(mw.buf) {
return mw.w.Write(p)
}
}
mw.wloc += copy(mw.buf[mw.wloc:], p)
return l, nil
}
// implements io.WriteString
func (mw *Writer) writeString(s string) error {
l := len(s)
if mw.avail() < l {
if err := mw.flush(); err != nil {
return err
}
if l > len(mw.buf) {
_, err := io.WriteString(mw.w, s)
return err
}
}
mw.wloc += copy(mw.buf[mw.wloc:], s)
return nil
}
// Reset changes the underlying writer used by the Writer
func (mw *Writer) Reset(w io.Writer) {
mw.buf = mw.buf[:cap(mw.buf)]
mw.w = w
mw.wloc = 0
}
// WriteMapHeader writes a map header of the given
// size to the writer
func (mw *Writer) WriteMapHeader(sz uint32) error {
switch {
case sz <= 15:
return mw.push(wfixmap(uint8(sz)))
case sz <= math.MaxUint16:
return mw.prefix16(mmap16, uint16(sz))
default:
return mw.prefix32(mmap32, sz)
}
}
// WriteArrayHeader writes an array header of the
// given size to the writer
func (mw *Writer) WriteArrayHeader(sz uint32) error {
switch {
case sz <= 15:
return mw.push(wfixarray(uint8(sz)))
case sz <= math.MaxUint16:
return mw.prefix16(marray16, uint16(sz))
default:
return mw.prefix32(marray32, sz)
}
}
// WriteNil writes a nil byte to the buffer
func (mw *Writer) WriteNil() error {
return mw.push(mnil)
}
// WriteFloat64 writes a float64 to the writer
func (mw *Writer) WriteFloat64(f float64) error {
return mw.prefix64(mfloat64, math.Float64bits(f))
}
// WriteFloat32 writes a float32 to the writer
func (mw *Writer) WriteFloat32(f float32) error {
return mw.prefix32(mfloat32, math.Float32bits(f))
}
// WriteInt64 writes an int64 to the writer
func (mw *Writer) WriteInt64(i int64) error {
if i >= 0 {
switch {
case i <= math.MaxInt8:
return mw.push(wfixint(uint8(i)))
case i <= math.MaxInt16:
return mw.prefix16(mint16, uint16(i))
case i <= math.MaxInt32:
return mw.prefix32(mint32, uint32(i))
default:
return mw.prefix64(mint64, uint64(i))
}
}
switch {
case i >= -32:
return mw.push(wnfixint(int8(i)))
case i >= math.MinInt8:
return mw.prefix8(mint8, uint8(i))
case i >= math.MinInt16:
return mw.prefix16(mint16, uint16(i))
case i >= math.MinInt32:
return mw.prefix32(mint32, uint32(i))
default:
return mw.prefix64(mint64, uint64(i))
}
}
// WriteInt8 writes an int8 to the writer
func (mw *Writer) WriteInt8(i int8) error { return mw.WriteInt64(int64(i)) }
// WriteInt16 writes an int16 to the writer
func (mw *Writer) WriteInt16(i int16) error { return mw.WriteInt64(int64(i)) }
// WriteInt32 writes an int32 to the writer
func (mw *Writer) WriteInt32(i int32) error { return mw.WriteInt64(int64(i)) }
// WriteInt writes an int to the writer
func (mw *Writer) WriteInt(i int) error { return mw.WriteInt64(int64(i)) }
// WriteUint64 writes a uint64 to the writer
func (mw *Writer) WriteUint64(u uint64) error {
switch {
case u <= (1<<7)-1:
return mw.push(wfixint(uint8(u)))
case u <= math.MaxUint8:
return mw.prefix8(muint8, uint8(u))
case u <= math.MaxUint16:
return mw.prefix16(muint16, uint16(u))
case u <= math.MaxUint32:
return mw.prefix32(muint32, uint32(u))
default:
return mw.prefix64(muint64, u)
}
}
// WriteByte is analogous to WriteUint8
func (mw *Writer) WriteByte(u byte) error { return mw.WriteUint8(uint8(u)) }
// WriteUint8 writes a uint8 to the writer
func (mw *Writer) WriteUint8(u uint8) error { return mw.WriteUint64(uint64(u)) }
// WriteUint16 writes a uint16 to the writer
func (mw *Writer) WriteUint16(u uint16) error { return mw.WriteUint64(uint64(u)) }
// WriteUint32 writes a uint32 to the writer
func (mw *Writer) WriteUint32(u uint32) error { return mw.WriteUint64(uint64(u)) }
// WriteUint writes a uint to the writer
func (mw *Writer) WriteUint(u uint) error { return mw.WriteUint64(uint64(u)) }
// WriteBytes writes binary as 'bin' to the writer
func (mw *Writer) WriteBytes(b []byte) error {
sz := uint32(len(b))
var err error
switch {
case sz <= math.MaxUint8:
err = mw.prefix8(mbin8, uint8(sz))
case sz <= math.MaxUint16:
err = mw.prefix16(mbin16, uint16(sz))
default:
err = mw.prefix32(mbin32, sz)
}
if err != nil {
return err
}
_, err = mw.Write(b)
return err
}
// WriteBytesHeader writes just the size header
// of a MessagePack 'bin' object. The user is responsible
// for then writing 'sz' more bytes into the stream.
func (mw *Writer) WriteBytesHeader(sz uint32) error {
switch {
case sz <= math.MaxUint8:
return mw.prefix8(mbin8, uint8(sz))
case sz <= math.MaxUint16:
return mw.prefix16(mbin16, uint16(sz))
default:
return mw.prefix32(mbin32, sz)
}
}
// WriteBool writes a bool to the writer
func (mw *Writer) WriteBool(b bool) error {
if b {
return mw.push(mtrue)
}
return mw.push(mfalse)
}
// WriteString writes a messagepack string to the writer.
// (This is NOT an implementation of io.StringWriter)
func (mw *Writer) WriteString(s string) error {
sz := uint32(len(s))
var err error
switch {
case sz <= 31:
err = mw.push(wfixstr(uint8(sz)))
case sz <= math.MaxUint8:
err = mw.prefix8(mstr8, uint8(sz))
case sz <= math.MaxUint16:
err = mw.prefix16(mstr16, uint16(sz))
default:
err = mw.prefix32(mstr32, sz)
}
if err != nil {
return err
}
return mw.writeString(s)
}
// WriteStringHeader writes just the string size
// header of a MessagePack 'str' object. The user
// is responsible for writing 'sz' more valid UTF-8
// bytes to the stream.
func (mw *Writer) WriteStringHeader(sz uint32) error {
switch {
case sz <= 31:
return mw.push(wfixstr(uint8(sz)))
case sz <= math.MaxUint8:
return mw.prefix8(mstr8, uint8(sz))
case sz <= math.MaxUint16:
return mw.prefix16(mstr16, uint16(sz))
default:
return mw.prefix32(mstr32, sz)
}
}
// WriteStringFromBytes writes a 'str' object
// from a []byte.
func (mw *Writer) WriteStringFromBytes(str []byte) error {
sz := uint32(len(str))
var err error
switch {
case sz <= 31:
err = mw.push(wfixstr(uint8(sz)))
case sz <= math.MaxUint8:
err = mw.prefix8(mstr8, uint8(sz))
case sz <= math.MaxUint16:
err = mw.prefix16(mstr16, uint16(sz))
default:
err = mw.prefix32(mstr32, sz)
}
if err != nil {
return err
}
_, err = mw.Write(str)
return err
}
// WriteComplex64 writes a complex64 to the writer
func (mw *Writer) WriteComplex64(f complex64) error {
o, err := mw.require(10)
if err != nil {
return err
}
mw.buf[o] = mfixext8
mw.buf[o+1] = Complex64Extension
big.PutUint32(mw.buf[o+2:], math.Float32bits(real(f)))
big.PutUint32(mw.buf[o+6:], math.Float32bits(imag(f)))
return nil
}
// WriteComplex128 writes a complex128 to the writer
func (mw *Writer) WriteComplex128(f complex128) error {
o, err := mw.require(18)
if err != nil {
return err
}
mw.buf[o] = mfixext16
mw.buf[o+1] = Complex128Extension
big.PutUint64(mw.buf[o+2:], math.Float64bits(real(f)))
big.PutUint64(mw.buf[o+10:], math.Float64bits(imag(f)))
return nil
}
// WriteMapStrStr writes a map[string]string to the writer
func (mw *Writer) WriteMapStrStr(mp map[string]string) (err error) {
err = mw.WriteMapHeader(uint32(len(mp)))
if err != nil {
return
}
for key, val := range mp {
err = mw.WriteString(key)
if err != nil {
return
}
err = mw.WriteString(val)
if err != nil {
return
}
}
return nil
}
// WriteMapStrIntf writes a map[string]interface to the writer
func (mw *Writer) WriteMapStrIntf(mp map[string]interface{}) (err error) {
err = mw.WriteMapHeader(uint32(len(mp)))
if err != nil {
return
}
for key, val := range mp {
err = mw.WriteString(key)
if err != nil {
return
}
err = mw.WriteIntf(val)
if err != nil {
return
}
}
return
}
// WriteTime writes a time.Time object to the wire.
//
// Time is encoded as Unix time, which means that
// location (time zone) data is removed from the object.
// The encoded object itself is 12 bytes: 8 bytes for
// a big-endian 64-bit integer denoting seconds
// elapsed since "zero" Unix time, followed by 4 bytes
// for a big-endian 32-bit signed integer denoting
// the nanosecond offset of the time. This encoding
// is intended to ease portability across languages.
// (Note that this is *not* the standard time.Time
// binary encoding, because its implementation relies
// heavily on the internal representation used by the
// time package.)
func (mw *Writer) WriteTime(t time.Time) error {
t = t.UTC()
o, err := mw.require(15)
if err != nil {
return err
}
mw.buf[o] = mext8
mw.buf[o+1] = 12
mw.buf[o+2] = TimeExtension
putUnix(mw.buf[o+3:], t.Unix(), int32(t.Nanosecond()))
return nil
}
// WriteIntf writes the concrete type of 'v'.
// WriteIntf will error if 'v' is not one of the following:
// - A bool, float, string, []byte, int, uint, or complex
// - A map of supported types (with string keys)
// - An array or slice of supported types
// - A pointer to a supported type
// - A type that satisfies the msgp.Encodable interface
// - A type that satisfies the msgp.Extension interface
func (mw *Writer) WriteIntf(v interface{}) error {
if v == nil {
return mw.WriteNil()
}
switch v := v.(type) {
// preferred interfaces
case Encodable:
return v.EncodeMsg(mw)
case Extension:
return mw.WriteExtension(v)
// concrete types
case bool:
return mw.WriteBool(v)
case float32:
return mw.WriteFloat32(v)
case float64:
return mw.WriteFloat64(v)
case complex64:
return mw.WriteComplex64(v)
case complex128:
return mw.WriteComplex128(v)
case uint8:
return mw.WriteUint8(v)
case uint16:
return mw.WriteUint16(v)
case uint32:
return mw.WriteUint32(v)
case uint64:
return mw.WriteUint64(v)
case uint:
return mw.WriteUint(v)
case int8:
return mw.WriteInt8(v)
case int16:
return mw.WriteInt16(v)
case int32:
return mw.WriteInt32(v)
case int64:
return mw.WriteInt64(v)
case int:
return mw.WriteInt(v)
case string:
return mw.WriteString(v)
case []byte:
return mw.WriteBytes(v)
case map[string]string:
return mw.WriteMapStrStr(v)
case map[string]interface{}:
return mw.WriteMapStrIntf(v)
case time.Time:
return mw.WriteTime(v)
}
val := reflect.ValueOf(v)
if !isSupported(val.Kind()) || !val.IsValid() {
return fmt.Errorf("msgp: type %s not supported", val)
}
switch val.Kind() {
case reflect.Ptr:
if val.IsNil() {
return mw.WriteNil()
}
return mw.WriteIntf(val.Elem().Interface())
case reflect.Slice:
return mw.writeSlice(val)
case reflect.Map:
return mw.writeMap(val)
}
return &ErrUnsupportedType{val.Type()}
}
func (mw *Writer) writeMap(v reflect.Value) (err error) {
if v.Type().Key().Kind() != reflect.String {
return errors.New("msgp: map keys must be strings")
}
ks := v.MapKeys()
err = mw.WriteMapHeader(uint32(len(ks)))
if err != nil {
return
}
for _, key := range ks {
val := v.MapIndex(key)
err = mw.WriteString(key.String())
if err != nil {
return
}
err = mw.WriteIntf(val.Interface())
if err != nil {
return
}
}
return
}
func (mw *Writer) writeSlice(v reflect.Value) (err error) {
// is []byte
if v.Type().ConvertibleTo(btsType) {
return mw.WriteBytes(v.Bytes())
}
sz := uint32(v.Len())
err = mw.WriteArrayHeader(sz)
if err != nil {
return
}
for i := uint32(0); i < sz; i++ {
err = mw.WriteIntf(v.Index(int(i)).Interface())
if err != nil {
return
}
}
return
}
func (mw *Writer) writeStruct(v reflect.Value) error {
if enc, ok := v.Interface().(Encodable); ok {
return enc.EncodeMsg(mw)
}
return fmt.Errorf("msgp: unsupported type: %s", v.Type())
}
func (mw *Writer) writeVal(v reflect.Value) error {
if !isSupported(v.Kind()) {
return fmt.Errorf("msgp: msgp/enc: type %q not supported", v.Type())
}
// shortcut for nil values
if v.IsNil() {
return mw.WriteNil()
}
switch v.Kind() {
case reflect.Bool:
return mw.WriteBool(v.Bool())
case reflect.Float32, reflect.Float64:
return mw.WriteFloat64(v.Float())
case reflect.Complex64, reflect.Complex128:
return mw.WriteComplex128(v.Complex())
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8:
return mw.WriteInt64(v.Int())
case reflect.Interface, reflect.Ptr:
if v.IsNil() {
mw.WriteNil()
}
return mw.writeVal(v.Elem())
case reflect.Map:
return mw.writeMap(v)
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint8:
return mw.WriteUint64(v.Uint())
case reflect.String:
return mw.WriteString(v.String())
case reflect.Slice, reflect.Array:
return mw.writeSlice(v)
case reflect.Struct:
return mw.writeStruct(v)
}
return fmt.Errorf("msgp: msgp/enc: type %q not supported", v.Type())
}
// is the reflect.Kind encodable?
func isSupported(k reflect.Kind) bool {
switch k {
case reflect.Func, reflect.Chan, reflect.Invalid, reflect.UnsafePointer:
return false
default:
return true
}
}
// GuessSize guesses the size of the underlying
// value of 'i'. If the underlying value is not
// a simple builtin (or []byte), GuessSize defaults
// to 512.
func GuessSize(i interface{}) int {
if i == nil {
return NilSize
}
switch i := i.(type) {
case Sizer:
return i.Msgsize()
case Extension:
return ExtensionPrefixSize + i.Len()
case float64:
return Float64Size
case float32:
return Float32Size
case uint8, uint16, uint32, uint64, uint:
return UintSize
case int8, int16, int32, int64, int:
return IntSize
case []byte:
return BytesPrefixSize + len(i)
case string:
return StringPrefixSize + len(i)
case complex64:
return Complex64Size
case complex128:
return Complex128Size
case bool:
return BoolSize
case map[string]interface{}:
s := MapHeaderSize
for key, val := range i {
s += StringPrefixSize + len(key) + GuessSize(val)
}
return s
case map[string]string:
s := MapHeaderSize
for key, val := range i {
s += 2*StringPrefixSize + len(key) + len(val)
}
return s
default:
return 512
}
}

411
vendor/github.com/tinylib/msgp/msgp/write_bytes.go generated vendored Normal file
View file

@ -0,0 +1,411 @@
package msgp
import (
"math"
"reflect"
"time"
)
// ensure 'sz' extra bytes in 'b' btw len(b) and cap(b)
func ensure(b []byte, sz int) ([]byte, int) {
l := len(b)
c := cap(b)
if c-l < sz {
o := make([]byte, (2*c)+sz) // exponential growth
n := copy(o, b)
return o[:n+sz], n
}
return b[:l+sz], l
}
// AppendMapHeader appends a map header with the
// given size to the slice
func AppendMapHeader(b []byte, sz uint32) []byte {
switch {
case sz <= 15:
return append(b, wfixmap(uint8(sz)))
case sz <= math.MaxUint16:
o, n := ensure(b, 3)
prefixu16(o[n:], mmap16, uint16(sz))
return o
default:
o, n := ensure(b, 5)
prefixu32(o[n:], mmap32, sz)
return o
}
}
// AppendArrayHeader appends an array header with
// the given size to the slice
func AppendArrayHeader(b []byte, sz uint32) []byte {
switch {
case sz <= 15:
return append(b, wfixarray(uint8(sz)))
case sz <= math.MaxUint16:
o, n := ensure(b, 3)
prefixu16(o[n:], marray16, uint16(sz))
return o
default:
o, n := ensure(b, 5)
prefixu32(o[n:], marray32, sz)
return o
}
}
// AppendNil appends a 'nil' byte to the slice
func AppendNil(b []byte) []byte { return append(b, mnil) }
// AppendFloat64 appends a float64 to the slice
func AppendFloat64(b []byte, f float64) []byte {
o, n := ensure(b, Float64Size)
prefixu64(o[n:], mfloat64, math.Float64bits(f))
return o
}
// AppendFloat32 appends a float32 to the slice
func AppendFloat32(b []byte, f float32) []byte {
o, n := ensure(b, Float32Size)
prefixu32(o[n:], mfloat32, math.Float32bits(f))
return o
}
// AppendInt64 appends an int64 to the slice
func AppendInt64(b []byte, i int64) []byte {
if i >= 0 {
switch {
case i <= math.MaxInt8:
return append(b, wfixint(uint8(i)))
case i <= math.MaxInt16:
o, n := ensure(b, 3)
putMint16(o[n:], int16(i))
return o
case i <= math.MaxInt32:
o, n := ensure(b, 5)
putMint32(o[n:], int32(i))
return o
default:
o, n := ensure(b, 9)
putMint64(o[n:], i)
return o
}
}
switch {
case i >= -32:
return append(b, wnfixint(int8(i)))
case i >= math.MinInt8:
o, n := ensure(b, 2)
putMint8(o[n:], int8(i))
return o
case i >= math.MinInt16:
o, n := ensure(b, 3)
putMint16(o[n:], int16(i))
return o
case i >= math.MinInt32:
o, n := ensure(b, 5)
putMint32(o[n:], int32(i))
return o
default:
o, n := ensure(b, 9)
putMint64(o[n:], i)
return o
}
}
// AppendInt appends an int to the slice
func AppendInt(b []byte, i int) []byte { return AppendInt64(b, int64(i)) }
// AppendInt8 appends an int8 to the slice
func AppendInt8(b []byte, i int8) []byte { return AppendInt64(b, int64(i)) }
// AppendInt16 appends an int16 to the slice
func AppendInt16(b []byte, i int16) []byte { return AppendInt64(b, int64(i)) }
// AppendInt32 appends an int32 to the slice
func AppendInt32(b []byte, i int32) []byte { return AppendInt64(b, int64(i)) }
// AppendUint64 appends a uint64 to the slice
func AppendUint64(b []byte, u uint64) []byte {
switch {
case u <= (1<<7)-1:
return append(b, wfixint(uint8(u)))
case u <= math.MaxUint8:
o, n := ensure(b, 2)
putMuint8(o[n:], uint8(u))
return o
case u <= math.MaxUint16:
o, n := ensure(b, 3)
putMuint16(o[n:], uint16(u))
return o
case u <= math.MaxUint32:
o, n := ensure(b, 5)
putMuint32(o[n:], uint32(u))
return o
default:
o, n := ensure(b, 9)
putMuint64(o[n:], u)
return o
}
}
// AppendUint appends a uint to the slice
func AppendUint(b []byte, u uint) []byte { return AppendUint64(b, uint64(u)) }
// AppendUint8 appends a uint8 to the slice
func AppendUint8(b []byte, u uint8) []byte { return AppendUint64(b, uint64(u)) }
// AppendByte is analogous to AppendUint8
func AppendByte(b []byte, u byte) []byte { return AppendUint8(b, uint8(u)) }
// AppendUint16 appends a uint16 to the slice
func AppendUint16(b []byte, u uint16) []byte { return AppendUint64(b, uint64(u)) }
// AppendUint32 appends a uint32 to the slice
func AppendUint32(b []byte, u uint32) []byte { return AppendUint64(b, uint64(u)) }
// AppendBytes appends bytes to the slice as MessagePack 'bin' data
func AppendBytes(b []byte, bts []byte) []byte {
sz := len(bts)
var o []byte
var n int
switch {
case sz <= math.MaxUint8:
o, n = ensure(b, 2+sz)
prefixu8(o[n:], mbin8, uint8(sz))
n += 2
case sz <= math.MaxUint16:
o, n = ensure(b, 3+sz)
prefixu16(o[n:], mbin16, uint16(sz))
n += 3
default:
o, n = ensure(b, 5+sz)
prefixu32(o[n:], mbin32, uint32(sz))
n += 5
}
return o[:n+copy(o[n:], bts)]
}
// AppendBool appends a bool to the slice
func AppendBool(b []byte, t bool) []byte {
if t {
return append(b, mtrue)
}
return append(b, mfalse)
}
// AppendString appends a string as a MessagePack 'str' to the slice
func AppendString(b []byte, s string) []byte {
sz := len(s)
var n int
var o []byte
switch {
case sz <= 31:
o, n = ensure(b, 1+sz)
o[n] = wfixstr(uint8(sz))
n++
case sz <= math.MaxUint8:
o, n = ensure(b, 2+sz)
prefixu8(o[n:], mstr8, uint8(sz))
n += 2
case sz <= math.MaxUint16:
o, n = ensure(b, 3+sz)
prefixu16(o[n:], mstr16, uint16(sz))
n += 3
default:
o, n = ensure(b, 5+sz)
prefixu32(o[n:], mstr32, uint32(sz))
n += 5
}
return o[:n+copy(o[n:], s)]
}
// AppendStringFromBytes appends a []byte
// as a MessagePack 'str' to the slice 'b.'
func AppendStringFromBytes(b []byte, str []byte) []byte {
sz := len(str)
var n int
var o []byte
switch {
case sz <= 31:
o, n = ensure(b, 1+sz)
o[n] = wfixstr(uint8(sz))
n++
case sz <= math.MaxUint8:
o, n = ensure(b, 2+sz)
prefixu8(o[n:], mstr8, uint8(sz))
n += 2
case sz <= math.MaxUint16:
o, n = ensure(b, 3+sz)
prefixu16(o[n:], mstr16, uint16(sz))
n += 3
default:
o, n = ensure(b, 5+sz)
prefixu32(o[n:], mstr32, uint32(sz))
n += 5
}
return o[:n+copy(o[n:], str)]
}
// AppendComplex64 appends a complex64 to the slice as a MessagePack extension
func AppendComplex64(b []byte, c complex64) []byte {
o, n := ensure(b, Complex64Size)
o[n] = mfixext8
o[n+1] = Complex64Extension
big.PutUint32(o[n+2:], math.Float32bits(real(c)))
big.PutUint32(o[n+6:], math.Float32bits(imag(c)))
return o
}
// AppendComplex128 appends a complex128 to the slice as a MessagePack extension
func AppendComplex128(b []byte, c complex128) []byte {
o, n := ensure(b, Complex128Size)
o[n] = mfixext16
o[n+1] = Complex128Extension
big.PutUint64(o[n+2:], math.Float64bits(real(c)))
big.PutUint64(o[n+10:], math.Float64bits(imag(c)))
return o
}
// AppendTime appends a time.Time to the slice as a MessagePack extension
func AppendTime(b []byte, t time.Time) []byte {
o, n := ensure(b, TimeSize)
t = t.UTC()
o[n] = mext8
o[n+1] = 12
o[n+2] = TimeExtension
putUnix(o[n+3:], t.Unix(), int32(t.Nanosecond()))
return o
}
// AppendMapStrStr appends a map[string]string to the slice
// as a MessagePack map with 'str'-type keys and values
func AppendMapStrStr(b []byte, m map[string]string) []byte {
sz := uint32(len(m))
b = AppendMapHeader(b, sz)
for key, val := range m {
b = AppendString(b, key)
b = AppendString(b, val)
}
return b
}
// AppendMapStrIntf appends a map[string]interface{} to the slice
// as a MessagePack map with 'str'-type keys.
func AppendMapStrIntf(b []byte, m map[string]interface{}) ([]byte, error) {
sz := uint32(len(m))
b = AppendMapHeader(b, sz)
var err error
for key, val := range m {
b = AppendString(b, key)
b, err = AppendIntf(b, val)
if err != nil {
return b, err
}
}
return b, nil
}
// AppendIntf appends the concrete type of 'i' to the
// provided []byte. 'i' must be one of the following:
// - 'nil'
// - A bool, float, string, []byte, int, uint, or complex
// - A map[string]interface{} or map[string]string
// - A []T, where T is another supported type
// - A *T, where T is another supported type
// - A type that satisfieds the msgp.Marshaler interface
// - A type that satisfies the msgp.Extension interface
func AppendIntf(b []byte, i interface{}) ([]byte, error) {
if i == nil {
return AppendNil(b), nil
}
// all the concrete types
// for which we have methods
switch i := i.(type) {
case Marshaler:
return i.MarshalMsg(b)
case Extension:
return AppendExtension(b, i)
case bool:
return AppendBool(b, i), nil
case float32:
return AppendFloat32(b, i), nil
case float64:
return AppendFloat64(b, i), nil
case complex64:
return AppendComplex64(b, i), nil
case complex128:
return AppendComplex128(b, i), nil
case string:
return AppendString(b, i), nil
case []byte:
return AppendBytes(b, i), nil
case int8:
return AppendInt8(b, i), nil
case int16:
return AppendInt16(b, i), nil
case int32:
return AppendInt32(b, i), nil
case int64:
return AppendInt64(b, i), nil
case int:
return AppendInt64(b, int64(i)), nil
case uint:
return AppendUint64(b, uint64(i)), nil
case uint8:
return AppendUint8(b, i), nil
case uint16:
return AppendUint16(b, i), nil
case uint32:
return AppendUint32(b, i), nil
case uint64:
return AppendUint64(b, i), nil
case time.Time:
return AppendTime(b, i), nil
case map[string]interface{}:
return AppendMapStrIntf(b, i)
case map[string]string:
return AppendMapStrStr(b, i), nil
case []interface{}:
b = AppendArrayHeader(b, uint32(len(i)))
var err error
for _, k := range i {
b, err = AppendIntf(b, k)
if err != nil {
return b, err
}
}
return b, nil
}
var err error
v := reflect.ValueOf(i)
switch v.Kind() {
case reflect.Array, reflect.Slice:
l := v.Len()
b = AppendArrayHeader(b, uint32(l))
for i := 0; i < l; i++ {
b, err = AppendIntf(b, v.Index(i).Interface())
if err != nil {
return b, err
}
}
return b, nil
case reflect.Ptr:
if v.IsNil() {
return AppendNil(b), err
}
b, err = AppendIntf(b, v.Elem().Interface())
return b, err
default:
return b, &ErrUnsupportedType{T: v.Type()}
}
}

24
vendor/gopkg.in/DataDog/dd-trace-go.v1/LICENSE generated vendored Normal file
View file

@ -0,0 +1,24 @@
Copyright (c) 2016, Datadog <info@datadoghq.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Datadog nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,2 @@
Component,Origin,License,Copyright
import,io.opentracing,Apache-2.0,Copyright 2016-2017 The OpenTracing Authors
1 Component Origin License Copyright
2 import io.opentracing Apache-2.0 Copyright 2016-2017 The OpenTracing Authors

View file

@ -0,0 +1,108 @@
// Package ddtrace contains the interfaces that specify the implementations of Datadog's
// tracing library, as well as a set of sub-packages containing various implementations:
// our native implementation ("tracer"), a wrapper that can be used with Opentracing
// ("opentracer") and a mock tracer to be used for testing ("mocktracer"). Additionally,
// package "ext" provides a set of tag names and values specific to Datadog's APM product.
//
// To get started, visit the documentation for any of the packages you'd like to begin
// with by accessing the subdirectories of this package: https://godoc.org/gopkg.in/DataDog/dd-trace-go.v1/ddtrace#pkg-subdirectories.
package ddtrace // import "gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
import "time"
// Tracer specifies an implementation of the Datadog tracer which allows starting
// and propagating spans. The official implementation if exposed as functions
// within the "tracer" package.
type Tracer interface {
// StartSpan starts a span with the given operation name and options.
StartSpan(operationName string, opts ...StartSpanOption) Span
// Extract extracts a span context from a given carrier. Note that baggage item
// keys will always be lower-cased to maintain consistency. It is impossible to
// maintain the original casing due to MIME header canonicalization standards.
Extract(carrier interface{}) (SpanContext, error)
// Inject injects a span context into the given carrier.
Inject(context SpanContext, carrier interface{}) error
// Stop stops the active tracer and sets the global tracer to a no-op. Calls to
// Stop should be idempotent.
Stop()
}
// Span represents a chunk of computation time. Spans have names, durations,
// timestamps and other metadata. A Tracer is used to create hierarchies of
// spans in a request, buffer and submit them to the server.
type Span interface {
// SetTag sets a key/value pair as metadata on the span.
SetTag(key string, value interface{})
// SetOperationName sets the operation name for this span. An operation name should be
// a representative name for a group of spans (e.g. "grpc.server" or "http.request").
SetOperationName(operationName string)
// BaggageItem returns the baggage item held by the given key.
BaggageItem(key string) string
// SetBaggageItem sets a new baggage item at the given key. The baggage
// item should propagate to all descendant spans, both in- and cross-process.
SetBaggageItem(key, val string)
// Finish finishes the current span with the given options. Finish calls should be idempotent.
Finish(opts ...FinishOption)
// Context returns the SpanContext of this Span.
Context() SpanContext
}
// SpanContext represents a span state that can propagate to descendant spans
// and across process boundaries. It contains all the information needed to
// spawn a direct descendant of the span that it belongs to. It can be used
// to create distributed tracing by propagating it using the provided interfaces.
type SpanContext interface {
// SpanID returns the span ID that this context is carrying.
SpanID() uint64
// TraceID returns the trace ID that this context is carrying.
TraceID() uint64
// ForeachBaggageItem provides an iterator over the key/value pairs set as
// baggage within this context. Iteration stops when the handler returns
// false.
ForeachBaggageItem(handler func(k, v string) bool)
}
// StartSpanOption is a configuration option that can be used with a Tracer's StartSpan method.
type StartSpanOption func(cfg *StartSpanConfig)
// FinishOption is a configuration option that can be used with a Span's Finish method.
type FinishOption func(cfg *FinishConfig)
// FinishConfig holds the configuration for finishing a span. It is usually passed around by
// reference to one or more FinishOption functions which shape it into its final form.
type FinishConfig struct {
// FinishTime represents the time that should be set as finishing time for the
// span. Implementations should use the current time when FinishTime.IsZero().
FinishTime time.Time
// Error holds an optional error that should be set on the span before
// finishing.
Error error
}
// StartSpanConfig holds the configuration for starting a new span. It is usually passed
// around by reference to one or more StartSpanOption functions which shape it into its
// final form.
type StartSpanConfig struct {
// Parent holds the SpanContext that should be used as a parent for the
// new span. If nil, implementations should return a root span.
Parent SpanContext
// StartTime holds the time that should be used as the start time of the span.
// Implementations should use the current time when StartTime.IsZero().
StartTime time.Time
// Tags holds a set of key/value pairs that should be set as metadata on the
// new span.
Tags map[string]interface{}
}

View file

@ -0,0 +1,19 @@
package ext // import "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
const (
// AppTypeWeb specifies the Web span type and can be used as a tag value
// for a span's SpanType tag.
AppTypeWeb = "web"
// AppTypeDB specifies the DB span type and can be used as a tag value
// for a span's SpanType tag.
AppTypeDB = "db"
// AppTypeCache specifies the Cache span type and can be used as a tag value
// for a span's SpanType tag.
AppTypeCache = "cache"
// AppTypeRPC specifies the RPC span type and can be used as a tag value
// for a span's SpanType tag.
AppTypeRPC = "rpc"
)

View file

@ -0,0 +1,21 @@
package ext
const (
// CassandraQuery is the tag name used for cassandra queries.
CassandraQuery = "cassandra.query"
// CassandraConsistencyLevel is the tag name to set for consitency level.
CassandraConsistencyLevel = "cassandra.consistency_level"
// CassandraCluster specifies the tag name that is used to set the cluster.
CassandraCluster = "cassandra.cluster"
// CassandraRowCount specifies the tag name to use when settings the row count.
CassandraRowCount = "cassandra.row_count"
// CassandraKeyspace is used as tag name for setting the key space.
CassandraKeyspace = "cassandra.keyspace"
// CassandraPaginated specifies the tag name for paginated queries.
CassandraPaginated = "cassandra.paginated"
)

View file

@ -0,0 +1,22 @@
package ext
// Priority is a hint given to the backend so that it knows which traces to reject or kept.
// In a distributed context, it should be set before any context propagation (fork, RPC calls) to be effective.
const (
// PriorityUserReject informs the backend that a trace should be rejected and not stored.
// This should be used by user code overriding default priority.
PriorityUserReject = -1
// PriorityAutoReject informs the backend that a trace should be rejected and not stored.
// This is used by the builtin sampler.
PriorityAutoReject = 0
// PriorityAutoKeep informs the backend that a trace should be kept and not stored.
// This is used by the builtin sampler.
PriorityAutoKeep = 1
// PriorityUserKeep informs the backend that a trace should be kept and not stored.
// This should be used by user code overriding default priority.
PriorityUserKeep = 2
)

View file

@ -0,0 +1,7 @@
package ext
// Standard system metadata names
const (
// The pid of the traced process
Pid = "system.pid"
)

View file

@ -0,0 +1,50 @@
// Package ext contains a set of Datadog-specific constants. Most of them are used
// for setting span metadata.
package ext
const (
// TargetHost sets the target host address.
TargetHost = "out.host"
// TargetPort sets the target host port.
TargetPort = "out.port"
// SamplingPriority is the tag that marks the sampling priority of a span.
SamplingPriority = "sampling.priority"
// SQLType sets the sql type tag.
SQLType = "sql"
// SQLQuery sets the sql query tag on a span.
SQLQuery = "sql.query"
// HTTPMethod specifies the HTTP method used in a span.
HTTPMethod = "http.method"
// HTTPCode sets the HTTP status code as a tag.
HTTPCode = "http.status_code"
// HTTPURL sets the HTTP URL for a span.
HTTPURL = "http.url"
// SpanType defines the Span type (web, db, cache).
SpanType = "span.type"
// ServiceName defines the Service name for this Span.
ServiceName = "service.name"
// ResourceName defines the Resource name for the Span.
ResourceName = "resource.name"
// Error specifies the error tag. It's value is usually of type "error".
Error = "error"
// ErrorMsg specifies the error message.
ErrorMsg = "error.msg"
// ErrorType specifies the error type.
ErrorType = "error.type"
// ErrorStack specifies the stack dump.
ErrorStack = "error.stack"
)

View file

@ -0,0 +1,94 @@
package internal // import "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal"
import (
"sync"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
)
var (
mu sync.RWMutex // guards globalTracer
globalTracer ddtrace.Tracer = &NoopTracer{}
)
// SetGlobalTracer sets the global tracer to t.
func SetGlobalTracer(t ddtrace.Tracer) {
mu.Lock()
defer mu.Unlock()
globalTracer = t
}
// GetGlobalTracer returns the currently active tracer.
func GetGlobalTracer() ddtrace.Tracer {
mu.RLock()
defer mu.RUnlock()
return globalTracer
}
// Testing is set to true when the mock tracer is active. It usually signifies that we are in a test
// environment. This value is used by tracer.Start to prevent overriding the GlobalTracer in tests.
var Testing = false
var _ ddtrace.Tracer = (*NoopTracer)(nil)
// NoopTracer is an implementation of ddtrace.Tracer that is a no-op.
type NoopTracer struct{}
// StartSpan implements ddtrace.Tracer.
func (NoopTracer) StartSpan(operationName string, opts ...ddtrace.StartSpanOption) ddtrace.Span {
return NoopSpan{}
}
// SetServiceInfo implements ddtrace.Tracer.
func (NoopTracer) SetServiceInfo(name, app, appType string) {}
// Extract implements ddtrace.Tracer.
func (NoopTracer) Extract(carrier interface{}) (ddtrace.SpanContext, error) {
return NoopSpanContext{}, nil
}
// Inject implements ddtrace.Tracer.
func (NoopTracer) Inject(context ddtrace.SpanContext, carrier interface{}) error { return nil }
// Stop implements ddtrace.Tracer.
func (NoopTracer) Stop() {}
var _ ddtrace.Span = (*NoopSpan)(nil)
// NoopSpan is an implementation of ddtrace.Span that is a no-op.
type NoopSpan struct{}
// SetTag implements ddtrace.Span.
func (NoopSpan) SetTag(key string, value interface{}) {}
// SetOperationName implements ddtrace.Span.
func (NoopSpan) SetOperationName(operationName string) {}
// BaggageItem implements ddtrace.Span.
func (NoopSpan) BaggageItem(key string) string { return "" }
// SetBaggageItem implements ddtrace.Span.
func (NoopSpan) SetBaggageItem(key, val string) {}
// Finish implements ddtrace.Span.
func (NoopSpan) Finish(opts ...ddtrace.FinishOption) {}
// Tracer implements ddtrace.Span.
func (NoopSpan) Tracer() ddtrace.Tracer { return NoopTracer{} }
// Context implements ddtrace.Span.
func (NoopSpan) Context() ddtrace.SpanContext { return NoopSpanContext{} }
var _ ddtrace.SpanContext = (*NoopSpanContext)(nil)
// NoopSpanContext is an implementation of ddtrace.SpanContext that is a no-op.
type NoopSpanContext struct{}
// SpanID implements ddtrace.SpanContext.
func (NoopSpanContext) SpanID() uint64 { return 0 }
// TraceID implements ddtrace.SpanContext.
func (NoopSpanContext) TraceID() uint64 { return 0 }
// ForeachBaggageItem implements ddtrace.SpanContext.
func (NoopSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {}

View file

@ -0,0 +1,24 @@
package opentracer // import "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer"
import (
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
opentracing "github.com/opentracing/opentracing-go"
)
// ServiceName can be used with opentracing.StartSpan to set the
// service name of a span.
func ServiceName(name string) opentracing.StartSpanOption {
return opentracing.Tag{Key: ext.ServiceName, Value: name}
}
// ResourceName can be used with opentracing.StartSpan to set the
// resource name of a span.
func ResourceName(name string) opentracing.StartSpanOption {
return opentracing.Tag{Key: ext.ResourceName, Value: name}
}
// SpanType can be used with opentracing.StartSpan to set the type of a span.
func SpanType(name string) opentracing.StartSpanOption {
return opentracing.Tag{Key: ext.SpanType, Value: name}
}

View file

@ -0,0 +1,83 @@
package opentracer
import (
"fmt"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
opentracing "github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/log"
)
var _ opentracing.Span = (*span)(nil)
// span implements opentracing.Span on top of ddtrace.Span.
type span struct {
ddtrace.Span
*opentracer
}
func (s *span) Context() opentracing.SpanContext { return s.Span.Context() }
func (s *span) Finish() { s.Span.Finish() }
func (s *span) Tracer() opentracing.Tracer { return s.opentracer }
func (s *span) LogEvent(event string) { /* deprecated */ }
func (s *span) LogEventWithPayload(event string, payload interface{}) { /* deprecated */ }
func (s *span) Log(data opentracing.LogData) { /* deprecated */ }
func (s *span) FinishWithOptions(opts opentracing.FinishOptions) {
for _, lr := range opts.LogRecords {
if len(lr.Fields) > 0 {
s.LogFields(lr.Fields...)
}
}
s.Span.Finish(tracer.FinishTime(opts.FinishTime))
}
func (s *span) LogFields(fields ...log.Field) {
// catch standard opentracing keys and adjust to internal ones as per spec:
// https://github.com/opentracing/specification/blob/master/semantic_conventions.md#log-fields-table
for _, f := range fields {
switch f.Key() {
case "event":
if v, ok := f.Value().(string); ok && v == "error" {
s.SetTag("error", true)
}
case "error", "error.object":
if err, ok := f.Value().(error); ok {
s.SetTag("error", err)
}
case "message":
s.SetTag(ext.ErrorMsg, fmt.Sprint(f.Value()))
case "stack":
s.SetTag(ext.ErrorStack, fmt.Sprint(f.Value()))
default:
// not implemented
}
}
}
func (s *span) LogKV(keyVals ...interface{}) {
fields, err := log.InterleavedKVToFields(keyVals...)
if err != nil {
// TODO(gbbr): create a log package
return
}
s.LogFields(fields...)
}
func (s *span) SetBaggageItem(key, val string) opentracing.Span {
s.Span.SetBaggageItem(key, val)
return s
}
func (s *span) SetOperationName(operationName string) opentracing.Span {
s.Span.SetOperationName(operationName)
return s
}
func (s *span) SetTag(key string, value interface{}) opentracing.Span {
s.Span.SetTag(key, value)
return s
}

View file

@ -0,0 +1,81 @@
// Package opentracer provides a wrapper on top of the Datadog tracer that can be used with Opentracing.
// It also provides a set of opentracing.StartSpanOption that are specific to Datadog's APM product.
// To use it, simply call "New".
//
// Note that there are currently some small incompatibilities between the Opentracing spec and the Datadog
// APM product, which we are in the process of addressing on the long term. When using Datadog, the
// Opentracing operation name is what is called resource in Datadog's terms and the Opentracing "component"
// tag is Datadog's operation name. Meaning that in order to define (in Opentracing terms) a span that
// has the operation name "/user/profile" and the component "http.request", one would do:
// opentracing.StartSpan("http.request", opentracer.ResourceName("/user/profile"))
//
// Some libraries and frameworks are supported out-of-the-box by using our integrations. You can see a list
// of supported integrations here: https://godoc.org/gopkg.in/DataDog/dd-trace-go.v1/contrib. They are fully
// compatible with the Opentracing implementation.
package opentracer
import (
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
opentracing "github.com/opentracing/opentracing-go"
)
// New creates, instantiates and returns an Opentracing compatible version of the
// Datadog tracer using the provided set of options.
func New(opts ...tracer.StartOption) opentracing.Tracer {
tracer.Start(opts...)
return &opentracer{internal.GetGlobalTracer()}
}
var _ opentracing.Tracer = (*opentracer)(nil)
// opentracer implements opentracing.Tracer on top of ddtrace.Tracer.
type opentracer struct{ ddtrace.Tracer }
// StartSpan implements opentracing.Tracer.
func (t *opentracer) StartSpan(operationName string, options ...opentracing.StartSpanOption) opentracing.Span {
var sso opentracing.StartSpanOptions
for _, o := range options {
o.Apply(&sso)
}
opts := []ddtrace.StartSpanOption{tracer.StartTime(sso.StartTime)}
for _, ref := range sso.References {
if v, ok := ref.ReferencedContext.(ddtrace.SpanContext); ok && ref.Type == opentracing.ChildOfRef {
opts = append(opts, tracer.ChildOf(v))
break // can only have one parent
}
}
for k, v := range sso.Tags {
opts = append(opts, tracer.Tag(k, v))
}
return &span{
Span: t.Tracer.StartSpan(operationName, opts...),
opentracer: t,
}
}
// Inject implements opentracing.Tracer.
func (t *opentracer) Inject(ctx opentracing.SpanContext, format interface{}, carrier interface{}) error {
sctx, ok := ctx.(ddtrace.SpanContext)
if !ok {
return opentracing.ErrUnsupportedFormat
}
switch format {
case opentracing.TextMap, opentracing.HTTPHeaders:
return t.Tracer.Inject(sctx, carrier)
default:
return opentracing.ErrUnsupportedFormat
}
}
// Extract implements opentracing.Tracer.
func (t *opentracer) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) {
switch format {
case opentracing.TextMap, opentracing.HTTPHeaders:
return t.Tracer.Extract(carrier)
default:
return nil, opentracing.ErrUnsupportedFormat
}
}

View file

@ -0,0 +1,42 @@
package tracer
import (
"context"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal"
)
type contextKey struct{}
var activeSpanKey = contextKey{}
// ContextWithSpan returns a copy of the given context which includes the span s.
func ContextWithSpan(ctx context.Context, s Span) context.Context {
return context.WithValue(ctx, activeSpanKey, s)
}
// SpanFromContext returns the span contained in the given context. A second return
// value indicates if a span was found in the context. If no span is found, a no-op
// span is returned.
func SpanFromContext(ctx context.Context) (Span, bool) {
if ctx == nil {
return &internal.NoopSpan{}, false
}
v := ctx.Value(activeSpanKey)
if s, ok := v.(ddtrace.Span); ok {
return s, true
}
return &internal.NoopSpan{}, false
}
// StartSpanFromContext returns a new span with the given operation name and options. If a span
// is found in the context, it will be used as the parent of the resulting span. If the ChildOf
// option is passed, the span from context will take precedence over it as the parent span.
func StartSpanFromContext(ctx context.Context, operationName string, opts ...StartSpanOption) (Span, context.Context) {
if s, ok := SpanFromContext(ctx); ok {
opts = append(opts, ChildOf(s.Context()))
}
s := StartSpan(operationName, opts...)
return s, ContextWithSpan(ctx, s)
}

View file

@ -0,0 +1,50 @@
// Package tracer contains Datadog's core tracing client. It is used to trace
// requests as they flow across web servers, databases and microservices, giving
// developers visibility into bottlenecks and troublesome requests. To start the
// tracer, simply call the start method along with an optional set of options.
// By default, the trace agent is considered to be found at "localhost:8126". In a
// setup where this would be different (let's say 127.0.0.1:1234), we could do:
// tracer.Start(tracer.WithAgentAddr("127.0.0.1:1234"))
// defer tracer.Stop()
//
// The tracing client can perform trace sampling. While the trace agent
// already samples traces to reduce bandwidth usage, client sampling reduces
// performance overhead. To make use of it, the package comes with a ready-to-use
// rate sampler that can be passed to the tracer. To use it and keep only 30% of the
// requests, one would do:
// s := tracer.NewRateSampler(0.3)
// tracer.Start(tracer.WithSampler(s))
//
// All spans created by the tracer contain a context hereby referred to as the span
// context. Note that this is different from Go's context. The span context is used
// to package essential information from a span, which is needed when creating child
// spans that inherit from it. Thus, a child span is created from a span's span context.
// The span context can originate from within the same process, but also a
// different process or even a different machine in the case of distributed tracing.
//
// To make use of distributed tracing, a span's context may be injected via a carrier
// into a transport (HTTP, RPC, etc.) to be extracted on the other end and used to
// create spans that are direct descendants of it. A couple of carrier interfaces
// which should cover most of the use-case scenarios are readily provided, such as
// HTTPCarrier and TextMapCarrier. Users are free to create their own, which will work
// with our propagation algorithm as long as they implement the TextMapReader and TextMapWriter
// interfaces. An example alternate implementation is the MDCarrier in our gRPC integration.
//
// As an example, injecting a span's context into an HTTP request would look like this:
// req, err := http.NewRequest("GET", "http://example.com", nil)
// // ...
// err := tracer.Inject(span.Context(), tracer.HTTPHeadersCarrier(req.Header))
// // ...
// http.DefaultClient.Do(req)
// Then, on the server side, to continue the trace one would do:
// sctx, err := tracer.Extract(tracer.HTTPHeadersCarrier(req.Header))
// // ...
// span := tracer.StartSpan("child.span", tracer.ChildOf(sctx))
// In the same manner, any means can be used as a carrier to inject a context into a transport. Go's
// context can also be used as a means to transport spans within the same process. The methods
// StartSpanFromContext, ContextWithSpan and SpanFromContext exist for this reason.
//
// Some libraries and frameworks are supported out-of-the-box by using one
// of our integrations. You can see a list of supported integrations here:
// https://godoc.org/gopkg.in/DataDog/dd-trace-go.v1/contrib
package tracer // import "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"

View file

@ -0,0 +1,69 @@
package tracer
import (
"fmt"
"log"
"strconv"
)
var errorPrefix = fmt.Sprintf("Datadog Tracer Error (%s): ", tracerVersion)
type traceEncodingError struct{ context error }
func (e *traceEncodingError) Error() string {
return fmt.Sprintf("error encoding trace: %s", e.context)
}
type spanBufferFullError struct{}
func (e *spanBufferFullError) Error() string {
return fmt.Sprintf("trace span cap (%d) reached, dropping trace", traceMaxSize)
}
type dataLossError struct {
count int // number of items lost
context error // any context error, if available
}
func (e *dataLossError) Error() string {
return fmt.Sprintf("lost traces (count: %d), error: %v", e.count, e.context)
}
type errorSummary struct {
Count int
Example string
}
func aggregateErrors(errChan <-chan error) map[string]errorSummary {
errs := make(map[string]errorSummary, len(errChan))
for {
select {
case err := <-errChan:
if err == nil {
break
}
key := fmt.Sprintf("%T", err)
summary := errs[key]
summary.Count++
summary.Example = err.Error()
errs[key] = summary
default: // stop when there's no more data
return errs
}
}
}
// logErrors logs the errors, preventing log file flooding, when there
// are many messages, it caps them and shows a quick summary.
// As of today it only logs using standard golang log package, but
// later we could send those stats to agent // TODO(ufoot).
func logErrors(errChan <-chan error) {
errs := aggregateErrors(errChan)
for _, v := range errs {
var repeat string
if v.Count > 1 {
repeat = " (repeated " + strconv.Itoa(v.Count) + " times)"
}
log.Println(errorPrefix + v.Example + repeat)
}
}

View file

@ -0,0 +1,163 @@
package tracer
import (
"os"
"path/filepath"
"time"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
)
// config holds the tracer configuration.
type config struct {
// debug, when true, writes details to logs.
debug bool
// serviceName specifies the name of this application.
serviceName string
// sampler specifies the sampler that will be used for sampling traces.
sampler Sampler
// agentAddr specifies the hostname and of the agent where the traces
// are sent to.
agentAddr string
// globalTags holds a set of tags that will be automatically applied to
// all spans.
globalTags map[string]interface{}
// transport specifies the Transport interface which will be used to send data to the agent.
transport transport
// propagator propagates span context cross-process
propagator Propagator
}
// StartOption represents a function that can be provided as a parameter to Start.
type StartOption func(*config)
// defaults sets the default values for a config.
func defaults(c *config) {
c.serviceName = filepath.Base(os.Args[0])
c.sampler = NewAllSampler()
c.agentAddr = defaultAddress
}
// WithDebugMode enables debug mode on the tracer, resulting in more verbose logging.
func WithDebugMode(enabled bool) StartOption {
return func(c *config) {
c.debug = enabled
}
}
// WithPropagator sets an alternative propagator to be used by the tracer.
func WithPropagator(p Propagator) StartOption {
return func(c *config) {
c.propagator = p
}
}
// WithServiceName sets the default service name to be used with the tracer.
func WithServiceName(name string) StartOption {
return func(c *config) {
c.serviceName = name
}
}
// WithAgentAddr sets the address where the agent is located. The default is
// localhost:8126. It should contain both host and port.
func WithAgentAddr(addr string) StartOption {
return func(c *config) {
c.agentAddr = addr
}
}
// WithGlobalTag sets a key/value pair which will be set as a tag on all spans
// created by tracer. This option may be used multiple times.
func WithGlobalTag(k string, v interface{}) StartOption {
return func(c *config) {
if c.globalTags == nil {
c.globalTags = make(map[string]interface{})
}
c.globalTags[k] = v
}
}
// WithSampler sets the given sampler to be used with the tracer. By default
// an all-permissive sampler is used.
func WithSampler(s Sampler) StartOption {
return func(c *config) {
c.sampler = s
}
}
// StartSpanOption is a configuration option for StartSpan. It is aliased in order
// to help godoc group all the functions returning it together. It is considered
// more correct to refer to it as the type as the origin, ddtrace.StartSpanOption.
type StartSpanOption = ddtrace.StartSpanOption
// Tag sets the given key/value pair as a tag on the started Span.
func Tag(k string, v interface{}) StartSpanOption {
return func(cfg *ddtrace.StartSpanConfig) {
if cfg.Tags == nil {
cfg.Tags = map[string]interface{}{}
}
cfg.Tags[k] = v
}
}
// ServiceName sets the given service name on the started span. For example "http.server".
func ServiceName(name string) StartSpanOption {
return Tag(ext.ServiceName, name)
}
// ResourceName sets the given resource name on the started span. A resource could
// be an SQL query, a URL, an RPC method or something else.
func ResourceName(name string) StartSpanOption {
return Tag(ext.ResourceName, name)
}
// SpanType sets the given span type on the started span. Some examples in the case of
// the Datadog APM product could be "web", "db" or "cache".
func SpanType(name string) StartSpanOption {
return Tag(ext.SpanType, name)
}
// ChildOf tells StartSpan to use the given span context as a parent for the
// created span.
func ChildOf(ctx ddtrace.SpanContext) StartSpanOption {
return func(cfg *ddtrace.StartSpanConfig) {
cfg.Parent = ctx
}
}
// StartTime sets a custom time as the start time for the created span. By
// default a span is started using the creation time.
func StartTime(t time.Time) StartSpanOption {
return func(cfg *ddtrace.StartSpanConfig) {
cfg.StartTime = t
}
}
// FinishOption is a configuration option for FinishSpan. It is aliased in order
// to help godoc group all the functions returning it together. It is considered
// more correct to refer to it as the type as the origin, ddtrace.FinishOption.
type FinishOption = ddtrace.FinishOption
// FinishTime sets the given time as the finishing time for the span. By default,
// the current time is used.
func FinishTime(t time.Time) FinishOption {
return func(cfg *ddtrace.FinishConfig) {
cfg.FinishTime = t
}
}
// WithError adds the given error to the span before marking it as finished. If it is
// nil it will be disregarded.
func WithError(err error) FinishOption {
return func(cfg *ddtrace.FinishConfig) {
cfg.Error = err
}
}

View file

@ -0,0 +1,116 @@
package tracer
import (
"bytes"
"encoding/binary"
"io"
"github.com/tinylib/msgp/msgp"
)
// payload is a wrapper on top of the msgpack encoder which allows constructing an
// encoded array by pushing its entries sequentially, one at a time. It basically
// allows us to encode as we would with a stream, except that the contents of the stream
// can be read as a slice by the msgpack decoder at any time. It follows the guidelines
// from the msgpack array spec:
// https://github.com/msgpack/msgpack/blob/master/spec.md#array-format-family
//
// payload implements io.Reader and can be used with the decoder directly. To create
// a new payload use the newPayload method.
//
// payload is not safe for concurrent use.
//
// This structure basically allows us to push traces into the payload one at a time
// in order to always have knowledge of the payload size, but also making it possible
// for the agent to decode it as an array.
type payload struct {
// header specifies the first few bytes in the msgpack stream
// indicating the type of array (fixarray, array16 or array32)
// and the number of items contained in the stream.
header []byte
// off specifies the current read position on the header.
off int
// count specifies the number of items in the stream.
count uint64
// buf holds the sequence of msgpack-encoded items.
buf bytes.Buffer
}
var _ io.Reader = (*payload)(nil)
// newPayload returns a ready to use payload.
func newPayload() *payload {
p := &payload{
header: make([]byte, 8),
off: 8,
}
return p
}
// push pushes a new item into the stream.
func (p *payload) push(t spanList) error {
if err := msgp.Encode(&p.buf, t); err != nil {
return err
}
p.count++
p.updateHeader()
return nil
}
// itemCount returns the number of items available in the srteam.
func (p *payload) itemCount() int {
return int(p.count)
}
// size returns the payload size in bytes. After the first read the value becomes
// inaccurate by up to 8 bytes.
func (p *payload) size() int {
return p.buf.Len() + len(p.header) - p.off
}
// reset resets the internal buffer, counter and read offset.
func (p *payload) reset() {
p.off = 8
p.count = 0
p.buf.Reset()
}
// https://github.com/msgpack/msgpack/blob/master/spec.md#array-format-family
const (
msgpackArrayFix byte = 144 // up to 15 items
msgpackArray16 = 0xdc // up to 2^16-1 items, followed by size in 2 bytes
msgpackArray32 = 0xdd // up to 2^32-1 items, followed by size in 4 bytes
)
// updateHeader updates the payload header based on the number of items currently
// present in the stream.
func (p *payload) updateHeader() {
n := p.count
switch {
case n <= 15:
p.header[7] = msgpackArrayFix + byte(n)
p.off = 7
case n <= 1<<16-1:
binary.BigEndian.PutUint64(p.header, n) // writes 2 bytes
p.header[5] = msgpackArray16
p.off = 5
default: // n <= 1<<32-1
binary.BigEndian.PutUint64(p.header, n) // writes 4 bytes
p.header[3] = msgpackArray32
p.off = 3
}
}
// Read implements io.Reader. It reads from the msgpack-encoded stream.
func (p *payload) Read(b []byte) (n int, err error) {
if p.off < len(p.header) {
// reading header
n = copy(b, p.header[p.off:])
p.off += n
return n, nil
}
return p.buf.Read(b)
}

View file

@ -0,0 +1,52 @@
package tracer
import (
"errors"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
)
// Propagator implementations should be able to inject and extract
// SpanContexts into an implementation specific carrier.
type Propagator interface {
// Inject takes the SpanContext and injects it into the carrier.
Inject(context ddtrace.SpanContext, carrier interface{}) error
// Extract returns the SpanContext from the given carrier.
Extract(carrier interface{}) (ddtrace.SpanContext, error)
}
// TextMapWriter allows setting key/value pairs of strings on the underlying
// data structure. Carriers implementing TextMapWriter are compatible to be
// used with Datadog's TextMapPropagator.
type TextMapWriter interface {
// Set sets the given key/value pair.
Set(key, val string)
}
// TextMapReader allows iterating over sets of key/value pairs. Carriers implementing
// TextMapReader are compatible to be used with Datadog's TextMapPropagator.
type TextMapReader interface {
// ForeachKey iterates over all keys that exist in the underlying
// carrier. It takes a callback function which will be called
// using all key/value pairs as arguments. ForeachKey will return
// the first error returned by the handler.
ForeachKey(handler func(key, val string) error) error
}
var (
// ErrInvalidCarrier is returned when the carrier provided to the propagator
// does not implemented the correct interfaces.
ErrInvalidCarrier = errors.New("invalid carrier")
// ErrInvalidSpanContext is returned when the span context found in the
// carrier is not of the expected type.
ErrInvalidSpanContext = errors.New("invalid span context")
// ErrSpanContextCorrupted is returned when there was a problem parsing
// the information found in the carrier.
ErrSpanContextCorrupted = errors.New("span context corrupted")
// ErrSpanContextNotFound represents missing information in the given carrier.
ErrSpanContextNotFound = errors.New("span context not found")
)

View file

@ -0,0 +1,50 @@
package tracer
import (
cryptorand "crypto/rand"
"log"
"math"
"math/big"
"math/rand"
"sync"
"time"
)
// random holds a thread-safe source of random numbers.
var random *rand.Rand
func init() {
var seed int64
n, err := cryptorand.Int(cryptorand.Reader, big.NewInt(math.MaxInt64))
if err == nil {
seed = n.Int64()
} else {
log.Printf("%scannot generate random seed: %v; using current time\n", errorPrefix, err)
seed = time.Now().UnixNano()
}
random = rand.New(&safeSource{
source: rand.NewSource(seed),
})
}
// safeSource holds a thread-safe implementation of rand.Source64.
type safeSource struct {
source rand.Source
sync.Mutex
}
func (rs *safeSource) Int63() int64 {
rs.Lock()
n := rs.source.Int63()
rs.Unlock()
return n
}
func (rs *safeSource) Uint64() uint64 { return uint64(rs.Int63()) }
func (rs *safeSource) Seed(seed int64) {
rs.Lock()
rs.Seed(seed)
rs.Unlock()
}

View file

@ -0,0 +1,72 @@
package tracer
import (
"math"
"sync"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
)
// Sampler is the generic interface of any sampler. It must be safe for concurrent use.
type Sampler interface {
// Sample returns true if the given span should be sampled.
Sample(span Span) bool
}
// RateSampler is a sampler implementation which randomly selects spans using a
// provided rate. For example, a rate of 0.75 will permit 75% of the spans.
// RateSampler implementations should be safe for concurrent use.
type RateSampler interface {
Sampler
// Rate returns the current sample rate.
Rate() float64
// SetRate sets a new sample rate.
SetRate(rate float64)
}
// rateSampler samples from a sample rate.
type rateSampler struct {
sync.RWMutex
rate float64
}
// NewAllSampler is a short-hand for NewRateSampler(1). It is all-permissive.
func NewAllSampler() RateSampler { return NewRateSampler(1) }
// NewRateSampler returns an initialized RateSampler with a given sample rate.
func NewRateSampler(rate float64) RateSampler {
return &rateSampler{rate: rate}
}
// Rate returns the current rate of the sampler.
func (r *rateSampler) Rate() float64 {
r.RLock()
defer r.RUnlock()
return r.rate
}
// SetRate sets a new sampling rate.
func (r *rateSampler) SetRate(rate float64) {
r.Lock()
r.rate = rate
r.Unlock()
}
// constants used for the Knuth hashing, same as agent.
const knuthFactor = uint64(1111111111111111111)
// Sample returns true if the given span should be sampled.
func (r *rateSampler) Sample(spn ddtrace.Span) bool {
s, ok := spn.(*span)
if !ok {
return false
}
r.RLock()
defer r.RUnlock()
if r.rate < 1 {
return s.TraceID*knuthFactor < uint64(r.rate*math.MaxUint64)
}
return true
}

View file

@ -0,0 +1,231 @@
//go:generate msgp -unexported -marshal=false -o=span_msgp.go -tests=false
package tracer
import (
"fmt"
"reflect"
"runtime/debug"
"strings"
"sync"
"time"
"github.com/tinylib/msgp/msgp"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
)
type (
// spanList implements msgp.Encodable on top of a slice of spans.
spanList []*span
// spanLists implements msgp.Decodable on top of a slice of spanList.
// This type is only used in tests.
spanLists []spanList
)
var (
_ ddtrace.Span = (*span)(nil)
_ msgp.Encodable = (*spanList)(nil)
_ msgp.Decodable = (*spanLists)(nil)
)
// span represents a computation. Callers must call Finish when a span is
// complete to ensure it's submitted.
type span struct {
sync.RWMutex `msg:"-"`
Name string `msg:"name"` // operation name
Service string `msg:"service"` // service name (i.e. "grpc.server", "http.request")
Resource string `msg:"resource"` // resource name (i.e. "/user?id=123", "SELECT * FROM users")
Type string `msg:"type"` // protocol associated with the span (i.e. "web", "db", "cache")
Start int64 `msg:"start"` // span start time expressed in nanoseconds since epoch
Duration int64 `msg:"duration"` // duration of the span expressed in nanoseconds
Meta map[string]string `msg:"meta,omitempty"` // arbitrary map of metadata
Metrics map[string]float64 `msg:"metrics,omitempty"` // arbitrary map of numeric metrics
SpanID uint64 `msg:"span_id"` // identifier of this span
TraceID uint64 `msg:"trace_id"` // identifier of the root span
ParentID uint64 `msg:"parent_id"` // identifier of the span's direct parent
Error int32 `msg:"error"` // error status of the span; 0 means no errors
finished bool `msg:"-"` // true if the span has been submitted to a tracer.
context *spanContext `msg:"-"` // span propagation context
}
// Context yields the SpanContext for this Span. Note that the return
// value of Context() is still valid after a call to Finish(). This is
// called the span context and it is different from Go's context.
func (s *span) Context() ddtrace.SpanContext { return s.context }
// SetBaggageItem sets a key/value pair as baggage on the span. Baggage items
// are propagated down to descendant spans and injected cross-process. Use with
// care as it adds extra load onto your tracing layer.
func (s *span) SetBaggageItem(key, val string) {
s.context.setBaggageItem(key, val)
}
// BaggageItem gets the value for a baggage item given its key. Returns the
// empty string if the value isn't found in this Span.
func (s *span) BaggageItem(key string) string {
return s.context.baggageItem(key)
}
// SetTag adds a set of key/value metadata to the span.
func (s *span) SetTag(key string, value interface{}) {
s.Lock()
defer s.Unlock()
// We don't lock spans when flushing, so we could have a data race when
// modifying a span as it's being flushed. This protects us against that
// race, since spans are marked `finished` before we flush them.
if s.finished {
return
}
if key == ext.Error {
s.setTagError(value)
return
}
if v, ok := value.(string); ok {
s.setTagString(key, v)
return
}
if v, ok := toFloat64(value); ok {
s.setTagNumeric(key, v)
return
}
// not numeric, not a string and not an error, the likelihood of this
// happening is close to zero, but we should nevertheless account for it.
s.Meta[key] = fmt.Sprint(value)
}
// setTagError sets the error tag. It accounts for various valid scenarios.
// This method is not safe for concurrent use.
func (s *span) setTagError(value interface{}) {
switch v := value.(type) {
case bool:
// bool value as per Opentracing spec.
if !v {
s.Error = 0
} else {
s.Error = 1
}
case error:
// if anyone sets an error value as the tag, be nice here
// and provide all the benefits.
s.Error = 1
s.Meta[ext.ErrorMsg] = v.Error()
s.Meta[ext.ErrorType] = reflect.TypeOf(v).String()
s.Meta[ext.ErrorStack] = string(debug.Stack())
case nil:
// no error
s.Error = 0
default:
// in all other cases, let's assume that setting this tag
// is the result of an error.
s.Error = 1
}
}
// setTagString sets a string tag. This method is not safe for concurrent use.
func (s *span) setTagString(key, v string) {
switch key {
case ext.ServiceName:
s.Service = v
case ext.ResourceName:
s.Resource = v
case ext.SpanType:
s.Type = v
default:
s.Meta[key] = v
}
}
// setTagNumeric sets a numeric tag, in our case called a metric. This method
// is not safe for concurrent use.
func (s *span) setTagNumeric(key string, v float64) {
switch key {
case ext.SamplingPriority:
// setting sampling priority per spec
s.Metrics[samplingPriorityKey] = v
s.context.setSamplingPriority(int(v))
default:
s.Metrics[key] = v
}
}
// Finish closes this Span (but not its children) providing the duration
// of its part of the tracing session.
func (s *span) Finish(opts ...ddtrace.FinishOption) {
var cfg ddtrace.FinishConfig
for _, fn := range opts {
fn(&cfg)
}
var t int64
if cfg.FinishTime.IsZero() {
t = now()
} else {
t = cfg.FinishTime.UnixNano()
}
if cfg.Error != nil {
s.SetTag(ext.Error, cfg.Error)
}
s.finish(t)
}
// SetOperationName sets or changes the operation name.
func (s *span) SetOperationName(operationName string) {
s.Lock()
defer s.Unlock()
s.Name = operationName
}
func (s *span) finish(finishTime int64) {
s.Lock()
defer s.Unlock()
// We don't lock spans when flushing, so we could have a data race when
// modifying a span as it's being flushed. This protects us against that
// race, since spans are marked `finished` before we flush them.
if s.finished {
// already finished
return
}
if s.Duration == 0 {
s.Duration = finishTime - s.Start
}
s.finished = true
if !s.context.sampled {
// not sampled
return
}
s.context.finish()
}
// String returns a human readable representation of the span. Not for
// production, just debugging.
func (s *span) String() string {
lines := []string{
fmt.Sprintf("Name: %s", s.Name),
fmt.Sprintf("Service: %s", s.Service),
fmt.Sprintf("Resource: %s", s.Resource),
fmt.Sprintf("TraceID: %d", s.TraceID),
fmt.Sprintf("SpanID: %d", s.SpanID),
fmt.Sprintf("ParentID: %d", s.ParentID),
fmt.Sprintf("Start: %s", time.Unix(0, s.Start)),
fmt.Sprintf("Duration: %s", time.Duration(s.Duration)),
fmt.Sprintf("Error: %d", s.Error),
fmt.Sprintf("Type: %s", s.Type),
"Tags:",
}
s.RLock()
for key, val := range s.Meta {
lines = append(lines, fmt.Sprintf("\t%s:%s", key, val))
}
for key, val := range s.Metrics {
lines = append(lines, fmt.Sprintf("\t%s:%f", key, val))
}
s.RUnlock()
return strings.Join(lines, "\n")
}
const samplingPriorityKey = "_sampling_priority_v1"

View file

@ -0,0 +1,448 @@
package tracer
// NOTE: THIS FILE WAS PRODUCED BY THE
// MSGP CODE GENERATION TOOL (github.com/tinylib/msgp)
// DO NOT EDIT
import (
"github.com/tinylib/msgp/msgp"
)
// DecodeMsg implements msgp.Decodable
func (z *span) DecodeMsg(dc *msgp.Reader) (err error) {
var field []byte
_ = field
var zb0001 uint32
zb0001, err = dc.ReadMapHeader()
if err != nil {
return
}
for zb0001 > 0 {
zb0001--
field, err = dc.ReadMapKeyPtr()
if err != nil {
return
}
switch msgp.UnsafeString(field) {
case "name":
z.Name, err = dc.ReadString()
if err != nil {
return
}
case "service":
z.Service, err = dc.ReadString()
if err != nil {
return
}
case "resource":
z.Resource, err = dc.ReadString()
if err != nil {
return
}
case "type":
z.Type, err = dc.ReadString()
if err != nil {
return
}
case "start":
z.Start, err = dc.ReadInt64()
if err != nil {
return
}
case "duration":
z.Duration, err = dc.ReadInt64()
if err != nil {
return
}
case "meta":
var zb0002 uint32
zb0002, err = dc.ReadMapHeader()
if err != nil {
return
}
if z.Meta == nil && zb0002 > 0 {
z.Meta = make(map[string]string, zb0002)
} else if len(z.Meta) > 0 {
for key, _ := range z.Meta {
delete(z.Meta, key)
}
}
for zb0002 > 0 {
zb0002--
var za0001 string
var za0002 string
za0001, err = dc.ReadString()
if err != nil {
return
}
za0002, err = dc.ReadString()
if err != nil {
return
}
z.Meta[za0001] = za0002
}
case "metrics":
var zb0003 uint32
zb0003, err = dc.ReadMapHeader()
if err != nil {
return
}
if z.Metrics == nil && zb0003 > 0 {
z.Metrics = make(map[string]float64, zb0003)
} else if len(z.Metrics) > 0 {
for key, _ := range z.Metrics {
delete(z.Metrics, key)
}
}
for zb0003 > 0 {
zb0003--
var za0003 string
var za0004 float64
za0003, err = dc.ReadString()
if err != nil {
return
}
za0004, err = dc.ReadFloat64()
if err != nil {
return
}
z.Metrics[za0003] = za0004
}
case "span_id":
z.SpanID, err = dc.ReadUint64()
if err != nil {
return
}
case "trace_id":
z.TraceID, err = dc.ReadUint64()
if err != nil {
return
}
case "parent_id":
z.ParentID, err = dc.ReadUint64()
if err != nil {
return
}
case "error":
z.Error, err = dc.ReadInt32()
if err != nil {
return
}
default:
err = dc.Skip()
if err != nil {
return
}
}
}
return
}
// EncodeMsg implements msgp.Encodable
func (z *span) EncodeMsg(en *msgp.Writer) (err error) {
// map header, size 12
// write "name"
err = en.Append(0x8c, 0xa4, 0x6e, 0x61, 0x6d, 0x65)
if err != nil {
return
}
err = en.WriteString(z.Name)
if err != nil {
return
}
// write "service"
err = en.Append(0xa7, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65)
if err != nil {
return
}
err = en.WriteString(z.Service)
if err != nil {
return
}
// write "resource"
err = en.Append(0xa8, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65)
if err != nil {
return
}
err = en.WriteString(z.Resource)
if err != nil {
return
}
// write "type"
err = en.Append(0xa4, 0x74, 0x79, 0x70, 0x65)
if err != nil {
return
}
err = en.WriteString(z.Type)
if err != nil {
return
}
// write "start"
err = en.Append(0xa5, 0x73, 0x74, 0x61, 0x72, 0x74)
if err != nil {
return
}
err = en.WriteInt64(z.Start)
if err != nil {
return
}
// write "duration"
err = en.Append(0xa8, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e)
if err != nil {
return
}
err = en.WriteInt64(z.Duration)
if err != nil {
return
}
// write "meta"
err = en.Append(0xa4, 0x6d, 0x65, 0x74, 0x61)
if err != nil {
return
}
err = en.WriteMapHeader(uint32(len(z.Meta)))
if err != nil {
return
}
for za0001, za0002 := range z.Meta {
err = en.WriteString(za0001)
if err != nil {
return
}
err = en.WriteString(za0002)
if err != nil {
return
}
}
// write "metrics"
err = en.Append(0xa7, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73)
if err != nil {
return
}
err = en.WriteMapHeader(uint32(len(z.Metrics)))
if err != nil {
return
}
for za0003, za0004 := range z.Metrics {
err = en.WriteString(za0003)
if err != nil {
return
}
err = en.WriteFloat64(za0004)
if err != nil {
return
}
}
// write "span_id"
err = en.Append(0xa7, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x69, 0x64)
if err != nil {
return
}
err = en.WriteUint64(z.SpanID)
if err != nil {
return
}
// write "trace_id"
err = en.Append(0xa8, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64)
if err != nil {
return
}
err = en.WriteUint64(z.TraceID)
if err != nil {
return
}
// write "parent_id"
err = en.Append(0xa9, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64)
if err != nil {
return
}
err = en.WriteUint64(z.ParentID)
if err != nil {
return
}
// write "error"
err = en.Append(0xa5, 0x65, 0x72, 0x72, 0x6f, 0x72)
if err != nil {
return
}
err = en.WriteInt32(z.Error)
if err != nil {
return
}
return
}
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
func (z *span) Msgsize() (s int) {
s = 1 + 5 + msgp.StringPrefixSize + len(z.Name) + 8 + msgp.StringPrefixSize + len(z.Service) + 9 + msgp.StringPrefixSize + len(z.Resource) + 5 + msgp.StringPrefixSize + len(z.Type) + 6 + msgp.Int64Size + 9 + msgp.Int64Size + 5 + msgp.MapHeaderSize
if z.Meta != nil {
for za0001, za0002 := range z.Meta {
_ = za0002
s += msgp.StringPrefixSize + len(za0001) + msgp.StringPrefixSize + len(za0002)
}
}
s += 8 + msgp.MapHeaderSize
if z.Metrics != nil {
for za0003, za0004 := range z.Metrics {
_ = za0004
s += msgp.StringPrefixSize + len(za0003) + msgp.Float64Size
}
}
s += 8 + msgp.Uint64Size + 9 + msgp.Uint64Size + 10 + msgp.Uint64Size + 6 + msgp.Int32Size
return
}
// DecodeMsg implements msgp.Decodable
func (z *spanList) DecodeMsg(dc *msgp.Reader) (err error) {
var zb0002 uint32
zb0002, err = dc.ReadArrayHeader()
if err != nil {
return
}
if cap((*z)) >= int(zb0002) {
(*z) = (*z)[:zb0002]
} else {
(*z) = make(spanList, zb0002)
}
for zb0001 := range *z {
if dc.IsNil() {
err = dc.ReadNil()
if err != nil {
return
}
(*z)[zb0001] = nil
} else {
if (*z)[zb0001] == nil {
(*z)[zb0001] = new(span)
}
err = (*z)[zb0001].DecodeMsg(dc)
if err != nil {
return
}
}
}
return
}
// EncodeMsg implements msgp.Encodable
func (z spanList) EncodeMsg(en *msgp.Writer) (err error) {
err = en.WriteArrayHeader(uint32(len(z)))
if err != nil {
return
}
for zb0003 := range z {
if z[zb0003] == nil {
err = en.WriteNil()
if err != nil {
return
}
} else {
err = z[zb0003].EncodeMsg(en)
if err != nil {
return
}
}
}
return
}
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
func (z spanList) Msgsize() (s int) {
s = msgp.ArrayHeaderSize
for zb0003 := range z {
if z[zb0003] == nil {
s += msgp.NilSize
} else {
s += z[zb0003].Msgsize()
}
}
return
}
// DecodeMsg implements msgp.Decodable
func (z *spanLists) DecodeMsg(dc *msgp.Reader) (err error) {
var zb0003 uint32
zb0003, err = dc.ReadArrayHeader()
if err != nil {
return
}
if cap((*z)) >= int(zb0003) {
(*z) = (*z)[:zb0003]
} else {
(*z) = make(spanLists, zb0003)
}
for zb0001 := range *z {
var zb0004 uint32
zb0004, err = dc.ReadArrayHeader()
if err != nil {
return
}
if cap((*z)[zb0001]) >= int(zb0004) {
(*z)[zb0001] = ((*z)[zb0001])[:zb0004]
} else {
(*z)[zb0001] = make(spanList, zb0004)
}
for zb0002 := range (*z)[zb0001] {
if dc.IsNil() {
err = dc.ReadNil()
if err != nil {
return
}
(*z)[zb0001][zb0002] = nil
} else {
if (*z)[zb0001][zb0002] == nil {
(*z)[zb0001][zb0002] = new(span)
}
err = (*z)[zb0001][zb0002].DecodeMsg(dc)
if err != nil {
return
}
}
}
}
return
}
// EncodeMsg implements msgp.Encodable
func (z spanLists) EncodeMsg(en *msgp.Writer) (err error) {
err = en.WriteArrayHeader(uint32(len(z)))
if err != nil {
return
}
for zb0005 := range z {
err = en.WriteArrayHeader(uint32(len(z[zb0005])))
if err != nil {
return
}
for zb0006 := range z[zb0005] {
if z[zb0005][zb0006] == nil {
err = en.WriteNil()
if err != nil {
return
}
} else {
err = z[zb0005][zb0006].EncodeMsg(en)
if err != nil {
return
}
}
}
}
return
}
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
func (z spanLists) Msgsize() (s int) {
s = msgp.ArrayHeaderSize
for zb0005 := range z {
s += msgp.ArrayHeaderSize
for zb0006 := range z[zb0005] {
if z[zb0005][zb0006] == nil {
s += msgp.NilSize
} else {
s += z[zb0005][zb0006].Msgsize()
}
}
}
return
}

View file

@ -0,0 +1,193 @@
package tracer
import (
"sync"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal"
)
var _ ddtrace.SpanContext = (*spanContext)(nil)
// SpanContext represents a span state that can propagate to descendant spans
// and across process boundaries. It contains all the information needed to
// spawn a direct descendant of the span that it belongs to. It can be used
// to create distributed tracing by propagating it using the provided interfaces.
type spanContext struct {
// the below group should propagate only locally
trace *trace // reference to the trace that this span belongs too
span *span // reference to the span that hosts this context
sampled bool // whether this span will be sampled or not
// the below group should propagate cross-process
traceID uint64
spanID uint64
mu sync.RWMutex // guards below fields
baggage map[string]string
priority int
hasPriority bool
}
// newSpanContext creates a new SpanContext to serve as context for the given
// span. If the provided parent is not nil, the context will inherit the trace,
// baggage and other values from it. This method also pushes the span into the
// new context's trace and as a result, it should not be called multiple times
// for the same span.
func newSpanContext(span *span, parent *spanContext) *spanContext {
context := &spanContext{
traceID: span.TraceID,
spanID: span.SpanID,
sampled: true,
span: span,
}
if v, ok := span.Metrics[samplingPriorityKey]; ok {
context.hasPriority = true
context.priority = int(v)
}
if parent != nil {
context.trace = parent.trace
context.sampled = parent.sampled
context.hasPriority = parent.hasSamplingPriority()
context.priority = parent.samplingPriority()
parent.ForeachBaggageItem(func(k, v string) bool {
context.setBaggageItem(k, v)
return true
})
}
if context.trace == nil {
context.trace = newTrace()
}
// put span in context's trace
context.trace.push(span)
return context
}
// SpanID implements ddtrace.SpanContext.
func (c *spanContext) SpanID() uint64 { return c.spanID }
// TraceID implements ddtrace.SpanContext.
func (c *spanContext) TraceID() uint64 { return c.traceID }
// ForeachBaggageItem implements ddtrace.SpanContext.
func (c *spanContext) ForeachBaggageItem(handler func(k, v string) bool) {
c.mu.RLock()
defer c.mu.RUnlock()
for k, v := range c.baggage {
if !handler(k, v) {
break
}
}
}
func (c *spanContext) setSamplingPriority(p int) {
c.mu.Lock()
defer c.mu.Unlock()
c.priority = p
c.hasPriority = true
}
func (c *spanContext) samplingPriority() int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.priority
}
func (c *spanContext) hasSamplingPriority() bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.hasPriority
}
func (c *spanContext) setBaggageItem(key, val string) {
c.mu.Lock()
defer c.mu.Unlock()
if c.baggage == nil {
c.baggage = make(map[string]string, 1)
}
c.baggage[key] = val
}
func (c *spanContext) baggageItem(key string) string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.baggage[key]
}
// finish marks this span as finished in the trace.
func (c *spanContext) finish() { c.trace.ackFinish() }
// trace holds information about a specific trace. This structure is shared
// between all spans in a trace.
type trace struct {
mu sync.RWMutex // guards below fields
spans []*span // all the spans that are part of this trace
finished int // the number of finished spans
full bool // signifies that the span buffer is full
}
var (
// traceStartSize is the initial size of our trace buffer,
// by default we allocate for a handful of spans within the trace,
// reasonable as span is actually way bigger, and avoids re-allocating
// over and over. Could be fine-tuned at runtime.
traceStartSize = 10
// traceMaxSize is the maximum number of spans we keep in memory.
// This is to avoid memory leaks, if above that value, spans are randomly
// dropped and ignore, resulting in corrupted tracing data, but ensuring
// original program continues to work as expected.
traceMaxSize = int(1e5)
)
// newTrace creates a new trace using the given callback which will be called
// upon completion of the trace.
func newTrace() *trace {
return &trace{spans: make([]*span, 0, traceStartSize)}
}
// push pushes a new span into the trace. If the buffer is full, it returns
// a errBufferFull error.
func (t *trace) push(sp *span) {
t.mu.Lock()
defer t.mu.Unlock()
if t.full {
return
}
if len(t.spans) >= traceMaxSize {
// capacity is reached, we will not be able to complete this trace.
t.full = true
t.spans = nil // GC
if tr, ok := internal.GetGlobalTracer().(*tracer); ok {
// we have a tracer we can submit errors too.
tr.pushError(&spanBufferFullError{})
}
return
}
t.spans = append(t.spans, sp)
}
// ackFinish aknowledges that another span in the trace has finished, and checks
// if the trace is complete, in which case it calls the onFinish function.
func (t *trace) ackFinish() {
t.mu.Lock()
defer t.mu.Unlock()
if t.full {
// capacity has been reached, the buffer is no longer tracking
// all the spans in the trace, so the below conditions will not
// be accurate and would trigger a pre-mature flush, exposing us
// to a race condition where spans can be modified while flushing.
return
}
t.finished++
if len(t.spans) != t.finished {
return
}
if tr, ok := internal.GetGlobalTracer().(*tracer); ok {
// we have a tracer that can receive completed traces.
tr.pushTrace(t.spans)
}
t.spans = nil
t.finished = 0 // important, because a buffer can be used for several flushes
}

View file

@ -0,0 +1,198 @@
package tracer
import (
"net/http"
"strconv"
"strings"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
)
// HTTPHeadersCarrier wraps an http.Header as a TextMapWriter and TextMapReader, allowing
// it to be used using the provided Propagator implementation.
type HTTPHeadersCarrier http.Header
var _ TextMapWriter = (*HTTPHeadersCarrier)(nil)
var _ TextMapReader = (*HTTPHeadersCarrier)(nil)
// Set implements TextMapWriter.
func (c HTTPHeadersCarrier) Set(key, val string) {
h := http.Header(c)
h.Add(key, val)
}
// ForeachKey implements TextMapReader.
func (c HTTPHeadersCarrier) ForeachKey(handler func(key, val string) error) error {
for k, vals := range c {
for _, v := range vals {
if err := handler(k, v); err != nil {
return err
}
}
}
return nil
}
// TextMapCarrier allows the use of a regular map[string]string as both TextMapWriter
// and TextMapReader, making it compatible with the provided Propagator.
type TextMapCarrier map[string]string
var _ TextMapWriter = (*TextMapCarrier)(nil)
var _ TextMapReader = (*TextMapCarrier)(nil)
// Set implements TextMapWriter.
func (c TextMapCarrier) Set(key, val string) {
c[key] = val
}
// ForeachKey conforms to the TextMapReader interface.
func (c TextMapCarrier) ForeachKey(handler func(key, val string) error) error {
for k, v := range c {
if err := handler(k, v); err != nil {
return err
}
}
return nil
}
const (
// DefaultBaggageHeaderPrefix specifies the prefix that will be used in
// HTTP headers or text maps to prefix baggage keys.
DefaultBaggageHeaderPrefix = "ot-baggage-"
// DefaultTraceIDHeader specifies the key that will be used in HTTP headers
// or text maps to store the trace ID.
DefaultTraceIDHeader = "x-datadog-trace-id"
// DefaultParentIDHeader specifies the key that will be used in HTTP headers
// or text maps to store the parent ID.
DefaultParentIDHeader = "x-datadog-parent-id"
// DefaultPriorityHeader specifies the key that will be used in HTTP headers
// or text maps to store the sampling priority value.
DefaultPriorityHeader = "x-datadog-sampling-priority"
)
// PropagatorConfig defines the configuration for initializing a propagator.
type PropagatorConfig struct {
// BaggagePrefix specifies the prefix that will be used to store baggage
// items in a map. It defaults to DefaultBaggageHeaderPrefix.
BaggagePrefix string
// TraceHeader specifies the map key that will be used to store the trace ID.
// It defaults to DefaultTraceIDHeader.
TraceHeader string
// ParentHeader specifies the map key that will be used to store the parent ID.
// It defaults to DefaultParentIDHeader.
ParentHeader string
// PriorityHeader specifies the map key that will be used to store the sampling priority.
// It deafults to DefaultPriorityHeader.
PriorityHeader string
}
// NewPropagator returns a new propagator which uses TextMap to inject
// and extract values. It propagates trace and span IDs and baggage.
// To use the defaults, nil may be provided in place of the config.
func NewPropagator(cfg *PropagatorConfig) Propagator {
if cfg == nil {
cfg = new(PropagatorConfig)
}
if cfg.BaggagePrefix == "" {
cfg.BaggagePrefix = DefaultBaggageHeaderPrefix
}
if cfg.TraceHeader == "" {
cfg.TraceHeader = DefaultTraceIDHeader
}
if cfg.ParentHeader == "" {
cfg.ParentHeader = DefaultParentIDHeader
}
if cfg.PriorityHeader == "" {
cfg.PriorityHeader = DefaultPriorityHeader
}
return &propagator{cfg}
}
// propagator implements a propagator which uses TextMap internally.
// It propagates the trace and span IDs, as well as the baggage from the
// context.
type propagator struct{ cfg *PropagatorConfig }
// Inject defines the Propagator to propagate SpanContext data
// out of the current process. The implementation propagates the
// TraceID and the current active SpanID, as well as the Span baggage.
func (p *propagator) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) error {
switch v := carrier.(type) {
case TextMapWriter:
return p.injectTextMap(spanCtx, v)
default:
return ErrInvalidCarrier
}
}
func (p *propagator) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWriter) error {
ctx, ok := spanCtx.(*spanContext)
if !ok || ctx.traceID == 0 || ctx.spanID == 0 {
return ErrInvalidSpanContext
}
// propagate the TraceID and the current active SpanID
writer.Set(p.cfg.TraceHeader, strconv.FormatUint(ctx.traceID, 10))
writer.Set(p.cfg.ParentHeader, strconv.FormatUint(ctx.spanID, 10))
if ctx.hasSamplingPriority() {
writer.Set(p.cfg.PriorityHeader, strconv.Itoa(ctx.samplingPriority()))
}
// propagate OpenTracing baggage
for k, v := range ctx.baggage {
writer.Set(p.cfg.BaggagePrefix+k, v)
}
return nil
}
// Extract implements Propagator.
func (p *propagator) Extract(carrier interface{}) (ddtrace.SpanContext, error) {
switch v := carrier.(type) {
case TextMapReader:
return p.extractTextMap(v)
default:
return nil, ErrInvalidCarrier
}
}
func (p *propagator) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, error) {
var ctx spanContext
err := reader.ForeachKey(func(k, v string) error {
var err error
key := strings.ToLower(k)
switch key {
case p.cfg.TraceHeader:
ctx.traceID, err = strconv.ParseUint(v, 10, 64)
if err != nil {
return ErrSpanContextCorrupted
}
case p.cfg.ParentHeader:
ctx.spanID, err = strconv.ParseUint(v, 10, 64)
if err != nil {
return ErrSpanContextCorrupted
}
case p.cfg.PriorityHeader:
ctx.priority, err = strconv.Atoi(v)
if err != nil {
return ErrSpanContextCorrupted
}
ctx.hasPriority = true
default:
if strings.HasPrefix(key, p.cfg.BaggagePrefix) {
ctx.setBaggageItem(strings.TrimPrefix(key, p.cfg.BaggagePrefix), v)
}
}
return nil
})
if err != nil {
return nil, err
}
if ctx.traceID == 0 || ctx.spanID == 0 {
return nil, ErrSpanContextNotFound
}
return &ctx, nil
}

View file

@ -0,0 +1,10 @@
// +build !windows
package tracer
import "time"
// now returns current UTC time in nanos.
func now() int64 {
return time.Now().UTC().UnixNano()
}

View file

@ -0,0 +1,35 @@
package tracer
import (
"log"
"time"
"golang.org/x/sys/windows"
)
// This method is more precise than the go1.8 time.Now on Windows
// See https://msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v=vs.85).aspx
// It is however ~10x slower and requires Windows 8+.
func highPrecisionNow() int64 {
var ft windows.Filetime
windows.GetSystemTimePreciseAsFileTime(&ft)
return ft.Nanoseconds()
}
func lowPrecisionNow() int64 {
return time.Now().UTC().UnixNano()
}
var now func() int64
// If GetSystemTimePreciseAsFileTime is not available we default to the less
// precise implementation based on time.Now()
func init() {
if err := windows.LoadGetSystemTimePreciseAsFileTime(); err != nil {
log.Printf("Unable to load high precison timer, defaulting to time.Now()")
now = lowPrecisionNow
} else {
log.Printf("Using high precision timer")
now = highPrecisionNow
}
}

View file

@ -0,0 +1,378 @@
package tracer
import (
"errors"
"log"
"os"
"strconv"
"time"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal"
)
var _ ddtrace.Tracer = (*tracer)(nil)
// tracer creates, buffers and submits Spans which are used to time blocks of
// computation. They are accumulated and streamed into an internal payload,
// which is flushed to the agent whenever its size exceeds a specific threshold
// or when a certain interval of time has passed, whichever happens first.
//
// tracer operates based on a worker loop which responds to various request
// channels. It additionally holds two buffers which accumulates error and trace
// queues to be processed by the payload encoder.
type tracer struct {
*config
*payload
flushAllReq chan chan<- struct{}
flushTracesReq chan struct{}
flushErrorsReq chan struct{}
exitReq chan struct{}
payloadQueue chan []*span
errorBuffer chan error
// stopped is a channel that will be closed when the worker has exited.
stopped chan struct{}
// syncPush is used for testing. When non-nil, it causes pushTrace to become
// a synchronous (blocking) operation, meaning that it will only return after
// the trace has been fully processed and added onto the payload.
syncPush chan struct{}
}
const (
// flushInterval is the interval at which the payload contents will be flushed
// to the transport.
flushInterval = 2 * time.Second
// payloadMaxLimit is the maximum payload size allowed and should indicate the
// maximum size of the package that the agent can receive.
payloadMaxLimit = 9.5 * 1024 * 1024 // 9.5 MB
// payloadSizeLimit specifies the maximum allowed size of the payload before
// it will trigger a flush to the transport.
payloadSizeLimit = payloadMaxLimit / 2
)
// Start starts the tracer with the given set of options. It will stop and replace
// any running tracer, meaning that calling it several times will result in a restart
// of the tracer by replacing the current instance with a new one.
func Start(opts ...StartOption) {
if internal.Testing {
return // mock tracer active
}
t := internal.GetGlobalTracer()
internal.SetGlobalTracer(newTracer(opts...))
t.Stop()
}
// Stop stops the started tracer. Subsequent calls are valid but become no-op.
func Stop() {
internal.SetGlobalTracer(&internal.NoopTracer{})
}
// Span is an alias for ddtrace.Span. It is here to allow godoc to group methods returning
// ddtrace.Span. It is recommended and is considered more correct to refer to this type as
// ddtrace.Span instead.
type Span = ddtrace.Span
// StartSpan starts a new span with the given operation name and set of options.
// If the tracer is not started, calling this function is a no-op.
func StartSpan(operationName string, opts ...StartSpanOption) Span {
return internal.GetGlobalTracer().StartSpan(operationName, opts...)
}
// Extract extracts a SpanContext from the carrier. The carrier is expected
// to implement TextMapReader, otherwise an error is returned.
// If the tracer is not started, calling this function is a no-op.
func Extract(carrier interface{}) (ddtrace.SpanContext, error) {
return internal.GetGlobalTracer().Extract(carrier)
}
// Inject injects the given SpanContext into the carrier. The carrier is
// expected to implement TextMapWriter, otherwise an error is returned.
// If the tracer is not started, calling this function is a no-op.
func Inject(ctx ddtrace.SpanContext, carrier interface{}) error {
return internal.GetGlobalTracer().Inject(ctx, carrier)
}
const (
// payloadQueueSize is the buffer size of the trace channel.
payloadQueueSize = 1000
// errorBufferSize is the buffer size of the error channel.
errorBufferSize = 200
)
func newTracer(opts ...StartOption) *tracer {
c := new(config)
defaults(c)
for _, fn := range opts {
fn(c)
}
if c.transport == nil {
c.transport = newTransport(c.agentAddr)
}
if c.propagator == nil {
c.propagator = NewPropagator(nil)
}
t := &tracer{
config: c,
payload: newPayload(),
flushAllReq: make(chan chan<- struct{}),
flushTracesReq: make(chan struct{}, 1),
flushErrorsReq: make(chan struct{}, 1),
exitReq: make(chan struct{}),
payloadQueue: make(chan []*span, payloadQueueSize),
errorBuffer: make(chan error, errorBufferSize),
stopped: make(chan struct{}),
}
go t.worker()
return t
}
// worker receives finished traces to be added into the payload, as well
// as periodically flushes traces to the transport.
func (t *tracer) worker() {
defer close(t.stopped)
ticker := time.NewTicker(flushInterval)
defer ticker.Stop()
for {
select {
case trace := <-t.payloadQueue:
t.pushPayload(trace)
case <-ticker.C:
t.flush()
case done := <-t.flushAllReq:
t.flush()
done <- struct{}{}
case <-t.flushTracesReq:
t.flushTraces()
case <-t.flushErrorsReq:
t.flushErrors()
case <-t.exitReq:
t.flush()
return
}
}
}
func (t *tracer) pushTrace(trace []*span) {
select {
case <-t.stopped:
return
default:
}
select {
case t.payloadQueue <- trace:
default:
t.pushError(&dataLossError{
context: errors.New("payload queue full, dropping trace"),
count: len(trace),
})
}
if t.syncPush != nil {
// only in tests
<-t.syncPush
}
}
func (t *tracer) pushError(err error) {
select {
case <-t.stopped:
return
default:
}
if len(t.errorBuffer) >= cap(t.errorBuffer)/2 { // starts being full, anticipate, try and flush soon
select {
case t.flushErrorsReq <- struct{}{}:
default: // a flush was already requested, skip
}
}
select {
case t.errorBuffer <- err:
default:
// OK, if we get this, our error error buffer is full,
// we can assume it is filled with meaningful messages which
// are going to be logged and hopefully read, nothing better
// we can do, blocking would make things worse.
}
}
// StartSpan creates, starts, and returns a new Span with the given `operationName`.
func (t *tracer) StartSpan(operationName string, options ...ddtrace.StartSpanOption) ddtrace.Span {
var opts ddtrace.StartSpanConfig
for _, fn := range options {
fn(&opts)
}
var startTime int64
if opts.StartTime.IsZero() {
startTime = now()
} else {
startTime = opts.StartTime.UnixNano()
}
var context *spanContext
if opts.Parent != nil {
if ctx, ok := opts.Parent.(*spanContext); ok {
context = ctx
}
}
id := random.Uint64()
// span defaults
span := &span{
Name: operationName,
Service: t.config.serviceName,
Resource: operationName,
Meta: map[string]string{},
Metrics: map[string]float64{},
SpanID: id,
TraceID: id,
ParentID: 0,
Start: startTime,
}
if context != nil {
// this is a child span
span.TraceID = context.traceID
span.ParentID = context.spanID
if context.hasSamplingPriority() {
span.Metrics[samplingPriorityKey] = float64(context.samplingPriority())
}
if context.span != nil {
context.span.RLock()
span.Service = context.span.Service
context.span.RUnlock()
}
}
span.context = newSpanContext(span, context)
if context == nil || context.span == nil {
// this is either a global root span or a process-level root span
span.SetTag(ext.Pid, strconv.Itoa(os.Getpid()))
t.sample(span)
}
// add tags from options
for k, v := range opts.Tags {
span.SetTag(k, v)
}
// add global tags
for k, v := range t.config.globalTags {
span.SetTag(k, v)
}
return span
}
// Stop stops the tracer.
func (t *tracer) Stop() {
select {
case <-t.stopped:
return
default:
t.exitReq <- struct{}{}
<-t.stopped
}
}
// Inject uses the configured or default TextMap Propagator.
func (t *tracer) Inject(ctx ddtrace.SpanContext, carrier interface{}) error {
return t.config.propagator.Inject(ctx, carrier)
}
// Extract uses the configured or default TextMap Propagator.
func (t *tracer) Extract(carrier interface{}) (ddtrace.SpanContext, error) {
return t.config.propagator.Extract(carrier)
}
// flushTraces will push any currently buffered traces to the server.
func (t *tracer) flushTraces() {
if t.payload.itemCount() == 0 {
return
}
size, count := t.payload.size(), t.payload.itemCount()
if t.config.debug {
log.Printf("Sending payload: size: %d traces: %d\n", size, count)
}
err := t.config.transport.send(t.payload)
if err != nil && size > payloadMaxLimit {
// we couldn't send the payload and it is getting too big to be
// accepted by the agent, we have to drop it.
t.payload.reset()
t.pushError(&dataLossError{context: err, count: count})
}
if err == nil {
// send succeeded
t.payload.reset()
}
}
// flushErrors will process log messages that were queued
func (t *tracer) flushErrors() {
logErrors(t.errorBuffer)
}
func (t *tracer) flush() {
t.flushTraces()
t.flushErrors()
}
// forceFlush forces a flush of data (traces and services) to the agent.
// Flushes are done by a background task on a regular basis, so you never
// need to call this manually, mostly useful for testing and debugging.
func (t *tracer) forceFlush() {
done := make(chan struct{})
t.flushAllReq <- done
<-done
}
// pushPayload pushes the trace onto the payload. If the payload becomes
// larger than the threshold as a result, it sends a flush request.
func (t *tracer) pushPayload(trace []*span) {
if err := t.payload.push(trace); err != nil {
t.pushError(&traceEncodingError{context: err})
}
if t.payload.size() > payloadSizeLimit {
// getting large
select {
case t.flushTracesReq <- struct{}{}:
default:
// flush already queued
}
}
if t.syncPush != nil {
// only in tests
t.syncPush <- struct{}{}
}
}
// sampleRateMetricKey is the metric key holding the applied sample rate. Has to be the same as the Agent.
const sampleRateMetricKey = "_sample_rate"
// Sample samples a span with the internal sampler.
func (t *tracer) sample(span *span) {
sampler := t.config.sampler
sampled := sampler.Sample(span)
span.context.sampled = sampled
if !sampled {
return
}
if rs, ok := sampler.(RateSampler); ok && rs.Rate() < 1 {
// the span was sampled using a rate sampler which wasn't all permissive,
// so we make note of the sampling rate.
span.Lock()
defer span.Unlock()
if span.finished {
// we don't touch finished span as they might be flushing
return
}
span.Metrics[sampleRateMetricKey] = rs.Rate()
}
}

View file

@ -0,0 +1,120 @@
package tracer
import (
"fmt"
"net"
"net/http"
"runtime"
"strconv"
"strings"
"time"
)
var tracerVersion = "v1.0"
const (
defaultHostname = "localhost"
defaultPort = "8126"
defaultAddress = defaultHostname + ":" + defaultPort
defaultHTTPTimeout = time.Second // defines the current timeout before giving up with the send process
traceCountHeader = "X-Datadog-Trace-Count" // header containing the number of traces in the payload
)
// Transport is an interface for span submission to the agent.
type transport interface {
send(p *payload) error
}
// newTransport returns a new Transport implementation that sends traces to a
// trace agent running on the given hostname and port. If the zero values for
// hostname and port are provided, the default values will be used ("localhost"
// for hostname, and "8126" for port).
//
// In general, using this method is only necessary if you have a trace agent
// running on a non-default port or if it's located on another machine.
func newTransport(addr string) transport {
return newHTTPTransport(addr)
}
// newDefaultTransport return a default transport for this tracing client
func newDefaultTransport() transport {
return newHTTPTransport(defaultAddress)
}
type httpTransport struct {
traceURL string // the delivery URL for traces
client *http.Client // the HTTP client used in the POST
headers map[string]string // the Transport headers
}
// newHTTPTransport returns an httpTransport for the given endpoint
func newHTTPTransport(addr string) *httpTransport {
// initialize the default EncoderPool with Encoder headers
defaultHeaders := map[string]string{
"Datadog-Meta-Lang": "go",
"Datadog-Meta-Lang-Version": strings.TrimPrefix(runtime.Version(), "go"),
"Datadog-Meta-Lang-Interpreter": runtime.Compiler + "-" + runtime.GOARCH + "-" + runtime.GOOS,
"Datadog-Meta-Tracer-Version": tracerVersion,
"Content-Type": "application/msgpack",
}
host, port, _ := net.SplitHostPort(addr)
if host == "" {
host = defaultHostname
}
if port == "" {
port = defaultPort
}
addr = fmt.Sprintf("%s:%s", host, port)
return &httpTransport{
traceURL: fmt.Sprintf("http://%s/v0.3/traces", addr),
client: &http.Client{
// We copy the transport to avoid using the default one, as it might be
// augmented with tracing and we don't want these calls to be recorded.
// See https://golang.org/pkg/net/http/#DefaultTransport .
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
Timeout: defaultHTTPTimeout,
},
headers: defaultHeaders,
}
}
func (t *httpTransport) send(p *payload) error {
// prepare the client and send the payload
req, err := http.NewRequest("POST", t.traceURL, p)
if err != nil {
return fmt.Errorf("cannot create http request: %v", err)
}
for header, value := range t.headers {
req.Header.Set(header, value)
}
req.Header.Set(traceCountHeader, strconv.Itoa(p.itemCount()))
req.Header.Set("Content-Length", strconv.Itoa(p.size()))
response, err := t.client.Do(req)
if err != nil {
return err
}
defer response.Body.Close()
if code := response.StatusCode; code >= 400 {
// error, check the body for context information and
// return a nice error.
msg := make([]byte, 1000)
n, _ := response.Body.Read(msg)
txt := http.StatusText(code)
if n > 0 {
return fmt.Errorf("%s (Status: %s)", msg[:n], txt)
}
return fmt.Errorf("%s", txt)
}
return nil
}

View file

@ -0,0 +1,32 @@
package tracer
// toFloat64 attempts to convert value into a float64. If it succeeds it returns
// the value and true, otherwise 0 and false.
func toFloat64(value interface{}) (f float64, ok bool) {
switch i := value.(type) {
case byte:
return float64(i), true
case float32:
return float64(i), true
case float64:
return i, true
case int:
return float64(i), true
case int16:
return float64(i), true
case int32:
return float64(i), true
case int64:
return float64(i), true
case uint:
return float64(i), true
case uint16:
return float64(i), true
case uint32:
return float64(i), true
case uint64:
return float64(i), true
default:
return 0, false
}
}