+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "fmt"
+ "io"
+)
+
+// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the formatted string as a value that satisfies error. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Errorf(format string, a ...interface{}) (err error) {
+ return fmt.Errorf(format, convertArgs(a)...)
+}
+
+// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
+func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprint(w, convertArgs(a)...)
+}
+
+// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
+ return fmt.Fprintf(w, format, convertArgs(a)...)
+}
+
+// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
+// passed with a default Formatter interface returned by NewFormatter. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
+func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprintln(w, convertArgs(a)...)
+}
+
+// Print is a wrapper for fmt.Print that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
+func Print(a ...interface{}) (n int, err error) {
+ return fmt.Print(convertArgs(a)...)
+}
+
+// Printf is a wrapper for fmt.Printf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Printf(format string, a ...interface{}) (n int, err error) {
+ return fmt.Printf(format, convertArgs(a)...)
+}
+
+// Println is a wrapper for fmt.Println that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
+func Println(a ...interface{}) (n int, err error) {
+ return fmt.Println(convertArgs(a)...)
+}
+
+// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
+func Sprint(a ...interface{}) string {
+ return fmt.Sprint(convertArgs(a)...)
+}
+
+// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Sprintf(format string, a ...interface{}) string {
+ return fmt.Sprintf(format, convertArgs(a)...)
+}
+
+// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
+// were passed with a default Formatter interface returned by NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
+func Sprintln(a ...interface{}) string {
+ return fmt.Sprintln(convertArgs(a)...)
+}
+
+// convertArgs accepts a slice of arguments and returns a slice of the same
+// length with each argument converted to a default spew Formatter interface.
+func convertArgs(args []interface{}) (formatters []interface{}) {
+ formatters = make([]interface{}, len(args))
+ for index, arg := range args {
+ formatters[index] = NewFormatter(arg)
+ }
+ return formatters
+}
diff --git a/vendor/github.com/eapache/go-resiliency/LICENSE b/vendor/github.com/eapache/go-resiliency/LICENSE
new file mode 100644
index 00000000..698a3f51
--- /dev/null
+++ b/vendor/github.com/eapache/go-resiliency/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Evan Huus
+
+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.
+
diff --git a/vendor/github.com/eapache/go-resiliency/breaker/README.md b/vendor/github.com/eapache/go-resiliency/breaker/README.md
new file mode 100644
index 00000000..7262bfc2
--- /dev/null
+++ b/vendor/github.com/eapache/go-resiliency/breaker/README.md
@@ -0,0 +1,33 @@
+circuit-breaker
+===============
+
+[](https://travis-ci.org/eapache/go-resiliency)
+[](https://godoc.org/github.com/eapache/go-resiliency/breaker)
+
+The circuit-breaker resiliency pattern for golang.
+
+Creating a breaker takes three parameters:
+- error threshold (for opening the breaker)
+- success threshold (for closing the breaker)
+- timeout (how long to keep the breaker open)
+
+```go
+b := breaker.New(3, 1, 5*time.Second)
+
+for {
+ result := b.Run(func() error {
+ // communicate with some external service and
+ // return an error if the communication failed
+ return nil
+ })
+
+ switch result {
+ case nil:
+ // success!
+ case breaker.ErrBreakerOpen:
+ // our function wasn't run because the breaker was open
+ default:
+ // some other error
+ }
+}
+```
diff --git a/vendor/github.com/eapache/go-resiliency/breaker/breaker.go b/vendor/github.com/eapache/go-resiliency/breaker/breaker.go
new file mode 100644
index 00000000..f88ca724
--- /dev/null
+++ b/vendor/github.com/eapache/go-resiliency/breaker/breaker.go
@@ -0,0 +1,161 @@
+// Package breaker implements the circuit-breaker resiliency pattern for Go.
+package breaker
+
+import (
+ "errors"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+// ErrBreakerOpen is the error returned from Run() when the function is not executed
+// because the breaker is currently open.
+var ErrBreakerOpen = errors.New("circuit breaker is open")
+
+const (
+ closed uint32 = iota
+ open
+ halfOpen
+)
+
+// Breaker implements the circuit-breaker resiliency pattern
+type Breaker struct {
+ errorThreshold, successThreshold int
+ timeout time.Duration
+
+ lock sync.Mutex
+ state uint32
+ errors, successes int
+ lastError time.Time
+}
+
+// New constructs a new circuit-breaker that starts closed.
+// From closed, the breaker opens if "errorThreshold" errors are seen
+// without an error-free period of at least "timeout". From open, the
+// breaker half-closes after "timeout". From half-open, the breaker closes
+// after "successThreshold" consecutive successes, or opens on a single error.
+func New(errorThreshold, successThreshold int, timeout time.Duration) *Breaker {
+ return &Breaker{
+ errorThreshold: errorThreshold,
+ successThreshold: successThreshold,
+ timeout: timeout,
+ }
+}
+
+// Run will either return ErrBreakerOpen immediately if the circuit-breaker is
+// already open, or it will run the given function and pass along its return
+// value. It is safe to call Run concurrently on the same Breaker.
+func (b *Breaker) Run(work func() error) error {
+ state := atomic.LoadUint32(&b.state)
+
+ if state == open {
+ return ErrBreakerOpen
+ }
+
+ return b.doWork(state, work)
+}
+
+// Go will either return ErrBreakerOpen immediately if the circuit-breaker is
+// already open, or it will run the given function in a separate goroutine.
+// If the function is run, Go will return nil immediately, and will *not* return
+// the return value of the function. It is safe to call Go concurrently on the
+// same Breaker.
+func (b *Breaker) Go(work func() error) error {
+ state := atomic.LoadUint32(&b.state)
+
+ if state == open {
+ return ErrBreakerOpen
+ }
+
+ // errcheck complains about ignoring the error return value, but
+ // that's on purpose; if you want an error from a goroutine you have to
+ // get it over a channel or something
+ go b.doWork(state, work)
+
+ return nil
+}
+
+func (b *Breaker) doWork(state uint32, work func() error) error {
+ var panicValue interface{}
+
+ result := func() error {
+ defer func() {
+ panicValue = recover()
+ }()
+ return work()
+ }()
+
+ if result == nil && panicValue == nil && state == closed {
+ // short-circuit the normal, success path without contending
+ // on the lock
+ return nil
+ }
+
+ // oh well, I guess we have to contend on the lock
+ b.processResult(result, panicValue)
+
+ if panicValue != nil {
+ // as close as Go lets us come to a "rethrow" although unfortunately
+ // we lose the original panicing location
+ panic(panicValue)
+ }
+
+ return result
+}
+
+func (b *Breaker) processResult(result error, panicValue interface{}) {
+ b.lock.Lock()
+ defer b.lock.Unlock()
+
+ if result == nil && panicValue == nil {
+ if b.state == halfOpen {
+ b.successes++
+ if b.successes == b.successThreshold {
+ b.closeBreaker()
+ }
+ }
+ } else {
+ if b.errors > 0 {
+ expiry := b.lastError.Add(b.timeout)
+ if time.Now().After(expiry) {
+ b.errors = 0
+ }
+ }
+
+ switch b.state {
+ case closed:
+ b.errors++
+ if b.errors == b.errorThreshold {
+ b.openBreaker()
+ } else {
+ b.lastError = time.Now()
+ }
+ case halfOpen:
+ b.openBreaker()
+ }
+ }
+}
+
+func (b *Breaker) openBreaker() {
+ b.changeState(open)
+ go b.timer()
+}
+
+func (b *Breaker) closeBreaker() {
+ b.changeState(closed)
+}
+
+func (b *Breaker) timer() {
+ time.Sleep(b.timeout)
+
+ b.lock.Lock()
+ defer b.lock.Unlock()
+
+ b.changeState(halfOpen)
+}
+
+func (b *Breaker) changeState(newState uint32) {
+ b.errors = 0
+ b.successes = 0
+ atomic.StoreUint32(&b.state, newState)
+}
diff --git a/vendor/github.com/eapache/go-xerial-snappy/.gitignore b/vendor/github.com/eapache/go-xerial-snappy/.gitignore
new file mode 100644
index 00000000..daf913b1
--- /dev/null
+++ b/vendor/github.com/eapache/go-xerial-snappy/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/eapache/go-xerial-snappy/.travis.yml b/vendor/github.com/eapache/go-xerial-snappy/.travis.yml
new file mode 100644
index 00000000..d6cf4f1f
--- /dev/null
+++ b/vendor/github.com/eapache/go-xerial-snappy/.travis.yml
@@ -0,0 +1,7 @@
+language: go
+
+go:
+- 1.5.4
+- 1.6.1
+
+sudo: false
diff --git a/vendor/github.com/eapache/go-xerial-snappy/LICENSE b/vendor/github.com/eapache/go-xerial-snappy/LICENSE
new file mode 100644
index 00000000..5bf3688d
--- /dev/null
+++ b/vendor/github.com/eapache/go-xerial-snappy/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Evan Huus
+
+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.
diff --git a/vendor/github.com/eapache/go-xerial-snappy/README.md b/vendor/github.com/eapache/go-xerial-snappy/README.md
new file mode 100644
index 00000000..3f2695c7
--- /dev/null
+++ b/vendor/github.com/eapache/go-xerial-snappy/README.md
@@ -0,0 +1,13 @@
+# go-xerial-snappy
+
+[](https://travis-ci.org/eapache/go-xerial-snappy)
+
+Xerial-compatible Snappy framing support for golang.
+
+Packages using Xerial for snappy encoding use a framing format incompatible with
+basically everything else in existence. This package wraps Go's built-in snappy
+package to support it.
+
+Apps that use this format include Apache Kafka (see
+https://github.com/dpkp/kafka-python/issues/126#issuecomment-35478921 for
+details).
diff --git a/vendor/github.com/eapache/go-xerial-snappy/snappy.go b/vendor/github.com/eapache/go-xerial-snappy/snappy.go
new file mode 100644
index 00000000..b8f8b51f
--- /dev/null
+++ b/vendor/github.com/eapache/go-xerial-snappy/snappy.go
@@ -0,0 +1,43 @@
+package snappy
+
+import (
+ "bytes"
+ "encoding/binary"
+
+ master "github.com/golang/snappy"
+)
+
+var xerialHeader = []byte{130, 83, 78, 65, 80, 80, 89, 0}
+
+// Encode encodes data as snappy with no framing header.
+func Encode(src []byte) []byte {
+ return master.Encode(nil, src)
+}
+
+// Decode decodes snappy data whether it is traditional unframed
+// or includes the xerial framing format.
+func Decode(src []byte) ([]byte, error) {
+ if !bytes.Equal(src[:8], xerialHeader) {
+ return master.Decode(nil, src)
+ }
+
+ var (
+ pos = uint32(16)
+ max = uint32(len(src))
+ dst = make([]byte, 0, len(src))
+ chunk []byte
+ err error
+ )
+ for pos < max {
+ size := binary.BigEndian.Uint32(src[pos : pos+4])
+ pos += 4
+
+ chunk, err = master.Decode(chunk, src[pos:pos+size])
+ if err != nil {
+ return nil, err
+ }
+ pos += size
+ dst = append(dst, chunk...)
+ }
+ return dst, nil
+}
diff --git a/vendor/github.com/eapache/queue/.gitignore b/vendor/github.com/eapache/queue/.gitignore
new file mode 100644
index 00000000..83656241
--- /dev/null
+++ b/vendor/github.com/eapache/queue/.gitignore
@@ -0,0 +1,23 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
diff --git a/vendor/github.com/eapache/queue/.travis.yml b/vendor/github.com/eapache/queue/.travis.yml
new file mode 100644
index 00000000..235a40a4
--- /dev/null
+++ b/vendor/github.com/eapache/queue/.travis.yml
@@ -0,0 +1,7 @@
+language: go
+sudo: false
+
+go:
+ - 1.2
+ - 1.3
+ - 1.4
diff --git a/vendor/github.com/eapache/queue/LICENSE b/vendor/github.com/eapache/queue/LICENSE
new file mode 100644
index 00000000..d5f36dbc
--- /dev/null
+++ b/vendor/github.com/eapache/queue/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Evan Huus
+
+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.
\ No newline at end of file
diff --git a/vendor/github.com/eapache/queue/README.md b/vendor/github.com/eapache/queue/README.md
new file mode 100644
index 00000000..8e782335
--- /dev/null
+++ b/vendor/github.com/eapache/queue/README.md
@@ -0,0 +1,16 @@
+Queue
+=====
+
+[](https://travis-ci.org/eapache/queue)
+[](https://godoc.org/github.com/eapache/queue)
+[](https://eapache.github.io/conduct.html)
+
+A fast Golang queue using a ring-buffer, based on the version suggested by Dariusz Górecki.
+Using this instead of other, simpler, queue implementations (slice+append or linked list) provides
+substantial memory and time benefits, and fewer GC pauses.
+
+The queue implemented here is as fast as it is in part because it is *not* thread-safe.
+
+Follows semantic versioning using https://gopkg.in/ - import from
+[`gopkg.in/eapache/queue.v1`](https://gopkg.in/eapache/queue.v1)
+for guaranteed API stability.
diff --git a/vendor/github.com/eapache/queue/queue.go b/vendor/github.com/eapache/queue/queue.go
new file mode 100644
index 00000000..2dc8d939
--- /dev/null
+++ b/vendor/github.com/eapache/queue/queue.go
@@ -0,0 +1,88 @@
+/*
+Package queue provides a fast, ring-buffer queue based on the version suggested by Dariusz Górecki.
+Using this instead of other, simpler, queue implementations (slice+append or linked list) provides
+substantial memory and time benefits, and fewer GC pauses.
+
+The queue implemented here is as fast as it is for an additional reason: it is *not* thread-safe.
+*/
+package queue
+
+const minQueueLen = 16
+
+// Queue represents a single instance of the queue data structure.
+type Queue struct {
+ buf []interface{}
+ head, tail, count int
+}
+
+// New constructs and returns a new Queue.
+func New() *Queue {
+ return &Queue{
+ buf: make([]interface{}, minQueueLen),
+ }
+}
+
+// Length returns the number of elements currently stored in the queue.
+func (q *Queue) Length() int {
+ return q.count
+}
+
+// resizes the queue to fit exactly twice its current contents
+// this can result in shrinking if the queue is less than half-full
+func (q *Queue) resize() {
+ newBuf := make([]interface{}, q.count*2)
+
+ if q.tail > q.head {
+ copy(newBuf, q.buf[q.head:q.tail])
+ } else {
+ n := copy(newBuf, q.buf[q.head:])
+ copy(newBuf[n:], q.buf[:q.tail])
+ }
+
+ q.head = 0
+ q.tail = q.count
+ q.buf = newBuf
+}
+
+// Add puts an element on the end of the queue.
+func (q *Queue) Add(elem interface{}) {
+ if q.count == len(q.buf) {
+ q.resize()
+ }
+
+ q.buf[q.tail] = elem
+ q.tail = (q.tail + 1) % len(q.buf)
+ q.count++
+}
+
+// Peek returns the element at the head of the queue. This call panics
+// if the queue is empty.
+func (q *Queue) Peek() interface{} {
+ if q.count <= 0 {
+ panic("queue: Peek() called on empty queue")
+ }
+ return q.buf[q.head]
+}
+
+// Get returns the element at index i in the queue. If the index is
+// invalid, the call will panic.
+func (q *Queue) Get(i int) interface{} {
+ if i < 0 || i >= q.count {
+ panic("queue: Get() called with index out of range")
+ }
+ return q.buf[(q.head+i)%len(q.buf)]
+}
+
+// Remove removes the element from the front of the queue. If you actually
+// want the element, call Peek first. This call panics if the queue is empty.
+func (q *Queue) Remove() {
+ if q.count <= 0 {
+ panic("queue: Remove() called on empty queue")
+ }
+ q.buf[q.head] = nil
+ q.head = (q.head + 1) % len(q.buf)
+ q.count--
+ if len(q.buf) > minQueueLen && q.count*4 == len(q.buf) {
+ q.resize()
+ }
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/.gitignore b/vendor/github.com/eclipse/paho.mqtt.golang/.gitignore
new file mode 100644
index 00000000..47bb0de4
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/.gitignore
@@ -0,0 +1,36 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+
+*.msg
+*.lok
+
+samples/trivial
+samples/trivial2
+samples/sample
+samples/reconnect
+samples/ssl
+samples/custom_store
+samples/simple
+samples/stdinpub
+samples/stdoutsub
+samples/routing
\ No newline at end of file
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/CONTRIBUTING.md b/vendor/github.com/eclipse/paho.mqtt.golang/CONTRIBUTING.md
new file mode 100644
index 00000000..9791dc60
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/CONTRIBUTING.md
@@ -0,0 +1,56 @@
+Contributing to Paho
+====================
+
+Thanks for your interest in this project.
+
+Project description:
+--------------------
+
+The Paho project has been created to provide scalable open-source implementations of open and standard messaging protocols aimed at new, existing, and emerging applications for Machine-to-Machine (M2M) and Internet of Things (IoT).
+Paho reflects the inherent physical and cost constraints of device connectivity. Its objectives include effective levels of decoupling between devices and applications, designed to keep markets open and encourage the rapid growth of scalable Web and Enterprise middleware and applications. Paho is being kicked off with MQTT publish/subscribe client implementations for use on embedded platforms, along with corresponding server support as determined by the community.
+
+- https://projects.eclipse.org/projects/technology.paho
+
+Developer resources:
+--------------------
+
+Information regarding source code management, builds, coding standards, and more.
+
+- https://projects.eclipse.org/projects/technology.paho/developer
+
+Contributor License Agreement:
+------------------------------
+
+Before your contribution can be accepted by the project, you need to create and electronically sign the Eclipse Foundation Contributor License Agreement (CLA).
+
+- http://www.eclipse.org/legal/CLA.php
+
+Contributing Code:
+------------------
+
+The Go client is developed in Github, see their documentation on the process of forking and pull requests; https://help.github.com/categories/collaborating-on-projects-using-pull-requests/
+
+Git commit messages should follow the style described here;
+
+http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
+
+Contact:
+--------
+
+Contact the project developers via the project's "dev" list.
+
+- https://dev.eclipse.org/mailman/listinfo/paho-dev
+
+Search for bugs:
+----------------
+
+This project uses Github issues to track ongoing development and issues.
+
+- https://github.com/eclipse/paho.mqtt.golang/issues
+
+Create a new bug:
+-----------------
+
+Be sure to search for existing bugs before you create another one. Remember that contributions are always welcome!
+
+- https://github.com/eclipse/paho.mqtt.golang/issues
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/DISTRIBUTION b/vendor/github.com/eclipse/paho.mqtt.golang/DISTRIBUTION
new file mode 100644
index 00000000..34e49731
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/DISTRIBUTION
@@ -0,0 +1,15 @@
+
+
+Eclipse Distribution License - v 1.0
+
+Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
+
+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 the Eclipse Foundation, Inc. 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/LICENSE b/vendor/github.com/eclipse/paho.mqtt.golang/LICENSE
new file mode 100644
index 00000000..aa7cc810
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/LICENSE
@@ -0,0 +1,87 @@
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
+
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
+
+iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained within the Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
\ No newline at end of file
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/README.md b/vendor/github.com/eclipse/paho.mqtt.golang/README.md
new file mode 100644
index 00000000..09e50072
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/README.md
@@ -0,0 +1,63 @@
+Eclipse Paho MQTT Go client
+===========================
+
+
+This repository contains the source code for the [Eclipse Paho](http://eclipse.org/paho) MQTT Go client library.
+
+This code builds a library which enable applications to connect to an [MQTT](http://mqtt.org) broker to publish messages, and to subscribe to topics and receive published messages.
+
+This library supports a fully asynchronous mode of operation.
+
+
+Installation and Build
+----------------------
+
+This client is designed to work with the standard Go tools, so installation is as easy as:
+
+```
+go get github.com/eclipse/paho.mqtt.golang
+```
+
+The client depends on Google's [websockets](https://godoc.org/golang.org/x/net/websocket) and [proxy](https://godoc.org/golang.org/x/net/proxy) package,
+also easily installed with the commands:
+
+```
+go get golang.org/x/net/websocket
+go get golang.org/x/net/proxy
+```
+
+
+Usage and API
+-------------
+
+Detailed API documentation is available by using to godoc tool, or can be browsed online
+using the [godoc.org](http://godoc.org/github.com/eclipse/paho.mqtt.golang) service.
+
+Make use of the library by importing it in your Go client source code. For example,
+```
+import "github.com/eclipse/paho.mqtt.golang"
+```
+
+Samples are available in the `cmd` directory for reference.
+
+
+Runtime tracing
+---------------
+
+Tracing is enabled by assigning logs (from the Go log package) to the logging endpoints, ERROR, CRITICAL, WARN and DEBUG
+
+
+Reporting bugs
+--------------
+
+Please report bugs by raising issues for this project in github https://github.com/eclipse/paho.mqtt.golang/issues
+
+
+More information
+----------------
+
+Discussion of the Paho clients takes place on the [Eclipse paho-dev mailing list](https://dev.eclipse.org/mailman/listinfo/paho-dev).
+
+General questions about the MQTT protocol are discussed in the [MQTT Google Group](https://groups.google.com/forum/?hl=en-US&fromgroups#!forum/mqtt).
+
+There is much more information available via the [MQTT community site](http://mqtt.org).
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/about.html b/vendor/github.com/eclipse/paho.mqtt.golang/about.html
new file mode 100644
index 00000000..b183f417
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/about.html
@@ -0,0 +1,41 @@
+
+
+
+About
+
+
+About This Content
+
+December 9, 2013
+License
+
+The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL") and Eclipse Distribution License Version 1.0 ("EDL").
+A copy of the EPL is available at
+http://www.eclipse.org/legal/epl-v10.html
+and a copy of the EDL is available at
+http://www.eclipse.org/org/documents/edl-v10.php.
+For purposes of the EPL, "Program" will mean the Content.
+
+If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at http://www.eclipse.org.
+
+
+ Third Party Content
+ The Content includes items that have been sourced from third parties as set out below. If you
+ did not receive this Content directly from the Eclipse Foundation, the following is provided
+ for informational purposes only, and you should look to the Redistributor's license for
+ terms and conditions of use.
+
+ None
+
+
+
+
+
+
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/client.go b/vendor/github.com/eclipse/paho.mqtt.golang/client.go
new file mode 100644
index 00000000..3bf0c6cf
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/client.go
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+// Package mqtt provides an MQTT v3.1.1 client library.
+package mqtt
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/eclipse/paho.mqtt.golang/packets"
+)
+
+const (
+ disconnected uint32 = iota
+ connecting
+ reconnecting
+ connected
+)
+
+// Client is the interface definition for a Client as used by this
+// library, the interface is primarily to allow mocking tests.
+//
+// It is an MQTT v3.1.1 client for communicating
+// with an MQTT server using non-blocking methods that allow work
+// to be done in the background.
+// An application may connect to an MQTT server using:
+// A plain TCP socket
+// A secure SSL/TLS socket
+// A websocket
+// To enable ensured message delivery at Quality of Service (QoS) levels
+// described in the MQTT spec, a message persistence mechanism must be
+// used. This is done by providing a type which implements the Store
+// interface. For convenience, FileStore and MemoryStore are provided
+// implementations that should be sufficient for most use cases. More
+// information can be found in their respective documentation.
+// Numerous connection options may be specified by configuring a
+// and then supplying a ClientOptions type.
+type Client interface {
+ IsConnected() bool
+ Connect() Token
+ Disconnect(quiesce uint)
+ Publish(topic string, qos byte, retained bool, payload interface{}) Token
+ Subscribe(topic string, qos byte, callback MessageHandler) Token
+ SubscribeMultiple(filters map[string]byte, callback MessageHandler) Token
+ Unsubscribe(topics ...string) Token
+ AddRoute(topic string, callback MessageHandler)
+}
+
+// client implements the Client interface
+type client struct {
+ sync.RWMutex
+ messageIds
+ conn net.Conn
+ ibound chan packets.ControlPacket
+ obound chan *PacketAndToken
+ oboundP chan *PacketAndToken
+ msgRouter *router
+ stopRouter chan bool
+ incomingPubChan chan *packets.PublishPacket
+ errors chan error
+ stop chan struct{}
+ persist Store
+ options ClientOptions
+ pingResp *sync.Cond
+ packetResp *sync.Cond
+ keepaliveReset *sync.Cond
+ status uint32
+ workers sync.WaitGroup
+}
+
+// NewClient will create an MQTT v3.1.1 client with all of the options specified
+// in the provided ClientOptions. The client must have the Connect method called
+// on it before it may be used. This is to make sure resources (such as a net
+// connection) are created before the application is actually ready.
+func NewClient(o *ClientOptions) Client {
+ c := &client{}
+ c.options = *o
+
+ if c.options.Store == nil {
+ c.options.Store = NewMemoryStore()
+ }
+ switch c.options.ProtocolVersion {
+ case 3, 4:
+ c.options.protocolVersionExplicit = true
+ default:
+ c.options.ProtocolVersion = 4
+ c.options.protocolVersionExplicit = false
+ }
+ c.persist = c.options.Store
+ c.status = disconnected
+ c.messageIds = messageIds{index: make(map[uint16]Token)}
+ c.msgRouter, c.stopRouter = newRouter()
+ c.msgRouter.setDefaultHandler(c.options.DefaultPublishHandler)
+ if !c.options.AutoReconnect {
+ c.options.MessageChannelDepth = 0
+ }
+ return c
+}
+
+func (c *client) AddRoute(topic string, callback MessageHandler) {
+ if callback != nil {
+ c.msgRouter.addRoute(topic, callback)
+ }
+}
+
+// IsConnected returns a bool signifying whether
+// the client is connected or not.
+func (c *client) IsConnected() bool {
+ c.RLock()
+ defer c.RUnlock()
+ status := atomic.LoadUint32(&c.status)
+ switch {
+ case status == connected:
+ return true
+ case c.options.AutoReconnect && status > disconnected:
+ return true
+ default:
+ return false
+ }
+}
+
+func (c *client) connectionStatus() uint32 {
+ c.RLock()
+ defer c.RUnlock()
+ status := atomic.LoadUint32(&c.status)
+ return status
+}
+
+func (c *client) setConnected(status uint32) {
+ c.Lock()
+ defer c.Unlock()
+ atomic.StoreUint32(&c.status, uint32(status))
+}
+
+//ErrNotConnected is the error returned from function calls that are
+//made when the client is not connected to a broker
+var ErrNotConnected = errors.New("Not Connected")
+
+// Connect will create a connection to the message broker
+// If clean session is false, then a slice will
+// be returned containing Receipts for all messages
+// that were in-flight at the last disconnect.
+// If clean session is true, then any existing client
+// state will be removed.
+func (c *client) Connect() Token {
+ var err error
+ t := newToken(packets.Connect).(*ConnectToken)
+ DEBUG.Println(CLI, "Connect()")
+
+ go func() {
+ c.persist.Open()
+
+ c.setConnected(connecting)
+ var rc byte
+ cm := newConnectMsgFromOptions(&c.options)
+ protocolVersion := c.options.ProtocolVersion
+
+ for _, broker := range c.options.Servers {
+ c.options.ProtocolVersion = protocolVersion
+ CONN:
+ DEBUG.Println(CLI, "about to write new connect msg")
+ c.conn, err = openConnection(broker, &c.options.TLSConfig, c.options.ConnectTimeout)
+ if err == nil {
+ DEBUG.Println(CLI, "socket connected to broker")
+ switch c.options.ProtocolVersion {
+ case 3:
+ DEBUG.Println(CLI, "Using MQTT 3.1 protocol")
+ cm.ProtocolName = "MQIsdp"
+ cm.ProtocolVersion = 3
+ default:
+ DEBUG.Println(CLI, "Using MQTT 3.1.1 protocol")
+ c.options.ProtocolVersion = 4
+ cm.ProtocolName = "MQTT"
+ cm.ProtocolVersion = 4
+ }
+ cm.Write(c.conn)
+
+ rc = c.connect()
+ if rc != packets.Accepted {
+ if c.conn != nil {
+ c.conn.Close()
+ c.conn = nil
+ }
+ //if the protocol version was explicitly set don't do any fallback
+ if c.options.protocolVersionExplicit {
+ ERROR.Println(CLI, "Connecting to", broker, "CONNACK was not CONN_ACCEPTED, but rather", packets.ConnackReturnCodes[rc])
+ continue
+ }
+ if c.options.ProtocolVersion == 4 {
+ DEBUG.Println(CLI, "Trying reconnect using MQTT 3.1 protocol")
+ c.options.ProtocolVersion = 3
+ goto CONN
+ }
+ }
+ break
+ } else {
+ ERROR.Println(CLI, err.Error())
+ WARN.Println(CLI, "failed to connect to broker, trying next")
+ rc = packets.ErrNetworkError
+ }
+ }
+
+ if c.conn == nil {
+ ERROR.Println(CLI, "Failed to connect to a broker")
+ t.returnCode = rc
+ if rc != packets.ErrNetworkError {
+ t.err = packets.ConnErrors[rc]
+ } else {
+ t.err = fmt.Errorf("%s : %s", packets.ConnErrors[rc], err)
+ }
+ c.setConnected(disconnected)
+ c.persist.Close()
+ t.flowComplete()
+ return
+ }
+
+ c.options.protocolVersionExplicit = true
+
+ c.obound = make(chan *PacketAndToken, c.options.MessageChannelDepth)
+ c.oboundP = make(chan *PacketAndToken, c.options.MessageChannelDepth)
+ c.ibound = make(chan packets.ControlPacket)
+ c.errors = make(chan error, 1)
+ c.stop = make(chan struct{})
+ c.pingResp = sync.NewCond(&sync.Mutex{})
+ c.packetResp = sync.NewCond(&sync.Mutex{})
+ c.keepaliveReset = sync.NewCond(&sync.Mutex{})
+
+ if c.options.KeepAlive != 0 {
+ c.workers.Add(1)
+ go keepalive(c)
+ }
+
+ c.incomingPubChan = make(chan *packets.PublishPacket, c.options.MessageChannelDepth)
+ c.msgRouter.matchAndDispatch(c.incomingPubChan, c.options.Order, c)
+
+ c.setConnected(connected)
+ DEBUG.Println(CLI, "client is connected")
+ if c.options.OnConnect != nil {
+ go c.options.OnConnect(c)
+ }
+
+ // Take care of any messages in the store
+ //var leftovers []Receipt
+ if c.options.CleanSession == false {
+ //leftovers = c.resume()
+ } else {
+ c.persist.Reset()
+ }
+
+ go errorWatch(c)
+
+ // Do not start incoming until resume has completed
+ c.workers.Add(3)
+ go alllogic(c)
+ go outgoing(c)
+ go incoming(c)
+
+ DEBUG.Println(CLI, "exit startClient")
+ t.flowComplete()
+ }()
+ return t
+}
+
+// internal function used to reconnect the client when it loses its connection
+func (c *client) reconnect() {
+ DEBUG.Println(CLI, "enter reconnect")
+ var (
+ err error
+
+ rc = byte(1)
+ sleep = time.Duration(1 * time.Second)
+ )
+
+ for rc != 0 && c.status != disconnected {
+ cm := newConnectMsgFromOptions(&c.options)
+
+ for _, broker := range c.options.Servers {
+ DEBUG.Println(CLI, "about to write new connect msg")
+ c.conn, err = openConnection(broker, &c.options.TLSConfig, c.options.ConnectTimeout)
+ if err == nil {
+ DEBUG.Println(CLI, "socket connected to broker")
+ switch c.options.ProtocolVersion {
+ case 3:
+ DEBUG.Println(CLI, "Using MQTT 3.1 protocol")
+ cm.ProtocolName = "MQIsdp"
+ cm.ProtocolVersion = 3
+ default:
+ DEBUG.Println(CLI, "Using MQTT 3.1.1 protocol")
+ cm.ProtocolName = "MQTT"
+ cm.ProtocolVersion = 4
+ }
+ cm.Write(c.conn)
+
+ rc = c.connect()
+ if rc != packets.Accepted {
+ c.conn.Close()
+ c.conn = nil
+ //if the protocol version was explicitly set don't do any fallback
+ if c.options.protocolVersionExplicit {
+ ERROR.Println(CLI, "Connecting to", broker, "CONNACK was not Accepted, but rather", packets.ConnackReturnCodes[rc])
+ continue
+ }
+ }
+ break
+ } else {
+ ERROR.Println(CLI, err.Error())
+ WARN.Println(CLI, "failed to connect to broker, trying next")
+ rc = packets.ErrNetworkError
+ }
+ }
+ if rc != 0 {
+ DEBUG.Println(CLI, "Reconnect failed, sleeping for", int(sleep.Seconds()), "seconds")
+ time.Sleep(sleep)
+ if sleep < c.options.MaxReconnectInterval {
+ sleep *= 2
+ }
+
+ if sleep > c.options.MaxReconnectInterval {
+ sleep = c.options.MaxReconnectInterval
+ }
+ }
+ }
+ // Disconnect() must have been called while we were trying to reconnect.
+ if c.connectionStatus() == disconnected {
+ DEBUG.Println(CLI, "Client moved to disconnected state while reconnecting, abandoning reconnect")
+ return
+ }
+
+ if c.options.KeepAlive != 0 {
+ c.workers.Add(1)
+ go keepalive(c)
+ }
+
+ c.stop = make(chan struct{})
+
+ c.setConnected(connected)
+ DEBUG.Println(CLI, "client is reconnected")
+ if c.options.OnConnect != nil {
+ go c.options.OnConnect(c)
+ }
+
+ go errorWatch(c)
+
+ c.workers.Add(3)
+ go alllogic(c)
+ go outgoing(c)
+ go incoming(c)
+}
+
+// This function is only used for receiving a connack
+// when the connection is first started.
+// This prevents receiving incoming data while resume
+// is in progress if clean session is false.
+func (c *client) connect() byte {
+ DEBUG.Println(NET, "connect started")
+
+ ca, err := packets.ReadPacket(c.conn)
+ if err != nil {
+ ERROR.Println(NET, "connect got error", err)
+ return packets.ErrNetworkError
+ }
+ if ca == nil {
+ ERROR.Println(NET, "received nil packet")
+ return packets.ErrNetworkError
+ }
+
+ msg, ok := ca.(*packets.ConnackPacket)
+ if !ok {
+ ERROR.Println(NET, "received msg that was not CONNACK")
+ return packets.ErrNetworkError
+ }
+
+ DEBUG.Println(NET, "received connack")
+ return msg.ReturnCode
+}
+
+// Disconnect will end the connection with the server, but not before waiting
+// the specified number of milliseconds to wait for existing work to be
+// completed.
+func (c *client) Disconnect(quiesce uint) {
+ status := atomic.LoadUint32(&c.status)
+ if status == connected {
+ DEBUG.Println(CLI, "disconnecting")
+ c.setConnected(disconnected)
+
+ dm := packets.NewControlPacket(packets.Disconnect).(*packets.DisconnectPacket)
+ dt := newToken(packets.Disconnect)
+ c.oboundP <- &PacketAndToken{p: dm, t: dt}
+
+ // wait for work to finish, or quiesce time consumed
+ dt.WaitTimeout(time.Duration(quiesce) * time.Millisecond)
+ } else {
+ WARN.Println(CLI, "Disconnect() called but not connected (disconnected/reconnecting)")
+ c.setConnected(disconnected)
+ }
+
+ c.disconnect()
+}
+
+// ForceDisconnect will end the connection with the mqtt broker immediately.
+func (c *client) forceDisconnect() {
+ if !c.IsConnected() {
+ WARN.Println(CLI, "already disconnected")
+ return
+ }
+ c.setConnected(disconnected)
+ c.conn.Close()
+ DEBUG.Println(CLI, "forcefully disconnecting")
+ c.disconnect()
+}
+
+func (c *client) internalConnLost(err error) {
+ // Only do anything if this was called and we are still "connected"
+ // forceDisconnect can cause incoming/outgoing/alllogic to end with
+ // error from closing the socket but state will be "disconnected"
+ if c.IsConnected() {
+ c.closeStop()
+ c.conn.Close()
+ c.workers.Wait()
+ if c.options.CleanSession {
+ c.messageIds.cleanUp()
+ }
+ if c.options.AutoReconnect {
+ c.setConnected(reconnecting)
+ go c.reconnect()
+ } else {
+ c.setConnected(disconnected)
+ }
+ if c.options.OnConnectionLost != nil {
+ go c.options.OnConnectionLost(c, err)
+ }
+ }
+}
+
+func (c *client) closeStop() {
+ c.Lock()
+ defer c.Unlock()
+ select {
+ case <-c.stop:
+ DEBUG.Println("In disconnect and stop channel is already closed")
+ default:
+ close(c.stop)
+ }
+}
+
+func (c *client) closeConn() {
+ c.Lock()
+ defer c.Unlock()
+ if c.conn != nil {
+ c.conn.Close()
+ }
+}
+
+func (c *client) disconnect() {
+ c.closeStop()
+ c.closeConn()
+ c.workers.Wait()
+ close(c.stopRouter)
+ DEBUG.Println(CLI, "disconnected")
+ c.persist.Close()
+}
+
+// Publish will publish a message with the specified QoS and content
+// to the specified topic.
+// Returns a token to track delivery of the message to the broker
+func (c *client) Publish(topic string, qos byte, retained bool, payload interface{}) Token {
+ token := newToken(packets.Publish).(*PublishToken)
+ DEBUG.Println(CLI, "enter Publish")
+ switch {
+ case !c.IsConnected():
+ token.err = ErrNotConnected
+ token.flowComplete()
+ return token
+ case c.connectionStatus() == reconnecting && qos == 0:
+ token.flowComplete()
+ return token
+ }
+ pub := packets.NewControlPacket(packets.Publish).(*packets.PublishPacket)
+ pub.Qos = qos
+ pub.TopicName = topic
+ pub.Retain = retained
+ switch payload.(type) {
+ case string:
+ pub.Payload = []byte(payload.(string))
+ case []byte:
+ pub.Payload = payload.([]byte)
+ default:
+ token.err = errors.New("Unknown payload type")
+ token.flowComplete()
+ return token
+ }
+
+ DEBUG.Println(CLI, "sending publish message, topic:", topic)
+ if pub.Qos != 0 && pub.MessageID == 0 {
+ pub.MessageID = c.getID(token)
+ token.messageID = pub.MessageID
+ }
+ persistOutbound(c.persist, pub)
+ c.obound <- &PacketAndToken{p: pub, t: token}
+ return token
+}
+
+// Subscribe starts a new subscription. Provide a MessageHandler to be executed when
+// a message is published on the topic provided.
+func (c *client) Subscribe(topic string, qos byte, callback MessageHandler) Token {
+ token := newToken(packets.Subscribe).(*SubscribeToken)
+ DEBUG.Println(CLI, "enter Subscribe")
+ if !c.IsConnected() {
+ token.err = ErrNotConnected
+ token.flowComplete()
+ return token
+ }
+ sub := packets.NewControlPacket(packets.Subscribe).(*packets.SubscribePacket)
+ if err := validateTopicAndQos(topic, qos); err != nil {
+ token.err = err
+ return token
+ }
+ sub.Topics = append(sub.Topics, topic)
+ sub.Qoss = append(sub.Qoss, qos)
+ DEBUG.Println(CLI, sub.String())
+
+ if callback != nil {
+ c.msgRouter.addRoute(topic, callback)
+ }
+
+ token.subs = append(token.subs, topic)
+ c.oboundP <- &PacketAndToken{p: sub, t: token}
+ DEBUG.Println(CLI, "exit Subscribe")
+ return token
+}
+
+// SubscribeMultiple starts a new subscription for multiple topics. Provide a MessageHandler to
+// be executed when a message is published on one of the topics provided.
+func (c *client) SubscribeMultiple(filters map[string]byte, callback MessageHandler) Token {
+ var err error
+ token := newToken(packets.Subscribe).(*SubscribeToken)
+ DEBUG.Println(CLI, "enter SubscribeMultiple")
+ if !c.IsConnected() {
+ token.err = ErrNotConnected
+ token.flowComplete()
+ return token
+ }
+ sub := packets.NewControlPacket(packets.Subscribe).(*packets.SubscribePacket)
+ if sub.Topics, sub.Qoss, err = validateSubscribeMap(filters); err != nil {
+ token.err = err
+ return token
+ }
+
+ if callback != nil {
+ for topic := range filters {
+ c.msgRouter.addRoute(topic, callback)
+ }
+ }
+ token.subs = make([]string, len(sub.Topics))
+ copy(token.subs, sub.Topics)
+ c.oboundP <- &PacketAndToken{p: sub, t: token}
+ DEBUG.Println(CLI, "exit SubscribeMultiple")
+ return token
+}
+
+// Unsubscribe will end the subscription from each of the topics provided.
+// Messages published to those topics from other clients will no longer be
+// received.
+func (c *client) Unsubscribe(topics ...string) Token {
+ token := newToken(packets.Unsubscribe).(*UnsubscribeToken)
+ DEBUG.Println(CLI, "enter Unsubscribe")
+ if !c.IsConnected() {
+ token.err = ErrNotConnected
+ token.flowComplete()
+ return token
+ }
+ unsub := packets.NewControlPacket(packets.Unsubscribe).(*packets.UnsubscribePacket)
+ unsub.Topics = make([]string, len(topics))
+ copy(unsub.Topics, topics)
+
+ c.oboundP <- &PacketAndToken{p: unsub, t: token}
+ for _, topic := range topics {
+ c.msgRouter.deleteRoute(topic)
+ }
+
+ DEBUG.Println(CLI, "exit Unsubscribe")
+ return token
+}
+
+func (c *client) OptionsReader() ClientOptionsReader {
+ r := ClientOptionsReader{options: &c.options}
+ return r
+}
+
+//DefaultConnectionLostHandler is a definition of a function that simply
+//reports to the DEBUG log the reason for the client losing a connection.
+func DefaultConnectionLostHandler(client Client, reason error) {
+ DEBUG.Println("Connection lost:", reason.Error())
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/components.go b/vendor/github.com/eclipse/paho.mqtt.golang/components.go
new file mode 100644
index 00000000..01f5fafd
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/components.go
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+package mqtt
+
+type component string
+
+// Component names for debug output
+const (
+ NET component = "[net] "
+ PNG component = "[pinger] "
+ CLI component = "[client] "
+ DEC component = "[decode] "
+ MES component = "[message] "
+ STR component = "[store] "
+ MID component = "[msgids] "
+ TST component = "[test] "
+ STA component = "[state] "
+ ERR component = "[error] "
+)
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/edl-v10 b/vendor/github.com/eclipse/paho.mqtt.golang/edl-v10
new file mode 100644
index 00000000..cf989f14
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/edl-v10
@@ -0,0 +1,15 @@
+
+Eclipse Distribution License - v 1.0
+
+Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
+
+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 the Eclipse Foundation, Inc. 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.
+
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/epl-v10 b/vendor/github.com/eclipse/paho.mqtt.golang/epl-v10
new file mode 100644
index 00000000..79e486c3
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/epl-v10
@@ -0,0 +1,70 @@
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+i) changes to the Program, and
+ii) additions to the Program;
+where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
+b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
+c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
+d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+b) its license agreement:
+i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
+ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
+iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
+iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+b) a copy of this Agreement must be included with each copy of the Program.
+Contributors may not remove or alter any copyright notices contained within the Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/filestore.go b/vendor/github.com/eclipse/paho.mqtt.golang/filestore.go
new file mode 100644
index 00000000..daf3a9e4
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/filestore.go
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+package mqtt
+
+import (
+ "io/ioutil"
+ "os"
+ "path"
+ "sync"
+
+ "github.com/eclipse/paho.mqtt.golang/packets"
+)
+
+const (
+ msgExt = ".msg"
+ tmpExt = ".tmp"
+ corruptExt = ".CORRUPT"
+)
+
+// FileStore implements the store interface using the filesystem to provide
+// true persistence, even across client failure. This is designed to use a
+// single directory per running client. If you are running multiple clients
+// on the same filesystem, you will need to be careful to specify unique
+// store directories for each.
+type FileStore struct {
+ sync.RWMutex
+ directory string
+ opened bool
+}
+
+// NewFileStore will create a new FileStore which stores its messages in the
+// directory provided.
+func NewFileStore(directory string) *FileStore {
+ store := &FileStore{
+ directory: directory,
+ opened: false,
+ }
+ return store
+}
+
+// Open will allow the FileStore to be used.
+func (store *FileStore) Open() {
+ store.Lock()
+ defer store.Unlock()
+ // if no store directory was specified in ClientOpts, by default use the
+ // current working directory
+ if store.directory == "" {
+ store.directory, _ = os.Getwd()
+ }
+
+ // if store dir exists, great, otherwise, create it
+ if !exists(store.directory) {
+ perms := os.FileMode(0770)
+ merr := os.MkdirAll(store.directory, perms)
+ chkerr(merr)
+ }
+ store.opened = true
+ DEBUG.Println(STR, "store is opened at", store.directory)
+}
+
+// Close will disallow the FileStore from being used.
+func (store *FileStore) Close() {
+ store.Lock()
+ defer store.Unlock()
+ store.opened = false
+ DEBUG.Println(STR, "store is closed")
+}
+
+// Put will put a message into the store, associated with the provided
+// key value.
+func (store *FileStore) Put(key string, m packets.ControlPacket) {
+ store.Lock()
+ defer store.Unlock()
+ if !store.opened {
+ ERROR.Println(STR, "Trying to use file store, but not open")
+ return
+ }
+ full := fullpath(store.directory, key)
+ write(store.directory, key, m)
+ if !exists(full) {
+ ERROR.Println(STR, "file not created:", full)
+ }
+}
+
+// Get will retrieve a message from the store, the one associated with
+// the provided key value.
+func (store *FileStore) Get(key string) packets.ControlPacket {
+ store.RLock()
+ defer store.RUnlock()
+ if !store.opened {
+ ERROR.Println(STR, "Trying to use file store, but not open")
+ return nil
+ }
+ filepath := fullpath(store.directory, key)
+ if !exists(filepath) {
+ return nil
+ }
+ mfile, oerr := os.Open(filepath)
+ chkerr(oerr)
+ msg, rerr := packets.ReadPacket(mfile)
+ chkerr(mfile.Close())
+
+ // Message was unreadable, return nil
+ if rerr != nil {
+ newpath := corruptpath(store.directory, key)
+ WARN.Println(STR, "corrupted file detected:", rerr.Error(), "archived at:", newpath)
+ os.Rename(filepath, newpath)
+ return nil
+ }
+ return msg
+}
+
+// All will provide a list of all of the keys associated with messages
+// currenly residing in the FileStore.
+func (store *FileStore) All() []string {
+ store.RLock()
+ defer store.RUnlock()
+ return store.all()
+}
+
+// Del will remove the persisted message associated with the provided
+// key from the FileStore.
+func (store *FileStore) Del(key string) {
+ store.Lock()
+ defer store.Unlock()
+ store.del(key)
+}
+
+// Reset will remove all persisted messages from the FileStore.
+func (store *FileStore) Reset() {
+ store.Lock()
+ defer store.Unlock()
+ WARN.Println(STR, "FileStore Reset")
+ for _, key := range store.all() {
+ store.del(key)
+ }
+}
+
+// lockless
+func (store *FileStore) all() []string {
+ if !store.opened {
+ ERROR.Println(STR, "Trying to use file store, but not open")
+ return nil
+ }
+ keys := []string{}
+ files, rderr := ioutil.ReadDir(store.directory)
+ chkerr(rderr)
+ for _, f := range files {
+ DEBUG.Println(STR, "file in All():", f.Name())
+ name := f.Name()
+ if name[len(name)-4:len(name)] != msgExt {
+ DEBUG.Println(STR, "skipping file, doesn't have right extension: ", name)
+ continue
+ }
+ key := name[0 : len(name)-4] // remove file extension
+ keys = append(keys, key)
+ }
+ return keys
+}
+
+// lockless
+func (store *FileStore) del(key string) {
+ if !store.opened {
+ ERROR.Println(STR, "Trying to use file store, but not open")
+ return
+ }
+ DEBUG.Println(STR, "store del filepath:", store.directory)
+ DEBUG.Println(STR, "store delete key:", key)
+ filepath := fullpath(store.directory, key)
+ DEBUG.Println(STR, "path of deletion:", filepath)
+ if !exists(filepath) {
+ WARN.Println(STR, "store could not delete key:", key)
+ return
+ }
+ rerr := os.Remove(filepath)
+ chkerr(rerr)
+ DEBUG.Println(STR, "del msg:", key)
+ if exists(filepath) {
+ ERROR.Println(STR, "file not deleted:", filepath)
+ }
+}
+
+func fullpath(store string, key string) string {
+ p := path.Join(store, key+msgExt)
+ return p
+}
+
+func tmppath(store string, key string) string {
+ p := path.Join(store, key+tmpExt)
+ return p
+}
+
+func corruptpath(store string, key string) string {
+ p := path.Join(store, key+corruptExt)
+ return p
+}
+
+// create file called "X.[messageid].tmp" located in the store
+// the contents of the file is the bytes of the message, then
+// rename it to "X.[messageid].msg", overwriting any existing
+// message with the same id
+// X will be 'i' for inbound messages, and O for outbound messages
+func write(store, key string, m packets.ControlPacket) {
+ temppath := tmppath(store, key)
+ f, err := os.Create(temppath)
+ chkerr(err)
+ werr := m.Write(f)
+ chkerr(werr)
+ cerr := f.Close()
+ chkerr(cerr)
+ rerr := os.Rename(temppath, fullpath(store, key))
+ chkerr(rerr)
+}
+
+func exists(file string) bool {
+ if _, err := os.Stat(file); err != nil {
+ if os.IsNotExist(err) {
+ return false
+ }
+ chkerr(err)
+ }
+ return true
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/memstore.go b/vendor/github.com/eclipse/paho.mqtt.golang/memstore.go
new file mode 100644
index 00000000..d3bfe084
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/memstore.go
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+package mqtt
+
+import (
+ "sync"
+
+ "github.com/eclipse/paho.mqtt.golang/packets"
+)
+
+// MemoryStore implements the store interface to provide a "persistence"
+// mechanism wholly stored in memory. This is only useful for
+// as long as the client instance exists.
+type MemoryStore struct {
+ sync.RWMutex
+ messages map[string]packets.ControlPacket
+ opened bool
+}
+
+// NewMemoryStore returns a pointer to a new instance of
+// MemoryStore, the instance is not initialized and ready to
+// use until Open() has been called on it.
+func NewMemoryStore() *MemoryStore {
+ store := &MemoryStore{
+ messages: make(map[string]packets.ControlPacket),
+ opened: false,
+ }
+ return store
+}
+
+// Open initializes a MemoryStore instance.
+func (store *MemoryStore) Open() {
+ store.Lock()
+ defer store.Unlock()
+ store.opened = true
+ DEBUG.Println(STR, "memorystore initialized")
+}
+
+// Put takes a key and a pointer to a Message and stores the
+// message.
+func (store *MemoryStore) Put(key string, message packets.ControlPacket) {
+ store.Lock()
+ defer store.Unlock()
+ if !store.opened {
+ ERROR.Println(STR, "Trying to use memory store, but not open")
+ return
+ }
+ store.messages[key] = message
+}
+
+// Get takes a key and looks in the store for a matching Message
+// returning either the Message pointer or nil.
+func (store *MemoryStore) Get(key string) packets.ControlPacket {
+ store.RLock()
+ defer store.RUnlock()
+ if !store.opened {
+ ERROR.Println(STR, "Trying to use memory store, but not open")
+ return nil
+ }
+ mid := mIDFromKey(key)
+ m := store.messages[key]
+ if m == nil {
+ CRITICAL.Println(STR, "memorystore get: message", mid, "not found")
+ } else {
+ DEBUG.Println(STR, "memorystore get: message", mid, "found")
+ }
+ return m
+}
+
+// All returns a slice of strings containing all the keys currently
+// in the MemoryStore.
+func (store *MemoryStore) All() []string {
+ store.RLock()
+ defer store.RUnlock()
+ if !store.opened {
+ ERROR.Println(STR, "Trying to use memory store, but not open")
+ return nil
+ }
+ keys := []string{}
+ for k := range store.messages {
+ keys = append(keys, k)
+ }
+ return keys
+}
+
+// Del takes a key, searches the MemoryStore and if the key is found
+// deletes the Message pointer associated with it.
+func (store *MemoryStore) Del(key string) {
+ store.Lock()
+ defer store.Unlock()
+ if !store.opened {
+ ERROR.Println(STR, "Trying to use memory store, but not open")
+ return
+ }
+ mid := mIDFromKey(key)
+ m := store.messages[key]
+ if m == nil {
+ WARN.Println(STR, "memorystore del: message", mid, "not found")
+ } else {
+ store.messages[key] = nil
+ DEBUG.Println(STR, "memorystore del: message", mid, "was deleted")
+ }
+}
+
+// Close will disallow modifications to the state of the store.
+func (store *MemoryStore) Close() {
+ store.Lock()
+ defer store.Unlock()
+ if !store.opened {
+ ERROR.Println(STR, "Trying to close memory store, but not open")
+ return
+ }
+ store.opened = false
+ DEBUG.Println(STR, "memorystore closed")
+}
+
+// Reset eliminates all persisted message data in the store.
+func (store *MemoryStore) Reset() {
+ store.Lock()
+ defer store.Unlock()
+ if !store.opened {
+ ERROR.Println(STR, "Trying to reset memory store, but not open")
+ }
+ store.messages = make(map[string]packets.ControlPacket)
+ WARN.Println(STR, "memorystore wiped")
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/message.go b/vendor/github.com/eclipse/paho.mqtt.golang/message.go
new file mode 100644
index 00000000..b1b71648
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/message.go
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+package mqtt
+
+import (
+ "github.com/eclipse/paho.mqtt.golang/packets"
+)
+
+// Message defines the externals that a message implementation must support
+// these are received messages that are passed to the callbacks, not internal
+// messages
+type Message interface {
+ Duplicate() bool
+ Qos() byte
+ Retained() bool
+ Topic() string
+ MessageID() uint16
+ Payload() []byte
+}
+
+type message struct {
+ duplicate bool
+ qos byte
+ retained bool
+ topic string
+ messageID uint16
+ payload []byte
+}
+
+func (m *message) Duplicate() bool {
+ return m.duplicate
+}
+
+func (m *message) Qos() byte {
+ return m.qos
+}
+
+func (m *message) Retained() bool {
+ return m.retained
+}
+
+func (m *message) Topic() string {
+ return m.topic
+}
+
+func (m *message) MessageID() uint16 {
+ return m.messageID
+}
+
+func (m *message) Payload() []byte {
+ return m.payload
+}
+
+func messageFromPublish(p *packets.PublishPacket) Message {
+ return &message{
+ duplicate: p.Dup,
+ qos: p.Qos,
+ retained: p.Retain,
+ topic: p.TopicName,
+ messageID: p.MessageID,
+ payload: p.Payload,
+ }
+}
+
+func newConnectMsgFromOptions(options *ClientOptions) *packets.ConnectPacket {
+ m := packets.NewControlPacket(packets.Connect).(*packets.ConnectPacket)
+
+ m.CleanSession = options.CleanSession
+ m.WillFlag = options.WillEnabled
+ m.WillRetain = options.WillRetained
+ m.ClientIdentifier = options.ClientID
+
+ if options.WillEnabled {
+ m.WillQos = options.WillQos
+ m.WillTopic = options.WillTopic
+ m.WillMessage = options.WillPayload
+ }
+
+ if options.Username != "" {
+ m.UsernameFlag = true
+ m.Username = options.Username
+ //mustn't have password without user as well
+ if options.Password != "" {
+ m.PasswordFlag = true
+ m.Password = []byte(options.Password)
+ }
+ }
+
+ m.Keepalive = uint16(options.KeepAlive.Seconds())
+
+ return m
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/messageids.go b/vendor/github.com/eclipse/paho.mqtt.golang/messageids.go
new file mode 100644
index 00000000..effb0ec9
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/messageids.go
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+package mqtt
+
+import (
+ "fmt"
+ "sync"
+ "time"
+)
+
+// MId is 16 bit message id as specified by the MQTT spec.
+// In general, these values should not be depended upon by
+// the client application.
+type MId uint16
+
+type messageIds struct {
+ sync.RWMutex
+ index map[uint16]Token
+}
+
+const (
+ midMin uint16 = 1
+ midMax uint16 = 65535
+)
+
+func (mids *messageIds) cleanUp() {
+ mids.Lock()
+ for _, token := range mids.index {
+ switch t := token.(type) {
+ case *PublishToken:
+ t.err = fmt.Errorf("Connection lost before Publish completed")
+ case *SubscribeToken:
+ t.err = fmt.Errorf("Connection lost before Subscribe completed")
+ case *UnsubscribeToken:
+ t.err = fmt.Errorf("Connection lost before Unsubscribe completed")
+ case nil:
+ continue
+ }
+ token.flowComplete()
+ }
+ mids.index = make(map[uint16]Token)
+ mids.Unlock()
+}
+
+func (mids *messageIds) freeID(id uint16) {
+ mids.Lock()
+ delete(mids.index, id)
+ mids.Unlock()
+}
+
+func (mids *messageIds) getID(t Token) uint16 {
+ mids.Lock()
+ defer mids.Unlock()
+ for i := midMin; i < midMax; i++ {
+ if _, ok := mids.index[i]; !ok {
+ mids.index[i] = t
+ return i
+ }
+ }
+ return 0
+}
+
+func (mids *messageIds) getToken(id uint16) Token {
+ mids.RLock()
+ defer mids.RUnlock()
+ if token, ok := mids.index[id]; ok {
+ return token
+ }
+ return &DummyToken{id: id}
+}
+
+type DummyToken struct {
+ id uint16
+}
+
+func (d *DummyToken) Wait() bool {
+ return true
+}
+
+func (d *DummyToken) WaitTimeout(t time.Duration) bool {
+ return true
+}
+
+func (d *DummyToken) flowComplete() {
+ ERROR.Printf("A lookup for token %d returned nil\n", d.id)
+}
+
+func (d *DummyToken) Error() error {
+ return nil
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/net.go b/vendor/github.com/eclipse/paho.mqtt.golang/net.go
new file mode 100644
index 00000000..3c9e9ba9
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/net.go
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+package mqtt
+
+import (
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "net"
+ "net/url"
+ "os"
+ "reflect"
+ "time"
+
+ "github.com/eclipse/paho.mqtt.golang/packets"
+ "golang.org/x/net/proxy"
+ "golang.org/x/net/websocket"
+)
+
+func signalError(c chan<- error, err error) {
+ select {
+ case c <- err:
+ default:
+ }
+}
+
+func openConnection(uri *url.URL, tlsc *tls.Config, timeout time.Duration) (net.Conn, error) {
+ switch uri.Scheme {
+ case "ws":
+ conn, err := websocket.Dial(uri.String(), "mqtt", fmt.Sprintf("http://%s", uri.Host))
+ if err != nil {
+ return nil, err
+ }
+ conn.PayloadType = websocket.BinaryFrame
+ return conn, err
+ case "wss":
+ config, _ := websocket.NewConfig(uri.String(), fmt.Sprintf("https://%s", uri.Host))
+ config.Protocol = []string{"mqtt"}
+ config.TlsConfig = tlsc
+ conn, err := websocket.DialConfig(config)
+ if err != nil {
+ return nil, err
+ }
+ conn.PayloadType = websocket.BinaryFrame
+ return conn, err
+ case "tcp":
+ allProxy := os.Getenv("all_proxy")
+ if len(allProxy) == 0 {
+ conn, err := net.DialTimeout("tcp", uri.Host, timeout)
+ if err != nil {
+ return nil, err
+ }
+ return conn, nil
+ } else {
+ proxyDialer := proxy.FromEnvironment()
+
+ conn, err := proxyDialer.Dial("tcp", uri.Host)
+ if err != nil {
+ return nil, err
+ }
+ return conn, nil
+ }
+ case "ssl":
+ fallthrough
+ case "tls":
+ fallthrough
+ case "tcps":
+ allProxy := os.Getenv("all_proxy")
+ if len(allProxy) == 0 {
+ conn, err := tls.DialWithDialer(&net.Dialer{Timeout: timeout}, "tcp", uri.Host, tlsc)
+ if err != nil {
+ return nil, err
+ }
+ return conn, nil
+ } else {
+ proxyDialer := proxy.FromEnvironment()
+
+ conn, err := proxyDialer.Dial("tcp", uri.Host)
+ if err != nil {
+ return nil, err
+ }
+
+ tlsConn := tls.Client(conn, tlsc)
+
+ err = tlsConn.Handshake()
+ if err != nil {
+ conn.Close()
+ return nil, err
+ }
+
+ return tlsConn, nil
+ }
+ }
+ return nil, errors.New("Unknown protocol")
+}
+
+// actually read incoming messages off the wire
+// send Message object into ibound channel
+func incoming(c *client) {
+ var err error
+ var cp packets.ControlPacket
+
+ defer c.workers.Done()
+
+ DEBUG.Println(NET, "incoming started")
+
+ for {
+ if cp, err = packets.ReadPacket(c.conn); err != nil {
+ break
+ }
+ DEBUG.Println(NET, "Received Message")
+ select {
+ case c.ibound <- cp:
+ // Notify keepalive logic that we recently received a packet
+ if c.options.KeepAlive != 0 {
+ c.packetResp.Broadcast()
+ }
+ case <-c.stop:
+ // This avoids a deadlock should a message arrive while shutting down.
+ // In that case the "reader" of c.ibound might already be gone
+ WARN.Println(NET, "incoming dropped a received message during shutdown")
+ break
+ }
+ }
+ // We received an error on read.
+ // If disconnect is in progress, swallow error and return
+ select {
+ case <-c.stop:
+ DEBUG.Println(NET, "incoming stopped")
+ return
+ // Not trying to disconnect, send the error to the errors channel
+ default:
+ ERROR.Println(NET, "incoming stopped with error", err)
+ signalError(c.errors, err)
+ return
+ }
+}
+
+// receive a Message object on obound, and then
+// actually send outgoing message to the wire
+func outgoing(c *client) {
+ defer c.workers.Done()
+ DEBUG.Println(NET, "outgoing started")
+
+ for {
+ DEBUG.Println(NET, "outgoing waiting for an outbound message")
+ select {
+ case <-c.stop:
+ DEBUG.Println(NET, "outgoing stopped")
+ return
+ case pub := <-c.obound:
+ msg := pub.p.(*packets.PublishPacket)
+
+ if c.options.WriteTimeout > 0 {
+ c.conn.SetWriteDeadline(time.Now().Add(c.options.WriteTimeout))
+ }
+
+ if err := msg.Write(c.conn); err != nil {
+ ERROR.Println(NET, "outgoing stopped with error", err)
+ signalError(c.errors, err)
+ return
+ }
+
+ if c.options.WriteTimeout > 0 {
+ // If we successfully wrote, we don't want the timeout to happen during an idle period
+ // so we reset it to infinite.
+ c.conn.SetWriteDeadline(time.Time{})
+ }
+
+ if msg.Qos == 0 {
+ pub.t.flowComplete()
+ }
+ DEBUG.Println(NET, "obound wrote msg, id:", msg.MessageID)
+ case msg := <-c.oboundP:
+ switch msg.p.(type) {
+ case *packets.SubscribePacket:
+ msg.p.(*packets.SubscribePacket).MessageID = c.getID(msg.t)
+ case *packets.UnsubscribePacket:
+ msg.p.(*packets.UnsubscribePacket).MessageID = c.getID(msg.t)
+ }
+ DEBUG.Println(NET, "obound priority msg to write, type", reflect.TypeOf(msg.p))
+ if err := msg.p.Write(c.conn); err != nil {
+ ERROR.Println(NET, "outgoing stopped with error", err)
+ signalError(c.errors, err)
+ return
+ }
+ switch msg.p.(type) {
+ case *packets.DisconnectPacket:
+ msg.t.(*DisconnectToken).flowComplete()
+ DEBUG.Println(NET, "outbound wrote disconnect, stopping")
+ return
+ }
+ }
+ // Reset ping timer after sending control packet.
+ if c.options.KeepAlive != 0 {
+ c.keepaliveReset.Broadcast()
+ }
+ }
+}
+
+// receive Message objects on ibound
+// store messages if necessary
+// send replies on obound
+// delete messages from store if necessary
+func alllogic(c *client) {
+ defer c.workers.Done()
+ DEBUG.Println(NET, "logic started")
+
+ for {
+ DEBUG.Println(NET, "logic waiting for msg on ibound")
+
+ select {
+ case msg := <-c.ibound:
+ DEBUG.Println(NET, "logic got msg on ibound")
+ persistInbound(c.persist, msg)
+ switch m := msg.(type) {
+ case *packets.PingrespPacket:
+ DEBUG.Println(NET, "received pingresp")
+ c.pingResp.Broadcast()
+ case *packets.SubackPacket:
+ DEBUG.Println(NET, "received suback, id:", m.MessageID)
+ token := c.getToken(m.MessageID)
+ switch t := token.(type) {
+ case *SubscribeToken:
+ DEBUG.Println(NET, "granted qoss", m.ReturnCodes)
+ for i, qos := range m.ReturnCodes {
+ t.subResult[t.subs[i]] = qos
+ }
+ }
+ token.flowComplete()
+ c.freeID(m.MessageID)
+ case *packets.UnsubackPacket:
+ DEBUG.Println(NET, "received unsuback, id:", m.MessageID)
+ c.getToken(m.MessageID).flowComplete()
+ c.freeID(m.MessageID)
+ case *packets.PublishPacket:
+ DEBUG.Println(NET, "received publish, msgId:", m.MessageID)
+ DEBUG.Println(NET, "putting msg on onPubChan")
+ switch m.Qos {
+ case 2:
+ c.incomingPubChan <- m
+ DEBUG.Println(NET, "done putting msg on incomingPubChan")
+ pr := packets.NewControlPacket(packets.Pubrec).(*packets.PubrecPacket)
+ pr.MessageID = m.MessageID
+ DEBUG.Println(NET, "putting pubrec msg on obound")
+ select {
+ case c.oboundP <- &PacketAndToken{p: pr, t: nil}:
+ case <-c.stop:
+ }
+ DEBUG.Println(NET, "done putting pubrec msg on obound")
+ case 1:
+ c.incomingPubChan <- m
+ DEBUG.Println(NET, "done putting msg on incomingPubChan")
+ pa := packets.NewControlPacket(packets.Puback).(*packets.PubackPacket)
+ pa.MessageID = m.MessageID
+ DEBUG.Println(NET, "putting puback msg on obound")
+ select {
+ case c.oboundP <- &PacketAndToken{p: pa, t: nil}:
+ case <-c.stop:
+ }
+ DEBUG.Println(NET, "done putting puback msg on obound")
+ case 0:
+ select {
+ case c.incomingPubChan <- m:
+ case <-c.stop:
+ }
+ DEBUG.Println(NET, "done putting msg on incomingPubChan")
+ }
+ case *packets.PubackPacket:
+ DEBUG.Println(NET, "received puback, id:", m.MessageID)
+ // c.receipts.get(msg.MsgId()) <- Receipt{}
+ // c.receipts.end(msg.MsgId())
+ c.getToken(m.MessageID).flowComplete()
+ c.freeID(m.MessageID)
+ case *packets.PubrecPacket:
+ DEBUG.Println(NET, "received pubrec, id:", m.MessageID)
+ prel := packets.NewControlPacket(packets.Pubrel).(*packets.PubrelPacket)
+ prel.MessageID = m.MessageID
+ select {
+ case c.oboundP <- &PacketAndToken{p: prel, t: nil}:
+ case <-c.stop:
+ }
+ case *packets.PubrelPacket:
+ DEBUG.Println(NET, "received pubrel, id:", m.MessageID)
+ pc := packets.NewControlPacket(packets.Pubcomp).(*packets.PubcompPacket)
+ pc.MessageID = m.MessageID
+ select {
+ case c.oboundP <- &PacketAndToken{p: pc, t: nil}:
+ case <-c.stop:
+ }
+ case *packets.PubcompPacket:
+ DEBUG.Println(NET, "received pubcomp, id:", m.MessageID)
+ c.getToken(m.MessageID).flowComplete()
+ c.freeID(m.MessageID)
+ }
+ case <-c.stop:
+ WARN.Println(NET, "logic stopped")
+ return
+ }
+ }
+}
+
+func errorWatch(c *client) {
+ select {
+ case <-c.stop:
+ WARN.Println(NET, "errorWatch stopped")
+ return
+ case err := <-c.errors:
+ ERROR.Println(NET, "error triggered, stopping")
+ go c.internalConnLost(err)
+ return
+ }
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/notice.html b/vendor/github.com/eclipse/paho.mqtt.golang/notice.html
new file mode 100644
index 00000000..f19c483b
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/notice.html
@@ -0,0 +1,108 @@
+
+
+
+
+
+Eclipse Foundation Software User Agreement
+
+
+
+Eclipse Foundation Software User Agreement
+February 1, 2011
+
+Usage Of Content
+
+THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
+ (COLLECTIVELY "CONTENT"). USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND
+ CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE
+ OF THE CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR
+ NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND
+ CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.
+
+Applicable Licenses
+
+Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 1.0
+ ("EPL"). A copy of the EPL is provided with this Content and is also available at http://www.eclipse.org/legal/epl-v10.html.
+ For purposes of the EPL, "Program" will mean the Content.
+
+Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse Foundation source code
+ repository ("Repository") in software modules ("Modules") and made available as downloadable archives ("Downloads").
+
+
+ - Content may be structured and packaged into modules to facilitate delivering, extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"), plug-in fragments ("Fragments"), and features ("Features").
+ - Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java™ ARchive) in a directory named "plugins".
+ - A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material. Each Feature may be packaged as a sub-directory in a directory named "features". Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of the Plug-ins
+ and/or Fragments associated with that Feature.
+ - Features may also include other Features ("Included Features"). Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of Included Features.
+
+
+The terms and conditions governing Plug-ins and Fragments should be contained in files named "about.html" ("Abouts"). The terms and conditions governing Features and
+Included Features should be contained in files named "license.html" ("Feature Licenses"). Abouts and Feature Licenses may be located in any directory of a Download or Module
+including, but not limited to the following locations:
+
+
+ - The top-level (root) directory
+ - Plug-in and Fragment directories
+ - Inside Plug-ins and Fragments packaged as JARs
+ - Sub-directories of the directory named "src" of certain Plug-ins
+ - Feature directories
+
+
+Note: if a Feature made available by the Eclipse Foundation is installed using the Provisioning Technology (as defined below), you must agree to a license ("Feature Update License") during the
+installation process. If the Feature contains Included Features, the Feature Update License should either provide you with the terms and conditions governing the Included Features or
+inform you where you can locate them. Feature Update Licenses may be found in the "license" property of files named "feature.properties" found within a Feature.
+Such Abouts, Feature Licenses, and Feature Update Licenses contain the terms and conditions (or references to such terms and conditions) that govern your use of the associated Content in
+that directory.
+
+THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE
+OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):
+
+
+
+IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License is provided, please
+contact the Eclipse Foundation to determine what terms and conditions govern that particular Content.
+
+
+Use of Provisioning Technology
+
+The Eclipse Foundation makes available provisioning software, examples of which include, but are not limited to, p2 and the Eclipse
+ Update Manager ("Provisioning Technology") for the purpose of allowing users to install software, documentation, information and/or
+ other materials (collectively "Installable Software"). This capability is provided with the intent of allowing such users to
+ install, extend and update Eclipse-based products. Information about packaging Installable Software is available at http://eclipse.org/equinox/p2/repository_packaging.html
+ ("Specification").
+
+You may use Provisioning Technology to allow other parties to install Installable Software. You shall be responsible for enabling the
+ applicable license agreements relating to the Installable Software to be presented to, and accepted by, the users of the Provisioning Technology
+ in accordance with the Specification. By using Provisioning Technology in such a manner and making it available in accordance with the
+ Specification, you further acknowledge your agreement to, and the acquisition of all necessary rights to permit the following:
+
+
+ - A series of actions may occur ("Provisioning Process") in which a user may execute the Provisioning Technology
+ on a machine ("Target Machine") with the intent of installing, extending or updating the functionality of an Eclipse-based
+ product.
+ - During the Provisioning Process, the Provisioning Technology may cause third party Installable Software or a portion thereof to be
+ accessed and copied to the Target Machine.
+ - Pursuant to the Specification, you will provide to the user the terms and conditions that govern the use of the Installable
+ Software ("Installable Software Agreement") and such Installable Software Agreement shall be accessed from the Target
+ Machine in accordance with the Specification. Such Installable Software Agreement must inform the user of the terms and conditions that govern
+ the Installable Software and must solicit acceptance by the end user in the manner prescribed in such Installable Software Agreement. Upon such
+ indication of agreement by the user, the provisioning Technology will complete installation of the Installable Software.
+
+
+Cryptography
+
+Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to
+ another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import,
+ possession, or use, and re-export of encryption software, to see if this is permitted.
+
+Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.
+
+
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/oops.go b/vendor/github.com/eclipse/paho.mqtt.golang/oops.go
new file mode 100644
index 00000000..39630d7f
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/oops.go
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+package mqtt
+
+func chkerr(e error) {
+ if e != nil {
+ panic(e)
+ }
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/options.go b/vendor/github.com/eclipse/paho.mqtt.golang/options.go
new file mode 100644
index 00000000..956772c4
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/options.go
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+package mqtt
+
+import (
+ "crypto/tls"
+ "net/url"
+ "time"
+)
+
+// MessageHandler is a callback type which can be set to be
+// executed upon the arrival of messages published to topics
+// to which the client is subscribed.
+type MessageHandler func(Client, Message)
+
+// ConnectionLostHandler is a callback type which can be set to be
+// executed upon an unintended disconnection from the MQTT broker.
+// Disconnects caused by calling Disconnect or ForceDisconnect will
+// not cause an OnConnectionLost callback to execute.
+type ConnectionLostHandler func(Client, error)
+
+// OnConnectHandler is a callback that is called when the client
+// state changes from unconnected/disconnected to connected. Both
+// at initial connection and on reconnection
+type OnConnectHandler func(Client)
+
+// ClientOptions contains configurable options for an Client.
+type ClientOptions struct {
+ Servers []*url.URL
+ ClientID string
+ Username string
+ Password string
+ CleanSession bool
+ Order bool
+ WillEnabled bool
+ WillTopic string
+ WillPayload []byte
+ WillQos byte
+ WillRetained bool
+ ProtocolVersion uint
+ protocolVersionExplicit bool
+ TLSConfig tls.Config
+ KeepAlive time.Duration
+ PingTimeout time.Duration
+ ConnectTimeout time.Duration
+ MaxReconnectInterval time.Duration
+ AutoReconnect bool
+ Store Store
+ DefaultPublishHandler MessageHandler
+ OnConnect OnConnectHandler
+ OnConnectionLost ConnectionLostHandler
+ WriteTimeout time.Duration
+ MessageChannelDepth uint
+}
+
+// NewClientOptions will create a new ClientClientOptions type with some
+// default values.
+// Port: 1883
+// CleanSession: True
+// Order: True
+// KeepAlive: 30 (seconds)
+// ConnectTimeout: 30 (seconds)
+// MaxReconnectInterval 10 (minutes)
+// AutoReconnect: True
+func NewClientOptions() *ClientOptions {
+ o := &ClientOptions{
+ Servers: nil,
+ ClientID: "",
+ Username: "",
+ Password: "",
+ CleanSession: true,
+ Order: true,
+ WillEnabled: false,
+ WillTopic: "",
+ WillPayload: nil,
+ WillQos: 0,
+ WillRetained: false,
+ ProtocolVersion: 0,
+ protocolVersionExplicit: false,
+ TLSConfig: tls.Config{},
+ KeepAlive: 30 * time.Second,
+ PingTimeout: 10 * time.Second,
+ ConnectTimeout: 30 * time.Second,
+ MaxReconnectInterval: 10 * time.Minute,
+ AutoReconnect: true,
+ Store: nil,
+ OnConnect: nil,
+ OnConnectionLost: DefaultConnectionLostHandler,
+ WriteTimeout: 0, // 0 represents timeout disabled
+ MessageChannelDepth: 100,
+ }
+ return o
+}
+
+// AddBroker adds a broker URI to the list of brokers to be used. The format should be
+// scheme://host:port
+// Where "scheme" is one of "tcp", "ssl", or "ws", "host" is the ip-address (or hostname)
+// and "port" is the port on which the broker is accepting connections.
+func (o *ClientOptions) AddBroker(server string) *ClientOptions {
+ brokerURI, err := url.Parse(server)
+ if err == nil {
+ o.Servers = append(o.Servers, brokerURI)
+ }
+ return o
+}
+
+// SetClientID will set the client id to be used by this client when
+// connecting to the MQTT broker. According to the MQTT v3.1 specification,
+// a client id mus be no longer than 23 characters.
+func (o *ClientOptions) SetClientID(id string) *ClientOptions {
+ o.ClientID = id
+ return o
+}
+
+// SetUsername will set the username to be used by this client when connecting
+// to the MQTT broker. Note: without the use of SSL/TLS, this information will
+// be sent in plaintext accross the wire.
+func (o *ClientOptions) SetUsername(u string) *ClientOptions {
+ o.Username = u
+ return o
+}
+
+// SetPassword will set the password to be used by this client when connecting
+// to the MQTT broker. Note: without the use of SSL/TLS, this information will
+// be sent in plaintext accross the wire.
+func (o *ClientOptions) SetPassword(p string) *ClientOptions {
+ o.Password = p
+ return o
+}
+
+// SetCleanSession will set the "clean session" flag in the connect message
+// when this client connects to an MQTT broker. By setting this flag, you are
+// indicating that no messages saved by the broker for this client should be
+// delivered. Any messages that were going to be sent by this client before
+// diconnecting previously but didn't will not be sent upon connecting to the
+// broker.
+func (o *ClientOptions) SetCleanSession(clean bool) *ClientOptions {
+ o.CleanSession = clean
+ return o
+}
+
+// SetOrderMatters will set the message routing to guarantee order within
+// each QoS level. By default, this value is true. If set to false,
+// this flag indicates that messages can be delivered asynchronously
+// from the client to the application and possibly arrive out of order.
+func (o *ClientOptions) SetOrderMatters(order bool) *ClientOptions {
+ o.Order = order
+ return o
+}
+
+// SetTLSConfig will set an SSL/TLS configuration to be used when connecting
+// to an MQTT broker. Please read the official Go documentation for more
+// information.
+func (o *ClientOptions) SetTLSConfig(t *tls.Config) *ClientOptions {
+ o.TLSConfig = *t
+ return o
+}
+
+// SetStore will set the implementation of the Store interface
+// used to provide message persistence in cases where QoS levels
+// QoS_ONE or QoS_TWO are used. If no store is provided, then the
+// client will use MemoryStore by default.
+func (o *ClientOptions) SetStore(s Store) *ClientOptions {
+ o.Store = s
+ return o
+}
+
+// SetKeepAlive will set the amount of time (in seconds) that the client
+// should wait before sending a PING request to the broker. This will
+// allow the client to know that a connection has not been lost with the
+// server.
+func (o *ClientOptions) SetKeepAlive(k time.Duration) *ClientOptions {
+ o.KeepAlive = k
+ return o
+}
+
+// SetPingTimeout will set the amount of time (in seconds) that the client
+// will wait after sending a PING request to the broker, before deciding
+// that the connection has been lost. Default is 10 seconds.
+func (o *ClientOptions) SetPingTimeout(k time.Duration) *ClientOptions {
+ o.PingTimeout = k
+ return o
+}
+
+// SetProtocolVersion sets the MQTT version to be used to connect to the
+// broker. Legitimate values are currently 3 - MQTT 3.1 or 4 - MQTT 3.1.1
+func (o *ClientOptions) SetProtocolVersion(pv uint) *ClientOptions {
+ if pv >= 3 && pv <= 4 {
+ o.ProtocolVersion = pv
+ o.protocolVersionExplicit = true
+ }
+ return o
+}
+
+// UnsetWill will cause any set will message to be disregarded.
+func (o *ClientOptions) UnsetWill() *ClientOptions {
+ o.WillEnabled = false
+ return o
+}
+
+// SetWill accepts a string will message to be set. When the client connects,
+// it will give this will message to the broker, which will then publish the
+// provided payload (the will) to any clients that are subscribed to the provided
+// topic.
+func (o *ClientOptions) SetWill(topic string, payload string, qos byte, retained bool) *ClientOptions {
+ o.SetBinaryWill(topic, []byte(payload), qos, retained)
+ return o
+}
+
+// SetBinaryWill accepts a []byte will message to be set. When the client connects,
+// it will give this will message to the broker, which will then publish the
+// provided payload (the will) to any clients that are subscribed to the provided
+// topic.
+func (o *ClientOptions) SetBinaryWill(topic string, payload []byte, qos byte, retained bool) *ClientOptions {
+ o.WillEnabled = true
+ o.WillTopic = topic
+ o.WillPayload = payload
+ o.WillQos = qos
+ o.WillRetained = retained
+ return o
+}
+
+// SetDefaultPublishHandler sets the MessageHandler that will be called when a message
+// is received that does not match any known subscriptions.
+func (o *ClientOptions) SetDefaultPublishHandler(defaultHandler MessageHandler) *ClientOptions {
+ o.DefaultPublishHandler = defaultHandler
+ return o
+}
+
+// SetOnConnectHandler sets the function to be called when the client is connected. Both
+// at initial connection time and upon automatic reconnect.
+func (o *ClientOptions) SetOnConnectHandler(onConn OnConnectHandler) *ClientOptions {
+ o.OnConnect = onConn
+ return o
+}
+
+// SetConnectionLostHandler will set the OnConnectionLost callback to be executed
+// in the case where the client unexpectedly loses connection with the MQTT broker.
+func (o *ClientOptions) SetConnectionLostHandler(onLost ConnectionLostHandler) *ClientOptions {
+ o.OnConnectionLost = onLost
+ return o
+}
+
+// SetWriteTimeout puts a limit on how long a mqtt publish should block until it unblocks with a
+// timeout error. A duration of 0 never times out. Default 30 seconds
+func (o *ClientOptions) SetWriteTimeout(t time.Duration) *ClientOptions {
+ o.WriteTimeout = t
+ return o
+}
+
+// SetConnectTimeout limits how long the client will wait when trying to open a connection
+// to an MQTT server before timeing out and erroring the attempt. A duration of 0 never times out.
+// Default 30 seconds. Currently only operational on TCP/TLS connections.
+func (o *ClientOptions) SetConnectTimeout(t time.Duration) *ClientOptions {
+ o.ConnectTimeout = t
+ return o
+}
+
+// SetMaxReconnectInterval sets the maximum time that will be waited between reconnection attempts
+// when connection is lost
+func (o *ClientOptions) SetMaxReconnectInterval(t time.Duration) *ClientOptions {
+ o.MaxReconnectInterval = t
+ return o
+}
+
+// SetAutoReconnect sets whether the automatic reconnection logic should be used
+// when the connection is lost, even if disabled the ConnectionLostHandler is still
+// called
+func (o *ClientOptions) SetAutoReconnect(a bool) *ClientOptions {
+ o.AutoReconnect = a
+ return o
+}
+
+// SetMessageChannelDepth sets the size of the internal queue that holds messages while the
+// client is temporairily offline, allowing the application to publish when the client is
+// reconnecting. This setting is only valid if AutoReconnect is set to true, it is otherwise
+// ignored.
+func (o *ClientOptions) SetMessageChannelDepth(s uint) *ClientOptions {
+ o.MessageChannelDepth = s
+ return o
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/options_reader.go b/vendor/github.com/eclipse/paho.mqtt.golang/options_reader.go
new file mode 100644
index 00000000..aab6e453
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/options_reader.go
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+package mqtt
+
+import (
+ "crypto/tls"
+ "net/url"
+ "time"
+)
+
+// ClientOptionsReader provides an interface for reading ClientOptions after the client has been initialized.
+type ClientOptionsReader struct {
+ options *ClientOptions
+}
+
+//Servers returns a slice of the servers defined in the clientoptions
+func (r *ClientOptionsReader) Servers() []*url.URL {
+ s := make([]*url.URL, len(r.options.Servers))
+
+ for i, u := range r.options.Servers {
+ nu := *u
+ s[i] = &nu
+ }
+
+ return s
+}
+
+//ClientID returns the set client id
+func (r *ClientOptionsReader) ClientID() string {
+ s := r.options.ClientID
+ return s
+}
+
+//Username returns the set username
+func (r *ClientOptionsReader) Username() string {
+ s := r.options.Username
+ return s
+}
+
+//Password returns the set password
+func (r *ClientOptionsReader) Password() string {
+ s := r.options.Password
+ return s
+}
+
+//CleanSession returns whether Cleansession is set
+func (r *ClientOptionsReader) CleanSession() bool {
+ s := r.options.CleanSession
+ return s
+}
+
+func (r *ClientOptionsReader) Order() bool {
+ s := r.options.Order
+ return s
+}
+
+func (r *ClientOptionsReader) WillEnabled() bool {
+ s := r.options.WillEnabled
+ return s
+}
+
+func (r *ClientOptionsReader) WillTopic() string {
+ s := r.options.WillTopic
+ return s
+}
+
+func (r *ClientOptionsReader) WillPayload() []byte {
+ s := r.options.WillPayload
+ return s
+}
+
+func (r *ClientOptionsReader) WillQos() byte {
+ s := r.options.WillQos
+ return s
+}
+
+func (r *ClientOptionsReader) WillRetained() bool {
+ s := r.options.WillRetained
+ return s
+}
+
+func (r *ClientOptionsReader) ProtocolVersion() uint {
+ s := r.options.ProtocolVersion
+ return s
+}
+
+func (r *ClientOptionsReader) TLSConfig() tls.Config {
+ s := r.options.TLSConfig
+ return s
+}
+
+func (r *ClientOptionsReader) KeepAlive() time.Duration {
+ s := r.options.KeepAlive
+ return s
+}
+
+func (r *ClientOptionsReader) PingTimeout() time.Duration {
+ s := r.options.PingTimeout
+ return s
+}
+
+func (r *ClientOptionsReader) ConnectTimeout() time.Duration {
+ s := r.options.ConnectTimeout
+ return s
+}
+
+func (r *ClientOptionsReader) MaxReconnectInterval() time.Duration {
+ s := r.options.MaxReconnectInterval
+ return s
+}
+
+func (r *ClientOptionsReader) AutoReconnect() bool {
+ s := r.options.AutoReconnect
+ return s
+}
+
+func (r *ClientOptionsReader) WriteTimeout() time.Duration {
+ s := r.options.WriteTimeout
+ return s
+}
+
+func (r *ClientOptionsReader) MessageChannelDepth() uint {
+ s := r.options.MessageChannelDepth
+ return s
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/connack.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/connack.go
new file mode 100644
index 00000000..a512ace0
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/connack.go
@@ -0,0 +1,51 @@
+package packets
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+)
+
+//ConnackPacket is an internal representation of the fields of the
+//Connack MQTT packet
+type ConnackPacket struct {
+ FixedHeader
+ SessionPresent bool
+ ReturnCode byte
+}
+
+func (ca *ConnackPacket) String() string {
+ str := fmt.Sprintf("%s", ca.FixedHeader)
+ str += " "
+ str += fmt.Sprintf("sessionpresent: %t returncode: %d", ca.SessionPresent, ca.ReturnCode)
+ return str
+}
+
+func (ca *ConnackPacket) Write(w io.Writer) error {
+ var body bytes.Buffer
+ var err error
+
+ body.WriteByte(boolToByte(ca.SessionPresent))
+ body.WriteByte(ca.ReturnCode)
+ ca.FixedHeader.RemainingLength = 2
+ packet := ca.FixedHeader.pack()
+ packet.Write(body.Bytes())
+ _, err = packet.WriteTo(w)
+
+ return err
+}
+
+//Unpack decodes the details of a ControlPacket after the fixed
+//header has been read
+func (ca *ConnackPacket) Unpack(b io.Reader) error {
+ ca.SessionPresent = 1&decodeByte(b) > 0
+ ca.ReturnCode = decodeByte(b)
+
+ return nil
+}
+
+//Details returns a Details struct containing the Qos and
+//MessageID of this ControlPacket
+func (ca *ConnackPacket) Details() Details {
+ return Details{Qos: 0, MessageID: 0}
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/connect.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/connect.go
new file mode 100644
index 00000000..3694464a
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/connect.go
@@ -0,0 +1,126 @@
+package packets
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+)
+
+//ConnectPacket is an internal representation of the fields of the
+//Connect MQTT packet
+type ConnectPacket struct {
+ FixedHeader
+ ProtocolName string
+ ProtocolVersion byte
+ CleanSession bool
+ WillFlag bool
+ WillQos byte
+ WillRetain bool
+ UsernameFlag bool
+ PasswordFlag bool
+ ReservedBit byte
+ Keepalive uint16
+
+ ClientIdentifier string
+ WillTopic string
+ WillMessage []byte
+ Username string
+ Password []byte
+}
+
+func (c *ConnectPacket) String() string {
+ str := fmt.Sprintf("%s", c.FixedHeader)
+ str += " "
+ str += fmt.Sprintf("protocolversion: %d protocolname: %s cleansession: %t willflag: %t WillQos: %d WillRetain: %t Usernameflag: %t Passwordflag: %t keepalive: %d clientId: %s willtopic: %s willmessage: %s Username: %s Password: %s", c.ProtocolVersion, c.ProtocolName, c.CleanSession, c.WillFlag, c.WillQos, c.WillRetain, c.UsernameFlag, c.PasswordFlag, c.Keepalive, c.ClientIdentifier, c.WillTopic, c.WillMessage, c.Username, c.Password)
+ return str
+}
+
+func (c *ConnectPacket) Write(w io.Writer) error {
+ var body bytes.Buffer
+ var err error
+
+ body.Write(encodeString(c.ProtocolName))
+ body.WriteByte(c.ProtocolVersion)
+ body.WriteByte(boolToByte(c.CleanSession)<<1 | boolToByte(c.WillFlag)<<2 | c.WillQos<<3 | boolToByte(c.WillRetain)<<5 | boolToByte(c.PasswordFlag)<<6 | boolToByte(c.UsernameFlag)<<7)
+ body.Write(encodeUint16(c.Keepalive))
+ body.Write(encodeString(c.ClientIdentifier))
+ if c.WillFlag {
+ body.Write(encodeString(c.WillTopic))
+ body.Write(encodeBytes(c.WillMessage))
+ }
+ if c.UsernameFlag {
+ body.Write(encodeString(c.Username))
+ }
+ if c.PasswordFlag {
+ body.Write(encodeBytes(c.Password))
+ }
+ c.FixedHeader.RemainingLength = body.Len()
+ packet := c.FixedHeader.pack()
+ packet.Write(body.Bytes())
+ _, err = packet.WriteTo(w)
+
+ return err
+}
+
+//Unpack decodes the details of a ControlPacket after the fixed
+//header has been read
+func (c *ConnectPacket) Unpack(b io.Reader) error {
+ c.ProtocolName = decodeString(b)
+ c.ProtocolVersion = decodeByte(b)
+ options := decodeByte(b)
+ c.ReservedBit = 1 & options
+ c.CleanSession = 1&(options>>1) > 0
+ c.WillFlag = 1&(options>>2) > 0
+ c.WillQos = 3 & (options >> 3)
+ c.WillRetain = 1&(options>>5) > 0
+ c.PasswordFlag = 1&(options>>6) > 0
+ c.UsernameFlag = 1&(options>>7) > 0
+ c.Keepalive = decodeUint16(b)
+ c.ClientIdentifier = decodeString(b)
+ if c.WillFlag {
+ c.WillTopic = decodeString(b)
+ c.WillMessage = decodeBytes(b)
+ }
+ if c.UsernameFlag {
+ c.Username = decodeString(b)
+ }
+ if c.PasswordFlag {
+ c.Password = decodeBytes(b)
+ }
+
+ return nil
+}
+
+//Validate performs validation of the fields of a Connect packet
+func (c *ConnectPacket) Validate() byte {
+ if c.PasswordFlag && !c.UsernameFlag {
+ return ErrRefusedBadUsernameOrPassword
+ }
+ if c.ReservedBit != 0 {
+ //Bad reserved bit
+ return ErrProtocolViolation
+ }
+ if (c.ProtocolName == "MQIsdp" && c.ProtocolVersion != 3) || (c.ProtocolName == "MQTT" && c.ProtocolVersion != 4) {
+ //Mismatched or unsupported protocol version
+ return ErrRefusedBadProtocolVersion
+ }
+ if c.ProtocolName != "MQIsdp" && c.ProtocolName != "MQTT" {
+ //Bad protocol name
+ return ErrProtocolViolation
+ }
+ if len(c.ClientIdentifier) > 65535 || len(c.Username) > 65535 || len(c.Password) > 65535 {
+ //Bad size field
+ return ErrProtocolViolation
+ }
+ if len(c.ClientIdentifier) == 0 && !c.CleanSession {
+ //Bad client identifier
+ return ErrRefusedIDRejected
+ }
+ return Accepted
+}
+
+//Details returns a Details struct containing the Qos and
+//MessageID of this ControlPacket
+func (c *ConnectPacket) Details() Details {
+ return Details{Qos: 0, MessageID: 0}
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/disconnect.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/disconnect.go
new file mode 100644
index 00000000..e5c18692
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/disconnect.go
@@ -0,0 +1,36 @@
+package packets
+
+import (
+ "fmt"
+ "io"
+)
+
+//DisconnectPacket is an internal representation of the fields of the
+//Disconnect MQTT packet
+type DisconnectPacket struct {
+ FixedHeader
+}
+
+func (d *DisconnectPacket) String() string {
+ str := fmt.Sprintf("%s", d.FixedHeader)
+ return str
+}
+
+func (d *DisconnectPacket) Write(w io.Writer) error {
+ packet := d.FixedHeader.pack()
+ _, err := packet.WriteTo(w)
+
+ return err
+}
+
+//Unpack decodes the details of a ControlPacket after the fixed
+//header has been read
+func (d *DisconnectPacket) Unpack(b io.Reader) error {
+ return nil
+}
+
+//Details returns a Details struct containing the Qos and
+//MessageID of this ControlPacket
+func (d *DisconnectPacket) Details() Details {
+ return Details{Qos: 0, MessageID: 0}
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/packets.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/packets.go
new file mode 100644
index 00000000..cbc194a2
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/packets.go
@@ -0,0 +1,322 @@
+package packets
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+)
+
+//ControlPacket defines the interface for structs intended to hold
+//decoded MQTT packets, either from being read or before being
+//written
+type ControlPacket interface {
+ Write(io.Writer) error
+ Unpack(io.Reader) error
+ String() string
+ Details() Details
+}
+
+//PacketNames maps the constants for each of the MQTT packet types
+//to a string representation of their name.
+var PacketNames = map[uint8]string{
+ 1: "CONNECT",
+ 2: "CONNACK",
+ 3: "PUBLISH",
+ 4: "PUBACK",
+ 5: "PUBREC",
+ 6: "PUBREL",
+ 7: "PUBCOMP",
+ 8: "SUBSCRIBE",
+ 9: "SUBACK",
+ 10: "UNSUBSCRIBE",
+ 11: "UNSUBACK",
+ 12: "PINGREQ",
+ 13: "PINGRESP",
+ 14: "DISCONNECT",
+}
+
+//Below are the constants assigned to each of the MQTT packet types
+const (
+ Connect = 1
+ Connack = 2
+ Publish = 3
+ Puback = 4
+ Pubrec = 5
+ Pubrel = 6
+ Pubcomp = 7
+ Subscribe = 8
+ Suback = 9
+ Unsubscribe = 10
+ Unsuback = 11
+ Pingreq = 12
+ Pingresp = 13
+ Disconnect = 14
+)
+
+//Below are the const definitions for error codes returned by
+//Connect()
+const (
+ Accepted = 0x00
+ ErrRefusedBadProtocolVersion = 0x01
+ ErrRefusedIDRejected = 0x02
+ ErrRefusedServerUnavailable = 0x03
+ ErrRefusedBadUsernameOrPassword = 0x04
+ ErrRefusedNotAuthorised = 0x05
+ ErrNetworkError = 0xFE
+ ErrProtocolViolation = 0xFF
+)
+
+//ConnackReturnCodes is a map of the error codes constants for Connect()
+//to a string representation of the error
+var ConnackReturnCodes = map[uint8]string{
+ 0: "Connection Accepted",
+ 1: "Connection Refused: Bad Protocol Version",
+ 2: "Connection Refused: Client Identifier Rejected",
+ 3: "Connection Refused: Server Unavailable",
+ 4: "Connection Refused: Username or Password in unknown format",
+ 5: "Connection Refused: Not Authorised",
+ 254: "Connection Error",
+ 255: "Connection Refused: Protocol Violation",
+}
+
+//ConnErrors is a map of the errors codes constants for Connect()
+//to a Go error
+var ConnErrors = map[byte]error{
+ Accepted: nil,
+ ErrRefusedBadProtocolVersion: errors.New("Unnacceptable protocol version"),
+ ErrRefusedIDRejected: errors.New("Identifier rejected"),
+ ErrRefusedServerUnavailable: errors.New("Server Unavailable"),
+ ErrRefusedBadUsernameOrPassword: errors.New("Bad user name or password"),
+ ErrRefusedNotAuthorised: errors.New("Not Authorized"),
+ ErrNetworkError: errors.New("Network Error"),
+ ErrProtocolViolation: errors.New("Protocol Violation"),
+}
+
+//ReadPacket takes an instance of an io.Reader (such as net.Conn) and attempts
+//to read an MQTT packet from the stream. It returns a ControlPacket
+//representing the decoded MQTT packet and an error. One of these returns will
+//always be nil, a nil ControlPacket indicating an error occurred.
+func ReadPacket(r io.Reader) (cp ControlPacket, err error) {
+ var fh FixedHeader
+ b := make([]byte, 1)
+
+ _, err = io.ReadFull(r, b)
+ if err != nil {
+ return nil, err
+ }
+ fh.unpack(b[0], r)
+ cp = NewControlPacketWithHeader(fh)
+ if cp == nil {
+ return nil, errors.New("Bad data from client")
+ }
+ packetBytes := make([]byte, fh.RemainingLength)
+ _, err = io.ReadFull(r, packetBytes)
+ if err != nil {
+ return nil, err
+ }
+ err = cp.Unpack(bytes.NewBuffer(packetBytes))
+ return cp, err
+}
+
+//NewControlPacket is used to create a new ControlPacket of the type specified
+//by packetType, this is usually done by reference to the packet type constants
+//defined in packets.go. The newly created ControlPacket is empty and a pointer
+//is returned.
+func NewControlPacket(packetType byte) (cp ControlPacket) {
+ switch packetType {
+ case Connect:
+ cp = &ConnectPacket{FixedHeader: FixedHeader{MessageType: Connect}}
+ case Connack:
+ cp = &ConnackPacket{FixedHeader: FixedHeader{MessageType: Connack}}
+ case Disconnect:
+ cp = &DisconnectPacket{FixedHeader: FixedHeader{MessageType: Disconnect}}
+ case Publish:
+ cp = &PublishPacket{FixedHeader: FixedHeader{MessageType: Publish}}
+ case Puback:
+ cp = &PubackPacket{FixedHeader: FixedHeader{MessageType: Puback}}
+ case Pubrec:
+ cp = &PubrecPacket{FixedHeader: FixedHeader{MessageType: Pubrec}}
+ case Pubrel:
+ cp = &PubrelPacket{FixedHeader: FixedHeader{MessageType: Pubrel, Qos: 1}}
+ case Pubcomp:
+ cp = &PubcompPacket{FixedHeader: FixedHeader{MessageType: Pubcomp}}
+ case Subscribe:
+ cp = &SubscribePacket{FixedHeader: FixedHeader{MessageType: Subscribe, Qos: 1}}
+ case Suback:
+ cp = &SubackPacket{FixedHeader: FixedHeader{MessageType: Suback}}
+ case Unsubscribe:
+ cp = &UnsubscribePacket{FixedHeader: FixedHeader{MessageType: Unsubscribe, Qos: 1}}
+ case Unsuback:
+ cp = &UnsubackPacket{FixedHeader: FixedHeader{MessageType: Unsuback}}
+ case Pingreq:
+ cp = &PingreqPacket{FixedHeader: FixedHeader{MessageType: Pingreq}}
+ case Pingresp:
+ cp = &PingrespPacket{FixedHeader: FixedHeader{MessageType: Pingresp}}
+ default:
+ return nil
+ }
+ return cp
+}
+
+//NewControlPacketWithHeader is used to create a new ControlPacket of the type
+//specified within the FixedHeader that is passed to the function.
+//The newly created ControlPacket is empty and a pointer is returned.
+func NewControlPacketWithHeader(fh FixedHeader) (cp ControlPacket) {
+ switch fh.MessageType {
+ case Connect:
+ cp = &ConnectPacket{FixedHeader: fh}
+ case Connack:
+ cp = &ConnackPacket{FixedHeader: fh}
+ case Disconnect:
+ cp = &DisconnectPacket{FixedHeader: fh}
+ case Publish:
+ cp = &PublishPacket{FixedHeader: fh}
+ case Puback:
+ cp = &PubackPacket{FixedHeader: fh}
+ case Pubrec:
+ cp = &PubrecPacket{FixedHeader: fh}
+ case Pubrel:
+ cp = &PubrelPacket{FixedHeader: fh}
+ case Pubcomp:
+ cp = &PubcompPacket{FixedHeader: fh}
+ case Subscribe:
+ cp = &SubscribePacket{FixedHeader: fh}
+ case Suback:
+ cp = &SubackPacket{FixedHeader: fh}
+ case Unsubscribe:
+ cp = &UnsubscribePacket{FixedHeader: fh}
+ case Unsuback:
+ cp = &UnsubackPacket{FixedHeader: fh}
+ case Pingreq:
+ cp = &PingreqPacket{FixedHeader: fh}
+ case Pingresp:
+ cp = &PingrespPacket{FixedHeader: fh}
+ default:
+ return nil
+ }
+ return cp
+}
+
+//Details struct returned by the Details() function called on
+//ControlPackets to present details of the Qos and MessageID
+//of the ControlPacket
+type Details struct {
+ Qos byte
+ MessageID uint16
+}
+
+//FixedHeader is a struct to hold the decoded information from
+//the fixed header of an MQTT ControlPacket
+type FixedHeader struct {
+ MessageType byte
+ Dup bool
+ Qos byte
+ Retain bool
+ RemainingLength int
+}
+
+func (fh FixedHeader) String() string {
+ return fmt.Sprintf("%s: dup: %t qos: %d retain: %t rLength: %d", PacketNames[fh.MessageType], fh.Dup, fh.Qos, fh.Retain, fh.RemainingLength)
+}
+
+func boolToByte(b bool) byte {
+ switch b {
+ case true:
+ return 1
+ default:
+ return 0
+ }
+}
+
+func (fh *FixedHeader) pack() bytes.Buffer {
+ var header bytes.Buffer
+ header.WriteByte(fh.MessageType<<4 | boolToByte(fh.Dup)<<3 | fh.Qos<<1 | boolToByte(fh.Retain))
+ header.Write(encodeLength(fh.RemainingLength))
+ return header
+}
+
+func (fh *FixedHeader) unpack(typeAndFlags byte, r io.Reader) {
+ fh.MessageType = typeAndFlags >> 4
+ fh.Dup = (typeAndFlags>>3)&0x01 > 0
+ fh.Qos = (typeAndFlags >> 1) & 0x03
+ fh.Retain = typeAndFlags&0x01 > 0
+ fh.RemainingLength = decodeLength(r)
+}
+
+func decodeByte(b io.Reader) byte {
+ num := make([]byte, 1)
+ b.Read(num)
+ return num[0]
+}
+
+func decodeUint16(b io.Reader) uint16 {
+ num := make([]byte, 2)
+ b.Read(num)
+ return binary.BigEndian.Uint16(num)
+}
+
+func encodeUint16(num uint16) []byte {
+ bytes := make([]byte, 2)
+ binary.BigEndian.PutUint16(bytes, num)
+ return bytes
+}
+
+func encodeString(field string) []byte {
+ fieldLength := make([]byte, 2)
+ binary.BigEndian.PutUint16(fieldLength, uint16(len(field)))
+ return append(fieldLength, []byte(field)...)
+}
+
+func decodeString(b io.Reader) string {
+ fieldLength := decodeUint16(b)
+ field := make([]byte, fieldLength)
+ b.Read(field)
+ return string(field)
+}
+
+func decodeBytes(b io.Reader) []byte {
+ fieldLength := decodeUint16(b)
+ field := make([]byte, fieldLength)
+ b.Read(field)
+ return field
+}
+
+func encodeBytes(field []byte) []byte {
+ fieldLength := make([]byte, 2)
+ binary.BigEndian.PutUint16(fieldLength, uint16(len(field)))
+ return append(fieldLength, field...)
+}
+
+func encodeLength(length int) []byte {
+ var encLength []byte
+ for {
+ digit := byte(length % 128)
+ length /= 128
+ if length > 0 {
+ digit |= 0x80
+ }
+ encLength = append(encLength, digit)
+ if length == 0 {
+ break
+ }
+ }
+ return encLength
+}
+
+func decodeLength(r io.Reader) int {
+ var rLength uint32
+ var multiplier uint32
+ b := make([]byte, 1)
+ for multiplier < 27 { //fix: Infinite '(digit & 128) == 1' will cause the dead loop
+ io.ReadFull(r, b)
+ digit := b[0]
+ rLength |= uint32(digit&127) << multiplier
+ if (digit & 128) == 0 {
+ break
+ }
+ multiplier += 7
+ }
+ return int(rLength)
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/pingreq.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/pingreq.go
new file mode 100644
index 00000000..5c3e88f9
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/pingreq.go
@@ -0,0 +1,36 @@
+package packets
+
+import (
+ "fmt"
+ "io"
+)
+
+//PingreqPacket is an internal representation of the fields of the
+//Pingreq MQTT packet
+type PingreqPacket struct {
+ FixedHeader
+}
+
+func (pr *PingreqPacket) String() string {
+ str := fmt.Sprintf("%s", pr.FixedHeader)
+ return str
+}
+
+func (pr *PingreqPacket) Write(w io.Writer) error {
+ packet := pr.FixedHeader.pack()
+ _, err := packet.WriteTo(w)
+
+ return err
+}
+
+//Unpack decodes the details of a ControlPacket after the fixed
+//header has been read
+func (pr *PingreqPacket) Unpack(b io.Reader) error {
+ return nil
+}
+
+//Details returns a Details struct containing the Qos and
+//MessageID of this ControlPacket
+func (pr *PingreqPacket) Details() Details {
+ return Details{Qos: 0, MessageID: 0}
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/pingresp.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/pingresp.go
new file mode 100644
index 00000000..39ebc001
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/pingresp.go
@@ -0,0 +1,36 @@
+package packets
+
+import (
+ "fmt"
+ "io"
+)
+
+//PingrespPacket is an internal representation of the fields of the
+//Pingresp MQTT packet
+type PingrespPacket struct {
+ FixedHeader
+}
+
+func (pr *PingrespPacket) String() string {
+ str := fmt.Sprintf("%s", pr.FixedHeader)
+ return str
+}
+
+func (pr *PingrespPacket) Write(w io.Writer) error {
+ packet := pr.FixedHeader.pack()
+ _, err := packet.WriteTo(w)
+
+ return err
+}
+
+//Unpack decodes the details of a ControlPacket after the fixed
+//header has been read
+func (pr *PingrespPacket) Unpack(b io.Reader) error {
+ return nil
+}
+
+//Details returns a Details struct containing the Qos and
+//MessageID of this ControlPacket
+func (pr *PingrespPacket) Details() Details {
+ return Details{Qos: 0, MessageID: 0}
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/puback.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/puback.go
new file mode 100644
index 00000000..e30402cd
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/puback.go
@@ -0,0 +1,44 @@
+package packets
+
+import (
+ "fmt"
+ "io"
+)
+
+//PubackPacket is an internal representation of the fields of the
+//Puback MQTT packet
+type PubackPacket struct {
+ FixedHeader
+ MessageID uint16
+}
+
+func (pa *PubackPacket) String() string {
+ str := fmt.Sprintf("%s", pa.FixedHeader)
+ str += " "
+ str += fmt.Sprintf("MessageID: %d", pa.MessageID)
+ return str
+}
+
+func (pa *PubackPacket) Write(w io.Writer) error {
+ var err error
+ pa.FixedHeader.RemainingLength = 2
+ packet := pa.FixedHeader.pack()
+ packet.Write(encodeUint16(pa.MessageID))
+ _, err = packet.WriteTo(w)
+
+ return err
+}
+
+//Unpack decodes the details of a ControlPacket after the fixed
+//header has been read
+func (pa *PubackPacket) Unpack(b io.Reader) error {
+ pa.MessageID = decodeUint16(b)
+
+ return nil
+}
+
+//Details returns a Details struct containing the Qos and
+//MessageID of this ControlPacket
+func (pa *PubackPacket) Details() Details {
+ return Details{Qos: pa.Qos, MessageID: pa.MessageID}
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/pubcomp.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/pubcomp.go
new file mode 100644
index 00000000..fb994ae7
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/pubcomp.go
@@ -0,0 +1,44 @@
+package packets
+
+import (
+ "fmt"
+ "io"
+)
+
+//PubcompPacket is an internal representation of the fields of the
+//Pubcomp MQTT packet
+type PubcompPacket struct {
+ FixedHeader
+ MessageID uint16
+}
+
+func (pc *PubcompPacket) String() string {
+ str := fmt.Sprintf("%s", pc.FixedHeader)
+ str += " "
+ str += fmt.Sprintf("MessageID: %d", pc.MessageID)
+ return str
+}
+
+func (pc *PubcompPacket) Write(w io.Writer) error {
+ var err error
+ pc.FixedHeader.RemainingLength = 2
+ packet := pc.FixedHeader.pack()
+ packet.Write(encodeUint16(pc.MessageID))
+ _, err = packet.WriteTo(w)
+
+ return err
+}
+
+//Unpack decodes the details of a ControlPacket after the fixed
+//header has been read
+func (pc *PubcompPacket) Unpack(b io.Reader) error {
+ pc.MessageID = decodeUint16(b)
+
+ return nil
+}
+
+//Details returns a Details struct containing the Qos and
+//MessageID of this ControlPacket
+func (pc *PubcompPacket) Details() Details {
+ return Details{Qos: pc.Qos, MessageID: pc.MessageID}
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/publish.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/publish.go
new file mode 100644
index 00000000..b660ef4c
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/publish.go
@@ -0,0 +1,80 @@
+package packets
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+)
+
+//PublishPacket is an internal representation of the fields of the
+//Publish MQTT packet
+type PublishPacket struct {
+ FixedHeader
+ TopicName string
+ MessageID uint16
+ Payload []byte
+}
+
+func (p *PublishPacket) String() string {
+ str := fmt.Sprintf("%s", p.FixedHeader)
+ str += " "
+ str += fmt.Sprintf("topicName: %s MessageID: %d", p.TopicName, p.MessageID)
+ str += " "
+ str += fmt.Sprintf("payload: %s", string(p.Payload))
+ return str
+}
+
+func (p *PublishPacket) Write(w io.Writer) error {
+ var body bytes.Buffer
+ var err error
+
+ body.Write(encodeString(p.TopicName))
+ if p.Qos > 0 {
+ body.Write(encodeUint16(p.MessageID))
+ }
+ p.FixedHeader.RemainingLength = body.Len() + len(p.Payload)
+ packet := p.FixedHeader.pack()
+ packet.Write(body.Bytes())
+ packet.Write(p.Payload)
+ _, err = w.Write(packet.Bytes())
+
+ return err
+}
+
+//Unpack decodes the details of a ControlPacket after the fixed
+//header has been read
+func (p *PublishPacket) Unpack(b io.Reader) error {
+ var payloadLength = p.FixedHeader.RemainingLength
+ p.TopicName = decodeString(b)
+ if p.Qos > 0 {
+ p.MessageID = decodeUint16(b)
+ payloadLength -= len(p.TopicName) + 4
+ } else {
+ payloadLength -= len(p.TopicName) + 2
+ }
+ if payloadLength < 0 {
+ return fmt.Errorf("Error upacking publish, payload length < 0")
+ }
+ p.Payload = make([]byte, payloadLength)
+ _, err := b.Read(p.Payload)
+
+ return err
+}
+
+//Copy creates a new PublishPacket with the same topic and payload
+//but an empty fixed header, useful for when you want to deliver
+//a message with different properties such as Qos but the same
+//content
+func (p *PublishPacket) Copy() *PublishPacket {
+ newP := NewControlPacket(Publish).(*PublishPacket)
+ newP.TopicName = p.TopicName
+ newP.Payload = p.Payload
+
+ return newP
+}
+
+//Details returns a Details struct containing the Qos and
+//MessageID of this ControlPacket
+func (p *PublishPacket) Details() Details {
+ return Details{Qos: p.Qos, MessageID: p.MessageID}
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/pubrec.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/pubrec.go
new file mode 100644
index 00000000..9874e641
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/pubrec.go
@@ -0,0 +1,44 @@
+package packets
+
+import (
+ "fmt"
+ "io"
+)
+
+//PubrecPacket is an internal representation of the fields of the
+//Pubrec MQTT packet
+type PubrecPacket struct {
+ FixedHeader
+ MessageID uint16
+}
+
+func (pr *PubrecPacket) String() string {
+ str := fmt.Sprintf("%s", pr.FixedHeader)
+ str += " "
+ str += fmt.Sprintf("MessageID: %d", pr.MessageID)
+ return str
+}
+
+func (pr *PubrecPacket) Write(w io.Writer) error {
+ var err error
+ pr.FixedHeader.RemainingLength = 2
+ packet := pr.FixedHeader.pack()
+ packet.Write(encodeUint16(pr.MessageID))
+ _, err = packet.WriteTo(w)
+
+ return err
+}
+
+//Unpack decodes the details of a ControlPacket after the fixed
+//header has been read
+func (pr *PubrecPacket) Unpack(b io.Reader) error {
+ pr.MessageID = decodeUint16(b)
+
+ return nil
+}
+
+//Details returns a Details struct containing the Qos and
+//MessageID of this ControlPacket
+func (pr *PubrecPacket) Details() Details {
+ return Details{Qos: pr.Qos, MessageID: pr.MessageID}
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/pubrel.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/pubrel.go
new file mode 100644
index 00000000..a7ecce75
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/pubrel.go
@@ -0,0 +1,44 @@
+package packets
+
+import (
+ "fmt"
+ "io"
+)
+
+//PubrelPacket is an internal representation of the fields of the
+//Pubrel MQTT packet
+type PubrelPacket struct {
+ FixedHeader
+ MessageID uint16
+}
+
+func (pr *PubrelPacket) String() string {
+ str := fmt.Sprintf("%s", pr.FixedHeader)
+ str += " "
+ str += fmt.Sprintf("MessageID: %d", pr.MessageID)
+ return str
+}
+
+func (pr *PubrelPacket) Write(w io.Writer) error {
+ var err error
+ pr.FixedHeader.RemainingLength = 2
+ packet := pr.FixedHeader.pack()
+ packet.Write(encodeUint16(pr.MessageID))
+ _, err = packet.WriteTo(w)
+
+ return err
+}
+
+//Unpack decodes the details of a ControlPacket after the fixed
+//header has been read
+func (pr *PubrelPacket) Unpack(b io.Reader) error {
+ pr.MessageID = decodeUint16(b)
+
+ return nil
+}
+
+//Details returns a Details struct containing the Qos and
+//MessageID of this ControlPacket
+func (pr *PubrelPacket) Details() Details {
+ return Details{Qos: pr.Qos, MessageID: pr.MessageID}
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/suback.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/suback.go
new file mode 100644
index 00000000..557a7dbe
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/suback.go
@@ -0,0 +1,52 @@
+package packets
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+)
+
+//SubackPacket is an internal representation of the fields of the
+//Suback MQTT packet
+type SubackPacket struct {
+ FixedHeader
+ MessageID uint16
+ ReturnCodes []byte
+}
+
+func (sa *SubackPacket) String() string {
+ str := fmt.Sprintf("%s", sa.FixedHeader)
+ str += " "
+ str += fmt.Sprintf("MessageID: %d", sa.MessageID)
+ return str
+}
+
+func (sa *SubackPacket) Write(w io.Writer) error {
+ var body bytes.Buffer
+ var err error
+ body.Write(encodeUint16(sa.MessageID))
+ body.Write(sa.ReturnCodes)
+ sa.FixedHeader.RemainingLength = body.Len()
+ packet := sa.FixedHeader.pack()
+ packet.Write(body.Bytes())
+ _, err = packet.WriteTo(w)
+
+ return err
+}
+
+//Unpack decodes the details of a ControlPacket after the fixed
+//header has been read
+func (sa *SubackPacket) Unpack(b io.Reader) error {
+ var qosBuffer bytes.Buffer
+ sa.MessageID = decodeUint16(b)
+ qosBuffer.ReadFrom(b)
+ sa.ReturnCodes = qosBuffer.Bytes()
+
+ return nil
+}
+
+//Details returns a Details struct containing the Qos and
+//MessageID of this ControlPacket
+func (sa *SubackPacket) Details() Details {
+ return Details{Qos: 0, MessageID: sa.MessageID}
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/subscribe.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/subscribe.go
new file mode 100644
index 00000000..c418ef0f
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/subscribe.go
@@ -0,0 +1,62 @@
+package packets
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+)
+
+//SubscribePacket is an internal representation of the fields of the
+//Subscribe MQTT packet
+type SubscribePacket struct {
+ FixedHeader
+ MessageID uint16
+ Topics []string
+ Qoss []byte
+}
+
+func (s *SubscribePacket) String() string {
+ str := fmt.Sprintf("%s", s.FixedHeader)
+ str += " "
+ str += fmt.Sprintf("MessageID: %d topics: %s", s.MessageID, s.Topics)
+ return str
+}
+
+func (s *SubscribePacket) Write(w io.Writer) error {
+ var body bytes.Buffer
+ var err error
+
+ body.Write(encodeUint16(s.MessageID))
+ for i, topic := range s.Topics {
+ body.Write(encodeString(topic))
+ body.WriteByte(s.Qoss[i])
+ }
+ s.FixedHeader.RemainingLength = body.Len()
+ packet := s.FixedHeader.pack()
+ packet.Write(body.Bytes())
+ _, err = packet.WriteTo(w)
+
+ return err
+}
+
+//Unpack decodes the details of a ControlPacket after the fixed
+//header has been read
+func (s *SubscribePacket) Unpack(b io.Reader) error {
+ s.MessageID = decodeUint16(b)
+ payloadLength := s.FixedHeader.RemainingLength - 2
+ for payloadLength > 0 {
+ topic := decodeString(b)
+ s.Topics = append(s.Topics, topic)
+ qos := decodeByte(b)
+ s.Qoss = append(s.Qoss, qos)
+ payloadLength -= 2 + len(topic) + 1 //2 bytes of string length, plus string, plus 1 byte for Qos
+ }
+
+ return nil
+}
+
+//Details returns a Details struct containing the Qos and
+//MessageID of this ControlPacket
+func (s *SubscribePacket) Details() Details {
+ return Details{Qos: 1, MessageID: s.MessageID}
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/unsuback.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/unsuback.go
new file mode 100644
index 00000000..b3b91ce3
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/unsuback.go
@@ -0,0 +1,44 @@
+package packets
+
+import (
+ "fmt"
+ "io"
+)
+
+//UnsubackPacket is an internal representation of the fields of the
+//Unsuback MQTT packet
+type UnsubackPacket struct {
+ FixedHeader
+ MessageID uint16
+}
+
+func (ua *UnsubackPacket) String() string {
+ str := fmt.Sprintf("%s", ua.FixedHeader)
+ str += " "
+ str += fmt.Sprintf("MessageID: %d", ua.MessageID)
+ return str
+}
+
+func (ua *UnsubackPacket) Write(w io.Writer) error {
+ var err error
+ ua.FixedHeader.RemainingLength = 2
+ packet := ua.FixedHeader.pack()
+ packet.Write(encodeUint16(ua.MessageID))
+ _, err = packet.WriteTo(w)
+
+ return err
+}
+
+//Unpack decodes the details of a ControlPacket after the fixed
+//header has been read
+func (ua *UnsubackPacket) Unpack(b io.Reader) error {
+ ua.MessageID = decodeUint16(b)
+
+ return nil
+}
+
+//Details returns a Details struct containing the Qos and
+//MessageID of this ControlPacket
+func (ua *UnsubackPacket) Details() Details {
+ return Details{Qos: 0, MessageID: ua.MessageID}
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/packets/unsubscribe.go b/vendor/github.com/eclipse/paho.mqtt.golang/packets/unsubscribe.go
new file mode 100644
index 00000000..dc6a89ec
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/packets/unsubscribe.go
@@ -0,0 +1,55 @@
+package packets
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+)
+
+//UnsubscribePacket is an internal representation of the fields of the
+//Unsubscribe MQTT packet
+type UnsubscribePacket struct {
+ FixedHeader
+ MessageID uint16
+ Topics []string
+}
+
+func (u *UnsubscribePacket) String() string {
+ str := fmt.Sprintf("%s", u.FixedHeader)
+ str += " "
+ str += fmt.Sprintf("MessageID: %d", u.MessageID)
+ return str
+}
+
+func (u *UnsubscribePacket) Write(w io.Writer) error {
+ var body bytes.Buffer
+ var err error
+ body.Write(encodeUint16(u.MessageID))
+ for _, topic := range u.Topics {
+ body.Write(encodeString(topic))
+ }
+ u.FixedHeader.RemainingLength = body.Len()
+ packet := u.FixedHeader.pack()
+ packet.Write(body.Bytes())
+ _, err = packet.WriteTo(w)
+
+ return err
+}
+
+//Unpack decodes the details of a ControlPacket after the fixed
+//header has been read
+func (u *UnsubscribePacket) Unpack(b io.Reader) error {
+ u.MessageID = decodeUint16(b)
+ var topic string
+ for topic = decodeString(b); topic != ""; topic = decodeString(b) {
+ u.Topics = append(u.Topics, topic)
+ }
+
+ return nil
+}
+
+//Details returns a Details struct containing the Qos and
+//MessageID of this ControlPacket
+func (u *UnsubscribePacket) Details() Details {
+ return Details{Qos: 1, MessageID: u.MessageID}
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/ping.go b/vendor/github.com/eclipse/paho.mqtt.golang/ping.go
new file mode 100644
index 00000000..fefcff08
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/ping.go
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+package mqtt
+
+import (
+ "errors"
+ "sync"
+ "time"
+
+ "github.com/eclipse/paho.mqtt.golang/packets"
+)
+
+func keepalive(c *client) {
+ DEBUG.Println(PNG, "keepalive starting")
+
+ var condWG sync.WaitGroup
+ pingStop := make(chan struct{})
+
+ defer func() {
+ close(pingStop)
+ c.keepaliveReset.Broadcast()
+ c.pingResp.Broadcast()
+ c.packetResp.Broadcast()
+ condWG.Wait()
+ c.workers.Done()
+ }()
+
+ receiveInterval := c.options.KeepAlive + (1 * time.Second)
+ pingTimer := timer{Timer: time.NewTimer(c.options.KeepAlive)}
+ receiveTimer := timer{Timer: time.NewTimer(receiveInterval)}
+ pingRespTimer := timer{Timer: time.NewTimer(c.options.PingTimeout)}
+
+ pingRespTimer.Stop()
+
+ condWG.Add(3)
+ go func() {
+ defer condWG.Done()
+ for {
+ c.pingResp.L.Lock()
+ c.pingResp.Wait()
+ c.pingResp.L.Unlock()
+ select {
+ case <-pingStop:
+ return
+ default:
+ }
+ DEBUG.Println(NET, "resetting ping timeout timer")
+ pingRespTimer.Stop()
+ pingTimer.Reset(c.options.KeepAlive)
+ receiveTimer.Reset(receiveInterval)
+ }
+ }()
+
+ go func() {
+ defer condWG.Done()
+ for {
+ c.packetResp.L.Lock()
+ c.packetResp.Wait()
+ c.packetResp.L.Unlock()
+ select {
+ case <-pingStop:
+ return
+ default:
+ }
+ DEBUG.Println(NET, "resetting receive timer")
+ receiveTimer.Reset(receiveInterval)
+ }
+ }()
+
+ go func() {
+ defer condWG.Done()
+ for {
+ c.keepaliveReset.L.Lock()
+ c.keepaliveReset.Wait()
+ c.keepaliveReset.L.Unlock()
+ select {
+ case <-pingStop:
+ return
+ default:
+ }
+ DEBUG.Println(NET, "resetting ping timer")
+ pingTimer.Reset(c.options.KeepAlive)
+ }
+ }()
+
+ for {
+ select {
+ case <-c.stop:
+ DEBUG.Println(PNG, "keepalive stopped")
+ return
+ case <-pingTimer.C:
+ sendPing(&pingTimer, &pingRespTimer, c)
+ case <-receiveTimer.C:
+ receiveTimer.SetRead(true)
+ receiveTimer.Reset(receiveInterval)
+ sendPing(&pingTimer, &pingRespTimer, c)
+ case <-pingRespTimer.C:
+ pingRespTimer.SetRead(true)
+ CRITICAL.Println(PNG, "pingresp not received, disconnecting")
+ c.errors <- errors.New("pingresp not received, disconnecting")
+ pingTimer.Stop()
+ return
+ }
+ }
+}
+
+type timer struct {
+ sync.Mutex
+ *time.Timer
+ readFrom bool
+}
+
+func (t *timer) SetRead(v bool) {
+ t.Lock()
+ t.readFrom = v
+ t.Unlock()
+}
+
+func (t *timer) Stop() bool {
+ t.Lock()
+ defer t.SetRead(true)
+ defer t.Unlock()
+
+ if !t.Timer.Stop() && !t.readFrom {
+ <-t.C
+ return false
+ }
+ return true
+}
+
+func (t *timer) Reset(d time.Duration) bool {
+ t.Lock()
+ defer t.SetRead(false)
+ defer t.Unlock()
+ if !t.Timer.Stop() && !t.readFrom {
+ <-t.C
+ }
+
+ return t.Timer.Reset(d)
+}
+
+func sendPing(pt *timer, rt *timer, c *client) {
+ pt.SetRead(true)
+ DEBUG.Println(PNG, "keepalive sending ping")
+ ping := packets.NewControlPacket(packets.Pingreq).(*packets.PingreqPacket)
+ //We don't want to wait behind large messages being sent, the Write call
+ //will block until it it able to send the packet.
+ ping.Write(c.conn)
+
+ rt.Reset(c.options.PingTimeout)
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/router.go b/vendor/github.com/eclipse/paho.mqtt.golang/router.go
new file mode 100644
index 00000000..9e44b0f7
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/router.go
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+package mqtt
+
+import (
+ "container/list"
+ "strings"
+ "sync"
+
+ "github.com/eclipse/paho.mqtt.golang/packets"
+)
+
+// route is a type which associates MQTT Topic strings with a
+// callback to be executed upon the arrival of a message associated
+// with a subscription to that topic.
+type route struct {
+ topic string
+ callback MessageHandler
+}
+
+// match takes a slice of strings which represent the route being tested having been split on '/'
+// separators, and a slice of strings representing the topic string in the published message, similarly
+// split.
+// The function determines if the topic string matches the route according to the MQTT topic rules
+// and returns a boolean of the outcome
+func match(route []string, topic []string) bool {
+ if len(route) == 0 {
+ if len(topic) == 0 {
+ return true
+ }
+ return false
+ }
+
+ if len(topic) == 0 {
+ if route[0] == "#" {
+ return true
+ }
+ return false
+ }
+
+ if route[0] == "#" {
+ return true
+ }
+
+ if (route[0] == "+") || (route[0] == topic[0]) {
+ return match(route[1:], topic[1:])
+ }
+
+ return false
+}
+
+func routeIncludesTopic(route, topic string) bool {
+ return match(strings.Split(route, "/"), strings.Split(topic, "/"))
+}
+
+// match takes the topic string of the published message and does a basic compare to the
+// string of the current Route, if they match it returns true
+func (r *route) match(topic string) bool {
+ return r.topic == topic || routeIncludesTopic(r.topic, topic)
+}
+
+type router struct {
+ sync.RWMutex
+ routes *list.List
+ defaultHandler MessageHandler
+ messages chan *packets.PublishPacket
+ stop chan bool
+}
+
+// newRouter returns a new instance of a Router and channel which can be used to tell the Router
+// to stop
+func newRouter() (*router, chan bool) {
+ router := &router{routes: list.New(), messages: make(chan *packets.PublishPacket), stop: make(chan bool)}
+ stop := router.stop
+ return router, stop
+}
+
+// addRoute takes a topic string and MessageHandler callback. It looks in the current list of
+// routes to see if there is already a matching Route. If there is it replaces the current
+// callback with the new one. If not it add a new entry to the list of Routes.
+func (r *router) addRoute(topic string, callback MessageHandler) {
+ r.Lock()
+ defer r.Unlock()
+ for e := r.routes.Front(); e != nil; e = e.Next() {
+ if e.Value.(*route).match(topic) {
+ r := e.Value.(*route)
+ r.callback = callback
+ return
+ }
+ }
+ r.routes.PushBack(&route{topic: topic, callback: callback})
+}
+
+// deleteRoute takes a route string, looks for a matching Route in the list of Routes. If
+// found it removes the Route from the list.
+func (r *router) deleteRoute(topic string) {
+ r.Lock()
+ defer r.Unlock()
+ for e := r.routes.Front(); e != nil; e = e.Next() {
+ if e.Value.(*route).match(topic) {
+ r.routes.Remove(e)
+ return
+ }
+ }
+}
+
+// setDefaultHandler assigns a default callback that will be called if no matching Route
+// is found for an incoming Publish.
+func (r *router) setDefaultHandler(handler MessageHandler) {
+ r.Lock()
+ defer r.Unlock()
+ r.defaultHandler = handler
+}
+
+// matchAndDispatch takes a channel of Message pointers as input and starts a go routine that
+// takes messages off the channel, matches them against the internal route list and calls the
+// associated callback (or the defaultHandler, if one exists and no other route matched). If
+// anything is sent down the stop channel the function will end.
+func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order bool, client *client) {
+ go func() {
+ for {
+ select {
+ case message := <-messages:
+ sent := false
+ r.RLock()
+ for e := r.routes.Front(); e != nil; e = e.Next() {
+ if e.Value.(*route).match(message.TopicName) {
+ if order {
+ e.Value.(*route).callback(client, messageFromPublish(message))
+ } else {
+ go e.Value.(*route).callback(client, messageFromPublish(message))
+ }
+ sent = true
+ }
+ }
+ if !sent && r.defaultHandler != nil {
+ if order {
+ r.defaultHandler(client, messageFromPublish(message))
+ } else {
+ go r.defaultHandler(client, messageFromPublish(message))
+ }
+ }
+ r.RUnlock()
+ case <-r.stop:
+ return
+ }
+ }
+ }()
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/store.go b/vendor/github.com/eclipse/paho.mqtt.golang/store.go
new file mode 100644
index 00000000..1cc9e1d3
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/store.go
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+package mqtt
+
+import (
+ "fmt"
+ "strconv"
+
+ "github.com/eclipse/paho.mqtt.golang/packets"
+)
+
+const (
+ inboundPrefix = "i."
+ outboundPrefix = "o."
+)
+
+// Store is an interface which can be used to provide implementations
+// for message persistence.
+// Because we may have to store distinct messages with the same
+// message ID, we need a unique key for each message. This is
+// possible by prepending "i." or "o." to each message id
+type Store interface {
+ Open()
+ Put(key string, message packets.ControlPacket)
+ Get(key string) packets.ControlPacket
+ All() []string
+ Del(key string)
+ Close()
+ Reset()
+}
+
+// A key MUST have the form "X.[messageid]"
+// where X is 'i' or 'o'
+func mIDFromKey(key string) uint16 {
+ s := key[2:]
+ i, err := strconv.Atoi(s)
+ chkerr(err)
+ return uint16(i)
+}
+
+// Return a string of the form "i.[id]"
+func inboundKeyFromMID(id uint16) string {
+ return fmt.Sprintf("%s%d", inboundPrefix, id)
+}
+
+// Return a string of the form "o.[id]"
+func outboundKeyFromMID(id uint16) string {
+ return fmt.Sprintf("%s%d", outboundPrefix, id)
+}
+
+// govern which outgoing messages are persisted
+func persistOutbound(s Store, m packets.ControlPacket) {
+ switch m.Details().Qos {
+ case 0:
+ switch m.(type) {
+ case *packets.PubackPacket, *packets.PubcompPacket:
+ // Sending puback. delete matching publish
+ // from ibound
+ s.Del(inboundKeyFromMID(m.Details().MessageID))
+ }
+ case 1:
+ switch m.(type) {
+ case *packets.PublishPacket, *packets.PubrelPacket, *packets.SubscribePacket, *packets.UnsubscribePacket:
+ // Sending publish. store in obound
+ // until puback received
+ s.Put(outboundKeyFromMID(m.Details().MessageID), m)
+ default:
+ ERROR.Println(STR, "Asked to persist an invalid message type")
+ }
+ case 2:
+ switch m.(type) {
+ case *packets.PublishPacket:
+ // Sending publish. store in obound
+ // until pubrel received
+ s.Put(outboundKeyFromMID(m.Details().MessageID), m)
+ default:
+ ERROR.Println(STR, "Asked to persist an invalid message type")
+ }
+ }
+}
+
+// govern which incoming messages are persisted
+func persistInbound(s Store, m packets.ControlPacket) {
+ switch m.Details().Qos {
+ case 0:
+ switch m.(type) {
+ case *packets.PubackPacket, *packets.SubackPacket, *packets.UnsubackPacket, *packets.PubcompPacket:
+ // Received a puback. delete matching publish
+ // from obound
+ s.Del(outboundKeyFromMID(m.Details().MessageID))
+ case *packets.PublishPacket, *packets.PubrecPacket, *packets.PingrespPacket, *packets.ConnackPacket:
+ default:
+ ERROR.Println(STR, "Asked to persist an invalid messages type")
+ }
+ case 1:
+ switch m.(type) {
+ case *packets.PublishPacket, *packets.PubrelPacket:
+ // Received a publish. store it in ibound
+ // until puback sent
+ s.Put(inboundKeyFromMID(m.Details().MessageID), m)
+ default:
+ ERROR.Println(STR, "Asked to persist an invalid messages type")
+ }
+ case 2:
+ switch m.(type) {
+ case *packets.PublishPacket:
+ // Received a publish. store it in ibound
+ // until pubrel received
+ s.Put(inboundKeyFromMID(m.Details().MessageID), m)
+ default:
+ ERROR.Println(STR, "Asked to persist an invalid messages type")
+ }
+ }
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/token.go b/vendor/github.com/eclipse/paho.mqtt.golang/token.go
new file mode 100644
index 00000000..dc54d6d6
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/token.go
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Allan Stockdill-Mander
+ */
+
+package mqtt
+
+import (
+ "sync"
+ "time"
+
+ "github.com/eclipse/paho.mqtt.golang/packets"
+)
+
+//PacketAndToken is a struct that contains both a ControlPacket and a
+//Token. This struct is passed via channels between the client interface
+//code and the underlying code responsible for sending and receiving
+//MQTT messages.
+type PacketAndToken struct {
+ p packets.ControlPacket
+ t Token
+}
+
+//Token defines the interface for the tokens used to indicate when
+//actions have completed.
+type Token interface {
+ Wait() bool
+ WaitTimeout(time.Duration) bool
+ flowComplete()
+ Error() error
+}
+
+type baseToken struct {
+ m sync.RWMutex
+ complete chan struct{}
+ ready bool
+ err error
+}
+
+// Wait will wait indefinitely for the Token to complete, ie the Publish
+// to be sent and confirmed receipt from the broker
+func (b *baseToken) Wait() bool {
+ b.m.Lock()
+ defer b.m.Unlock()
+ if !b.ready {
+ <-b.complete
+ b.ready = true
+ }
+ return b.ready
+}
+
+// WaitTimeout takes a time in ms to wait for the flow associated with the
+// Token to complete, returns true if it returned before the timeout or
+// returns false if the timeout occurred. In the case of a timeout the Token
+// does not have an error set in case the caller wishes to wait again
+func (b *baseToken) WaitTimeout(d time.Duration) bool {
+ b.m.Lock()
+ defer b.m.Unlock()
+ if !b.ready {
+ select {
+ case <-b.complete:
+ b.ready = true
+ case <-time.After(d):
+ }
+ }
+ return b.ready
+}
+
+func (b *baseToken) flowComplete() {
+ close(b.complete)
+}
+
+func (b *baseToken) Error() error {
+ b.m.RLock()
+ defer b.m.RUnlock()
+ return b.err
+}
+
+func newToken(tType byte) Token {
+ switch tType {
+ case packets.Connect:
+ return &ConnectToken{baseToken: baseToken{complete: make(chan struct{})}}
+ case packets.Subscribe:
+ return &SubscribeToken{baseToken: baseToken{complete: make(chan struct{})}, subResult: make(map[string]byte)}
+ case packets.Publish:
+ return &PublishToken{baseToken: baseToken{complete: make(chan struct{})}}
+ case packets.Unsubscribe:
+ return &UnsubscribeToken{baseToken: baseToken{complete: make(chan struct{})}}
+ case packets.Disconnect:
+ return &DisconnectToken{baseToken: baseToken{complete: make(chan struct{})}}
+ }
+ return nil
+}
+
+//ConnectToken is an extension of Token containing the extra fields
+//required to provide information about calls to Connect()
+type ConnectToken struct {
+ baseToken
+ returnCode byte
+}
+
+//ReturnCode returns the acknowlegement code in the connack sent
+//in response to a Connect()
+func (c *ConnectToken) ReturnCode() byte {
+ c.m.RLock()
+ defer c.m.RUnlock()
+ return c.returnCode
+}
+
+//PublishToken is an extension of Token containing the extra fields
+//required to provide information about calls to Publish()
+type PublishToken struct {
+ baseToken
+ messageID uint16
+}
+
+//MessageID returns the MQTT message ID that was assigned to the
+//Publish packet when it was sent to the broker
+func (p *PublishToken) MessageID() uint16 {
+ return p.messageID
+}
+
+//SubscribeToken is an extension of Token containing the extra fields
+//required to provide information about calls to Subscribe()
+type SubscribeToken struct {
+ baseToken
+ subs []string
+ subResult map[string]byte
+}
+
+//Result returns a map of topics that were subscribed to along with
+//the matching return code from the broker. This is either the Qos
+//value of the subscription or an error code.
+func (s *SubscribeToken) Result() map[string]byte {
+ s.m.RLock()
+ defer s.m.RUnlock()
+ return s.subResult
+}
+
+//UnsubscribeToken is an extension of Token containing the extra fields
+//required to provide information about calls to Unsubscribe()
+type UnsubscribeToken struct {
+ baseToken
+}
+
+//DisconnectToken is an extension of Token containing the extra fields
+//required to provide information about calls to Disconnect()
+type DisconnectToken struct {
+ baseToken
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/topic.go b/vendor/github.com/eclipse/paho.mqtt.golang/topic.go
new file mode 100644
index 00000000..6fa3ad2a
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/topic.go
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+package mqtt
+
+import (
+ "errors"
+ "strings"
+)
+
+//ErrInvalidQos is the error returned when an packet is to be sent
+//with an invalid Qos value
+var ErrInvalidQos = errors.New("Invalid QoS")
+
+//ErrInvalidTopicEmptyString is the error returned when a topic string
+//is passed in that is 0 length
+var ErrInvalidTopicEmptyString = errors.New("Invalid Topic; empty string")
+
+//ErrInvalidTopicMultilevel is the error returned when a topic string
+//is passed in that has the multi level wildcard in any position but
+//the last
+var ErrInvalidTopicMultilevel = errors.New("Invalid Topic; multi-level wildcard must be last level")
+
+// Topic Names and Topic Filters
+// The MQTT v3.1.1 spec clarifies a number of ambiguities with regard
+// to the validity of Topic strings.
+// - A Topic must be between 1 and 65535 bytes.
+// - A Topic is case sensitive.
+// - A Topic may contain whitespace.
+// - A Topic containing a leading forward slash is different than a Topic without.
+// - A Topic may be "/" (two levels, both empty string).
+// - A Topic must be UTF-8 encoded.
+// - A Topic may contain any number of levels.
+// - A Topic may contain an empty level (two forward slashes in a row).
+// - A TopicName may not contain a wildcard.
+// - A TopicFilter may only have a # (multi-level) wildcard as the last level.
+// - A TopicFilter may contain any number of + (single-level) wildcards.
+// - A TopicFilter with a # will match the absense of a level
+// Example: a subscription to "foo/#" will match messages published to "foo".
+
+func validateSubscribeMap(subs map[string]byte) ([]string, []byte, error) {
+ var topics []string
+ var qoss []byte
+ for topic, qos := range subs {
+ if err := validateTopicAndQos(topic, qos); err != nil {
+ return nil, nil, err
+ }
+ topics = append(topics, topic)
+ qoss = append(qoss, qos)
+ }
+
+ return topics, qoss, nil
+}
+
+func validateTopicAndQos(topic string, qos byte) error {
+ if len(topic) == 0 {
+ return ErrInvalidTopicEmptyString
+ }
+
+ levels := strings.Split(topic, "/")
+ for i, level := range levels {
+ if level == "#" && i != len(levels)-1 {
+ return ErrInvalidTopicMultilevel
+ }
+ }
+
+ if qos < 0 || qos > 2 {
+ return ErrInvalidQos
+ }
+ return nil
+}
diff --git a/vendor/github.com/eclipse/paho.mqtt.golang/trace.go b/vendor/github.com/eclipse/paho.mqtt.golang/trace.go
new file mode 100644
index 00000000..2f5a0146
--- /dev/null
+++ b/vendor/github.com/eclipse/paho.mqtt.golang/trace.go
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Seth Hoenig
+ * Allan Stockdill-Mander
+ * Mike Robertson
+ */
+
+package mqtt
+
+import (
+ "io/ioutil"
+ "log"
+)
+
+// Internal levels of library output that are initialised to not print
+// anything but can be overridden by programmer
+var (
+ ERROR *log.Logger
+ CRITICAL *log.Logger
+ WARN *log.Logger
+ DEBUG *log.Logger
+)
+
+func init() {
+ ERROR = log.New(ioutil.Discard, "", 0)
+ CRITICAL = log.New(ioutil.Discard, "", 0)
+ WARN = log.New(ioutil.Discard, "", 0)
+ DEBUG = log.New(ioutil.Discard, "", 0)
+}
diff --git a/vendor/github.com/golang/protobuf/AUTHORS b/vendor/github.com/golang/protobuf/AUTHORS
new file mode 100644
index 00000000..15167cd7
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/AUTHORS
@@ -0,0 +1,3 @@
+# This source code refers to The Go Authors for copyright purposes.
+# The master list of authors is in the main Go distribution,
+# visible at http://tip.golang.org/AUTHORS.
diff --git a/vendor/github.com/golang/protobuf/CONTRIBUTORS b/vendor/github.com/golang/protobuf/CONTRIBUTORS
new file mode 100644
index 00000000..1c4577e9
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/CONTRIBUTORS
@@ -0,0 +1,3 @@
+# This source code was written by the Go contributors.
+# The master list of contributors is in the main Go distribution,
+# visible at http://tip.golang.org/CONTRIBUTORS.
diff --git a/vendor/github.com/golang/protobuf/LICENSE b/vendor/github.com/golang/protobuf/LICENSE
new file mode 100644
index 00000000..1b1b1921
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/LICENSE
@@ -0,0 +1,31 @@
+Go support for Protocol Buffers - Google's data interchange format
+
+Copyright 2010 The Go Authors. All rights reserved.
+https://github.com/golang/protobuf
+
+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 Google Inc. 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 THE COPYRIGHT
+OWNER OR CONTRIBUTORS 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.
+
diff --git a/vendor/github.com/golang/protobuf/proto/Makefile b/vendor/github.com/golang/protobuf/proto/Makefile
new file mode 100644
index 00000000..e2e0651a
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/Makefile
@@ -0,0 +1,43 @@
+# Go support for Protocol Buffers - Google's data interchange format
+#
+# Copyright 2010 The Go Authors. All rights reserved.
+# https://github.com/golang/protobuf
+#
+# 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 Google Inc. 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 THE COPYRIGHT
+# OWNER OR CONTRIBUTORS 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.
+
+install:
+ go install
+
+test: install generate-test-pbs
+ go test
+
+
+generate-test-pbs:
+ make install
+ make -C testdata
+ protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata,Mgoogle/protobuf/any.proto=github.com/golang/protobuf/ptypes/any:. proto3_proto/proto3.proto
+ make
diff --git a/vendor/github.com/golang/protobuf/proto/clone.go b/vendor/github.com/golang/protobuf/proto/clone.go
new file mode 100644
index 00000000..e392575b
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/clone.go
@@ -0,0 +1,229 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2011 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+// Protocol buffer deep copy and merge.
+// TODO: RawMessage.
+
+package proto
+
+import (
+ "log"
+ "reflect"
+ "strings"
+)
+
+// Clone returns a deep copy of a protocol buffer.
+func Clone(pb Message) Message {
+ in := reflect.ValueOf(pb)
+ if in.IsNil() {
+ return pb
+ }
+
+ out := reflect.New(in.Type().Elem())
+ // out is empty so a merge is a deep copy.
+ mergeStruct(out.Elem(), in.Elem())
+ return out.Interface().(Message)
+}
+
+// Merge merges src into dst.
+// Required and optional fields that are set in src will be set to that value in dst.
+// Elements of repeated fields will be appended.
+// Merge panics if src and dst are not the same type, or if dst is nil.
+func Merge(dst, src Message) {
+ in := reflect.ValueOf(src)
+ out := reflect.ValueOf(dst)
+ if out.IsNil() {
+ panic("proto: nil destination")
+ }
+ if in.Type() != out.Type() {
+ // Explicit test prior to mergeStruct so that mistyped nils will fail
+ panic("proto: type mismatch")
+ }
+ if in.IsNil() {
+ // Merging nil into non-nil is a quiet no-op
+ return
+ }
+ mergeStruct(out.Elem(), in.Elem())
+}
+
+func mergeStruct(out, in reflect.Value) {
+ sprop := GetProperties(in.Type())
+ for i := 0; i < in.NumField(); i++ {
+ f := in.Type().Field(i)
+ if strings.HasPrefix(f.Name, "XXX_") {
+ continue
+ }
+ mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i])
+ }
+
+ if emIn, ok := extendable(in.Addr().Interface()); ok {
+ emOut, _ := extendable(out.Addr().Interface())
+ mIn, muIn := emIn.extensionsRead()
+ if mIn != nil {
+ mOut := emOut.extensionsWrite()
+ muIn.Lock()
+ mergeExtension(mOut, mIn)
+ muIn.Unlock()
+ }
+ }
+
+ uf := in.FieldByName("XXX_unrecognized")
+ if !uf.IsValid() {
+ return
+ }
+ uin := uf.Bytes()
+ if len(uin) > 0 {
+ out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...))
+ }
+}
+
+// mergeAny performs a merge between two values of the same type.
+// viaPtr indicates whether the values were indirected through a pointer (implying proto2).
+// prop is set if this is a struct field (it may be nil).
+func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) {
+ if in.Type() == protoMessageType {
+ if !in.IsNil() {
+ if out.IsNil() {
+ out.Set(reflect.ValueOf(Clone(in.Interface().(Message))))
+ } else {
+ Merge(out.Interface().(Message), in.Interface().(Message))
+ }
+ }
+ return
+ }
+ switch in.Kind() {
+ case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
+ reflect.String, reflect.Uint32, reflect.Uint64:
+ if !viaPtr && isProto3Zero(in) {
+ return
+ }
+ out.Set(in)
+ case reflect.Interface:
+ // Probably a oneof field; copy non-nil values.
+ if in.IsNil() {
+ return
+ }
+ // Allocate destination if it is not set, or set to a different type.
+ // Otherwise we will merge as normal.
+ if out.IsNil() || out.Elem().Type() != in.Elem().Type() {
+ out.Set(reflect.New(in.Elem().Elem().Type())) // interface -> *T -> T -> new(T)
+ }
+ mergeAny(out.Elem(), in.Elem(), false, nil)
+ case reflect.Map:
+ if in.Len() == 0 {
+ return
+ }
+ if out.IsNil() {
+ out.Set(reflect.MakeMap(in.Type()))
+ }
+ // For maps with value types of *T or []byte we need to deep copy each value.
+ elemKind := in.Type().Elem().Kind()
+ for _, key := range in.MapKeys() {
+ var val reflect.Value
+ switch elemKind {
+ case reflect.Ptr:
+ val = reflect.New(in.Type().Elem().Elem())
+ mergeAny(val, in.MapIndex(key), false, nil)
+ case reflect.Slice:
+ val = in.MapIndex(key)
+ val = reflect.ValueOf(append([]byte{}, val.Bytes()...))
+ default:
+ val = in.MapIndex(key)
+ }
+ out.SetMapIndex(key, val)
+ }
+ case reflect.Ptr:
+ if in.IsNil() {
+ return
+ }
+ if out.IsNil() {
+ out.Set(reflect.New(in.Elem().Type()))
+ }
+ mergeAny(out.Elem(), in.Elem(), true, nil)
+ case reflect.Slice:
+ if in.IsNil() {
+ return
+ }
+ if in.Type().Elem().Kind() == reflect.Uint8 {
+ // []byte is a scalar bytes field, not a repeated field.
+
+ // Edge case: if this is in a proto3 message, a zero length
+ // bytes field is considered the zero value, and should not
+ // be merged.
+ if prop != nil && prop.proto3 && in.Len() == 0 {
+ return
+ }
+
+ // Make a deep copy.
+ // Append to []byte{} instead of []byte(nil) so that we never end up
+ // with a nil result.
+ out.SetBytes(append([]byte{}, in.Bytes()...))
+ return
+ }
+ n := in.Len()
+ if out.IsNil() {
+ out.Set(reflect.MakeSlice(in.Type(), 0, n))
+ }
+ switch in.Type().Elem().Kind() {
+ case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
+ reflect.String, reflect.Uint32, reflect.Uint64:
+ out.Set(reflect.AppendSlice(out, in))
+ default:
+ for i := 0; i < n; i++ {
+ x := reflect.Indirect(reflect.New(in.Type().Elem()))
+ mergeAny(x, in.Index(i), false, nil)
+ out.Set(reflect.Append(out, x))
+ }
+ }
+ case reflect.Struct:
+ mergeStruct(out, in)
+ default:
+ // unknown type, so not a protocol buffer
+ log.Printf("proto: don't know how to copy %v", in)
+ }
+}
+
+func mergeExtension(out, in map[int32]Extension) {
+ for extNum, eIn := range in {
+ eOut := Extension{desc: eIn.desc}
+ if eIn.value != nil {
+ v := reflect.New(reflect.TypeOf(eIn.value)).Elem()
+ mergeAny(v, reflect.ValueOf(eIn.value), false, nil)
+ eOut.value = v.Interface()
+ }
+ if eIn.enc != nil {
+ eOut.enc = make([]byte, len(eIn.enc))
+ copy(eOut.enc, eIn.enc)
+ }
+
+ out[extNum] = eOut
+ }
+}
diff --git a/vendor/github.com/golang/protobuf/proto/decode.go b/vendor/github.com/golang/protobuf/proto/decode.go
new file mode 100644
index 00000000..aa207298
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/decode.go
@@ -0,0 +1,970 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+package proto
+
+/*
+ * Routines for decoding protocol buffer data to construct in-memory representations.
+ */
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+)
+
+// errOverflow is returned when an integer is too large to be represented.
+var errOverflow = errors.New("proto: integer overflow")
+
+// ErrInternalBadWireType is returned by generated code when an incorrect
+// wire type is encountered. It does not get returned to user code.
+var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof")
+
+// The fundamental decoders that interpret bytes on the wire.
+// Those that take integer types all return uint64 and are
+// therefore of type valueDecoder.
+
+// DecodeVarint reads a varint-encoded integer from the slice.
+// It returns the integer and the number of bytes consumed, or
+// zero if there is not enough.
+// This is the format for the
+// int32, int64, uint32, uint64, bool, and enum
+// protocol buffer types.
+func DecodeVarint(buf []byte) (x uint64, n int) {
+ for shift := uint(0); shift < 64; shift += 7 {
+ if n >= len(buf) {
+ return 0, 0
+ }
+ b := uint64(buf[n])
+ n++
+ x |= (b & 0x7F) << shift
+ if (b & 0x80) == 0 {
+ return x, n
+ }
+ }
+
+ // The number is too large to represent in a 64-bit value.
+ return 0, 0
+}
+
+func (p *Buffer) decodeVarintSlow() (x uint64, err error) {
+ i := p.index
+ l := len(p.buf)
+
+ for shift := uint(0); shift < 64; shift += 7 {
+ if i >= l {
+ err = io.ErrUnexpectedEOF
+ return
+ }
+ b := p.buf[i]
+ i++
+ x |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ p.index = i
+ return
+ }
+ }
+
+ // The number is too large to represent in a 64-bit value.
+ err = errOverflow
+ return
+}
+
+// DecodeVarint reads a varint-encoded integer from the Buffer.
+// This is the format for the
+// int32, int64, uint32, uint64, bool, and enum
+// protocol buffer types.
+func (p *Buffer) DecodeVarint() (x uint64, err error) {
+ i := p.index
+ buf := p.buf
+
+ if i >= len(buf) {
+ return 0, io.ErrUnexpectedEOF
+ } else if buf[i] < 0x80 {
+ p.index++
+ return uint64(buf[i]), nil
+ } else if len(buf)-i < 10 {
+ return p.decodeVarintSlow()
+ }
+
+ var b uint64
+ // we already checked the first byte
+ x = uint64(buf[i]) - 0x80
+ i++
+
+ b = uint64(buf[i])
+ i++
+ x += b << 7
+ if b&0x80 == 0 {
+ goto done
+ }
+ x -= 0x80 << 7
+
+ b = uint64(buf[i])
+ i++
+ x += b << 14
+ if b&0x80 == 0 {
+ goto done
+ }
+ x -= 0x80 << 14
+
+ b = uint64(buf[i])
+ i++
+ x += b << 21
+ if b&0x80 == 0 {
+ goto done
+ }
+ x -= 0x80 << 21
+
+ b = uint64(buf[i])
+ i++
+ x += b << 28
+ if b&0x80 == 0 {
+ goto done
+ }
+ x -= 0x80 << 28
+
+ b = uint64(buf[i])
+ i++
+ x += b << 35
+ if b&0x80 == 0 {
+ goto done
+ }
+ x -= 0x80 << 35
+
+ b = uint64(buf[i])
+ i++
+ x += b << 42
+ if b&0x80 == 0 {
+ goto done
+ }
+ x -= 0x80 << 42
+
+ b = uint64(buf[i])
+ i++
+ x += b << 49
+ if b&0x80 == 0 {
+ goto done
+ }
+ x -= 0x80 << 49
+
+ b = uint64(buf[i])
+ i++
+ x += b << 56
+ if b&0x80 == 0 {
+ goto done
+ }
+ x -= 0x80 << 56
+
+ b = uint64(buf[i])
+ i++
+ x += b << 63
+ if b&0x80 == 0 {
+ goto done
+ }
+ // x -= 0x80 << 63 // Always zero.
+
+ return 0, errOverflow
+
+done:
+ p.index = i
+ return x, nil
+}
+
+// DecodeFixed64 reads a 64-bit integer from the Buffer.
+// This is the format for the
+// fixed64, sfixed64, and double protocol buffer types.
+func (p *Buffer) DecodeFixed64() (x uint64, err error) {
+ // x, err already 0
+ i := p.index + 8
+ if i < 0 || i > len(p.buf) {
+ err = io.ErrUnexpectedEOF
+ return
+ }
+ p.index = i
+
+ x = uint64(p.buf[i-8])
+ x |= uint64(p.buf[i-7]) << 8
+ x |= uint64(p.buf[i-6]) << 16
+ x |= uint64(p.buf[i-5]) << 24
+ x |= uint64(p.buf[i-4]) << 32
+ x |= uint64(p.buf[i-3]) << 40
+ x |= uint64(p.buf[i-2]) << 48
+ x |= uint64(p.buf[i-1]) << 56
+ return
+}
+
+// DecodeFixed32 reads a 32-bit integer from the Buffer.
+// This is the format for the
+// fixed32, sfixed32, and float protocol buffer types.
+func (p *Buffer) DecodeFixed32() (x uint64, err error) {
+ // x, err already 0
+ i := p.index + 4
+ if i < 0 || i > len(p.buf) {
+ err = io.ErrUnexpectedEOF
+ return
+ }
+ p.index = i
+
+ x = uint64(p.buf[i-4])
+ x |= uint64(p.buf[i-3]) << 8
+ x |= uint64(p.buf[i-2]) << 16
+ x |= uint64(p.buf[i-1]) << 24
+ return
+}
+
+// DecodeZigzag64 reads a zigzag-encoded 64-bit integer
+// from the Buffer.
+// This is the format used for the sint64 protocol buffer type.
+func (p *Buffer) DecodeZigzag64() (x uint64, err error) {
+ x, err = p.DecodeVarint()
+ if err != nil {
+ return
+ }
+ x = (x >> 1) ^ uint64((int64(x&1)<<63)>>63)
+ return
+}
+
+// DecodeZigzag32 reads a zigzag-encoded 32-bit integer
+// from the Buffer.
+// This is the format used for the sint32 protocol buffer type.
+func (p *Buffer) DecodeZigzag32() (x uint64, err error) {
+ x, err = p.DecodeVarint()
+ if err != nil {
+ return
+ }
+ x = uint64((uint32(x) >> 1) ^ uint32((int32(x&1)<<31)>>31))
+ return
+}
+
+// These are not ValueDecoders: they produce an array of bytes or a string.
+// bytes, embedded messages
+
+// DecodeRawBytes reads a count-delimited byte buffer from the Buffer.
+// This is the format used for the bytes protocol buffer
+// type and for embedded messages.
+func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err error) {
+ n, err := p.DecodeVarint()
+ if err != nil {
+ return nil, err
+ }
+
+ nb := int(n)
+ if nb < 0 {
+ return nil, fmt.Errorf("proto: bad byte length %d", nb)
+ }
+ end := p.index + nb
+ if end < p.index || end > len(p.buf) {
+ return nil, io.ErrUnexpectedEOF
+ }
+
+ if !alloc {
+ // todo: check if can get more uses of alloc=false
+ buf = p.buf[p.index:end]
+ p.index += nb
+ return
+ }
+
+ buf = make([]byte, nb)
+ copy(buf, p.buf[p.index:])
+ p.index += nb
+ return
+}
+
+// DecodeStringBytes reads an encoded string from the Buffer.
+// This is the format used for the proto2 string type.
+func (p *Buffer) DecodeStringBytes() (s string, err error) {
+ buf, err := p.DecodeRawBytes(false)
+ if err != nil {
+ return
+ }
+ return string(buf), nil
+}
+
+// Skip the next item in the buffer. Its wire type is decoded and presented as an argument.
+// If the protocol buffer has extensions, and the field matches, add it as an extension.
+// Otherwise, if the XXX_unrecognized field exists, append the skipped data there.
+func (o *Buffer) skipAndSave(t reflect.Type, tag, wire int, base structPointer, unrecField field) error {
+ oi := o.index
+
+ err := o.skip(t, tag, wire)
+ if err != nil {
+ return err
+ }
+
+ if !unrecField.IsValid() {
+ return nil
+ }
+
+ ptr := structPointer_Bytes(base, unrecField)
+
+ // Add the skipped field to struct field
+ obuf := o.buf
+
+ o.buf = *ptr
+ o.EncodeVarint(uint64(tag<<3 | wire))
+ *ptr = append(o.buf, obuf[oi:o.index]...)
+
+ o.buf = obuf
+
+ return nil
+}
+
+// Skip the next item in the buffer. Its wire type is decoded and presented as an argument.
+func (o *Buffer) skip(t reflect.Type, tag, wire int) error {
+
+ var u uint64
+ var err error
+
+ switch wire {
+ case WireVarint:
+ _, err = o.DecodeVarint()
+ case WireFixed64:
+ _, err = o.DecodeFixed64()
+ case WireBytes:
+ _, err = o.DecodeRawBytes(false)
+ case WireFixed32:
+ _, err = o.DecodeFixed32()
+ case WireStartGroup:
+ for {
+ u, err = o.DecodeVarint()
+ if err != nil {
+ break
+ }
+ fwire := int(u & 0x7)
+ if fwire == WireEndGroup {
+ break
+ }
+ ftag := int(u >> 3)
+ err = o.skip(t, ftag, fwire)
+ if err != nil {
+ break
+ }
+ }
+ default:
+ err = fmt.Errorf("proto: can't skip unknown wire type %d for %s", wire, t)
+ }
+ return err
+}
+
+// Unmarshaler is the interface representing objects that can
+// unmarshal themselves. The method should reset the receiver before
+// decoding starts. The argument points to data that may be
+// overwritten, so implementations should not keep references to the
+// buffer.
+type Unmarshaler interface {
+ Unmarshal([]byte) error
+}
+
+// Unmarshal parses the protocol buffer representation in buf and places the
+// decoded result in pb. If the struct underlying pb does not match
+// the data in buf, the results can be unpredictable.
+//
+// Unmarshal resets pb before starting to unmarshal, so any
+// existing data in pb is always removed. Use UnmarshalMerge
+// to preserve and append to existing data.
+func Unmarshal(buf []byte, pb Message) error {
+ pb.Reset()
+ return UnmarshalMerge(buf, pb)
+}
+
+// UnmarshalMerge parses the protocol buffer representation in buf and
+// writes the decoded result to pb. If the struct underlying pb does not match
+// the data in buf, the results can be unpredictable.
+//
+// UnmarshalMerge merges into existing data in pb.
+// Most code should use Unmarshal instead.
+func UnmarshalMerge(buf []byte, pb Message) error {
+ // If the object can unmarshal itself, let it.
+ if u, ok := pb.(Unmarshaler); ok {
+ return u.Unmarshal(buf)
+ }
+ return NewBuffer(buf).Unmarshal(pb)
+}
+
+// DecodeMessage reads a count-delimited message from the Buffer.
+func (p *Buffer) DecodeMessage(pb Message) error {
+ enc, err := p.DecodeRawBytes(false)
+ if err != nil {
+ return err
+ }
+ return NewBuffer(enc).Unmarshal(pb)
+}
+
+// DecodeGroup reads a tag-delimited group from the Buffer.
+func (p *Buffer) DecodeGroup(pb Message) error {
+ typ, base, err := getbase(pb)
+ if err != nil {
+ return err
+ }
+ return p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), true, base)
+}
+
+// Unmarshal parses the protocol buffer representation in the
+// Buffer and places the decoded result in pb. If the struct
+// underlying pb does not match the data in the buffer, the results can be
+// unpredictable.
+//
+// Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal.
+func (p *Buffer) Unmarshal(pb Message) error {
+ // If the object can unmarshal itself, let it.
+ if u, ok := pb.(Unmarshaler); ok {
+ err := u.Unmarshal(p.buf[p.index:])
+ p.index = len(p.buf)
+ return err
+ }
+
+ typ, base, err := getbase(pb)
+ if err != nil {
+ return err
+ }
+
+ err = p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), false, base)
+
+ if collectStats {
+ stats.Decode++
+ }
+
+ return err
+}
+
+// unmarshalType does the work of unmarshaling a structure.
+func (o *Buffer) unmarshalType(st reflect.Type, prop *StructProperties, is_group bool, base structPointer) error {
+ var state errorState
+ required, reqFields := prop.reqCount, uint64(0)
+
+ var err error
+ for err == nil && o.index < len(o.buf) {
+ oi := o.index
+ var u uint64
+ u, err = o.DecodeVarint()
+ if err != nil {
+ break
+ }
+ wire := int(u & 0x7)
+ if wire == WireEndGroup {
+ if is_group {
+ if required > 0 {
+ // Not enough information to determine the exact field.
+ // (See below.)
+ return &RequiredNotSetError{"{Unknown}"}
+ }
+ return nil // input is satisfied
+ }
+ return fmt.Errorf("proto: %s: wiretype end group for non-group", st)
+ }
+ tag := int(u >> 3)
+ if tag <= 0 {
+ return fmt.Errorf("proto: %s: illegal tag %d (wire type %d)", st, tag, wire)
+ }
+ fieldnum, ok := prop.decoderTags.get(tag)
+ if !ok {
+ // Maybe it's an extension?
+ if prop.extendable {
+ if e, _ := extendable(structPointer_Interface(base, st)); isExtensionField(e, int32(tag)) {
+ if err = o.skip(st, tag, wire); err == nil {
+ extmap := e.extensionsWrite()
+ ext := extmap[int32(tag)] // may be missing
+ ext.enc = append(ext.enc, o.buf[oi:o.index]...)
+ extmap[int32(tag)] = ext
+ }
+ continue
+ }
+ }
+ // Maybe it's a oneof?
+ if prop.oneofUnmarshaler != nil {
+ m := structPointer_Interface(base, st).(Message)
+ // First return value indicates whether tag is a oneof field.
+ ok, err = prop.oneofUnmarshaler(m, tag, wire, o)
+ if err == ErrInternalBadWireType {
+ // Map the error to something more descriptive.
+ // Do the formatting here to save generated code space.
+ err = fmt.Errorf("bad wiretype for oneof field in %T", m)
+ }
+ if ok {
+ continue
+ }
+ }
+ err = o.skipAndSave(st, tag, wire, base, prop.unrecField)
+ continue
+ }
+ p := prop.Prop[fieldnum]
+
+ if p.dec == nil {
+ fmt.Fprintf(os.Stderr, "proto: no protobuf decoder for %s.%s\n", st, st.Field(fieldnum).Name)
+ continue
+ }
+ dec := p.dec
+ if wire != WireStartGroup && wire != p.WireType {
+ if wire == WireBytes && p.packedDec != nil {
+ // a packable field
+ dec = p.packedDec
+ } else {
+ err = fmt.Errorf("proto: bad wiretype for field %s.%s: got wiretype %d, want %d", st, st.Field(fieldnum).Name, wire, p.WireType)
+ continue
+ }
+ }
+ decErr := dec(o, p, base)
+ if decErr != nil && !state.shouldContinue(decErr, p) {
+ err = decErr
+ }
+ if err == nil && p.Required {
+ // Successfully decoded a required field.
+ if tag <= 64 {
+ // use bitmap for fields 1-64 to catch field reuse.
+ var mask uint64 = 1 << uint64(tag-1)
+ if reqFields&mask == 0 {
+ // new required field
+ reqFields |= mask
+ required--
+ }
+ } else {
+ // This is imprecise. It can be fooled by a required field
+ // with a tag > 64 that is encoded twice; that's very rare.
+ // A fully correct implementation would require allocating
+ // a data structure, which we would like to avoid.
+ required--
+ }
+ }
+ }
+ if err == nil {
+ if is_group {
+ return io.ErrUnexpectedEOF
+ }
+ if state.err != nil {
+ return state.err
+ }
+ if required > 0 {
+ // Not enough information to determine the exact field. If we use extra
+ // CPU, we could determine the field only if the missing required field
+ // has a tag <= 64 and we check reqFields.
+ return &RequiredNotSetError{"{Unknown}"}
+ }
+ }
+ return err
+}
+
+// Individual type decoders
+// For each,
+// u is the decoded value,
+// v is a pointer to the field (pointer) in the struct
+
+// Sizes of the pools to allocate inside the Buffer.
+// The goal is modest amortization and allocation
+// on at least 16-byte boundaries.
+const (
+ boolPoolSize = 16
+ uint32PoolSize = 8
+ uint64PoolSize = 4
+)
+
+// Decode a bool.
+func (o *Buffer) dec_bool(p *Properties, base structPointer) error {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+ if len(o.bools) == 0 {
+ o.bools = make([]bool, boolPoolSize)
+ }
+ o.bools[0] = u != 0
+ *structPointer_Bool(base, p.field) = &o.bools[0]
+ o.bools = o.bools[1:]
+ return nil
+}
+
+func (o *Buffer) dec_proto3_bool(p *Properties, base structPointer) error {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+ *structPointer_BoolVal(base, p.field) = u != 0
+ return nil
+}
+
+// Decode an int32.
+func (o *Buffer) dec_int32(p *Properties, base structPointer) error {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+ word32_Set(structPointer_Word32(base, p.field), o, uint32(u))
+ return nil
+}
+
+func (o *Buffer) dec_proto3_int32(p *Properties, base structPointer) error {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+ word32Val_Set(structPointer_Word32Val(base, p.field), uint32(u))
+ return nil
+}
+
+// Decode an int64.
+func (o *Buffer) dec_int64(p *Properties, base structPointer) error {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+ word64_Set(structPointer_Word64(base, p.field), o, u)
+ return nil
+}
+
+func (o *Buffer) dec_proto3_int64(p *Properties, base structPointer) error {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+ word64Val_Set(structPointer_Word64Val(base, p.field), o, u)
+ return nil
+}
+
+// Decode a string.
+func (o *Buffer) dec_string(p *Properties, base structPointer) error {
+ s, err := o.DecodeStringBytes()
+ if err != nil {
+ return err
+ }
+ *structPointer_String(base, p.field) = &s
+ return nil
+}
+
+func (o *Buffer) dec_proto3_string(p *Properties, base structPointer) error {
+ s, err := o.DecodeStringBytes()
+ if err != nil {
+ return err
+ }
+ *structPointer_StringVal(base, p.field) = s
+ return nil
+}
+
+// Decode a slice of bytes ([]byte).
+func (o *Buffer) dec_slice_byte(p *Properties, base structPointer) error {
+ b, err := o.DecodeRawBytes(true)
+ if err != nil {
+ return err
+ }
+ *structPointer_Bytes(base, p.field) = b
+ return nil
+}
+
+// Decode a slice of bools ([]bool).
+func (o *Buffer) dec_slice_bool(p *Properties, base structPointer) error {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+ v := structPointer_BoolSlice(base, p.field)
+ *v = append(*v, u != 0)
+ return nil
+}
+
+// Decode a slice of bools ([]bool) in packed format.
+func (o *Buffer) dec_slice_packed_bool(p *Properties, base structPointer) error {
+ v := structPointer_BoolSlice(base, p.field)
+
+ nn, err := o.DecodeVarint()
+ if err != nil {
+ return err
+ }
+ nb := int(nn) // number of bytes of encoded bools
+ fin := o.index + nb
+ if fin < o.index {
+ return errOverflow
+ }
+
+ y := *v
+ for o.index < fin {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+ y = append(y, u != 0)
+ }
+
+ *v = y
+ return nil
+}
+
+// Decode a slice of int32s ([]int32).
+func (o *Buffer) dec_slice_int32(p *Properties, base structPointer) error {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+ structPointer_Word32Slice(base, p.field).Append(uint32(u))
+ return nil
+}
+
+// Decode a slice of int32s ([]int32) in packed format.
+func (o *Buffer) dec_slice_packed_int32(p *Properties, base structPointer) error {
+ v := structPointer_Word32Slice(base, p.field)
+
+ nn, err := o.DecodeVarint()
+ if err != nil {
+ return err
+ }
+ nb := int(nn) // number of bytes of encoded int32s
+
+ fin := o.index + nb
+ if fin < o.index {
+ return errOverflow
+ }
+ for o.index < fin {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+ v.Append(uint32(u))
+ }
+ return nil
+}
+
+// Decode a slice of int64s ([]int64).
+func (o *Buffer) dec_slice_int64(p *Properties, base structPointer) error {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+
+ structPointer_Word64Slice(base, p.field).Append(u)
+ return nil
+}
+
+// Decode a slice of int64s ([]int64) in packed format.
+func (o *Buffer) dec_slice_packed_int64(p *Properties, base structPointer) error {
+ v := structPointer_Word64Slice(base, p.field)
+
+ nn, err := o.DecodeVarint()
+ if err != nil {
+ return err
+ }
+ nb := int(nn) // number of bytes of encoded int64s
+
+ fin := o.index + nb
+ if fin < o.index {
+ return errOverflow
+ }
+ for o.index < fin {
+ u, err := p.valDec(o)
+ if err != nil {
+ return err
+ }
+ v.Append(u)
+ }
+ return nil
+}
+
+// Decode a slice of strings ([]string).
+func (o *Buffer) dec_slice_string(p *Properties, base structPointer) error {
+ s, err := o.DecodeStringBytes()
+ if err != nil {
+ return err
+ }
+ v := structPointer_StringSlice(base, p.field)
+ *v = append(*v, s)
+ return nil
+}
+
+// Decode a slice of slice of bytes ([][]byte).
+func (o *Buffer) dec_slice_slice_byte(p *Properties, base structPointer) error {
+ b, err := o.DecodeRawBytes(true)
+ if err != nil {
+ return err
+ }
+ v := structPointer_BytesSlice(base, p.field)
+ *v = append(*v, b)
+ return nil
+}
+
+// Decode a map field.
+func (o *Buffer) dec_new_map(p *Properties, base structPointer) error {
+ raw, err := o.DecodeRawBytes(false)
+ if err != nil {
+ return err
+ }
+ oi := o.index // index at the end of this map entry
+ o.index -= len(raw) // move buffer back to start of map entry
+
+ mptr := structPointer_NewAt(base, p.field, p.mtype) // *map[K]V
+ if mptr.Elem().IsNil() {
+ mptr.Elem().Set(reflect.MakeMap(mptr.Type().Elem()))
+ }
+ v := mptr.Elem() // map[K]V
+
+ // Prepare addressable doubly-indirect placeholders for the key and value types.
+ // See enc_new_map for why.
+ keyptr := reflect.New(reflect.PtrTo(p.mtype.Key())).Elem() // addressable *K
+ keybase := toStructPointer(keyptr.Addr()) // **K
+
+ var valbase structPointer
+ var valptr reflect.Value
+ switch p.mtype.Elem().Kind() {
+ case reflect.Slice:
+ // []byte
+ var dummy []byte
+ valptr = reflect.ValueOf(&dummy) // *[]byte
+ valbase = toStructPointer(valptr) // *[]byte
+ case reflect.Ptr:
+ // message; valptr is **Msg; need to allocate the intermediate pointer
+ valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V
+ valptr.Set(reflect.New(valptr.Type().Elem()))
+ valbase = toStructPointer(valptr)
+ default:
+ // everything else
+ valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V
+ valbase = toStructPointer(valptr.Addr()) // **V
+ }
+
+ // Decode.
+ // This parses a restricted wire format, namely the encoding of a message
+ // with two fields. See enc_new_map for the format.
+ for o.index < oi {
+ // tagcode for key and value properties are always a single byte
+ // because they have tags 1 and 2.
+ tagcode := o.buf[o.index]
+ o.index++
+ switch tagcode {
+ case p.mkeyprop.tagcode[0]:
+ if err := p.mkeyprop.dec(o, p.mkeyprop, keybase); err != nil {
+ return err
+ }
+ case p.mvalprop.tagcode[0]:
+ if err := p.mvalprop.dec(o, p.mvalprop, valbase); err != nil {
+ return err
+ }
+ default:
+ // TODO: Should we silently skip this instead?
+ return fmt.Errorf("proto: bad map data tag %d", raw[0])
+ }
+ }
+ keyelem, valelem := keyptr.Elem(), valptr.Elem()
+ if !keyelem.IsValid() {
+ keyelem = reflect.Zero(p.mtype.Key())
+ }
+ if !valelem.IsValid() {
+ valelem = reflect.Zero(p.mtype.Elem())
+ }
+
+ v.SetMapIndex(keyelem, valelem)
+ return nil
+}
+
+// Decode a group.
+func (o *Buffer) dec_struct_group(p *Properties, base structPointer) error {
+ bas := structPointer_GetStructPointer(base, p.field)
+ if structPointer_IsNil(bas) {
+ // allocate new nested message
+ bas = toStructPointer(reflect.New(p.stype))
+ structPointer_SetStructPointer(base, p.field, bas)
+ }
+ return o.unmarshalType(p.stype, p.sprop, true, bas)
+}
+
+// Decode an embedded message.
+func (o *Buffer) dec_struct_message(p *Properties, base structPointer) (err error) {
+ raw, e := o.DecodeRawBytes(false)
+ if e != nil {
+ return e
+ }
+
+ bas := structPointer_GetStructPointer(base, p.field)
+ if structPointer_IsNil(bas) {
+ // allocate new nested message
+ bas = toStructPointer(reflect.New(p.stype))
+ structPointer_SetStructPointer(base, p.field, bas)
+ }
+
+ // If the object can unmarshal itself, let it.
+ if p.isUnmarshaler {
+ iv := structPointer_Interface(bas, p.stype)
+ return iv.(Unmarshaler).Unmarshal(raw)
+ }
+
+ obuf := o.buf
+ oi := o.index
+ o.buf = raw
+ o.index = 0
+
+ err = o.unmarshalType(p.stype, p.sprop, false, bas)
+ o.buf = obuf
+ o.index = oi
+
+ return err
+}
+
+// Decode a slice of embedded messages.
+func (o *Buffer) dec_slice_struct_message(p *Properties, base structPointer) error {
+ return o.dec_slice_struct(p, false, base)
+}
+
+// Decode a slice of embedded groups.
+func (o *Buffer) dec_slice_struct_group(p *Properties, base structPointer) error {
+ return o.dec_slice_struct(p, true, base)
+}
+
+// Decode a slice of structs ([]*struct).
+func (o *Buffer) dec_slice_struct(p *Properties, is_group bool, base structPointer) error {
+ v := reflect.New(p.stype)
+ bas := toStructPointer(v)
+ structPointer_StructPointerSlice(base, p.field).Append(bas)
+
+ if is_group {
+ err := o.unmarshalType(p.stype, p.sprop, is_group, bas)
+ return err
+ }
+
+ raw, err := o.DecodeRawBytes(false)
+ if err != nil {
+ return err
+ }
+
+ // If the object can unmarshal itself, let it.
+ if p.isUnmarshaler {
+ iv := v.Interface()
+ return iv.(Unmarshaler).Unmarshal(raw)
+ }
+
+ obuf := o.buf
+ oi := o.index
+ o.buf = raw
+ o.index = 0
+
+ err = o.unmarshalType(p.stype, p.sprop, is_group, bas)
+
+ o.buf = obuf
+ o.index = oi
+
+ return err
+}
diff --git a/vendor/github.com/golang/protobuf/proto/encode.go b/vendor/github.com/golang/protobuf/proto/encode.go
new file mode 100644
index 00000000..8b84d1b2
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/encode.go
@@ -0,0 +1,1362 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+package proto
+
+/*
+ * Routines for encoding data into the wire format for protocol buffers.
+ */
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "sort"
+)
+
+// RequiredNotSetError is the error returned if Marshal is called with
+// a protocol buffer struct whose required fields have not
+// all been initialized. It is also the error returned if Unmarshal is
+// called with an encoded protocol buffer that does not include all the
+// required fields.
+//
+// When printed, RequiredNotSetError reports the first unset required field in a
+// message. If the field cannot be precisely determined, it is reported as
+// "{Unknown}".
+type RequiredNotSetError struct {
+ field string
+}
+
+func (e *RequiredNotSetError) Error() string {
+ return fmt.Sprintf("proto: required field %q not set", e.field)
+}
+
+var (
+ // errRepeatedHasNil is the error returned if Marshal is called with
+ // a struct with a repeated field containing a nil element.
+ errRepeatedHasNil = errors.New("proto: repeated field has nil element")
+
+ // errOneofHasNil is the error returned if Marshal is called with
+ // a struct with a oneof field containing a nil element.
+ errOneofHasNil = errors.New("proto: oneof field has nil value")
+
+ // ErrNil is the error returned if Marshal is called with nil.
+ ErrNil = errors.New("proto: Marshal called with nil")
+
+ // ErrTooLarge is the error returned if Marshal is called with a
+ // message that encodes to >2GB.
+ ErrTooLarge = errors.New("proto: message encodes to over 2 GB")
+)
+
+// The fundamental encoders that put bytes on the wire.
+// Those that take integer types all accept uint64 and are
+// therefore of type valueEncoder.
+
+const maxVarintBytes = 10 // maximum length of a varint
+
+// maxMarshalSize is the largest allowed size of an encoded protobuf,
+// since C++ and Java use signed int32s for the size.
+const maxMarshalSize = 1<<31 - 1
+
+// EncodeVarint returns the varint encoding of x.
+// This is the format for the
+// int32, int64, uint32, uint64, bool, and enum
+// protocol buffer types.
+// Not used by the package itself, but helpful to clients
+// wishing to use the same encoding.
+func EncodeVarint(x uint64) []byte {
+ var buf [maxVarintBytes]byte
+ var n int
+ for n = 0; x > 127; n++ {
+ buf[n] = 0x80 | uint8(x&0x7F)
+ x >>= 7
+ }
+ buf[n] = uint8(x)
+ n++
+ return buf[0:n]
+}
+
+// EncodeVarint writes a varint-encoded integer to the Buffer.
+// This is the format for the
+// int32, int64, uint32, uint64, bool, and enum
+// protocol buffer types.
+func (p *Buffer) EncodeVarint(x uint64) error {
+ for x >= 1<<7 {
+ p.buf = append(p.buf, uint8(x&0x7f|0x80))
+ x >>= 7
+ }
+ p.buf = append(p.buf, uint8(x))
+ return nil
+}
+
+// SizeVarint returns the varint encoding size of an integer.
+func SizeVarint(x uint64) int {
+ return sizeVarint(x)
+}
+
+func sizeVarint(x uint64) (n int) {
+ for {
+ n++
+ x >>= 7
+ if x == 0 {
+ break
+ }
+ }
+ return n
+}
+
+// EncodeFixed64 writes a 64-bit integer to the Buffer.
+// This is the format for the
+// fixed64, sfixed64, and double protocol buffer types.
+func (p *Buffer) EncodeFixed64(x uint64) error {
+ p.buf = append(p.buf,
+ uint8(x),
+ uint8(x>>8),
+ uint8(x>>16),
+ uint8(x>>24),
+ uint8(x>>32),
+ uint8(x>>40),
+ uint8(x>>48),
+ uint8(x>>56))
+ return nil
+}
+
+func sizeFixed64(x uint64) int {
+ return 8
+}
+
+// EncodeFixed32 writes a 32-bit integer to the Buffer.
+// This is the format for the
+// fixed32, sfixed32, and float protocol buffer types.
+func (p *Buffer) EncodeFixed32(x uint64) error {
+ p.buf = append(p.buf,
+ uint8(x),
+ uint8(x>>8),
+ uint8(x>>16),
+ uint8(x>>24))
+ return nil
+}
+
+func sizeFixed32(x uint64) int {
+ return 4
+}
+
+// EncodeZigzag64 writes a zigzag-encoded 64-bit integer
+// to the Buffer.
+// This is the format used for the sint64 protocol buffer type.
+func (p *Buffer) EncodeZigzag64(x uint64) error {
+ // use signed number to get arithmetic right shift.
+ return p.EncodeVarint((x << 1) ^ uint64((int64(x) >> 63)))
+}
+
+func sizeZigzag64(x uint64) int {
+ return sizeVarint((x << 1) ^ uint64((int64(x) >> 63)))
+}
+
+// EncodeZigzag32 writes a zigzag-encoded 32-bit integer
+// to the Buffer.
+// This is the format used for the sint32 protocol buffer type.
+func (p *Buffer) EncodeZigzag32(x uint64) error {
+ // use signed number to get arithmetic right shift.
+ return p.EncodeVarint(uint64((uint32(x) << 1) ^ uint32((int32(x) >> 31))))
+}
+
+func sizeZigzag32(x uint64) int {
+ return sizeVarint(uint64((uint32(x) << 1) ^ uint32((int32(x) >> 31))))
+}
+
+// EncodeRawBytes writes a count-delimited byte buffer to the Buffer.
+// This is the format used for the bytes protocol buffer
+// type and for embedded messages.
+func (p *Buffer) EncodeRawBytes(b []byte) error {
+ p.EncodeVarint(uint64(len(b)))
+ p.buf = append(p.buf, b...)
+ return nil
+}
+
+func sizeRawBytes(b []byte) int {
+ return sizeVarint(uint64(len(b))) +
+ len(b)
+}
+
+// EncodeStringBytes writes an encoded string to the Buffer.
+// This is the format used for the proto2 string type.
+func (p *Buffer) EncodeStringBytes(s string) error {
+ p.EncodeVarint(uint64(len(s)))
+ p.buf = append(p.buf, s...)
+ return nil
+}
+
+func sizeStringBytes(s string) int {
+ return sizeVarint(uint64(len(s))) +
+ len(s)
+}
+
+// Marshaler is the interface representing objects that can marshal themselves.
+type Marshaler interface {
+ Marshal() ([]byte, error)
+}
+
+// Marshal takes the protocol buffer
+// and encodes it into the wire format, returning the data.
+func Marshal(pb Message) ([]byte, error) {
+ // Can the object marshal itself?
+ if m, ok := pb.(Marshaler); ok {
+ return m.Marshal()
+ }
+ p := NewBuffer(nil)
+ err := p.Marshal(pb)
+ if p.buf == nil && err == nil {
+ // Return a non-nil slice on success.
+ return []byte{}, nil
+ }
+ return p.buf, err
+}
+
+// EncodeMessage writes the protocol buffer to the Buffer,
+// prefixed by a varint-encoded length.
+func (p *Buffer) EncodeMessage(pb Message) error {
+ t, base, err := getbase(pb)
+ if structPointer_IsNil(base) {
+ return ErrNil
+ }
+ if err == nil {
+ var state errorState
+ err = p.enc_len_struct(GetProperties(t.Elem()), base, &state)
+ }
+ return err
+}
+
+// Marshal takes the protocol buffer
+// and encodes it into the wire format, writing the result to the
+// Buffer.
+func (p *Buffer) Marshal(pb Message) error {
+ // Can the object marshal itself?
+ if m, ok := pb.(Marshaler); ok {
+ data, err := m.Marshal()
+ p.buf = append(p.buf, data...)
+ return err
+ }
+
+ t, base, err := getbase(pb)
+ if structPointer_IsNil(base) {
+ return ErrNil
+ }
+ if err == nil {
+ err = p.enc_struct(GetProperties(t.Elem()), base)
+ }
+
+ if collectStats {
+ (stats).Encode++ // Parens are to work around a goimports bug.
+ }
+
+ if len(p.buf) > maxMarshalSize {
+ return ErrTooLarge
+ }
+ return err
+}
+
+// Size returns the encoded size of a protocol buffer.
+func Size(pb Message) (n int) {
+ // Can the object marshal itself? If so, Size is slow.
+ // TODO: add Size to Marshaler, or add a Sizer interface.
+ if m, ok := pb.(Marshaler); ok {
+ b, _ := m.Marshal()
+ return len(b)
+ }
+
+ t, base, err := getbase(pb)
+ if structPointer_IsNil(base) {
+ return 0
+ }
+ if err == nil {
+ n = size_struct(GetProperties(t.Elem()), base)
+ }
+
+ if collectStats {
+ (stats).Size++ // Parens are to work around a goimports bug.
+ }
+
+ return
+}
+
+// Individual type encoders.
+
+// Encode a bool.
+func (o *Buffer) enc_bool(p *Properties, base structPointer) error {
+ v := *structPointer_Bool(base, p.field)
+ if v == nil {
+ return ErrNil
+ }
+ x := 0
+ if *v {
+ x = 1
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ p.valEnc(o, uint64(x))
+ return nil
+}
+
+func (o *Buffer) enc_proto3_bool(p *Properties, base structPointer) error {
+ v := *structPointer_BoolVal(base, p.field)
+ if !v {
+ return ErrNil
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ p.valEnc(o, 1)
+ return nil
+}
+
+func size_bool(p *Properties, base structPointer) int {
+ v := *structPointer_Bool(base, p.field)
+ if v == nil {
+ return 0
+ }
+ return len(p.tagcode) + 1 // each bool takes exactly one byte
+}
+
+func size_proto3_bool(p *Properties, base structPointer) int {
+ v := *structPointer_BoolVal(base, p.field)
+ if !v && !p.oneof {
+ return 0
+ }
+ return len(p.tagcode) + 1 // each bool takes exactly one byte
+}
+
+// Encode an int32.
+func (o *Buffer) enc_int32(p *Properties, base structPointer) error {
+ v := structPointer_Word32(base, p.field)
+ if word32_IsNil(v) {
+ return ErrNil
+ }
+ x := int32(word32_Get(v)) // permit sign extension to use full 64-bit range
+ o.buf = append(o.buf, p.tagcode...)
+ p.valEnc(o, uint64(x))
+ return nil
+}
+
+func (o *Buffer) enc_proto3_int32(p *Properties, base structPointer) error {
+ v := structPointer_Word32Val(base, p.field)
+ x := int32(word32Val_Get(v)) // permit sign extension to use full 64-bit range
+ if x == 0 {
+ return ErrNil
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ p.valEnc(o, uint64(x))
+ return nil
+}
+
+func size_int32(p *Properties, base structPointer) (n int) {
+ v := structPointer_Word32(base, p.field)
+ if word32_IsNil(v) {
+ return 0
+ }
+ x := int32(word32_Get(v)) // permit sign extension to use full 64-bit range
+ n += len(p.tagcode)
+ n += p.valSize(uint64(x))
+ return
+}
+
+func size_proto3_int32(p *Properties, base structPointer) (n int) {
+ v := structPointer_Word32Val(base, p.field)
+ x := int32(word32Val_Get(v)) // permit sign extension to use full 64-bit range
+ if x == 0 && !p.oneof {
+ return 0
+ }
+ n += len(p.tagcode)
+ n += p.valSize(uint64(x))
+ return
+}
+
+// Encode a uint32.
+// Exactly the same as int32, except for no sign extension.
+func (o *Buffer) enc_uint32(p *Properties, base structPointer) error {
+ v := structPointer_Word32(base, p.field)
+ if word32_IsNil(v) {
+ return ErrNil
+ }
+ x := word32_Get(v)
+ o.buf = append(o.buf, p.tagcode...)
+ p.valEnc(o, uint64(x))
+ return nil
+}
+
+func (o *Buffer) enc_proto3_uint32(p *Properties, base structPointer) error {
+ v := structPointer_Word32Val(base, p.field)
+ x := word32Val_Get(v)
+ if x == 0 {
+ return ErrNil
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ p.valEnc(o, uint64(x))
+ return nil
+}
+
+func size_uint32(p *Properties, base structPointer) (n int) {
+ v := structPointer_Word32(base, p.field)
+ if word32_IsNil(v) {
+ return 0
+ }
+ x := word32_Get(v)
+ n += len(p.tagcode)
+ n += p.valSize(uint64(x))
+ return
+}
+
+func size_proto3_uint32(p *Properties, base structPointer) (n int) {
+ v := structPointer_Word32Val(base, p.field)
+ x := word32Val_Get(v)
+ if x == 0 && !p.oneof {
+ return 0
+ }
+ n += len(p.tagcode)
+ n += p.valSize(uint64(x))
+ return
+}
+
+// Encode an int64.
+func (o *Buffer) enc_int64(p *Properties, base structPointer) error {
+ v := structPointer_Word64(base, p.field)
+ if word64_IsNil(v) {
+ return ErrNil
+ }
+ x := word64_Get(v)
+ o.buf = append(o.buf, p.tagcode...)
+ p.valEnc(o, x)
+ return nil
+}
+
+func (o *Buffer) enc_proto3_int64(p *Properties, base structPointer) error {
+ v := structPointer_Word64Val(base, p.field)
+ x := word64Val_Get(v)
+ if x == 0 {
+ return ErrNil
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ p.valEnc(o, x)
+ return nil
+}
+
+func size_int64(p *Properties, base structPointer) (n int) {
+ v := structPointer_Word64(base, p.field)
+ if word64_IsNil(v) {
+ return 0
+ }
+ x := word64_Get(v)
+ n += len(p.tagcode)
+ n += p.valSize(x)
+ return
+}
+
+func size_proto3_int64(p *Properties, base structPointer) (n int) {
+ v := structPointer_Word64Val(base, p.field)
+ x := word64Val_Get(v)
+ if x == 0 && !p.oneof {
+ return 0
+ }
+ n += len(p.tagcode)
+ n += p.valSize(x)
+ return
+}
+
+// Encode a string.
+func (o *Buffer) enc_string(p *Properties, base structPointer) error {
+ v := *structPointer_String(base, p.field)
+ if v == nil {
+ return ErrNil
+ }
+ x := *v
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeStringBytes(x)
+ return nil
+}
+
+func (o *Buffer) enc_proto3_string(p *Properties, base structPointer) error {
+ v := *structPointer_StringVal(base, p.field)
+ if v == "" {
+ return ErrNil
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeStringBytes(v)
+ return nil
+}
+
+func size_string(p *Properties, base structPointer) (n int) {
+ v := *structPointer_String(base, p.field)
+ if v == nil {
+ return 0
+ }
+ x := *v
+ n += len(p.tagcode)
+ n += sizeStringBytes(x)
+ return
+}
+
+func size_proto3_string(p *Properties, base structPointer) (n int) {
+ v := *structPointer_StringVal(base, p.field)
+ if v == "" && !p.oneof {
+ return 0
+ }
+ n += len(p.tagcode)
+ n += sizeStringBytes(v)
+ return
+}
+
+// All protocol buffer fields are nillable, but be careful.
+func isNil(v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+ return v.IsNil()
+ }
+ return false
+}
+
+// Encode a message struct.
+func (o *Buffer) enc_struct_message(p *Properties, base structPointer) error {
+ var state errorState
+ structp := structPointer_GetStructPointer(base, p.field)
+ if structPointer_IsNil(structp) {
+ return ErrNil
+ }
+
+ // Can the object marshal itself?
+ if p.isMarshaler {
+ m := structPointer_Interface(structp, p.stype).(Marshaler)
+ data, err := m.Marshal()
+ if err != nil && !state.shouldContinue(err, nil) {
+ return err
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeRawBytes(data)
+ return state.err
+ }
+
+ o.buf = append(o.buf, p.tagcode...)
+ return o.enc_len_struct(p.sprop, structp, &state)
+}
+
+func size_struct_message(p *Properties, base structPointer) int {
+ structp := structPointer_GetStructPointer(base, p.field)
+ if structPointer_IsNil(structp) {
+ return 0
+ }
+
+ // Can the object marshal itself?
+ if p.isMarshaler {
+ m := structPointer_Interface(structp, p.stype).(Marshaler)
+ data, _ := m.Marshal()
+ n0 := len(p.tagcode)
+ n1 := sizeRawBytes(data)
+ return n0 + n1
+ }
+
+ n0 := len(p.tagcode)
+ n1 := size_struct(p.sprop, structp)
+ n2 := sizeVarint(uint64(n1)) // size of encoded length
+ return n0 + n1 + n2
+}
+
+// Encode a group struct.
+func (o *Buffer) enc_struct_group(p *Properties, base structPointer) error {
+ var state errorState
+ b := structPointer_GetStructPointer(base, p.field)
+ if structPointer_IsNil(b) {
+ return ErrNil
+ }
+
+ o.EncodeVarint(uint64((p.Tag << 3) | WireStartGroup))
+ err := o.enc_struct(p.sprop, b)
+ if err != nil && !state.shouldContinue(err, nil) {
+ return err
+ }
+ o.EncodeVarint(uint64((p.Tag << 3) | WireEndGroup))
+ return state.err
+}
+
+func size_struct_group(p *Properties, base structPointer) (n int) {
+ b := structPointer_GetStructPointer(base, p.field)
+ if structPointer_IsNil(b) {
+ return 0
+ }
+
+ n += sizeVarint(uint64((p.Tag << 3) | WireStartGroup))
+ n += size_struct(p.sprop, b)
+ n += sizeVarint(uint64((p.Tag << 3) | WireEndGroup))
+ return
+}
+
+// Encode a slice of bools ([]bool).
+func (o *Buffer) enc_slice_bool(p *Properties, base structPointer) error {
+ s := *structPointer_BoolSlice(base, p.field)
+ l := len(s)
+ if l == 0 {
+ return ErrNil
+ }
+ for _, x := range s {
+ o.buf = append(o.buf, p.tagcode...)
+ v := uint64(0)
+ if x {
+ v = 1
+ }
+ p.valEnc(o, v)
+ }
+ return nil
+}
+
+func size_slice_bool(p *Properties, base structPointer) int {
+ s := *structPointer_BoolSlice(base, p.field)
+ l := len(s)
+ if l == 0 {
+ return 0
+ }
+ return l * (len(p.tagcode) + 1) // each bool takes exactly one byte
+}
+
+// Encode a slice of bools ([]bool) in packed format.
+func (o *Buffer) enc_slice_packed_bool(p *Properties, base structPointer) error {
+ s := *structPointer_BoolSlice(base, p.field)
+ l := len(s)
+ if l == 0 {
+ return ErrNil
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeVarint(uint64(l)) // each bool takes exactly one byte
+ for _, x := range s {
+ v := uint64(0)
+ if x {
+ v = 1
+ }
+ p.valEnc(o, v)
+ }
+ return nil
+}
+
+func size_slice_packed_bool(p *Properties, base structPointer) (n int) {
+ s := *structPointer_BoolSlice(base, p.field)
+ l := len(s)
+ if l == 0 {
+ return 0
+ }
+ n += len(p.tagcode)
+ n += sizeVarint(uint64(l))
+ n += l // each bool takes exactly one byte
+ return
+}
+
+// Encode a slice of bytes ([]byte).
+func (o *Buffer) enc_slice_byte(p *Properties, base structPointer) error {
+ s := *structPointer_Bytes(base, p.field)
+ if s == nil {
+ return ErrNil
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeRawBytes(s)
+ return nil
+}
+
+func (o *Buffer) enc_proto3_slice_byte(p *Properties, base structPointer) error {
+ s := *structPointer_Bytes(base, p.field)
+ if len(s) == 0 {
+ return ErrNil
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeRawBytes(s)
+ return nil
+}
+
+func size_slice_byte(p *Properties, base structPointer) (n int) {
+ s := *structPointer_Bytes(base, p.field)
+ if s == nil && !p.oneof {
+ return 0
+ }
+ n += len(p.tagcode)
+ n += sizeRawBytes(s)
+ return
+}
+
+func size_proto3_slice_byte(p *Properties, base structPointer) (n int) {
+ s := *structPointer_Bytes(base, p.field)
+ if len(s) == 0 && !p.oneof {
+ return 0
+ }
+ n += len(p.tagcode)
+ n += sizeRawBytes(s)
+ return
+}
+
+// Encode a slice of int32s ([]int32).
+func (o *Buffer) enc_slice_int32(p *Properties, base structPointer) error {
+ s := structPointer_Word32Slice(base, p.field)
+ l := s.Len()
+ if l == 0 {
+ return ErrNil
+ }
+ for i := 0; i < l; i++ {
+ o.buf = append(o.buf, p.tagcode...)
+ x := int32(s.Index(i)) // permit sign extension to use full 64-bit range
+ p.valEnc(o, uint64(x))
+ }
+ return nil
+}
+
+func size_slice_int32(p *Properties, base structPointer) (n int) {
+ s := structPointer_Word32Slice(base, p.field)
+ l := s.Len()
+ if l == 0 {
+ return 0
+ }
+ for i := 0; i < l; i++ {
+ n += len(p.tagcode)
+ x := int32(s.Index(i)) // permit sign extension to use full 64-bit range
+ n += p.valSize(uint64(x))
+ }
+ return
+}
+
+// Encode a slice of int32s ([]int32) in packed format.
+func (o *Buffer) enc_slice_packed_int32(p *Properties, base structPointer) error {
+ s := structPointer_Word32Slice(base, p.field)
+ l := s.Len()
+ if l == 0 {
+ return ErrNil
+ }
+ // TODO: Reuse a Buffer.
+ buf := NewBuffer(nil)
+ for i := 0; i < l; i++ {
+ x := int32(s.Index(i)) // permit sign extension to use full 64-bit range
+ p.valEnc(buf, uint64(x))
+ }
+
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeVarint(uint64(len(buf.buf)))
+ o.buf = append(o.buf, buf.buf...)
+ return nil
+}
+
+func size_slice_packed_int32(p *Properties, base structPointer) (n int) {
+ s := structPointer_Word32Slice(base, p.field)
+ l := s.Len()
+ if l == 0 {
+ return 0
+ }
+ var bufSize int
+ for i := 0; i < l; i++ {
+ x := int32(s.Index(i)) // permit sign extension to use full 64-bit range
+ bufSize += p.valSize(uint64(x))
+ }
+
+ n += len(p.tagcode)
+ n += sizeVarint(uint64(bufSize))
+ n += bufSize
+ return
+}
+
+// Encode a slice of uint32s ([]uint32).
+// Exactly the same as int32, except for no sign extension.
+func (o *Buffer) enc_slice_uint32(p *Properties, base structPointer) error {
+ s := structPointer_Word32Slice(base, p.field)
+ l := s.Len()
+ if l == 0 {
+ return ErrNil
+ }
+ for i := 0; i < l; i++ {
+ o.buf = append(o.buf, p.tagcode...)
+ x := s.Index(i)
+ p.valEnc(o, uint64(x))
+ }
+ return nil
+}
+
+func size_slice_uint32(p *Properties, base structPointer) (n int) {
+ s := structPointer_Word32Slice(base, p.field)
+ l := s.Len()
+ if l == 0 {
+ return 0
+ }
+ for i := 0; i < l; i++ {
+ n += len(p.tagcode)
+ x := s.Index(i)
+ n += p.valSize(uint64(x))
+ }
+ return
+}
+
+// Encode a slice of uint32s ([]uint32) in packed format.
+// Exactly the same as int32, except for no sign extension.
+func (o *Buffer) enc_slice_packed_uint32(p *Properties, base structPointer) error {
+ s := structPointer_Word32Slice(base, p.field)
+ l := s.Len()
+ if l == 0 {
+ return ErrNil
+ }
+ // TODO: Reuse a Buffer.
+ buf := NewBuffer(nil)
+ for i := 0; i < l; i++ {
+ p.valEnc(buf, uint64(s.Index(i)))
+ }
+
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeVarint(uint64(len(buf.buf)))
+ o.buf = append(o.buf, buf.buf...)
+ return nil
+}
+
+func size_slice_packed_uint32(p *Properties, base structPointer) (n int) {
+ s := structPointer_Word32Slice(base, p.field)
+ l := s.Len()
+ if l == 0 {
+ return 0
+ }
+ var bufSize int
+ for i := 0; i < l; i++ {
+ bufSize += p.valSize(uint64(s.Index(i)))
+ }
+
+ n += len(p.tagcode)
+ n += sizeVarint(uint64(bufSize))
+ n += bufSize
+ return
+}
+
+// Encode a slice of int64s ([]int64).
+func (o *Buffer) enc_slice_int64(p *Properties, base structPointer) error {
+ s := structPointer_Word64Slice(base, p.field)
+ l := s.Len()
+ if l == 0 {
+ return ErrNil
+ }
+ for i := 0; i < l; i++ {
+ o.buf = append(o.buf, p.tagcode...)
+ p.valEnc(o, s.Index(i))
+ }
+ return nil
+}
+
+func size_slice_int64(p *Properties, base structPointer) (n int) {
+ s := structPointer_Word64Slice(base, p.field)
+ l := s.Len()
+ if l == 0 {
+ return 0
+ }
+ for i := 0; i < l; i++ {
+ n += len(p.tagcode)
+ n += p.valSize(s.Index(i))
+ }
+ return
+}
+
+// Encode a slice of int64s ([]int64) in packed format.
+func (o *Buffer) enc_slice_packed_int64(p *Properties, base structPointer) error {
+ s := structPointer_Word64Slice(base, p.field)
+ l := s.Len()
+ if l == 0 {
+ return ErrNil
+ }
+ // TODO: Reuse a Buffer.
+ buf := NewBuffer(nil)
+ for i := 0; i < l; i++ {
+ p.valEnc(buf, s.Index(i))
+ }
+
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeVarint(uint64(len(buf.buf)))
+ o.buf = append(o.buf, buf.buf...)
+ return nil
+}
+
+func size_slice_packed_int64(p *Properties, base structPointer) (n int) {
+ s := structPointer_Word64Slice(base, p.field)
+ l := s.Len()
+ if l == 0 {
+ return 0
+ }
+ var bufSize int
+ for i := 0; i < l; i++ {
+ bufSize += p.valSize(s.Index(i))
+ }
+
+ n += len(p.tagcode)
+ n += sizeVarint(uint64(bufSize))
+ n += bufSize
+ return
+}
+
+// Encode a slice of slice of bytes ([][]byte).
+func (o *Buffer) enc_slice_slice_byte(p *Properties, base structPointer) error {
+ ss := *structPointer_BytesSlice(base, p.field)
+ l := len(ss)
+ if l == 0 {
+ return ErrNil
+ }
+ for i := 0; i < l; i++ {
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeRawBytes(ss[i])
+ }
+ return nil
+}
+
+func size_slice_slice_byte(p *Properties, base structPointer) (n int) {
+ ss := *structPointer_BytesSlice(base, p.field)
+ l := len(ss)
+ if l == 0 {
+ return 0
+ }
+ n += l * len(p.tagcode)
+ for i := 0; i < l; i++ {
+ n += sizeRawBytes(ss[i])
+ }
+ return
+}
+
+// Encode a slice of strings ([]string).
+func (o *Buffer) enc_slice_string(p *Properties, base structPointer) error {
+ ss := *structPointer_StringSlice(base, p.field)
+ l := len(ss)
+ for i := 0; i < l; i++ {
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeStringBytes(ss[i])
+ }
+ return nil
+}
+
+func size_slice_string(p *Properties, base structPointer) (n int) {
+ ss := *structPointer_StringSlice(base, p.field)
+ l := len(ss)
+ n += l * len(p.tagcode)
+ for i := 0; i < l; i++ {
+ n += sizeStringBytes(ss[i])
+ }
+ return
+}
+
+// Encode a slice of message structs ([]*struct).
+func (o *Buffer) enc_slice_struct_message(p *Properties, base structPointer) error {
+ var state errorState
+ s := structPointer_StructPointerSlice(base, p.field)
+ l := s.Len()
+
+ for i := 0; i < l; i++ {
+ structp := s.Index(i)
+ if structPointer_IsNil(structp) {
+ return errRepeatedHasNil
+ }
+
+ // Can the object marshal itself?
+ if p.isMarshaler {
+ m := structPointer_Interface(structp, p.stype).(Marshaler)
+ data, err := m.Marshal()
+ if err != nil && !state.shouldContinue(err, nil) {
+ return err
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeRawBytes(data)
+ continue
+ }
+
+ o.buf = append(o.buf, p.tagcode...)
+ err := o.enc_len_struct(p.sprop, structp, &state)
+ if err != nil && !state.shouldContinue(err, nil) {
+ if err == ErrNil {
+ return errRepeatedHasNil
+ }
+ return err
+ }
+ }
+ return state.err
+}
+
+func size_slice_struct_message(p *Properties, base structPointer) (n int) {
+ s := structPointer_StructPointerSlice(base, p.field)
+ l := s.Len()
+ n += l * len(p.tagcode)
+ for i := 0; i < l; i++ {
+ structp := s.Index(i)
+ if structPointer_IsNil(structp) {
+ return // return the size up to this point
+ }
+
+ // Can the object marshal itself?
+ if p.isMarshaler {
+ m := structPointer_Interface(structp, p.stype).(Marshaler)
+ data, _ := m.Marshal()
+ n += sizeRawBytes(data)
+ continue
+ }
+
+ n0 := size_struct(p.sprop, structp)
+ n1 := sizeVarint(uint64(n0)) // size of encoded length
+ n += n0 + n1
+ }
+ return
+}
+
+// Encode a slice of group structs ([]*struct).
+func (o *Buffer) enc_slice_struct_group(p *Properties, base structPointer) error {
+ var state errorState
+ s := structPointer_StructPointerSlice(base, p.field)
+ l := s.Len()
+
+ for i := 0; i < l; i++ {
+ b := s.Index(i)
+ if structPointer_IsNil(b) {
+ return errRepeatedHasNil
+ }
+
+ o.EncodeVarint(uint64((p.Tag << 3) | WireStartGroup))
+
+ err := o.enc_struct(p.sprop, b)
+
+ if err != nil && !state.shouldContinue(err, nil) {
+ if err == ErrNil {
+ return errRepeatedHasNil
+ }
+ return err
+ }
+
+ o.EncodeVarint(uint64((p.Tag << 3) | WireEndGroup))
+ }
+ return state.err
+}
+
+func size_slice_struct_group(p *Properties, base structPointer) (n int) {
+ s := structPointer_StructPointerSlice(base, p.field)
+ l := s.Len()
+
+ n += l * sizeVarint(uint64((p.Tag<<3)|WireStartGroup))
+ n += l * sizeVarint(uint64((p.Tag<<3)|WireEndGroup))
+ for i := 0; i < l; i++ {
+ b := s.Index(i)
+ if structPointer_IsNil(b) {
+ return // return size up to this point
+ }
+
+ n += size_struct(p.sprop, b)
+ }
+ return
+}
+
+// Encode an extension map.
+func (o *Buffer) enc_map(p *Properties, base structPointer) error {
+ exts := structPointer_ExtMap(base, p.field)
+ if err := encodeExtensionsMap(*exts); err != nil {
+ return err
+ }
+
+ return o.enc_map_body(*exts)
+}
+
+func (o *Buffer) enc_exts(p *Properties, base structPointer) error {
+ exts := structPointer_Extensions(base, p.field)
+
+ v, mu := exts.extensionsRead()
+ if v == nil {
+ return nil
+ }
+
+ mu.Lock()
+ defer mu.Unlock()
+ if err := encodeExtensionsMap(v); err != nil {
+ return err
+ }
+
+ return o.enc_map_body(v)
+}
+
+func (o *Buffer) enc_map_body(v map[int32]Extension) error {
+ // Fast-path for common cases: zero or one extensions.
+ if len(v) <= 1 {
+ for _, e := range v {
+ o.buf = append(o.buf, e.enc...)
+ }
+ return nil
+ }
+
+ // Sort keys to provide a deterministic encoding.
+ keys := make([]int, 0, len(v))
+ for k := range v {
+ keys = append(keys, int(k))
+ }
+ sort.Ints(keys)
+
+ for _, k := range keys {
+ o.buf = append(o.buf, v[int32(k)].enc...)
+ }
+ return nil
+}
+
+func size_map(p *Properties, base structPointer) int {
+ v := structPointer_ExtMap(base, p.field)
+ return extensionsMapSize(*v)
+}
+
+func size_exts(p *Properties, base structPointer) int {
+ v := structPointer_Extensions(base, p.field)
+ return extensionsSize(v)
+}
+
+// Encode a map field.
+func (o *Buffer) enc_new_map(p *Properties, base structPointer) error {
+ var state errorState // XXX: or do we need to plumb this through?
+
+ /*
+ A map defined as
+ map map_field = N;
+ is encoded in the same way as
+ message MapFieldEntry {
+ key_type key = 1;
+ value_type value = 2;
+ }
+ repeated MapFieldEntry map_field = N;
+ */
+
+ v := structPointer_NewAt(base, p.field, p.mtype).Elem() // map[K]V
+ if v.Len() == 0 {
+ return nil
+ }
+
+ keycopy, valcopy, keybase, valbase := mapEncodeScratch(p.mtype)
+
+ enc := func() error {
+ if err := p.mkeyprop.enc(o, p.mkeyprop, keybase); err != nil {
+ return err
+ }
+ if err := p.mvalprop.enc(o, p.mvalprop, valbase); err != nil && err != ErrNil {
+ return err
+ }
+ return nil
+ }
+
+ // Don't sort map keys. It is not required by the spec, and C++ doesn't do it.
+ for _, key := range v.MapKeys() {
+ val := v.MapIndex(key)
+
+ keycopy.Set(key)
+ valcopy.Set(val)
+
+ o.buf = append(o.buf, p.tagcode...)
+ if err := o.enc_len_thing(enc, &state); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func size_new_map(p *Properties, base structPointer) int {
+ v := structPointer_NewAt(base, p.field, p.mtype).Elem() // map[K]V
+
+ keycopy, valcopy, keybase, valbase := mapEncodeScratch(p.mtype)
+
+ n := 0
+ for _, key := range v.MapKeys() {
+ val := v.MapIndex(key)
+ keycopy.Set(key)
+ valcopy.Set(val)
+
+ // Tag codes for key and val are the responsibility of the sub-sizer.
+ keysize := p.mkeyprop.size(p.mkeyprop, keybase)
+ valsize := p.mvalprop.size(p.mvalprop, valbase)
+ entry := keysize + valsize
+ // Add on tag code and length of map entry itself.
+ n += len(p.tagcode) + sizeVarint(uint64(entry)) + entry
+ }
+ return n
+}
+
+// mapEncodeScratch returns a new reflect.Value matching the map's value type,
+// and a structPointer suitable for passing to an encoder or sizer.
+func mapEncodeScratch(mapType reflect.Type) (keycopy, valcopy reflect.Value, keybase, valbase structPointer) {
+ // Prepare addressable doubly-indirect placeholders for the key and value types.
+ // This is needed because the element-type encoders expect **T, but the map iteration produces T.
+
+ keycopy = reflect.New(mapType.Key()).Elem() // addressable K
+ keyptr := reflect.New(reflect.PtrTo(keycopy.Type())).Elem() // addressable *K
+ keyptr.Set(keycopy.Addr()) //
+ keybase = toStructPointer(keyptr.Addr()) // **K
+
+ // Value types are more varied and require special handling.
+ switch mapType.Elem().Kind() {
+ case reflect.Slice:
+ // []byte
+ var dummy []byte
+ valcopy = reflect.ValueOf(&dummy).Elem() // addressable []byte
+ valbase = toStructPointer(valcopy.Addr())
+ case reflect.Ptr:
+ // message; the generated field type is map[K]*Msg (so V is *Msg),
+ // so we only need one level of indirection.
+ valcopy = reflect.New(mapType.Elem()).Elem() // addressable V
+ valbase = toStructPointer(valcopy.Addr())
+ default:
+ // everything else
+ valcopy = reflect.New(mapType.Elem()).Elem() // addressable V
+ valptr := reflect.New(reflect.PtrTo(valcopy.Type())).Elem() // addressable *V
+ valptr.Set(valcopy.Addr()) //
+ valbase = toStructPointer(valptr.Addr()) // **V
+ }
+ return
+}
+
+// Encode a struct.
+func (o *Buffer) enc_struct(prop *StructProperties, base structPointer) error {
+ var state errorState
+ // Encode fields in tag order so that decoders may use optimizations
+ // that depend on the ordering.
+ // https://developers.google.com/protocol-buffers/docs/encoding#order
+ for _, i := range prop.order {
+ p := prop.Prop[i]
+ if p.enc != nil {
+ err := p.enc(o, p, base)
+ if err != nil {
+ if err == ErrNil {
+ if p.Required && state.err == nil {
+ state.err = &RequiredNotSetError{p.Name}
+ }
+ } else if err == errRepeatedHasNil {
+ // Give more context to nil values in repeated fields.
+ return errors.New("repeated field " + p.OrigName + " has nil element")
+ } else if !state.shouldContinue(err, p) {
+ return err
+ }
+ }
+ if len(o.buf) > maxMarshalSize {
+ return ErrTooLarge
+ }
+ }
+ }
+
+ // Do oneof fields.
+ if prop.oneofMarshaler != nil {
+ m := structPointer_Interface(base, prop.stype).(Message)
+ if err := prop.oneofMarshaler(m, o); err == ErrNil {
+ return errOneofHasNil
+ } else if err != nil {
+ return err
+ }
+ }
+
+ // Add unrecognized fields at the end.
+ if prop.unrecField.IsValid() {
+ v := *structPointer_Bytes(base, prop.unrecField)
+ if len(o.buf)+len(v) > maxMarshalSize {
+ return ErrTooLarge
+ }
+ if len(v) > 0 {
+ o.buf = append(o.buf, v...)
+ }
+ }
+
+ return state.err
+}
+
+func size_struct(prop *StructProperties, base structPointer) (n int) {
+ for _, i := range prop.order {
+ p := prop.Prop[i]
+ if p.size != nil {
+ n += p.size(p, base)
+ }
+ }
+
+ // Add unrecognized fields at the end.
+ if prop.unrecField.IsValid() {
+ v := *structPointer_Bytes(base, prop.unrecField)
+ n += len(v)
+ }
+
+ // Factor in any oneof fields.
+ if prop.oneofSizer != nil {
+ m := structPointer_Interface(base, prop.stype).(Message)
+ n += prop.oneofSizer(m)
+ }
+
+ return
+}
+
+var zeroes [20]byte // longer than any conceivable sizeVarint
+
+// Encode a struct, preceded by its encoded length (as a varint).
+func (o *Buffer) enc_len_struct(prop *StructProperties, base structPointer, state *errorState) error {
+ return o.enc_len_thing(func() error { return o.enc_struct(prop, base) }, state)
+}
+
+// Encode something, preceded by its encoded length (as a varint).
+func (o *Buffer) enc_len_thing(enc func() error, state *errorState) error {
+ iLen := len(o.buf)
+ o.buf = append(o.buf, 0, 0, 0, 0) // reserve four bytes for length
+ iMsg := len(o.buf)
+ err := enc()
+ if err != nil && !state.shouldContinue(err, nil) {
+ return err
+ }
+ lMsg := len(o.buf) - iMsg
+ lLen := sizeVarint(uint64(lMsg))
+ switch x := lLen - (iMsg - iLen); {
+ case x > 0: // actual length is x bytes larger than the space we reserved
+ // Move msg x bytes right.
+ o.buf = append(o.buf, zeroes[:x]...)
+ copy(o.buf[iMsg+x:], o.buf[iMsg:iMsg+lMsg])
+ case x < 0: // actual length is x bytes smaller than the space we reserved
+ // Move msg x bytes left.
+ copy(o.buf[iMsg+x:], o.buf[iMsg:iMsg+lMsg])
+ o.buf = o.buf[:len(o.buf)+x] // x is negative
+ }
+ // Encode the length in the reserved space.
+ o.buf = o.buf[:iLen]
+ o.EncodeVarint(uint64(lMsg))
+ o.buf = o.buf[:len(o.buf)+lMsg]
+ return state.err
+}
+
+// errorState maintains the first error that occurs and updates that error
+// with additional context.
+type errorState struct {
+ err error
+}
+
+// shouldContinue reports whether encoding should continue upon encountering the
+// given error. If the error is RequiredNotSetError, shouldContinue returns true
+// and, if this is the first appearance of that error, remembers it for future
+// reporting.
+//
+// If prop is not nil, it may update any error with additional context about the
+// field with the error.
+func (s *errorState) shouldContinue(err error, prop *Properties) bool {
+ // Ignore unset required fields.
+ reqNotSet, ok := err.(*RequiredNotSetError)
+ if !ok {
+ return false
+ }
+ if s.err == nil {
+ if prop != nil {
+ err = &RequiredNotSetError{prop.Name + "." + reqNotSet.field}
+ }
+ s.err = err
+ }
+ return true
+}
diff --git a/vendor/github.com/golang/protobuf/proto/equal.go b/vendor/github.com/golang/protobuf/proto/equal.go
new file mode 100644
index 00000000..2ed1cf59
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/equal.go
@@ -0,0 +1,300 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2011 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+// Protocol buffer comparison.
+
+package proto
+
+import (
+ "bytes"
+ "log"
+ "reflect"
+ "strings"
+)
+
+/*
+Equal returns true iff protocol buffers a and b are equal.
+The arguments must both be pointers to protocol buffer structs.
+
+Equality is defined in this way:
+ - Two messages are equal iff they are the same type,
+ corresponding fields are equal, unknown field sets
+ are equal, and extensions sets are equal.
+ - Two set scalar fields are equal iff their values are equal.
+ If the fields are of a floating-point type, remember that
+ NaN != x for all x, including NaN. If the message is defined
+ in a proto3 .proto file, fields are not "set"; specifically,
+ zero length proto3 "bytes" fields are equal (nil == {}).
+ - Two repeated fields are equal iff their lengths are the same,
+ and their corresponding elements are equal. Note a "bytes" field,
+ although represented by []byte, is not a repeated field and the
+ rule for the scalar fields described above applies.
+ - Two unset fields are equal.
+ - Two unknown field sets are equal if their current
+ encoded state is equal.
+ - Two extension sets are equal iff they have corresponding
+ elements that are pairwise equal.
+ - Two map fields are equal iff their lengths are the same,
+ and they contain the same set of elements. Zero-length map
+ fields are equal.
+ - Every other combination of things are not equal.
+
+The return value is undefined if a and b are not protocol buffers.
+*/
+func Equal(a, b Message) bool {
+ if a == nil || b == nil {
+ return a == b
+ }
+ v1, v2 := reflect.ValueOf(a), reflect.ValueOf(b)
+ if v1.Type() != v2.Type() {
+ return false
+ }
+ if v1.Kind() == reflect.Ptr {
+ if v1.IsNil() {
+ return v2.IsNil()
+ }
+ if v2.IsNil() {
+ return false
+ }
+ v1, v2 = v1.Elem(), v2.Elem()
+ }
+ if v1.Kind() != reflect.Struct {
+ return false
+ }
+ return equalStruct(v1, v2)
+}
+
+// v1 and v2 are known to have the same type.
+func equalStruct(v1, v2 reflect.Value) bool {
+ sprop := GetProperties(v1.Type())
+ for i := 0; i < v1.NumField(); i++ {
+ f := v1.Type().Field(i)
+ if strings.HasPrefix(f.Name, "XXX_") {
+ continue
+ }
+ f1, f2 := v1.Field(i), v2.Field(i)
+ if f.Type.Kind() == reflect.Ptr {
+ if n1, n2 := f1.IsNil(), f2.IsNil(); n1 && n2 {
+ // both unset
+ continue
+ } else if n1 != n2 {
+ // set/unset mismatch
+ return false
+ }
+ b1, ok := f1.Interface().(raw)
+ if ok {
+ b2 := f2.Interface().(raw)
+ // RawMessage
+ if !bytes.Equal(b1.Bytes(), b2.Bytes()) {
+ return false
+ }
+ continue
+ }
+ f1, f2 = f1.Elem(), f2.Elem()
+ }
+ if !equalAny(f1, f2, sprop.Prop[i]) {
+ return false
+ }
+ }
+
+ if em1 := v1.FieldByName("XXX_InternalExtensions"); em1.IsValid() {
+ em2 := v2.FieldByName("XXX_InternalExtensions")
+ if !equalExtensions(v1.Type(), em1.Interface().(XXX_InternalExtensions), em2.Interface().(XXX_InternalExtensions)) {
+ return false
+ }
+ }
+
+ if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() {
+ em2 := v2.FieldByName("XXX_extensions")
+ if !equalExtMap(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) {
+ return false
+ }
+ }
+
+ uf := v1.FieldByName("XXX_unrecognized")
+ if !uf.IsValid() {
+ return true
+ }
+
+ u1 := uf.Bytes()
+ u2 := v2.FieldByName("XXX_unrecognized").Bytes()
+ if !bytes.Equal(u1, u2) {
+ return false
+ }
+
+ return true
+}
+
+// v1 and v2 are known to have the same type.
+// prop may be nil.
+func equalAny(v1, v2 reflect.Value, prop *Properties) bool {
+ if v1.Type() == protoMessageType {
+ m1, _ := v1.Interface().(Message)
+ m2, _ := v2.Interface().(Message)
+ return Equal(m1, m2)
+ }
+ switch v1.Kind() {
+ case reflect.Bool:
+ return v1.Bool() == v2.Bool()
+ case reflect.Float32, reflect.Float64:
+ return v1.Float() == v2.Float()
+ case reflect.Int32, reflect.Int64:
+ return v1.Int() == v2.Int()
+ case reflect.Interface:
+ // Probably a oneof field; compare the inner values.
+ n1, n2 := v1.IsNil(), v2.IsNil()
+ if n1 || n2 {
+ return n1 == n2
+ }
+ e1, e2 := v1.Elem(), v2.Elem()
+ if e1.Type() != e2.Type() {
+ return false
+ }
+ return equalAny(e1, e2, nil)
+ case reflect.Map:
+ if v1.Len() != v2.Len() {
+ return false
+ }
+ for _, key := range v1.MapKeys() {
+ val2 := v2.MapIndex(key)
+ if !val2.IsValid() {
+ // This key was not found in the second map.
+ return false
+ }
+ if !equalAny(v1.MapIndex(key), val2, nil) {
+ return false
+ }
+ }
+ return true
+ case reflect.Ptr:
+ // Maps may have nil values in them, so check for nil.
+ if v1.IsNil() && v2.IsNil() {
+ return true
+ }
+ if v1.IsNil() != v2.IsNil() {
+ return false
+ }
+ return equalAny(v1.Elem(), v2.Elem(), prop)
+ case reflect.Slice:
+ if v1.Type().Elem().Kind() == reflect.Uint8 {
+ // short circuit: []byte
+
+ // Edge case: if this is in a proto3 message, a zero length
+ // bytes field is considered the zero value.
+ if prop != nil && prop.proto3 && v1.Len() == 0 && v2.Len() == 0 {
+ return true
+ }
+ if v1.IsNil() != v2.IsNil() {
+ return false
+ }
+ return bytes.Equal(v1.Interface().([]byte), v2.Interface().([]byte))
+ }
+
+ if v1.Len() != v2.Len() {
+ return false
+ }
+ for i := 0; i < v1.Len(); i++ {
+ if !equalAny(v1.Index(i), v2.Index(i), prop) {
+ return false
+ }
+ }
+ return true
+ case reflect.String:
+ return v1.Interface().(string) == v2.Interface().(string)
+ case reflect.Struct:
+ return equalStruct(v1, v2)
+ case reflect.Uint32, reflect.Uint64:
+ return v1.Uint() == v2.Uint()
+ }
+
+ // unknown type, so not a protocol buffer
+ log.Printf("proto: don't know how to compare %v", v1)
+ return false
+}
+
+// base is the struct type that the extensions are based on.
+// x1 and x2 are InternalExtensions.
+func equalExtensions(base reflect.Type, x1, x2 XXX_InternalExtensions) bool {
+ em1, _ := x1.extensionsRead()
+ em2, _ := x2.extensionsRead()
+ return equalExtMap(base, em1, em2)
+}
+
+func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool {
+ if len(em1) != len(em2) {
+ return false
+ }
+
+ for extNum, e1 := range em1 {
+ e2, ok := em2[extNum]
+ if !ok {
+ return false
+ }
+
+ m1, m2 := e1.value, e2.value
+
+ if m1 != nil && m2 != nil {
+ // Both are unencoded.
+ if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
+ return false
+ }
+ continue
+ }
+
+ // At least one is encoded. To do a semantically correct comparison
+ // we need to unmarshal them first.
+ var desc *ExtensionDesc
+ if m := extensionMaps[base]; m != nil {
+ desc = m[extNum]
+ }
+ if desc == nil {
+ log.Printf("proto: don't know how to compare extension %d of %v", extNum, base)
+ continue
+ }
+ var err error
+ if m1 == nil {
+ m1, err = decodeExtension(e1.enc, desc)
+ }
+ if m2 == nil && err == nil {
+ m2, err = decodeExtension(e2.enc, desc)
+ }
+ if err != nil {
+ // The encoded form is invalid.
+ log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err)
+ return false
+ }
+ if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
+ return false
+ }
+ }
+
+ return true
+}
diff --git a/vendor/github.com/golang/protobuf/proto/extensions.go b/vendor/github.com/golang/protobuf/proto/extensions.go
new file mode 100644
index 00000000..eaad2183
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/extensions.go
@@ -0,0 +1,587 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+package proto
+
+/*
+ * Types and routines for supporting protocol buffer extensions.
+ */
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+ "sync"
+)
+
+// ErrMissingExtension is the error returned by GetExtension if the named extension is not in the message.
+var ErrMissingExtension = errors.New("proto: missing extension")
+
+// ExtensionRange represents a range of message extensions for a protocol buffer.
+// Used in code generated by the protocol compiler.
+type ExtensionRange struct {
+ Start, End int32 // both inclusive
+}
+
+// extendableProto is an interface implemented by any protocol buffer generated by the current
+// proto compiler that may be extended.
+type extendableProto interface {
+ Message
+ ExtensionRangeArray() []ExtensionRange
+ extensionsWrite() map[int32]Extension
+ extensionsRead() (map[int32]Extension, sync.Locker)
+}
+
+// extendableProtoV1 is an interface implemented by a protocol buffer generated by the previous
+// version of the proto compiler that may be extended.
+type extendableProtoV1 interface {
+ Message
+ ExtensionRangeArray() []ExtensionRange
+ ExtensionMap() map[int32]Extension
+}
+
+// extensionAdapter is a wrapper around extendableProtoV1 that implements extendableProto.
+type extensionAdapter struct {
+ extendableProtoV1
+}
+
+func (e extensionAdapter) extensionsWrite() map[int32]Extension {
+ return e.ExtensionMap()
+}
+
+func (e extensionAdapter) extensionsRead() (map[int32]Extension, sync.Locker) {
+ return e.ExtensionMap(), notLocker{}
+}
+
+// notLocker is a sync.Locker whose Lock and Unlock methods are nops.
+type notLocker struct{}
+
+func (n notLocker) Lock() {}
+func (n notLocker) Unlock() {}
+
+// extendable returns the extendableProto interface for the given generated proto message.
+// If the proto message has the old extension format, it returns a wrapper that implements
+// the extendableProto interface.
+func extendable(p interface{}) (extendableProto, bool) {
+ if ep, ok := p.(extendableProto); ok {
+ return ep, ok
+ }
+ if ep, ok := p.(extendableProtoV1); ok {
+ return extensionAdapter{ep}, ok
+ }
+ return nil, false
+}
+
+// XXX_InternalExtensions is an internal representation of proto extensions.
+//
+// Each generated message struct type embeds an anonymous XXX_InternalExtensions field,
+// thus gaining the unexported 'extensions' method, which can be called only from the proto package.
+//
+// The methods of XXX_InternalExtensions are not concurrency safe in general,
+// but calls to logically read-only methods such as has and get may be executed concurrently.
+type XXX_InternalExtensions struct {
+ // The struct must be indirect so that if a user inadvertently copies a
+ // generated message and its embedded XXX_InternalExtensions, they
+ // avoid the mayhem of a copied mutex.
+ //
+ // The mutex serializes all logically read-only operations to p.extensionMap.
+ // It is up to the client to ensure that write operations to p.extensionMap are
+ // mutually exclusive with other accesses.
+ p *struct {
+ mu sync.Mutex
+ extensionMap map[int32]Extension
+ }
+}
+
+// extensionsWrite returns the extension map, creating it on first use.
+func (e *XXX_InternalExtensions) extensionsWrite() map[int32]Extension {
+ if e.p == nil {
+ e.p = new(struct {
+ mu sync.Mutex
+ extensionMap map[int32]Extension
+ })
+ e.p.extensionMap = make(map[int32]Extension)
+ }
+ return e.p.extensionMap
+}
+
+// extensionsRead returns the extensions map for read-only use. It may be nil.
+// The caller must hold the returned mutex's lock when accessing Elements within the map.
+func (e *XXX_InternalExtensions) extensionsRead() (map[int32]Extension, sync.Locker) {
+ if e.p == nil {
+ return nil, nil
+ }
+ return e.p.extensionMap, &e.p.mu
+}
+
+var extendableProtoType = reflect.TypeOf((*extendableProto)(nil)).Elem()
+var extendableProtoV1Type = reflect.TypeOf((*extendableProtoV1)(nil)).Elem()
+
+// ExtensionDesc represents an extension specification.
+// Used in generated code from the protocol compiler.
+type ExtensionDesc struct {
+ ExtendedType Message // nil pointer to the type that is being extended
+ ExtensionType interface{} // nil pointer to the extension type
+ Field int32 // field number
+ Name string // fully-qualified name of extension, for text formatting
+ Tag string // protobuf tag style
+ Filename string // name of the file in which the extension is defined
+}
+
+func (ed *ExtensionDesc) repeated() bool {
+ t := reflect.TypeOf(ed.ExtensionType)
+ return t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
+}
+
+// Extension represents an extension in a message.
+type Extension struct {
+ // When an extension is stored in a message using SetExtension
+ // only desc and value are set. When the message is marshaled
+ // enc will be set to the encoded form of the message.
+ //
+ // When a message is unmarshaled and contains extensions, each
+ // extension will have only enc set. When such an extension is
+ // accessed using GetExtension (or GetExtensions) desc and value
+ // will be set.
+ desc *ExtensionDesc
+ value interface{}
+ enc []byte
+}
+
+// SetRawExtension is for testing only.
+func SetRawExtension(base Message, id int32, b []byte) {
+ epb, ok := extendable(base)
+ if !ok {
+ return
+ }
+ extmap := epb.extensionsWrite()
+ extmap[id] = Extension{enc: b}
+}
+
+// isExtensionField returns true iff the given field number is in an extension range.
+func isExtensionField(pb extendableProto, field int32) bool {
+ for _, er := range pb.ExtensionRangeArray() {
+ if er.Start <= field && field <= er.End {
+ return true
+ }
+ }
+ return false
+}
+
+// checkExtensionTypes checks that the given extension is valid for pb.
+func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error {
+ var pbi interface{} = pb
+ // Check the extended type.
+ if ea, ok := pbi.(extensionAdapter); ok {
+ pbi = ea.extendableProtoV1
+ }
+ if a, b := reflect.TypeOf(pbi), reflect.TypeOf(extension.ExtendedType); a != b {
+ return errors.New("proto: bad extended type; " + b.String() + " does not extend " + a.String())
+ }
+ // Check the range.
+ if !isExtensionField(pb, extension.Field) {
+ return errors.New("proto: bad extension number; not in declared ranges")
+ }
+ return nil
+}
+
+// extPropKey is sufficient to uniquely identify an extension.
+type extPropKey struct {
+ base reflect.Type
+ field int32
+}
+
+var extProp = struct {
+ sync.RWMutex
+ m map[extPropKey]*Properties
+}{
+ m: make(map[extPropKey]*Properties),
+}
+
+func extensionProperties(ed *ExtensionDesc) *Properties {
+ key := extPropKey{base: reflect.TypeOf(ed.ExtendedType), field: ed.Field}
+
+ extProp.RLock()
+ if prop, ok := extProp.m[key]; ok {
+ extProp.RUnlock()
+ return prop
+ }
+ extProp.RUnlock()
+
+ extProp.Lock()
+ defer extProp.Unlock()
+ // Check again.
+ if prop, ok := extProp.m[key]; ok {
+ return prop
+ }
+
+ prop := new(Properties)
+ prop.Init(reflect.TypeOf(ed.ExtensionType), "unknown_name", ed.Tag, nil)
+ extProp.m[key] = prop
+ return prop
+}
+
+// encode encodes any unmarshaled (unencoded) extensions in e.
+func encodeExtensions(e *XXX_InternalExtensions) error {
+ m, mu := e.extensionsRead()
+ if m == nil {
+ return nil // fast path
+ }
+ mu.Lock()
+ defer mu.Unlock()
+ return encodeExtensionsMap(m)
+}
+
+// encode encodes any unmarshaled (unencoded) extensions in e.
+func encodeExtensionsMap(m map[int32]Extension) error {
+ for k, e := range m {
+ if e.value == nil || e.desc == nil {
+ // Extension is only in its encoded form.
+ continue
+ }
+
+ // We don't skip extensions that have an encoded form set,
+ // because the extension value may have been mutated after
+ // the last time this function was called.
+
+ et := reflect.TypeOf(e.desc.ExtensionType)
+ props := extensionProperties(e.desc)
+
+ p := NewBuffer(nil)
+ // If e.value has type T, the encoder expects a *struct{ X T }.
+ // Pass a *T with a zero field and hope it all works out.
+ x := reflect.New(et)
+ x.Elem().Set(reflect.ValueOf(e.value))
+ if err := props.enc(p, props, toStructPointer(x)); err != nil {
+ return err
+ }
+ e.enc = p.buf
+ m[k] = e
+ }
+ return nil
+}
+
+func extensionsSize(e *XXX_InternalExtensions) (n int) {
+ m, mu := e.extensionsRead()
+ if m == nil {
+ return 0
+ }
+ mu.Lock()
+ defer mu.Unlock()
+ return extensionsMapSize(m)
+}
+
+func extensionsMapSize(m map[int32]Extension) (n int) {
+ for _, e := range m {
+ if e.value == nil || e.desc == nil {
+ // Extension is only in its encoded form.
+ n += len(e.enc)
+ continue
+ }
+
+ // We don't skip extensions that have an encoded form set,
+ // because the extension value may have been mutated after
+ // the last time this function was called.
+
+ et := reflect.TypeOf(e.desc.ExtensionType)
+ props := extensionProperties(e.desc)
+
+ // If e.value has type T, the encoder expects a *struct{ X T }.
+ // Pass a *T with a zero field and hope it all works out.
+ x := reflect.New(et)
+ x.Elem().Set(reflect.ValueOf(e.value))
+ n += props.size(props, toStructPointer(x))
+ }
+ return
+}
+
+// HasExtension returns whether the given extension is present in pb.
+func HasExtension(pb Message, extension *ExtensionDesc) bool {
+ // TODO: Check types, field numbers, etc.?
+ epb, ok := extendable(pb)
+ if !ok {
+ return false
+ }
+ extmap, mu := epb.extensionsRead()
+ if extmap == nil {
+ return false
+ }
+ mu.Lock()
+ _, ok = extmap[extension.Field]
+ mu.Unlock()
+ return ok
+}
+
+// ClearExtension removes the given extension from pb.
+func ClearExtension(pb Message, extension *ExtensionDesc) {
+ epb, ok := extendable(pb)
+ if !ok {
+ return
+ }
+ // TODO: Check types, field numbers, etc.?
+ extmap := epb.extensionsWrite()
+ delete(extmap, extension.Field)
+}
+
+// GetExtension parses and returns the given extension of pb.
+// If the extension is not present and has no default value it returns ErrMissingExtension.
+func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
+ epb, ok := extendable(pb)
+ if !ok {
+ return nil, errors.New("proto: not an extendable proto")
+ }
+
+ if err := checkExtensionTypes(epb, extension); err != nil {
+ return nil, err
+ }
+
+ emap, mu := epb.extensionsRead()
+ if emap == nil {
+ return defaultExtensionValue(extension)
+ }
+ mu.Lock()
+ defer mu.Unlock()
+ e, ok := emap[extension.Field]
+ if !ok {
+ // defaultExtensionValue returns the default value or
+ // ErrMissingExtension if there is no default.
+ return defaultExtensionValue(extension)
+ }
+
+ if e.value != nil {
+ // Already decoded. Check the descriptor, though.
+ if e.desc != extension {
+ // This shouldn't happen. If it does, it means that
+ // GetExtension was called twice with two different
+ // descriptors with the same field number.
+ return nil, errors.New("proto: descriptor conflict")
+ }
+ return e.value, nil
+ }
+
+ v, err := decodeExtension(e.enc, extension)
+ if err != nil {
+ return nil, err
+ }
+
+ // Remember the decoded version and drop the encoded version.
+ // That way it is safe to mutate what we return.
+ e.value = v
+ e.desc = extension
+ e.enc = nil
+ emap[extension.Field] = e
+ return e.value, nil
+}
+
+// defaultExtensionValue returns the default value for extension.
+// If no default for an extension is defined ErrMissingExtension is returned.
+func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
+ t := reflect.TypeOf(extension.ExtensionType)
+ props := extensionProperties(extension)
+
+ sf, _, err := fieldDefault(t, props)
+ if err != nil {
+ return nil, err
+ }
+
+ if sf == nil || sf.value == nil {
+ // There is no default value.
+ return nil, ErrMissingExtension
+ }
+
+ if t.Kind() != reflect.Ptr {
+ // We do not need to return a Ptr, we can directly return sf.value.
+ return sf.value, nil
+ }
+
+ // We need to return an interface{} that is a pointer to sf.value.
+ value := reflect.New(t).Elem()
+ value.Set(reflect.New(value.Type().Elem()))
+ if sf.kind == reflect.Int32 {
+ // We may have an int32 or an enum, but the underlying data is int32.
+ // Since we can't set an int32 into a non int32 reflect.value directly
+ // set it as a int32.
+ value.Elem().SetInt(int64(sf.value.(int32)))
+ } else {
+ value.Elem().Set(reflect.ValueOf(sf.value))
+ }
+ return value.Interface(), nil
+}
+
+// decodeExtension decodes an extension encoded in b.
+func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
+ o := NewBuffer(b)
+
+ t := reflect.TypeOf(extension.ExtensionType)
+
+ props := extensionProperties(extension)
+
+ // t is a pointer to a struct, pointer to basic type or a slice.
+ // Allocate a "field" to store the pointer/slice itself; the
+ // pointer/slice will be stored here. We pass
+ // the address of this field to props.dec.
+ // This passes a zero field and a *t and lets props.dec
+ // interpret it as a *struct{ x t }.
+ value := reflect.New(t).Elem()
+
+ for {
+ // Discard wire type and field number varint. It isn't needed.
+ if _, err := o.DecodeVarint(); err != nil {
+ return nil, err
+ }
+
+ if err := props.dec(o, props, toStructPointer(value.Addr())); err != nil {
+ return nil, err
+ }
+
+ if o.index >= len(o.buf) {
+ break
+ }
+ }
+ return value.Interface(), nil
+}
+
+// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
+// The returned slice has the same length as es; missing extensions will appear as nil elements.
+func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) {
+ epb, ok := extendable(pb)
+ if !ok {
+ return nil, errors.New("proto: not an extendable proto")
+ }
+ extensions = make([]interface{}, len(es))
+ for i, e := range es {
+ extensions[i], err = GetExtension(epb, e)
+ if err == ErrMissingExtension {
+ err = nil
+ }
+ if err != nil {
+ return
+ }
+ }
+ return
+}
+
+// ExtensionDescs returns a new slice containing pb's extension descriptors, in undefined order.
+// For non-registered extensions, ExtensionDescs returns an incomplete descriptor containing
+// just the Field field, which defines the extension's field number.
+func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) {
+ epb, ok := extendable(pb)
+ if !ok {
+ return nil, fmt.Errorf("proto: %T is not an extendable proto.Message", pb)
+ }
+ registeredExtensions := RegisteredExtensions(pb)
+
+ emap, mu := epb.extensionsRead()
+ if emap == nil {
+ return nil, nil
+ }
+ mu.Lock()
+ defer mu.Unlock()
+ extensions := make([]*ExtensionDesc, 0, len(emap))
+ for extid, e := range emap {
+ desc := e.desc
+ if desc == nil {
+ desc = registeredExtensions[extid]
+ if desc == nil {
+ desc = &ExtensionDesc{Field: extid}
+ }
+ }
+
+ extensions = append(extensions, desc)
+ }
+ return extensions, nil
+}
+
+// SetExtension sets the specified extension of pb to the specified value.
+func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error {
+ epb, ok := extendable(pb)
+ if !ok {
+ return errors.New("proto: not an extendable proto")
+ }
+ if err := checkExtensionTypes(epb, extension); err != nil {
+ return err
+ }
+ typ := reflect.TypeOf(extension.ExtensionType)
+ if typ != reflect.TypeOf(value) {
+ return errors.New("proto: bad extension value type")
+ }
+ // nil extension values need to be caught early, because the
+ // encoder can't distinguish an ErrNil due to a nil extension
+ // from an ErrNil due to a missing field. Extensions are
+ // always optional, so the encoder would just swallow the error
+ // and drop all the extensions from the encoded message.
+ if reflect.ValueOf(value).IsNil() {
+ return fmt.Errorf("proto: SetExtension called with nil value of type %T", value)
+ }
+
+ extmap := epb.extensionsWrite()
+ extmap[extension.Field] = Extension{desc: extension, value: value}
+ return nil
+}
+
+// ClearAllExtensions clears all extensions from pb.
+func ClearAllExtensions(pb Message) {
+ epb, ok := extendable(pb)
+ if !ok {
+ return
+ }
+ m := epb.extensionsWrite()
+ for k := range m {
+ delete(m, k)
+ }
+}
+
+// A global registry of extensions.
+// The generated code will register the generated descriptors by calling RegisterExtension.
+
+var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc)
+
+// RegisterExtension is called from the generated code.
+func RegisterExtension(desc *ExtensionDesc) {
+ st := reflect.TypeOf(desc.ExtendedType).Elem()
+ m := extensionMaps[st]
+ if m == nil {
+ m = make(map[int32]*ExtensionDesc)
+ extensionMaps[st] = m
+ }
+ if _, ok := m[desc.Field]; ok {
+ panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field)))
+ }
+ m[desc.Field] = desc
+}
+
+// RegisteredExtensions returns a map of the registered extensions of a
+// protocol buffer struct, indexed by the extension number.
+// The argument pb should be a nil pointer to the struct type.
+func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc {
+ return extensionMaps[reflect.TypeOf(pb).Elem()]
+}
diff --git a/vendor/github.com/golang/protobuf/proto/lib.go b/vendor/github.com/golang/protobuf/proto/lib.go
new file mode 100644
index 00000000..1c225504
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/lib.go
@@ -0,0 +1,897 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+/*
+Package proto converts data structures to and from the wire format of
+protocol buffers. It works in concert with the Go source code generated
+for .proto files by the protocol compiler.
+
+A summary of the properties of the protocol buffer interface
+for a protocol buffer variable v:
+
+ - Names are turned from camel_case to CamelCase for export.
+ - There are no methods on v to set fields; just treat
+ them as structure fields.
+ - There are getters that return a field's value if set,
+ and return the field's default value if unset.
+ The getters work even if the receiver is a nil message.
+ - The zero value for a struct is its correct initialization state.
+ All desired fields must be set before marshaling.
+ - A Reset() method will restore a protobuf struct to its zero state.
+ - Non-repeated fields are pointers to the values; nil means unset.
+ That is, optional or required field int32 f becomes F *int32.
+ - Repeated fields are slices.
+ - Helper functions are available to aid the setting of fields.
+ msg.Foo = proto.String("hello") // set field
+ - Constants are defined to hold the default values of all fields that
+ have them. They have the form Default_StructName_FieldName.
+ Because the getter methods handle defaulted values,
+ direct use of these constants should be rare.
+ - Enums are given type names and maps from names to values.
+ Enum values are prefixed by the enclosing message's name, or by the
+ enum's type name if it is a top-level enum. Enum types have a String
+ method, and a Enum method to assist in message construction.
+ - Nested messages, groups and enums have type names prefixed with the name of
+ the surrounding message type.
+ - Extensions are given descriptor names that start with E_,
+ followed by an underscore-delimited list of the nested messages
+ that contain it (if any) followed by the CamelCased name of the
+ extension field itself. HasExtension, ClearExtension, GetExtension
+ and SetExtension are functions for manipulating extensions.
+ - Oneof field sets are given a single field in their message,
+ with distinguished wrapper types for each possible field value.
+ - Marshal and Unmarshal are functions to encode and decode the wire format.
+
+When the .proto file specifies `syntax="proto3"`, there are some differences:
+
+ - Non-repeated fields of non-message type are values instead of pointers.
+ - Enum types do not get an Enum method.
+
+The simplest way to describe this is to see an example.
+Given file test.proto, containing
+
+ package example;
+
+ enum FOO { X = 17; }
+
+ message Test {
+ required string label = 1;
+ optional int32 type = 2 [default=77];
+ repeated int64 reps = 3;
+ optional group OptionalGroup = 4 {
+ required string RequiredField = 5;
+ }
+ oneof union {
+ int32 number = 6;
+ string name = 7;
+ }
+ }
+
+The resulting file, test.pb.go, is:
+
+ package example
+
+ import proto "github.com/golang/protobuf/proto"
+ import math "math"
+
+ type FOO int32
+ const (
+ FOO_X FOO = 17
+ )
+ var FOO_name = map[int32]string{
+ 17: "X",
+ }
+ var FOO_value = map[string]int32{
+ "X": 17,
+ }
+
+ func (x FOO) Enum() *FOO {
+ p := new(FOO)
+ *p = x
+ return p
+ }
+ func (x FOO) String() string {
+ return proto.EnumName(FOO_name, int32(x))
+ }
+ func (x *FOO) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(FOO_value, data)
+ if err != nil {
+ return err
+ }
+ *x = FOO(value)
+ return nil
+ }
+
+ type Test struct {
+ Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"`
+ Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"`
+ Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"`
+ Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"`
+ // Types that are valid to be assigned to Union:
+ // *Test_Number
+ // *Test_Name
+ Union isTest_Union `protobuf_oneof:"union"`
+ XXX_unrecognized []byte `json:"-"`
+ }
+ func (m *Test) Reset() { *m = Test{} }
+ func (m *Test) String() string { return proto.CompactTextString(m) }
+ func (*Test) ProtoMessage() {}
+
+ type isTest_Union interface {
+ isTest_Union()
+ }
+
+ type Test_Number struct {
+ Number int32 `protobuf:"varint,6,opt,name=number"`
+ }
+ type Test_Name struct {
+ Name string `protobuf:"bytes,7,opt,name=name"`
+ }
+
+ func (*Test_Number) isTest_Union() {}
+ func (*Test_Name) isTest_Union() {}
+
+ func (m *Test) GetUnion() isTest_Union {
+ if m != nil {
+ return m.Union
+ }
+ return nil
+ }
+ const Default_Test_Type int32 = 77
+
+ func (m *Test) GetLabel() string {
+ if m != nil && m.Label != nil {
+ return *m.Label
+ }
+ return ""
+ }
+
+ func (m *Test) GetType() int32 {
+ if m != nil && m.Type != nil {
+ return *m.Type
+ }
+ return Default_Test_Type
+ }
+
+ func (m *Test) GetOptionalgroup() *Test_OptionalGroup {
+ if m != nil {
+ return m.Optionalgroup
+ }
+ return nil
+ }
+
+ type Test_OptionalGroup struct {
+ RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"`
+ }
+ func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} }
+ func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) }
+
+ func (m *Test_OptionalGroup) GetRequiredField() string {
+ if m != nil && m.RequiredField != nil {
+ return *m.RequiredField
+ }
+ return ""
+ }
+
+ func (m *Test) GetNumber() int32 {
+ if x, ok := m.GetUnion().(*Test_Number); ok {
+ return x.Number
+ }
+ return 0
+ }
+
+ func (m *Test) GetName() string {
+ if x, ok := m.GetUnion().(*Test_Name); ok {
+ return x.Name
+ }
+ return ""
+ }
+
+ func init() {
+ proto.RegisterEnum("example.FOO", FOO_name, FOO_value)
+ }
+
+To create and play with a Test object:
+
+ package main
+
+ import (
+ "log"
+
+ "github.com/golang/protobuf/proto"
+ pb "./example.pb"
+ )
+
+ func main() {
+ test := &pb.Test{
+ Label: proto.String("hello"),
+ Type: proto.Int32(17),
+ Reps: []int64{1, 2, 3},
+ Optionalgroup: &pb.Test_OptionalGroup{
+ RequiredField: proto.String("good bye"),
+ },
+ Union: &pb.Test_Name{"fred"},
+ }
+ data, err := proto.Marshal(test)
+ if err != nil {
+ log.Fatal("marshaling error: ", err)
+ }
+ newTest := &pb.Test{}
+ err = proto.Unmarshal(data, newTest)
+ if err != nil {
+ log.Fatal("unmarshaling error: ", err)
+ }
+ // Now test and newTest contain the same data.
+ if test.GetLabel() != newTest.GetLabel() {
+ log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel())
+ }
+ // Use a type switch to determine which oneof was set.
+ switch u := test.Union.(type) {
+ case *pb.Test_Number: // u.Number contains the number.
+ case *pb.Test_Name: // u.Name contains the string.
+ }
+ // etc.
+ }
+*/
+package proto
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+ "reflect"
+ "sort"
+ "strconv"
+ "sync"
+)
+
+// Message is implemented by generated protocol buffer messages.
+type Message interface {
+ Reset()
+ String() string
+ ProtoMessage()
+}
+
+// Stats records allocation details about the protocol buffer encoders
+// and decoders. Useful for tuning the library itself.
+type Stats struct {
+ Emalloc uint64 // mallocs in encode
+ Dmalloc uint64 // mallocs in decode
+ Encode uint64 // number of encodes
+ Decode uint64 // number of decodes
+ Chit uint64 // number of cache hits
+ Cmiss uint64 // number of cache misses
+ Size uint64 // number of sizes
+}
+
+// Set to true to enable stats collection.
+const collectStats = false
+
+var stats Stats
+
+// GetStats returns a copy of the global Stats structure.
+func GetStats() Stats { return stats }
+
+// A Buffer is a buffer manager for marshaling and unmarshaling
+// protocol buffers. It may be reused between invocations to
+// reduce memory usage. It is not necessary to use a Buffer;
+// the global functions Marshal and Unmarshal create a
+// temporary Buffer and are fine for most applications.
+type Buffer struct {
+ buf []byte // encode/decode byte stream
+ index int // read point
+
+ // pools of basic types to amortize allocation.
+ bools []bool
+ uint32s []uint32
+ uint64s []uint64
+
+ // extra pools, only used with pointer_reflect.go
+ int32s []int32
+ int64s []int64
+ float32s []float32
+ float64s []float64
+}
+
+// NewBuffer allocates a new Buffer and initializes its internal data to
+// the contents of the argument slice.
+func NewBuffer(e []byte) *Buffer {
+ return &Buffer{buf: e}
+}
+
+// Reset resets the Buffer, ready for marshaling a new protocol buffer.
+func (p *Buffer) Reset() {
+ p.buf = p.buf[0:0] // for reading/writing
+ p.index = 0 // for reading
+}
+
+// SetBuf replaces the internal buffer with the slice,
+// ready for unmarshaling the contents of the slice.
+func (p *Buffer) SetBuf(s []byte) {
+ p.buf = s
+ p.index = 0
+}
+
+// Bytes returns the contents of the Buffer.
+func (p *Buffer) Bytes() []byte { return p.buf }
+
+/*
+ * Helper routines for simplifying the creation of optional fields of basic type.
+ */
+
+// Bool is a helper routine that allocates a new bool value
+// to store v and returns a pointer to it.
+func Bool(v bool) *bool {
+ return &v
+}
+
+// Int32 is a helper routine that allocates a new int32 value
+// to store v and returns a pointer to it.
+func Int32(v int32) *int32 {
+ return &v
+}
+
+// Int is a helper routine that allocates a new int32 value
+// to store v and returns a pointer to it, but unlike Int32
+// its argument value is an int.
+func Int(v int) *int32 {
+ p := new(int32)
+ *p = int32(v)
+ return p
+}
+
+// Int64 is a helper routine that allocates a new int64 value
+// to store v and returns a pointer to it.
+func Int64(v int64) *int64 {
+ return &v
+}
+
+// Float32 is a helper routine that allocates a new float32 value
+// to store v and returns a pointer to it.
+func Float32(v float32) *float32 {
+ return &v
+}
+
+// Float64 is a helper routine that allocates a new float64 value
+// to store v and returns a pointer to it.
+func Float64(v float64) *float64 {
+ return &v
+}
+
+// Uint32 is a helper routine that allocates a new uint32 value
+// to store v and returns a pointer to it.
+func Uint32(v uint32) *uint32 {
+ return &v
+}
+
+// Uint64 is a helper routine that allocates a new uint64 value
+// to store v and returns a pointer to it.
+func Uint64(v uint64) *uint64 {
+ return &v
+}
+
+// String is a helper routine that allocates a new string value
+// to store v and returns a pointer to it.
+func String(v string) *string {
+ return &v
+}
+
+// EnumName is a helper function to simplify printing protocol buffer enums
+// by name. Given an enum map and a value, it returns a useful string.
+func EnumName(m map[int32]string, v int32) string {
+ s, ok := m[v]
+ if ok {
+ return s
+ }
+ return strconv.Itoa(int(v))
+}
+
+// UnmarshalJSONEnum is a helper function to simplify recovering enum int values
+// from their JSON-encoded representation. Given a map from the enum's symbolic
+// names to its int values, and a byte buffer containing the JSON-encoded
+// value, it returns an int32 that can be cast to the enum type by the caller.
+//
+// The function can deal with both JSON representations, numeric and symbolic.
+func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) {
+ if data[0] == '"' {
+ // New style: enums are strings.
+ var repr string
+ if err := json.Unmarshal(data, &repr); err != nil {
+ return -1, err
+ }
+ val, ok := m[repr]
+ if !ok {
+ return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr)
+ }
+ return val, nil
+ }
+ // Old style: enums are ints.
+ var val int32
+ if err := json.Unmarshal(data, &val); err != nil {
+ return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName)
+ }
+ return val, nil
+}
+
+// DebugPrint dumps the encoded data in b in a debugging format with a header
+// including the string s. Used in testing but made available for general debugging.
+func (p *Buffer) DebugPrint(s string, b []byte) {
+ var u uint64
+
+ obuf := p.buf
+ index := p.index
+ p.buf = b
+ p.index = 0
+ depth := 0
+
+ fmt.Printf("\n--- %s ---\n", s)
+
+out:
+ for {
+ for i := 0; i < depth; i++ {
+ fmt.Print(" ")
+ }
+
+ index := p.index
+ if index == len(p.buf) {
+ break
+ }
+
+ op, err := p.DecodeVarint()
+ if err != nil {
+ fmt.Printf("%3d: fetching op err %v\n", index, err)
+ break out
+ }
+ tag := op >> 3
+ wire := op & 7
+
+ switch wire {
+ default:
+ fmt.Printf("%3d: t=%3d unknown wire=%d\n",
+ index, tag, wire)
+ break out
+
+ case WireBytes:
+ var r []byte
+
+ r, err = p.DecodeRawBytes(false)
+ if err != nil {
+ break out
+ }
+ fmt.Printf("%3d: t=%3d bytes [%d]", index, tag, len(r))
+ if len(r) <= 6 {
+ for i := 0; i < len(r); i++ {
+ fmt.Printf(" %.2x", r[i])
+ }
+ } else {
+ for i := 0; i < 3; i++ {
+ fmt.Printf(" %.2x", r[i])
+ }
+ fmt.Printf(" ..")
+ for i := len(r) - 3; i < len(r); i++ {
+ fmt.Printf(" %.2x", r[i])
+ }
+ }
+ fmt.Printf("\n")
+
+ case WireFixed32:
+ u, err = p.DecodeFixed32()
+ if err != nil {
+ fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err)
+ break out
+ }
+ fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u)
+
+ case WireFixed64:
+ u, err = p.DecodeFixed64()
+ if err != nil {
+ fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err)
+ break out
+ }
+ fmt.Printf("%3d: t=%3d fix64 %d\n", index, tag, u)
+
+ case WireVarint:
+ u, err = p.DecodeVarint()
+ if err != nil {
+ fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err)
+ break out
+ }
+ fmt.Printf("%3d: t=%3d varint %d\n", index, tag, u)
+
+ case WireStartGroup:
+ fmt.Printf("%3d: t=%3d start\n", index, tag)
+ depth++
+
+ case WireEndGroup:
+ depth--
+ fmt.Printf("%3d: t=%3d end\n", index, tag)
+ }
+ }
+
+ if depth != 0 {
+ fmt.Printf("%3d: start-end not balanced %d\n", p.index, depth)
+ }
+ fmt.Printf("\n")
+
+ p.buf = obuf
+ p.index = index
+}
+
+// SetDefaults sets unset protocol buffer fields to their default values.
+// It only modifies fields that are both unset and have defined defaults.
+// It recursively sets default values in any non-nil sub-messages.
+func SetDefaults(pb Message) {
+ setDefaults(reflect.ValueOf(pb), true, false)
+}
+
+// v is a pointer to a struct.
+func setDefaults(v reflect.Value, recur, zeros bool) {
+ v = v.Elem()
+
+ defaultMu.RLock()
+ dm, ok := defaults[v.Type()]
+ defaultMu.RUnlock()
+ if !ok {
+ dm = buildDefaultMessage(v.Type())
+ defaultMu.Lock()
+ defaults[v.Type()] = dm
+ defaultMu.Unlock()
+ }
+
+ for _, sf := range dm.scalars {
+ f := v.Field(sf.index)
+ if !f.IsNil() {
+ // field already set
+ continue
+ }
+ dv := sf.value
+ if dv == nil && !zeros {
+ // no explicit default, and don't want to set zeros
+ continue
+ }
+ fptr := f.Addr().Interface() // **T
+ // TODO: Consider batching the allocations we do here.
+ switch sf.kind {
+ case reflect.Bool:
+ b := new(bool)
+ if dv != nil {
+ *b = dv.(bool)
+ }
+ *(fptr.(**bool)) = b
+ case reflect.Float32:
+ f := new(float32)
+ if dv != nil {
+ *f = dv.(float32)
+ }
+ *(fptr.(**float32)) = f
+ case reflect.Float64:
+ f := new(float64)
+ if dv != nil {
+ *f = dv.(float64)
+ }
+ *(fptr.(**float64)) = f
+ case reflect.Int32:
+ // might be an enum
+ if ft := f.Type(); ft != int32PtrType {
+ // enum
+ f.Set(reflect.New(ft.Elem()))
+ if dv != nil {
+ f.Elem().SetInt(int64(dv.(int32)))
+ }
+ } else {
+ // int32 field
+ i := new(int32)
+ if dv != nil {
+ *i = dv.(int32)
+ }
+ *(fptr.(**int32)) = i
+ }
+ case reflect.Int64:
+ i := new(int64)
+ if dv != nil {
+ *i = dv.(int64)
+ }
+ *(fptr.(**int64)) = i
+ case reflect.String:
+ s := new(string)
+ if dv != nil {
+ *s = dv.(string)
+ }
+ *(fptr.(**string)) = s
+ case reflect.Uint8:
+ // exceptional case: []byte
+ var b []byte
+ if dv != nil {
+ db := dv.([]byte)
+ b = make([]byte, len(db))
+ copy(b, db)
+ } else {
+ b = []byte{}
+ }
+ *(fptr.(*[]byte)) = b
+ case reflect.Uint32:
+ u := new(uint32)
+ if dv != nil {
+ *u = dv.(uint32)
+ }
+ *(fptr.(**uint32)) = u
+ case reflect.Uint64:
+ u := new(uint64)
+ if dv != nil {
+ *u = dv.(uint64)
+ }
+ *(fptr.(**uint64)) = u
+ default:
+ log.Printf("proto: can't set default for field %v (sf.kind=%v)", f, sf.kind)
+ }
+ }
+
+ for _, ni := range dm.nested {
+ f := v.Field(ni)
+ // f is *T or []*T or map[T]*T
+ switch f.Kind() {
+ case reflect.Ptr:
+ if f.IsNil() {
+ continue
+ }
+ setDefaults(f, recur, zeros)
+
+ case reflect.Slice:
+ for i := 0; i < f.Len(); i++ {
+ e := f.Index(i)
+ if e.IsNil() {
+ continue
+ }
+ setDefaults(e, recur, zeros)
+ }
+
+ case reflect.Map:
+ for _, k := range f.MapKeys() {
+ e := f.MapIndex(k)
+ if e.IsNil() {
+ continue
+ }
+ setDefaults(e, recur, zeros)
+ }
+ }
+ }
+}
+
+var (
+ // defaults maps a protocol buffer struct type to a slice of the fields,
+ // with its scalar fields set to their proto-declared non-zero default values.
+ defaultMu sync.RWMutex
+ defaults = make(map[reflect.Type]defaultMessage)
+
+ int32PtrType = reflect.TypeOf((*int32)(nil))
+)
+
+// defaultMessage represents information about the default values of a message.
+type defaultMessage struct {
+ scalars []scalarField
+ nested []int // struct field index of nested messages
+}
+
+type scalarField struct {
+ index int // struct field index
+ kind reflect.Kind // element type (the T in *T or []T)
+ value interface{} // the proto-declared default value, or nil
+}
+
+// t is a struct type.
+func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
+ sprop := GetProperties(t)
+ for _, prop := range sprop.Prop {
+ fi, ok := sprop.decoderTags.get(prop.Tag)
+ if !ok {
+ // XXX_unrecognized
+ continue
+ }
+ ft := t.Field(fi).Type
+
+ sf, nested, err := fieldDefault(ft, prop)
+ switch {
+ case err != nil:
+ log.Print(err)
+ case nested:
+ dm.nested = append(dm.nested, fi)
+ case sf != nil:
+ sf.index = fi
+ dm.scalars = append(dm.scalars, *sf)
+ }
+ }
+
+ return dm
+}
+
+// fieldDefault returns the scalarField for field type ft.
+// sf will be nil if the field can not have a default.
+// nestedMessage will be true if this is a nested message.
+// Note that sf.index is not set on return.
+func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) {
+ var canHaveDefault bool
+ switch ft.Kind() {
+ case reflect.Ptr:
+ if ft.Elem().Kind() == reflect.Struct {
+ nestedMessage = true
+ } else {
+ canHaveDefault = true // proto2 scalar field
+ }
+
+ case reflect.Slice:
+ switch ft.Elem().Kind() {
+ case reflect.Ptr:
+ nestedMessage = true // repeated message
+ case reflect.Uint8:
+ canHaveDefault = true // bytes field
+ }
+
+ case reflect.Map:
+ if ft.Elem().Kind() == reflect.Ptr {
+ nestedMessage = true // map with message values
+ }
+ }
+
+ if !canHaveDefault {
+ if nestedMessage {
+ return nil, true, nil
+ }
+ return nil, false, nil
+ }
+
+ // We now know that ft is a pointer or slice.
+ sf = &scalarField{kind: ft.Elem().Kind()}
+
+ // scalar fields without defaults
+ if !prop.HasDefault {
+ return sf, false, nil
+ }
+
+ // a scalar field: either *T or []byte
+ switch ft.Elem().Kind() {
+ case reflect.Bool:
+ x, err := strconv.ParseBool(prop.Default)
+ if err != nil {
+ return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err)
+ }
+ sf.value = x
+ case reflect.Float32:
+ x, err := strconv.ParseFloat(prop.Default, 32)
+ if err != nil {
+ return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err)
+ }
+ sf.value = float32(x)
+ case reflect.Float64:
+ x, err := strconv.ParseFloat(prop.Default, 64)
+ if err != nil {
+ return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err)
+ }
+ sf.value = x
+ case reflect.Int32:
+ x, err := strconv.ParseInt(prop.Default, 10, 32)
+ if err != nil {
+ return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err)
+ }
+ sf.value = int32(x)
+ case reflect.Int64:
+ x, err := strconv.ParseInt(prop.Default, 10, 64)
+ if err != nil {
+ return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err)
+ }
+ sf.value = x
+ case reflect.String:
+ sf.value = prop.Default
+ case reflect.Uint8:
+ // []byte (not *uint8)
+ sf.value = []byte(prop.Default)
+ case reflect.Uint32:
+ x, err := strconv.ParseUint(prop.Default, 10, 32)
+ if err != nil {
+ return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err)
+ }
+ sf.value = uint32(x)
+ case reflect.Uint64:
+ x, err := strconv.ParseUint(prop.Default, 10, 64)
+ if err != nil {
+ return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err)
+ }
+ sf.value = x
+ default:
+ return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind())
+ }
+
+ return sf, false, nil
+}
+
+// Map fields may have key types of non-float scalars, strings and enums.
+// The easiest way to sort them in some deterministic order is to use fmt.
+// If this turns out to be inefficient we can always consider other options,
+// such as doing a Schwartzian transform.
+
+func mapKeys(vs []reflect.Value) sort.Interface {
+ s := mapKeySorter{
+ vs: vs,
+ // default Less function: textual comparison
+ less: func(a, b reflect.Value) bool {
+ return fmt.Sprint(a.Interface()) < fmt.Sprint(b.Interface())
+ },
+ }
+
+ // Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps;
+ // numeric keys are sorted numerically.
+ if len(vs) == 0 {
+ return s
+ }
+ switch vs[0].Kind() {
+ case reflect.Int32, reflect.Int64:
+ s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
+ case reflect.Uint32, reflect.Uint64:
+ s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
+ }
+
+ return s
+}
+
+type mapKeySorter struct {
+ vs []reflect.Value
+ less func(a, b reflect.Value) bool
+}
+
+func (s mapKeySorter) Len() int { return len(s.vs) }
+func (s mapKeySorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] }
+func (s mapKeySorter) Less(i, j int) bool {
+ return s.less(s.vs[i], s.vs[j])
+}
+
+// isProto3Zero reports whether v is a zero proto3 value.
+func isProto3Zero(v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Bool:
+ return !v.Bool()
+ case reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Uint32, reflect.Uint64:
+ return v.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return v.Float() == 0
+ case reflect.String:
+ return v.String() == ""
+ }
+ return false
+}
+
+// ProtoPackageIsVersion2 is referenced from generated protocol buffer files
+// to assert that that code is compatible with this version of the proto package.
+const ProtoPackageIsVersion2 = true
+
+// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
+// to assert that that code is compatible with this version of the proto package.
+const ProtoPackageIsVersion1 = true
diff --git a/vendor/github.com/golang/protobuf/proto/message_set.go b/vendor/github.com/golang/protobuf/proto/message_set.go
new file mode 100644
index 00000000..fd982dec
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/message_set.go
@@ -0,0 +1,311 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+package proto
+
+/*
+ * Support for message sets.
+ */
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "reflect"
+ "sort"
+)
+
+// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID.
+// A message type ID is required for storing a protocol buffer in a message set.
+var errNoMessageTypeID = errors.New("proto does not have a message type ID")
+
+// The first two types (_MessageSet_Item and messageSet)
+// model what the protocol compiler produces for the following protocol message:
+// message MessageSet {
+// repeated group Item = 1 {
+// required int32 type_id = 2;
+// required string message = 3;
+// };
+// }
+// That is the MessageSet wire format. We can't use a proto to generate these
+// because that would introduce a circular dependency between it and this package.
+
+type _MessageSet_Item struct {
+ TypeId *int32 `protobuf:"varint,2,req,name=type_id"`
+ Message []byte `protobuf:"bytes,3,req,name=message"`
+}
+
+type messageSet struct {
+ Item []*_MessageSet_Item `protobuf:"group,1,rep"`
+ XXX_unrecognized []byte
+ // TODO: caching?
+}
+
+// Make sure messageSet is a Message.
+var _ Message = (*messageSet)(nil)
+
+// messageTypeIder is an interface satisfied by a protocol buffer type
+// that may be stored in a MessageSet.
+type messageTypeIder interface {
+ MessageTypeId() int32
+}
+
+func (ms *messageSet) find(pb Message) *_MessageSet_Item {
+ mti, ok := pb.(messageTypeIder)
+ if !ok {
+ return nil
+ }
+ id := mti.MessageTypeId()
+ for _, item := range ms.Item {
+ if *item.TypeId == id {
+ return item
+ }
+ }
+ return nil
+}
+
+func (ms *messageSet) Has(pb Message) bool {
+ if ms.find(pb) != nil {
+ return true
+ }
+ return false
+}
+
+func (ms *messageSet) Unmarshal(pb Message) error {
+ if item := ms.find(pb); item != nil {
+ return Unmarshal(item.Message, pb)
+ }
+ if _, ok := pb.(messageTypeIder); !ok {
+ return errNoMessageTypeID
+ }
+ return nil // TODO: return error instead?
+}
+
+func (ms *messageSet) Marshal(pb Message) error {
+ msg, err := Marshal(pb)
+ if err != nil {
+ return err
+ }
+ if item := ms.find(pb); item != nil {
+ // reuse existing item
+ item.Message = msg
+ return nil
+ }
+
+ mti, ok := pb.(messageTypeIder)
+ if !ok {
+ return errNoMessageTypeID
+ }
+
+ mtid := mti.MessageTypeId()
+ ms.Item = append(ms.Item, &_MessageSet_Item{
+ TypeId: &mtid,
+ Message: msg,
+ })
+ return nil
+}
+
+func (ms *messageSet) Reset() { *ms = messageSet{} }
+func (ms *messageSet) String() string { return CompactTextString(ms) }
+func (*messageSet) ProtoMessage() {}
+
+// Support for the message_set_wire_format message option.
+
+func skipVarint(buf []byte) []byte {
+ i := 0
+ for ; buf[i]&0x80 != 0; i++ {
+ }
+ return buf[i+1:]
+}
+
+// MarshalMessageSet encodes the extension map represented by m in the message set wire format.
+// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option.
+func MarshalMessageSet(exts interface{}) ([]byte, error) {
+ var m map[int32]Extension
+ switch exts := exts.(type) {
+ case *XXX_InternalExtensions:
+ if err := encodeExtensions(exts); err != nil {
+ return nil, err
+ }
+ m, _ = exts.extensionsRead()
+ case map[int32]Extension:
+ if err := encodeExtensionsMap(exts); err != nil {
+ return nil, err
+ }
+ m = exts
+ default:
+ return nil, errors.New("proto: not an extension map")
+ }
+
+ // Sort extension IDs to provide a deterministic encoding.
+ // See also enc_map in encode.go.
+ ids := make([]int, 0, len(m))
+ for id := range m {
+ ids = append(ids, int(id))
+ }
+ sort.Ints(ids)
+
+ ms := &messageSet{Item: make([]*_MessageSet_Item, 0, len(m))}
+ for _, id := range ids {
+ e := m[int32(id)]
+ // Remove the wire type and field number varint, as well as the length varint.
+ msg := skipVarint(skipVarint(e.enc))
+
+ ms.Item = append(ms.Item, &_MessageSet_Item{
+ TypeId: Int32(int32(id)),
+ Message: msg,
+ })
+ }
+ return Marshal(ms)
+}
+
+// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
+// It is called by generated Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
+func UnmarshalMessageSet(buf []byte, exts interface{}) error {
+ var m map[int32]Extension
+ switch exts := exts.(type) {
+ case *XXX_InternalExtensions:
+ m = exts.extensionsWrite()
+ case map[int32]Extension:
+ m = exts
+ default:
+ return errors.New("proto: not an extension map")
+ }
+
+ ms := new(messageSet)
+ if err := Unmarshal(buf, ms); err != nil {
+ return err
+ }
+ for _, item := range ms.Item {
+ id := *item.TypeId
+ msg := item.Message
+
+ // Restore wire type and field number varint, plus length varint.
+ // Be careful to preserve duplicate items.
+ b := EncodeVarint(uint64(id)<<3 | WireBytes)
+ if ext, ok := m[id]; ok {
+ // Existing data; rip off the tag and length varint
+ // so we join the new data correctly.
+ // We can assume that ext.enc is set because we are unmarshaling.
+ o := ext.enc[len(b):] // skip wire type and field number
+ _, n := DecodeVarint(o) // calculate length of length varint
+ o = o[n:] // skip length varint
+ msg = append(o, msg...) // join old data and new data
+ }
+ b = append(b, EncodeVarint(uint64(len(msg)))...)
+ b = append(b, msg...)
+
+ m[id] = Extension{enc: b}
+ }
+ return nil
+}
+
+// MarshalMessageSetJSON encodes the extension map represented by m in JSON format.
+// It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
+func MarshalMessageSetJSON(exts interface{}) ([]byte, error) {
+ var m map[int32]Extension
+ switch exts := exts.(type) {
+ case *XXX_InternalExtensions:
+ m, _ = exts.extensionsRead()
+ case map[int32]Extension:
+ m = exts
+ default:
+ return nil, errors.New("proto: not an extension map")
+ }
+ var b bytes.Buffer
+ b.WriteByte('{')
+
+ // Process the map in key order for deterministic output.
+ ids := make([]int32, 0, len(m))
+ for id := range m {
+ ids = append(ids, id)
+ }
+ sort.Sort(int32Slice(ids)) // int32Slice defined in text.go
+
+ for i, id := range ids {
+ ext := m[id]
+ if i > 0 {
+ b.WriteByte(',')
+ }
+
+ msd, ok := messageSetMap[id]
+ if !ok {
+ // Unknown type; we can't render it, so skip it.
+ continue
+ }
+ fmt.Fprintf(&b, `"[%s]":`, msd.name)
+
+ x := ext.value
+ if x == nil {
+ x = reflect.New(msd.t.Elem()).Interface()
+ if err := Unmarshal(ext.enc, x.(Message)); err != nil {
+ return nil, err
+ }
+ }
+ d, err := json.Marshal(x)
+ if err != nil {
+ return nil, err
+ }
+ b.Write(d)
+ }
+ b.WriteByte('}')
+ return b.Bytes(), nil
+}
+
+// UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format.
+// It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
+func UnmarshalMessageSetJSON(buf []byte, exts interface{}) error {
+ // Common-case fast path.
+ if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) {
+ return nil
+ }
+
+ // This is fairly tricky, and it's not clear that it is needed.
+ return errors.New("TODO: UnmarshalMessageSetJSON not yet implemented")
+}
+
+// A global registry of types that can be used in a MessageSet.
+
+var messageSetMap = make(map[int32]messageSetDesc)
+
+type messageSetDesc struct {
+ t reflect.Type // pointer to struct
+ name string
+}
+
+// RegisterMessageSetType is called from the generated code.
+func RegisterMessageSetType(m Message, fieldNum int32, name string) {
+ messageSetMap[fieldNum] = messageSetDesc{
+ t: reflect.TypeOf(m),
+ name: name,
+ }
+}
diff --git a/vendor/github.com/golang/protobuf/proto/pointer_reflect.go b/vendor/github.com/golang/protobuf/proto/pointer_reflect.go
new file mode 100644
index 00000000..fb512e2e
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/pointer_reflect.go
@@ -0,0 +1,484 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2012 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+// +build appengine js
+
+// This file contains an implementation of proto field accesses using package reflect.
+// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can
+// be used on App Engine.
+
+package proto
+
+import (
+ "math"
+ "reflect"
+)
+
+// A structPointer is a pointer to a struct.
+type structPointer struct {
+ v reflect.Value
+}
+
+// toStructPointer returns a structPointer equivalent to the given reflect value.
+// The reflect value must itself be a pointer to a struct.
+func toStructPointer(v reflect.Value) structPointer {
+ return structPointer{v}
+}
+
+// IsNil reports whether p is nil.
+func structPointer_IsNil(p structPointer) bool {
+ return p.v.IsNil()
+}
+
+// Interface returns the struct pointer as an interface value.
+func structPointer_Interface(p structPointer, _ reflect.Type) interface{} {
+ return p.v.Interface()
+}
+
+// A field identifies a field in a struct, accessible from a structPointer.
+// In this implementation, a field is identified by the sequence of field indices
+// passed to reflect's FieldByIndex.
+type field []int
+
+// toField returns a field equivalent to the given reflect field.
+func toField(f *reflect.StructField) field {
+ return f.Index
+}
+
+// invalidField is an invalid field identifier.
+var invalidField = field(nil)
+
+// IsValid reports whether the field identifier is valid.
+func (f field) IsValid() bool { return f != nil }
+
+// field returns the given field in the struct as a reflect value.
+func structPointer_field(p structPointer, f field) reflect.Value {
+ // Special case: an extension map entry with a value of type T
+ // passes a *T to the struct-handling code with a zero field,
+ // expecting that it will be treated as equivalent to *struct{ X T },
+ // which has the same memory layout. We have to handle that case
+ // specially, because reflect will panic if we call FieldByIndex on a
+ // non-struct.
+ if f == nil {
+ return p.v.Elem()
+ }
+
+ return p.v.Elem().FieldByIndex(f)
+}
+
+// ifield returns the given field in the struct as an interface value.
+func structPointer_ifield(p structPointer, f field) interface{} {
+ return structPointer_field(p, f).Addr().Interface()
+}
+
+// Bytes returns the address of a []byte field in the struct.
+func structPointer_Bytes(p structPointer, f field) *[]byte {
+ return structPointer_ifield(p, f).(*[]byte)
+}
+
+// BytesSlice returns the address of a [][]byte field in the struct.
+func structPointer_BytesSlice(p structPointer, f field) *[][]byte {
+ return structPointer_ifield(p, f).(*[][]byte)
+}
+
+// Bool returns the address of a *bool field in the struct.
+func structPointer_Bool(p structPointer, f field) **bool {
+ return structPointer_ifield(p, f).(**bool)
+}
+
+// BoolVal returns the address of a bool field in the struct.
+func structPointer_BoolVal(p structPointer, f field) *bool {
+ return structPointer_ifield(p, f).(*bool)
+}
+
+// BoolSlice returns the address of a []bool field in the struct.
+func structPointer_BoolSlice(p structPointer, f field) *[]bool {
+ return structPointer_ifield(p, f).(*[]bool)
+}
+
+// String returns the address of a *string field in the struct.
+func structPointer_String(p structPointer, f field) **string {
+ return structPointer_ifield(p, f).(**string)
+}
+
+// StringVal returns the address of a string field in the struct.
+func structPointer_StringVal(p structPointer, f field) *string {
+ return structPointer_ifield(p, f).(*string)
+}
+
+// StringSlice returns the address of a []string field in the struct.
+func structPointer_StringSlice(p structPointer, f field) *[]string {
+ return structPointer_ifield(p, f).(*[]string)
+}
+
+// Extensions returns the address of an extension map field in the struct.
+func structPointer_Extensions(p structPointer, f field) *XXX_InternalExtensions {
+ return structPointer_ifield(p, f).(*XXX_InternalExtensions)
+}
+
+// ExtMap returns the address of an extension map field in the struct.
+func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
+ return structPointer_ifield(p, f).(*map[int32]Extension)
+}
+
+// NewAt returns the reflect.Value for a pointer to a field in the struct.
+func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value {
+ return structPointer_field(p, f).Addr()
+}
+
+// SetStructPointer writes a *struct field in the struct.
+func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
+ structPointer_field(p, f).Set(q.v)
+}
+
+// GetStructPointer reads a *struct field in the struct.
+func structPointer_GetStructPointer(p structPointer, f field) structPointer {
+ return structPointer{structPointer_field(p, f)}
+}
+
+// StructPointerSlice the address of a []*struct field in the struct.
+func structPointer_StructPointerSlice(p structPointer, f field) structPointerSlice {
+ return structPointerSlice{structPointer_field(p, f)}
+}
+
+// A structPointerSlice represents the address of a slice of pointers to structs
+// (themselves messages or groups). That is, v.Type() is *[]*struct{...}.
+type structPointerSlice struct {
+ v reflect.Value
+}
+
+func (p structPointerSlice) Len() int { return p.v.Len() }
+func (p structPointerSlice) Index(i int) structPointer { return structPointer{p.v.Index(i)} }
+func (p structPointerSlice) Append(q structPointer) {
+ p.v.Set(reflect.Append(p.v, q.v))
+}
+
+var (
+ int32Type = reflect.TypeOf(int32(0))
+ uint32Type = reflect.TypeOf(uint32(0))
+ float32Type = reflect.TypeOf(float32(0))
+ int64Type = reflect.TypeOf(int64(0))
+ uint64Type = reflect.TypeOf(uint64(0))
+ float64Type = reflect.TypeOf(float64(0))
+)
+
+// A word32 represents a field of type *int32, *uint32, *float32, or *enum.
+// That is, v.Type() is *int32, *uint32, *float32, or *enum and v is assignable.
+type word32 struct {
+ v reflect.Value
+}
+
+// IsNil reports whether p is nil.
+func word32_IsNil(p word32) bool {
+ return p.v.IsNil()
+}
+
+// Set sets p to point at a newly allocated word with bits set to x.
+func word32_Set(p word32, o *Buffer, x uint32) {
+ t := p.v.Type().Elem()
+ switch t {
+ case int32Type:
+ if len(o.int32s) == 0 {
+ o.int32s = make([]int32, uint32PoolSize)
+ }
+ o.int32s[0] = int32(x)
+ p.v.Set(reflect.ValueOf(&o.int32s[0]))
+ o.int32s = o.int32s[1:]
+ return
+ case uint32Type:
+ if len(o.uint32s) == 0 {
+ o.uint32s = make([]uint32, uint32PoolSize)
+ }
+ o.uint32s[0] = x
+ p.v.Set(reflect.ValueOf(&o.uint32s[0]))
+ o.uint32s = o.uint32s[1:]
+ return
+ case float32Type:
+ if len(o.float32s) == 0 {
+ o.float32s = make([]float32, uint32PoolSize)
+ }
+ o.float32s[0] = math.Float32frombits(x)
+ p.v.Set(reflect.ValueOf(&o.float32s[0]))
+ o.float32s = o.float32s[1:]
+ return
+ }
+
+ // must be enum
+ p.v.Set(reflect.New(t))
+ p.v.Elem().SetInt(int64(int32(x)))
+}
+
+// Get gets the bits pointed at by p, as a uint32.
+func word32_Get(p word32) uint32 {
+ elem := p.v.Elem()
+ switch elem.Kind() {
+ case reflect.Int32:
+ return uint32(elem.Int())
+ case reflect.Uint32:
+ return uint32(elem.Uint())
+ case reflect.Float32:
+ return math.Float32bits(float32(elem.Float()))
+ }
+ panic("unreachable")
+}
+
+// Word32 returns a reference to a *int32, *uint32, *float32, or *enum field in the struct.
+func structPointer_Word32(p structPointer, f field) word32 {
+ return word32{structPointer_field(p, f)}
+}
+
+// A word32Val represents a field of type int32, uint32, float32, or enum.
+// That is, v.Type() is int32, uint32, float32, or enum and v is assignable.
+type word32Val struct {
+ v reflect.Value
+}
+
+// Set sets *p to x.
+func word32Val_Set(p word32Val, x uint32) {
+ switch p.v.Type() {
+ case int32Type:
+ p.v.SetInt(int64(x))
+ return
+ case uint32Type:
+ p.v.SetUint(uint64(x))
+ return
+ case float32Type:
+ p.v.SetFloat(float64(math.Float32frombits(x)))
+ return
+ }
+
+ // must be enum
+ p.v.SetInt(int64(int32(x)))
+}
+
+// Get gets the bits pointed at by p, as a uint32.
+func word32Val_Get(p word32Val) uint32 {
+ elem := p.v
+ switch elem.Kind() {
+ case reflect.Int32:
+ return uint32(elem.Int())
+ case reflect.Uint32:
+ return uint32(elem.Uint())
+ case reflect.Float32:
+ return math.Float32bits(float32(elem.Float()))
+ }
+ panic("unreachable")
+}
+
+// Word32Val returns a reference to a int32, uint32, float32, or enum field in the struct.
+func structPointer_Word32Val(p structPointer, f field) word32Val {
+ return word32Val{structPointer_field(p, f)}
+}
+
+// A word32Slice is a slice of 32-bit values.
+// That is, v.Type() is []int32, []uint32, []float32, or []enum.
+type word32Slice struct {
+ v reflect.Value
+}
+
+func (p word32Slice) Append(x uint32) {
+ n, m := p.v.Len(), p.v.Cap()
+ if n < m {
+ p.v.SetLen(n + 1)
+ } else {
+ t := p.v.Type().Elem()
+ p.v.Set(reflect.Append(p.v, reflect.Zero(t)))
+ }
+ elem := p.v.Index(n)
+ switch elem.Kind() {
+ case reflect.Int32:
+ elem.SetInt(int64(int32(x)))
+ case reflect.Uint32:
+ elem.SetUint(uint64(x))
+ case reflect.Float32:
+ elem.SetFloat(float64(math.Float32frombits(x)))
+ }
+}
+
+func (p word32Slice) Len() int {
+ return p.v.Len()
+}
+
+func (p word32Slice) Index(i int) uint32 {
+ elem := p.v.Index(i)
+ switch elem.Kind() {
+ case reflect.Int32:
+ return uint32(elem.Int())
+ case reflect.Uint32:
+ return uint32(elem.Uint())
+ case reflect.Float32:
+ return math.Float32bits(float32(elem.Float()))
+ }
+ panic("unreachable")
+}
+
+// Word32Slice returns a reference to a []int32, []uint32, []float32, or []enum field in the struct.
+func structPointer_Word32Slice(p structPointer, f field) word32Slice {
+ return word32Slice{structPointer_field(p, f)}
+}
+
+// word64 is like word32 but for 64-bit values.
+type word64 struct {
+ v reflect.Value
+}
+
+func word64_Set(p word64, o *Buffer, x uint64) {
+ t := p.v.Type().Elem()
+ switch t {
+ case int64Type:
+ if len(o.int64s) == 0 {
+ o.int64s = make([]int64, uint64PoolSize)
+ }
+ o.int64s[0] = int64(x)
+ p.v.Set(reflect.ValueOf(&o.int64s[0]))
+ o.int64s = o.int64s[1:]
+ return
+ case uint64Type:
+ if len(o.uint64s) == 0 {
+ o.uint64s = make([]uint64, uint64PoolSize)
+ }
+ o.uint64s[0] = x
+ p.v.Set(reflect.ValueOf(&o.uint64s[0]))
+ o.uint64s = o.uint64s[1:]
+ return
+ case float64Type:
+ if len(o.float64s) == 0 {
+ o.float64s = make([]float64, uint64PoolSize)
+ }
+ o.float64s[0] = math.Float64frombits(x)
+ p.v.Set(reflect.ValueOf(&o.float64s[0]))
+ o.float64s = o.float64s[1:]
+ return
+ }
+ panic("unreachable")
+}
+
+func word64_IsNil(p word64) bool {
+ return p.v.IsNil()
+}
+
+func word64_Get(p word64) uint64 {
+ elem := p.v.Elem()
+ switch elem.Kind() {
+ case reflect.Int64:
+ return uint64(elem.Int())
+ case reflect.Uint64:
+ return elem.Uint()
+ case reflect.Float64:
+ return math.Float64bits(elem.Float())
+ }
+ panic("unreachable")
+}
+
+func structPointer_Word64(p structPointer, f field) word64 {
+ return word64{structPointer_field(p, f)}
+}
+
+// word64Val is like word32Val but for 64-bit values.
+type word64Val struct {
+ v reflect.Value
+}
+
+func word64Val_Set(p word64Val, o *Buffer, x uint64) {
+ switch p.v.Type() {
+ case int64Type:
+ p.v.SetInt(int64(x))
+ return
+ case uint64Type:
+ p.v.SetUint(x)
+ return
+ case float64Type:
+ p.v.SetFloat(math.Float64frombits(x))
+ return
+ }
+ panic("unreachable")
+}
+
+func word64Val_Get(p word64Val) uint64 {
+ elem := p.v
+ switch elem.Kind() {
+ case reflect.Int64:
+ return uint64(elem.Int())
+ case reflect.Uint64:
+ return elem.Uint()
+ case reflect.Float64:
+ return math.Float64bits(elem.Float())
+ }
+ panic("unreachable")
+}
+
+func structPointer_Word64Val(p structPointer, f field) word64Val {
+ return word64Val{structPointer_field(p, f)}
+}
+
+type word64Slice struct {
+ v reflect.Value
+}
+
+func (p word64Slice) Append(x uint64) {
+ n, m := p.v.Len(), p.v.Cap()
+ if n < m {
+ p.v.SetLen(n + 1)
+ } else {
+ t := p.v.Type().Elem()
+ p.v.Set(reflect.Append(p.v, reflect.Zero(t)))
+ }
+ elem := p.v.Index(n)
+ switch elem.Kind() {
+ case reflect.Int64:
+ elem.SetInt(int64(int64(x)))
+ case reflect.Uint64:
+ elem.SetUint(uint64(x))
+ case reflect.Float64:
+ elem.SetFloat(float64(math.Float64frombits(x)))
+ }
+}
+
+func (p word64Slice) Len() int {
+ return p.v.Len()
+}
+
+func (p word64Slice) Index(i int) uint64 {
+ elem := p.v.Index(i)
+ switch elem.Kind() {
+ case reflect.Int64:
+ return uint64(elem.Int())
+ case reflect.Uint64:
+ return uint64(elem.Uint())
+ case reflect.Float64:
+ return math.Float64bits(float64(elem.Float()))
+ }
+ panic("unreachable")
+}
+
+func structPointer_Word64Slice(p structPointer, f field) word64Slice {
+ return word64Slice{structPointer_field(p, f)}
+}
diff --git a/vendor/github.com/golang/protobuf/proto/pointer_unsafe.go b/vendor/github.com/golang/protobuf/proto/pointer_unsafe.go
new file mode 100644
index 00000000..6b5567d4
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/pointer_unsafe.go
@@ -0,0 +1,270 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2012 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+// +build !appengine,!js
+
+// This file contains the implementation of the proto field accesses using package unsafe.
+
+package proto
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+// NOTE: These type_Foo functions would more idiomatically be methods,
+// but Go does not allow methods on pointer types, and we must preserve
+// some pointer type for the garbage collector. We use these
+// funcs with clunky names as our poor approximation to methods.
+//
+// An alternative would be
+// type structPointer struct { p unsafe.Pointer }
+// but that does not registerize as well.
+
+// A structPointer is a pointer to a struct.
+type structPointer unsafe.Pointer
+
+// toStructPointer returns a structPointer equivalent to the given reflect value.
+func toStructPointer(v reflect.Value) structPointer {
+ return structPointer(unsafe.Pointer(v.Pointer()))
+}
+
+// IsNil reports whether p is nil.
+func structPointer_IsNil(p structPointer) bool {
+ return p == nil
+}
+
+// Interface returns the struct pointer, assumed to have element type t,
+// as an interface value.
+func structPointer_Interface(p structPointer, t reflect.Type) interface{} {
+ return reflect.NewAt(t, unsafe.Pointer(p)).Interface()
+}
+
+// A field identifies a field in a struct, accessible from a structPointer.
+// In this implementation, a field is identified by its byte offset from the start of the struct.
+type field uintptr
+
+// toField returns a field equivalent to the given reflect field.
+func toField(f *reflect.StructField) field {
+ return field(f.Offset)
+}
+
+// invalidField is an invalid field identifier.
+const invalidField = ^field(0)
+
+// IsValid reports whether the field identifier is valid.
+func (f field) IsValid() bool {
+ return f != ^field(0)
+}
+
+// Bytes returns the address of a []byte field in the struct.
+func structPointer_Bytes(p structPointer, f field) *[]byte {
+ return (*[]byte)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+// BytesSlice returns the address of a [][]byte field in the struct.
+func structPointer_BytesSlice(p structPointer, f field) *[][]byte {
+ return (*[][]byte)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+// Bool returns the address of a *bool field in the struct.
+func structPointer_Bool(p structPointer, f field) **bool {
+ return (**bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+// BoolVal returns the address of a bool field in the struct.
+func structPointer_BoolVal(p structPointer, f field) *bool {
+ return (*bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+// BoolSlice returns the address of a []bool field in the struct.
+func structPointer_BoolSlice(p structPointer, f field) *[]bool {
+ return (*[]bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+// String returns the address of a *string field in the struct.
+func structPointer_String(p structPointer, f field) **string {
+ return (**string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+// StringVal returns the address of a string field in the struct.
+func structPointer_StringVal(p structPointer, f field) *string {
+ return (*string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+// StringSlice returns the address of a []string field in the struct.
+func structPointer_StringSlice(p structPointer, f field) *[]string {
+ return (*[]string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+// ExtMap returns the address of an extension map field in the struct.
+func structPointer_Extensions(p structPointer, f field) *XXX_InternalExtensions {
+ return (*XXX_InternalExtensions)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
+ return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+// NewAt returns the reflect.Value for a pointer to a field in the struct.
+func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value {
+ return reflect.NewAt(typ, unsafe.Pointer(uintptr(p)+uintptr(f)))
+}
+
+// SetStructPointer writes a *struct field in the struct.
+func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
+ *(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f))) = q
+}
+
+// GetStructPointer reads a *struct field in the struct.
+func structPointer_GetStructPointer(p structPointer, f field) structPointer {
+ return *(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+// StructPointerSlice the address of a []*struct field in the struct.
+func structPointer_StructPointerSlice(p structPointer, f field) *structPointerSlice {
+ return (*structPointerSlice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+// A structPointerSlice represents a slice of pointers to structs (themselves submessages or groups).
+type structPointerSlice []structPointer
+
+func (v *structPointerSlice) Len() int { return len(*v) }
+func (v *structPointerSlice) Index(i int) structPointer { return (*v)[i] }
+func (v *structPointerSlice) Append(p structPointer) { *v = append(*v, p) }
+
+// A word32 is the address of a "pointer to 32-bit value" field.
+type word32 **uint32
+
+// IsNil reports whether *v is nil.
+func word32_IsNil(p word32) bool {
+ return *p == nil
+}
+
+// Set sets *v to point at a newly allocated word set to x.
+func word32_Set(p word32, o *Buffer, x uint32) {
+ if len(o.uint32s) == 0 {
+ o.uint32s = make([]uint32, uint32PoolSize)
+ }
+ o.uint32s[0] = x
+ *p = &o.uint32s[0]
+ o.uint32s = o.uint32s[1:]
+}
+
+// Get gets the value pointed at by *v.
+func word32_Get(p word32) uint32 {
+ return **p
+}
+
+// Word32 returns the address of a *int32, *uint32, *float32, or *enum field in the struct.
+func structPointer_Word32(p structPointer, f field) word32 {
+ return word32((**uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
+}
+
+// A word32Val is the address of a 32-bit value field.
+type word32Val *uint32
+
+// Set sets *p to x.
+func word32Val_Set(p word32Val, x uint32) {
+ *p = x
+}
+
+// Get gets the value pointed at by p.
+func word32Val_Get(p word32Val) uint32 {
+ return *p
+}
+
+// Word32Val returns the address of a *int32, *uint32, *float32, or *enum field in the struct.
+func structPointer_Word32Val(p structPointer, f field) word32Val {
+ return word32Val((*uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
+}
+
+// A word32Slice is a slice of 32-bit values.
+type word32Slice []uint32
+
+func (v *word32Slice) Append(x uint32) { *v = append(*v, x) }
+func (v *word32Slice) Len() int { return len(*v) }
+func (v *word32Slice) Index(i int) uint32 { return (*v)[i] }
+
+// Word32Slice returns the address of a []int32, []uint32, []float32, or []enum field in the struct.
+func structPointer_Word32Slice(p structPointer, f field) *word32Slice {
+ return (*word32Slice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+// word64 is like word32 but for 64-bit values.
+type word64 **uint64
+
+func word64_Set(p word64, o *Buffer, x uint64) {
+ if len(o.uint64s) == 0 {
+ o.uint64s = make([]uint64, uint64PoolSize)
+ }
+ o.uint64s[0] = x
+ *p = &o.uint64s[0]
+ o.uint64s = o.uint64s[1:]
+}
+
+func word64_IsNil(p word64) bool {
+ return *p == nil
+}
+
+func word64_Get(p word64) uint64 {
+ return **p
+}
+
+func structPointer_Word64(p structPointer, f field) word64 {
+ return word64((**uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
+}
+
+// word64Val is like word32Val but for 64-bit values.
+type word64Val *uint64
+
+func word64Val_Set(p word64Val, o *Buffer, x uint64) {
+ *p = x
+}
+
+func word64Val_Get(p word64Val) uint64 {
+ return *p
+}
+
+func structPointer_Word64Val(p structPointer, f field) word64Val {
+ return word64Val((*uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
+}
+
+// word64Slice is like word32Slice but for 64-bit values.
+type word64Slice []uint64
+
+func (v *word64Slice) Append(x uint64) { *v = append(*v, x) }
+func (v *word64Slice) Len() int { return len(*v) }
+func (v *word64Slice) Index(i int) uint64 { return (*v)[i] }
+
+func structPointer_Word64Slice(p structPointer, f field) *word64Slice {
+ return (*word64Slice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
diff --git a/vendor/github.com/golang/protobuf/proto/properties.go b/vendor/github.com/golang/protobuf/proto/properties.go
new file mode 100644
index 00000000..ec2289c0
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/properties.go
@@ -0,0 +1,872 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+package proto
+
+/*
+ * Routines for encoding data into the wire format for protocol buffers.
+ */
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+const debug bool = false
+
+// Constants that identify the encoding of a value on the wire.
+const (
+ WireVarint = 0
+ WireFixed64 = 1
+ WireBytes = 2
+ WireStartGroup = 3
+ WireEndGroup = 4
+ WireFixed32 = 5
+)
+
+const startSize = 10 // initial slice/string sizes
+
+// Encoders are defined in encode.go
+// An encoder outputs the full representation of a field, including its
+// tag and encoder type.
+type encoder func(p *Buffer, prop *Properties, base structPointer) error
+
+// A valueEncoder encodes a single integer in a particular encoding.
+type valueEncoder func(o *Buffer, x uint64) error
+
+// Sizers are defined in encode.go
+// A sizer returns the encoded size of a field, including its tag and encoder
+// type.
+type sizer func(prop *Properties, base structPointer) int
+
+// A valueSizer returns the encoded size of a single integer in a particular
+// encoding.
+type valueSizer func(x uint64) int
+
+// Decoders are defined in decode.go
+// A decoder creates a value from its wire representation.
+// Unrecognized subelements are saved in unrec.
+type decoder func(p *Buffer, prop *Properties, base structPointer) error
+
+// A valueDecoder decodes a single integer in a particular encoding.
+type valueDecoder func(o *Buffer) (x uint64, err error)
+
+// A oneofMarshaler does the marshaling for all oneof fields in a message.
+type oneofMarshaler func(Message, *Buffer) error
+
+// A oneofUnmarshaler does the unmarshaling for a oneof field in a message.
+type oneofUnmarshaler func(Message, int, int, *Buffer) (bool, error)
+
+// A oneofSizer does the sizing for all oneof fields in a message.
+type oneofSizer func(Message) int
+
+// tagMap is an optimization over map[int]int for typical protocol buffer
+// use-cases. Encoded protocol buffers are often in tag order with small tag
+// numbers.
+type tagMap struct {
+ fastTags []int
+ slowTags map[int]int
+}
+
+// tagMapFastLimit is the upper bound on the tag number that will be stored in
+// the tagMap slice rather than its map.
+const tagMapFastLimit = 1024
+
+func (p *tagMap) get(t int) (int, bool) {
+ if t > 0 && t < tagMapFastLimit {
+ if t >= len(p.fastTags) {
+ return 0, false
+ }
+ fi := p.fastTags[t]
+ return fi, fi >= 0
+ }
+ fi, ok := p.slowTags[t]
+ return fi, ok
+}
+
+func (p *tagMap) put(t int, fi int) {
+ if t > 0 && t < tagMapFastLimit {
+ for len(p.fastTags) < t+1 {
+ p.fastTags = append(p.fastTags, -1)
+ }
+ p.fastTags[t] = fi
+ return
+ }
+ if p.slowTags == nil {
+ p.slowTags = make(map[int]int)
+ }
+ p.slowTags[t] = fi
+}
+
+// StructProperties represents properties for all the fields of a struct.
+// decoderTags and decoderOrigNames should only be used by the decoder.
+type StructProperties struct {
+ Prop []*Properties // properties for each field
+ reqCount int // required count
+ decoderTags tagMap // map from proto tag to struct field number
+ decoderOrigNames map[string]int // map from original name to struct field number
+ order []int // list of struct field numbers in tag order
+ unrecField field // field id of the XXX_unrecognized []byte field
+ extendable bool // is this an extendable proto
+
+ oneofMarshaler oneofMarshaler
+ oneofUnmarshaler oneofUnmarshaler
+ oneofSizer oneofSizer
+ stype reflect.Type
+
+ // OneofTypes contains information about the oneof fields in this message.
+ // It is keyed by the original name of a field.
+ OneofTypes map[string]*OneofProperties
+}
+
+// OneofProperties represents information about a specific field in a oneof.
+type OneofProperties struct {
+ Type reflect.Type // pointer to generated struct type for this oneof field
+ Field int // struct field number of the containing oneof in the message
+ Prop *Properties
+}
+
+// Implement the sorting interface so we can sort the fields in tag order, as recommended by the spec.
+// See encode.go, (*Buffer).enc_struct.
+
+func (sp *StructProperties) Len() int { return len(sp.order) }
+func (sp *StructProperties) Less(i, j int) bool {
+ return sp.Prop[sp.order[i]].Tag < sp.Prop[sp.order[j]].Tag
+}
+func (sp *StructProperties) Swap(i, j int) { sp.order[i], sp.order[j] = sp.order[j], sp.order[i] }
+
+// Properties represents the protocol-specific behavior of a single struct field.
+type Properties struct {
+ Name string // name of the field, for error messages
+ OrigName string // original name before protocol compiler (always set)
+ JSONName string // name to use for JSON; determined by protoc
+ Wire string
+ WireType int
+ Tag int
+ Required bool
+ Optional bool
+ Repeated bool
+ Packed bool // relevant for repeated primitives only
+ Enum string // set for enum types only
+ proto3 bool // whether this is known to be a proto3 field; set for []byte only
+ oneof bool // whether this is a oneof field
+
+ Default string // default value
+ HasDefault bool // whether an explicit default was provided
+ def_uint64 uint64
+
+ enc encoder
+ valEnc valueEncoder // set for bool and numeric types only
+ field field
+ tagcode []byte // encoding of EncodeVarint((Tag<<3)|WireType)
+ tagbuf [8]byte
+ stype reflect.Type // set for struct types only
+ sprop *StructProperties // set for struct types only
+ isMarshaler bool
+ isUnmarshaler bool
+
+ mtype reflect.Type // set for map types only
+ mkeyprop *Properties // set for map types only
+ mvalprop *Properties // set for map types only
+
+ size sizer
+ valSize valueSizer // set for bool and numeric types only
+
+ dec decoder
+ valDec valueDecoder // set for bool and numeric types only
+
+ // If this is a packable field, this will be the decoder for the packed version of the field.
+ packedDec decoder
+}
+
+// String formats the properties in the protobuf struct field tag style.
+func (p *Properties) String() string {
+ s := p.Wire
+ s = ","
+ s += strconv.Itoa(p.Tag)
+ if p.Required {
+ s += ",req"
+ }
+ if p.Optional {
+ s += ",opt"
+ }
+ if p.Repeated {
+ s += ",rep"
+ }
+ if p.Packed {
+ s += ",packed"
+ }
+ s += ",name=" + p.OrigName
+ if p.JSONName != p.OrigName {
+ s += ",json=" + p.JSONName
+ }
+ if p.proto3 {
+ s += ",proto3"
+ }
+ if p.oneof {
+ s += ",oneof"
+ }
+ if len(p.Enum) > 0 {
+ s += ",enum=" + p.Enum
+ }
+ if p.HasDefault {
+ s += ",def=" + p.Default
+ }
+ return s
+}
+
+// Parse populates p by parsing a string in the protobuf struct field tag style.
+func (p *Properties) Parse(s string) {
+ // "bytes,49,opt,name=foo,def=hello!"
+ fields := strings.Split(s, ",") // breaks def=, but handled below.
+ if len(fields) < 2 {
+ fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s)
+ return
+ }
+
+ p.Wire = fields[0]
+ switch p.Wire {
+ case "varint":
+ p.WireType = WireVarint
+ p.valEnc = (*Buffer).EncodeVarint
+ p.valDec = (*Buffer).DecodeVarint
+ p.valSize = sizeVarint
+ case "fixed32":
+ p.WireType = WireFixed32
+ p.valEnc = (*Buffer).EncodeFixed32
+ p.valDec = (*Buffer).DecodeFixed32
+ p.valSize = sizeFixed32
+ case "fixed64":
+ p.WireType = WireFixed64
+ p.valEnc = (*Buffer).EncodeFixed64
+ p.valDec = (*Buffer).DecodeFixed64
+ p.valSize = sizeFixed64
+ case "zigzag32":
+ p.WireType = WireVarint
+ p.valEnc = (*Buffer).EncodeZigzag32
+ p.valDec = (*Buffer).DecodeZigzag32
+ p.valSize = sizeZigzag32
+ case "zigzag64":
+ p.WireType = WireVarint
+ p.valEnc = (*Buffer).EncodeZigzag64
+ p.valDec = (*Buffer).DecodeZigzag64
+ p.valSize = sizeZigzag64
+ case "bytes", "group":
+ p.WireType = WireBytes
+ // no numeric converter for non-numeric types
+ default:
+ fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s)
+ return
+ }
+
+ var err error
+ p.Tag, err = strconv.Atoi(fields[1])
+ if err != nil {
+ return
+ }
+
+ for i := 2; i < len(fields); i++ {
+ f := fields[i]
+ switch {
+ case f == "req":
+ p.Required = true
+ case f == "opt":
+ p.Optional = true
+ case f == "rep":
+ p.Repeated = true
+ case f == "packed":
+ p.Packed = true
+ case strings.HasPrefix(f, "name="):
+ p.OrigName = f[5:]
+ case strings.HasPrefix(f, "json="):
+ p.JSONName = f[5:]
+ case strings.HasPrefix(f, "enum="):
+ p.Enum = f[5:]
+ case f == "proto3":
+ p.proto3 = true
+ case f == "oneof":
+ p.oneof = true
+ case strings.HasPrefix(f, "def="):
+ p.HasDefault = true
+ p.Default = f[4:] // rest of string
+ if i+1 < len(fields) {
+ // Commas aren't escaped, and def is always last.
+ p.Default += "," + strings.Join(fields[i+1:], ",")
+ break
+ }
+ }
+ }
+}
+
+func logNoSliceEnc(t1, t2 reflect.Type) {
+ fmt.Fprintf(os.Stderr, "proto: no slice oenc for %T = []%T\n", t1, t2)
+}
+
+var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem()
+
+// Initialize the fields for encoding and decoding.
+func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lockGetProp bool) {
+ p.enc = nil
+ p.dec = nil
+ p.size = nil
+
+ switch t1 := typ; t1.Kind() {
+ default:
+ fmt.Fprintf(os.Stderr, "proto: no coders for %v\n", t1)
+
+ // proto3 scalar types
+
+ case reflect.Bool:
+ p.enc = (*Buffer).enc_proto3_bool
+ p.dec = (*Buffer).dec_proto3_bool
+ p.size = size_proto3_bool
+ case reflect.Int32:
+ p.enc = (*Buffer).enc_proto3_int32
+ p.dec = (*Buffer).dec_proto3_int32
+ p.size = size_proto3_int32
+ case reflect.Uint32:
+ p.enc = (*Buffer).enc_proto3_uint32
+ p.dec = (*Buffer).dec_proto3_int32 // can reuse
+ p.size = size_proto3_uint32
+ case reflect.Int64, reflect.Uint64:
+ p.enc = (*Buffer).enc_proto3_int64
+ p.dec = (*Buffer).dec_proto3_int64
+ p.size = size_proto3_int64
+ case reflect.Float32:
+ p.enc = (*Buffer).enc_proto3_uint32 // can just treat them as bits
+ p.dec = (*Buffer).dec_proto3_int32
+ p.size = size_proto3_uint32
+ case reflect.Float64:
+ p.enc = (*Buffer).enc_proto3_int64 // can just treat them as bits
+ p.dec = (*Buffer).dec_proto3_int64
+ p.size = size_proto3_int64
+ case reflect.String:
+ p.enc = (*Buffer).enc_proto3_string
+ p.dec = (*Buffer).dec_proto3_string
+ p.size = size_proto3_string
+
+ case reflect.Ptr:
+ switch t2 := t1.Elem(); t2.Kind() {
+ default:
+ fmt.Fprintf(os.Stderr, "proto: no encoder function for %v -> %v\n", t1, t2)
+ break
+ case reflect.Bool:
+ p.enc = (*Buffer).enc_bool
+ p.dec = (*Buffer).dec_bool
+ p.size = size_bool
+ case reflect.Int32:
+ p.enc = (*Buffer).enc_int32
+ p.dec = (*Buffer).dec_int32
+ p.size = size_int32
+ case reflect.Uint32:
+ p.enc = (*Buffer).enc_uint32
+ p.dec = (*Buffer).dec_int32 // can reuse
+ p.size = size_uint32
+ case reflect.Int64, reflect.Uint64:
+ p.enc = (*Buffer).enc_int64
+ p.dec = (*Buffer).dec_int64
+ p.size = size_int64
+ case reflect.Float32:
+ p.enc = (*Buffer).enc_uint32 // can just treat them as bits
+ p.dec = (*Buffer).dec_int32
+ p.size = size_uint32
+ case reflect.Float64:
+ p.enc = (*Buffer).enc_int64 // can just treat them as bits
+ p.dec = (*Buffer).dec_int64
+ p.size = size_int64
+ case reflect.String:
+ p.enc = (*Buffer).enc_string
+ p.dec = (*Buffer).dec_string
+ p.size = size_string
+ case reflect.Struct:
+ p.stype = t1.Elem()
+ p.isMarshaler = isMarshaler(t1)
+ p.isUnmarshaler = isUnmarshaler(t1)
+ if p.Wire == "bytes" {
+ p.enc = (*Buffer).enc_struct_message
+ p.dec = (*Buffer).dec_struct_message
+ p.size = size_struct_message
+ } else {
+ p.enc = (*Buffer).enc_struct_group
+ p.dec = (*Buffer).dec_struct_group
+ p.size = size_struct_group
+ }
+ }
+
+ case reflect.Slice:
+ switch t2 := t1.Elem(); t2.Kind() {
+ default:
+ logNoSliceEnc(t1, t2)
+ break
+ case reflect.Bool:
+ if p.Packed {
+ p.enc = (*Buffer).enc_slice_packed_bool
+ p.size = size_slice_packed_bool
+ } else {
+ p.enc = (*Buffer).enc_slice_bool
+ p.size = size_slice_bool
+ }
+ p.dec = (*Buffer).dec_slice_bool
+ p.packedDec = (*Buffer).dec_slice_packed_bool
+ case reflect.Int32:
+ if p.Packed {
+ p.enc = (*Buffer).enc_slice_packed_int32
+ p.size = size_slice_packed_int32
+ } else {
+ p.enc = (*Buffer).enc_slice_int32
+ p.size = size_slice_int32
+ }
+ p.dec = (*Buffer).dec_slice_int32
+ p.packedDec = (*Buffer).dec_slice_packed_int32
+ case reflect.Uint32:
+ if p.Packed {
+ p.enc = (*Buffer).enc_slice_packed_uint32
+ p.size = size_slice_packed_uint32
+ } else {
+ p.enc = (*Buffer).enc_slice_uint32
+ p.size = size_slice_uint32
+ }
+ p.dec = (*Buffer).dec_slice_int32
+ p.packedDec = (*Buffer).dec_slice_packed_int32
+ case reflect.Int64, reflect.Uint64:
+ if p.Packed {
+ p.enc = (*Buffer).enc_slice_packed_int64
+ p.size = size_slice_packed_int64
+ } else {
+ p.enc = (*Buffer).enc_slice_int64
+ p.size = size_slice_int64
+ }
+ p.dec = (*Buffer).dec_slice_int64
+ p.packedDec = (*Buffer).dec_slice_packed_int64
+ case reflect.Uint8:
+ p.dec = (*Buffer).dec_slice_byte
+ if p.proto3 {
+ p.enc = (*Buffer).enc_proto3_slice_byte
+ p.size = size_proto3_slice_byte
+ } else {
+ p.enc = (*Buffer).enc_slice_byte
+ p.size = size_slice_byte
+ }
+ case reflect.Float32, reflect.Float64:
+ switch t2.Bits() {
+ case 32:
+ // can just treat them as bits
+ if p.Packed {
+ p.enc = (*Buffer).enc_slice_packed_uint32
+ p.size = size_slice_packed_uint32
+ } else {
+ p.enc = (*Buffer).enc_slice_uint32
+ p.size = size_slice_uint32
+ }
+ p.dec = (*Buffer).dec_slice_int32
+ p.packedDec = (*Buffer).dec_slice_packed_int32
+ case 64:
+ // can just treat them as bits
+ if p.Packed {
+ p.enc = (*Buffer).enc_slice_packed_int64
+ p.size = size_slice_packed_int64
+ } else {
+ p.enc = (*Buffer).enc_slice_int64
+ p.size = size_slice_int64
+ }
+ p.dec = (*Buffer).dec_slice_int64
+ p.packedDec = (*Buffer).dec_slice_packed_int64
+ default:
+ logNoSliceEnc(t1, t2)
+ break
+ }
+ case reflect.String:
+ p.enc = (*Buffer).enc_slice_string
+ p.dec = (*Buffer).dec_slice_string
+ p.size = size_slice_string
+ case reflect.Ptr:
+ switch t3 := t2.Elem(); t3.Kind() {
+ default:
+ fmt.Fprintf(os.Stderr, "proto: no ptr oenc for %T -> %T -> %T\n", t1, t2, t3)
+ break
+ case reflect.Struct:
+ p.stype = t2.Elem()
+ p.isMarshaler = isMarshaler(t2)
+ p.isUnmarshaler = isUnmarshaler(t2)
+ if p.Wire == "bytes" {
+ p.enc = (*Buffer).enc_slice_struct_message
+ p.dec = (*Buffer).dec_slice_struct_message
+ p.size = size_slice_struct_message
+ } else {
+ p.enc = (*Buffer).enc_slice_struct_group
+ p.dec = (*Buffer).dec_slice_struct_group
+ p.size = size_slice_struct_group
+ }
+ }
+ case reflect.Slice:
+ switch t2.Elem().Kind() {
+ default:
+ fmt.Fprintf(os.Stderr, "proto: no slice elem oenc for %T -> %T -> %T\n", t1, t2, t2.Elem())
+ break
+ case reflect.Uint8:
+ p.enc = (*Buffer).enc_slice_slice_byte
+ p.dec = (*Buffer).dec_slice_slice_byte
+ p.size = size_slice_slice_byte
+ }
+ }
+
+ case reflect.Map:
+ p.enc = (*Buffer).enc_new_map
+ p.dec = (*Buffer).dec_new_map
+ p.size = size_new_map
+
+ p.mtype = t1
+ p.mkeyprop = &Properties{}
+ p.mkeyprop.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
+ p.mvalprop = &Properties{}
+ vtype := p.mtype.Elem()
+ if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
+ // The value type is not a message (*T) or bytes ([]byte),
+ // so we need encoders for the pointer to this type.
+ vtype = reflect.PtrTo(vtype)
+ }
+ p.mvalprop.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
+ }
+
+ // precalculate tag code
+ wire := p.WireType
+ if p.Packed {
+ wire = WireBytes
+ }
+ x := uint32(p.Tag)<<3 | uint32(wire)
+ i := 0
+ for i = 0; x > 127; i++ {
+ p.tagbuf[i] = 0x80 | uint8(x&0x7F)
+ x >>= 7
+ }
+ p.tagbuf[i] = uint8(x)
+ p.tagcode = p.tagbuf[0 : i+1]
+
+ if p.stype != nil {
+ if lockGetProp {
+ p.sprop = GetProperties(p.stype)
+ } else {
+ p.sprop = getPropertiesLocked(p.stype)
+ }
+ }
+}
+
+var (
+ marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
+ unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
+)
+
+// isMarshaler reports whether type t implements Marshaler.
+func isMarshaler(t reflect.Type) bool {
+ // We're checking for (likely) pointer-receiver methods
+ // so if t is not a pointer, something is very wrong.
+ // The calls above only invoke isMarshaler on pointer types.
+ if t.Kind() != reflect.Ptr {
+ panic("proto: misuse of isMarshaler")
+ }
+ return t.Implements(marshalerType)
+}
+
+// isUnmarshaler reports whether type t implements Unmarshaler.
+func isUnmarshaler(t reflect.Type) bool {
+ // We're checking for (likely) pointer-receiver methods
+ // so if t is not a pointer, something is very wrong.
+ // The calls above only invoke isUnmarshaler on pointer types.
+ if t.Kind() != reflect.Ptr {
+ panic("proto: misuse of isUnmarshaler")
+ }
+ return t.Implements(unmarshalerType)
+}
+
+// Init populates the properties from a protocol buffer struct tag.
+func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
+ p.init(typ, name, tag, f, true)
+}
+
+func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructField, lockGetProp bool) {
+ // "bytes,49,opt,def=hello!"
+ p.Name = name
+ p.OrigName = name
+ if f != nil {
+ p.field = toField(f)
+ }
+ if tag == "" {
+ return
+ }
+ p.Parse(tag)
+ p.setEncAndDec(typ, f, lockGetProp)
+}
+
+var (
+ propertiesMu sync.RWMutex
+ propertiesMap = make(map[reflect.Type]*StructProperties)
+)
+
+// GetProperties returns the list of properties for the type represented by t.
+// t must represent a generated struct type of a protocol message.
+func GetProperties(t reflect.Type) *StructProperties {
+ if t.Kind() != reflect.Struct {
+ panic("proto: type must have kind struct")
+ }
+
+ // Most calls to GetProperties in a long-running program will be
+ // retrieving details for types we have seen before.
+ propertiesMu.RLock()
+ sprop, ok := propertiesMap[t]
+ propertiesMu.RUnlock()
+ if ok {
+ if collectStats {
+ stats.Chit++
+ }
+ return sprop
+ }
+
+ propertiesMu.Lock()
+ sprop = getPropertiesLocked(t)
+ propertiesMu.Unlock()
+ return sprop
+}
+
+// getPropertiesLocked requires that propertiesMu is held.
+func getPropertiesLocked(t reflect.Type) *StructProperties {
+ if prop, ok := propertiesMap[t]; ok {
+ if collectStats {
+ stats.Chit++
+ }
+ return prop
+ }
+ if collectStats {
+ stats.Cmiss++
+ }
+
+ prop := new(StructProperties)
+ // in case of recursive protos, fill this in now.
+ propertiesMap[t] = prop
+
+ // build properties
+ prop.extendable = reflect.PtrTo(t).Implements(extendableProtoType) ||
+ reflect.PtrTo(t).Implements(extendableProtoV1Type)
+ prop.unrecField = invalidField
+ prop.Prop = make([]*Properties, t.NumField())
+ prop.order = make([]int, t.NumField())
+
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ p := new(Properties)
+ name := f.Name
+ p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false)
+
+ if f.Name == "XXX_InternalExtensions" { // special case
+ p.enc = (*Buffer).enc_exts
+ p.dec = nil // not needed
+ p.size = size_exts
+ } else if f.Name == "XXX_extensions" { // special case
+ p.enc = (*Buffer).enc_map
+ p.dec = nil // not needed
+ p.size = size_map
+ } else if f.Name == "XXX_unrecognized" { // special case
+ prop.unrecField = toField(&f)
+ }
+ oneof := f.Tag.Get("protobuf_oneof") // special case
+ if oneof != "" {
+ // Oneof fields don't use the traditional protobuf tag.
+ p.OrigName = oneof
+ }
+ prop.Prop[i] = p
+ prop.order[i] = i
+ if debug {
+ print(i, " ", f.Name, " ", t.String(), " ")
+ if p.Tag > 0 {
+ print(p.String())
+ }
+ print("\n")
+ }
+ if p.enc == nil && !strings.HasPrefix(f.Name, "XXX_") && oneof == "" {
+ fmt.Fprintln(os.Stderr, "proto: no encoder for", f.Name, f.Type.String(), "[GetProperties]")
+ }
+ }
+
+ // Re-order prop.order.
+ sort.Sort(prop)
+
+ type oneofMessage interface {
+ XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
+ }
+ if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
+ var oots []interface{}
+ prop.oneofMarshaler, prop.oneofUnmarshaler, prop.oneofSizer, oots = om.XXX_OneofFuncs()
+ prop.stype = t
+
+ // Interpret oneof metadata.
+ prop.OneofTypes = make(map[string]*OneofProperties)
+ for _, oot := range oots {
+ oop := &OneofProperties{
+ Type: reflect.ValueOf(oot).Type(), // *T
+ Prop: new(Properties),
+ }
+ sft := oop.Type.Elem().Field(0)
+ oop.Prop.Name = sft.Name
+ oop.Prop.Parse(sft.Tag.Get("protobuf"))
+ // There will be exactly one interface field that
+ // this new value is assignable to.
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ if f.Type.Kind() != reflect.Interface {
+ continue
+ }
+ if !oop.Type.AssignableTo(f.Type) {
+ continue
+ }
+ oop.Field = i
+ break
+ }
+ prop.OneofTypes[oop.Prop.OrigName] = oop
+ }
+ }
+
+ // build required counts
+ // build tags
+ reqCount := 0
+ prop.decoderOrigNames = make(map[string]int)
+ for i, p := range prop.Prop {
+ if strings.HasPrefix(p.Name, "XXX_") {
+ // Internal fields should not appear in tags/origNames maps.
+ // They are handled specially when encoding and decoding.
+ continue
+ }
+ if p.Required {
+ reqCount++
+ }
+ prop.decoderTags.put(p.Tag, i)
+ prop.decoderOrigNames[p.OrigName] = i
+ }
+ prop.reqCount = reqCount
+
+ return prop
+}
+
+// Return the Properties object for the x[0]'th field of the structure.
+func propByIndex(t reflect.Type, x []int) *Properties {
+ if len(x) != 1 {
+ fmt.Fprintf(os.Stderr, "proto: field index dimension %d (not 1) for type %s\n", len(x), t)
+ return nil
+ }
+ prop := GetProperties(t)
+ return prop.Prop[x[0]]
+}
+
+// Get the address and type of a pointer to a struct from an interface.
+func getbase(pb Message) (t reflect.Type, b structPointer, err error) {
+ if pb == nil {
+ err = ErrNil
+ return
+ }
+ // get the reflect type of the pointer to the struct.
+ t = reflect.TypeOf(pb)
+ // get the address of the struct.
+ value := reflect.ValueOf(pb)
+ b = toStructPointer(value)
+ return
+}
+
+// A global registry of enum types.
+// The generated code will register the generated maps by calling RegisterEnum.
+
+var enumValueMaps = make(map[string]map[string]int32)
+
+// RegisterEnum is called from the generated code to install the enum descriptor
+// maps into the global table to aid parsing text format protocol buffers.
+func RegisterEnum(typeName string, unusedNameMap map[int32]string, valueMap map[string]int32) {
+ if _, ok := enumValueMaps[typeName]; ok {
+ panic("proto: duplicate enum registered: " + typeName)
+ }
+ enumValueMaps[typeName] = valueMap
+}
+
+// EnumValueMap returns the mapping from names to integers of the
+// enum type enumType, or a nil if not found.
+func EnumValueMap(enumType string) map[string]int32 {
+ return enumValueMaps[enumType]
+}
+
+// A registry of all linked message types.
+// The string is a fully-qualified proto name ("pkg.Message").
+var (
+ protoTypes = make(map[string]reflect.Type)
+ revProtoTypes = make(map[reflect.Type]string)
+)
+
+// RegisterType is called from generated code and maps from the fully qualified
+// proto name to the type (pointer to struct) of the protocol buffer.
+func RegisterType(x Message, name string) {
+ if _, ok := protoTypes[name]; ok {
+ // TODO: Some day, make this a panic.
+ log.Printf("proto: duplicate proto type registered: %s", name)
+ return
+ }
+ t := reflect.TypeOf(x)
+ protoTypes[name] = t
+ revProtoTypes[t] = name
+}
+
+// MessageName returns the fully-qualified proto name for the given message type.
+func MessageName(x Message) string {
+ type xname interface {
+ XXX_MessageName() string
+ }
+ if m, ok := x.(xname); ok {
+ return m.XXX_MessageName()
+ }
+ return revProtoTypes[reflect.TypeOf(x)]
+}
+
+// MessageType returns the message type (pointer to struct) for a named message.
+func MessageType(name string) reflect.Type { return protoTypes[name] }
+
+// A registry of all linked proto files.
+var (
+ protoFiles = make(map[string][]byte) // file name => fileDescriptor
+)
+
+// RegisterFile is called from generated code and maps from the
+// full file name of a .proto file to its compressed FileDescriptorProto.
+func RegisterFile(filename string, fileDescriptor []byte) {
+ protoFiles[filename] = fileDescriptor
+}
+
+// FileDescriptor returns the compressed FileDescriptorProto for a .proto file.
+func FileDescriptor(filename string) []byte { return protoFiles[filename] }
diff --git a/vendor/github.com/golang/protobuf/proto/text.go b/vendor/github.com/golang/protobuf/proto/text.go
new file mode 100644
index 00000000..965876bf
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/text.go
@@ -0,0 +1,854 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+package proto
+
+// Functions for writing the text protocol buffer format.
+
+import (
+ "bufio"
+ "bytes"
+ "encoding"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "math"
+ "reflect"
+ "sort"
+ "strings"
+)
+
+var (
+ newline = []byte("\n")
+ spaces = []byte(" ")
+ gtNewline = []byte(">\n")
+ endBraceNewline = []byte("}\n")
+ backslashN = []byte{'\\', 'n'}
+ backslashR = []byte{'\\', 'r'}
+ backslashT = []byte{'\\', 't'}
+ backslashDQ = []byte{'\\', '"'}
+ backslashBS = []byte{'\\', '\\'}
+ posInf = []byte("inf")
+ negInf = []byte("-inf")
+ nan = []byte("nan")
+)
+
+type writer interface {
+ io.Writer
+ WriteByte(byte) error
+}
+
+// textWriter is an io.Writer that tracks its indentation level.
+type textWriter struct {
+ ind int
+ complete bool // if the current position is a complete line
+ compact bool // whether to write out as a one-liner
+ w writer
+}
+
+func (w *textWriter) WriteString(s string) (n int, err error) {
+ if !strings.Contains(s, "\n") {
+ if !w.compact && w.complete {
+ w.writeIndent()
+ }
+ w.complete = false
+ return io.WriteString(w.w, s)
+ }
+ // WriteString is typically called without newlines, so this
+ // codepath and its copy are rare. We copy to avoid
+ // duplicating all of Write's logic here.
+ return w.Write([]byte(s))
+}
+
+func (w *textWriter) Write(p []byte) (n int, err error) {
+ newlines := bytes.Count(p, newline)
+ if newlines == 0 {
+ if !w.compact && w.complete {
+ w.writeIndent()
+ }
+ n, err = w.w.Write(p)
+ w.complete = false
+ return n, err
+ }
+
+ frags := bytes.SplitN(p, newline, newlines+1)
+ if w.compact {
+ for i, frag := range frags {
+ if i > 0 {
+ if err := w.w.WriteByte(' '); err != nil {
+ return n, err
+ }
+ n++
+ }
+ nn, err := w.w.Write(frag)
+ n += nn
+ if err != nil {
+ return n, err
+ }
+ }
+ return n, nil
+ }
+
+ for i, frag := range frags {
+ if w.complete {
+ w.writeIndent()
+ }
+ nn, err := w.w.Write(frag)
+ n += nn
+ if err != nil {
+ return n, err
+ }
+ if i+1 < len(frags) {
+ if err := w.w.WriteByte('\n'); err != nil {
+ return n, err
+ }
+ n++
+ }
+ }
+ w.complete = len(frags[len(frags)-1]) == 0
+ return n, nil
+}
+
+func (w *textWriter) WriteByte(c byte) error {
+ if w.compact && c == '\n' {
+ c = ' '
+ }
+ if !w.compact && w.complete {
+ w.writeIndent()
+ }
+ err := w.w.WriteByte(c)
+ w.complete = c == '\n'
+ return err
+}
+
+func (w *textWriter) indent() { w.ind++ }
+
+func (w *textWriter) unindent() {
+ if w.ind == 0 {
+ log.Print("proto: textWriter unindented too far")
+ return
+ }
+ w.ind--
+}
+
+func writeName(w *textWriter, props *Properties) error {
+ if _, err := w.WriteString(props.OrigName); err != nil {
+ return err
+ }
+ if props.Wire != "group" {
+ return w.WriteByte(':')
+ }
+ return nil
+}
+
+// raw is the interface satisfied by RawMessage.
+type raw interface {
+ Bytes() []byte
+}
+
+func requiresQuotes(u string) bool {
+ // When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
+ for _, ch := range u {
+ switch {
+ case ch == '.' || ch == '/' || ch == '_':
+ continue
+ case '0' <= ch && ch <= '9':
+ continue
+ case 'A' <= ch && ch <= 'Z':
+ continue
+ case 'a' <= ch && ch <= 'z':
+ continue
+ default:
+ return true
+ }
+ }
+ return false
+}
+
+// isAny reports whether sv is a google.protobuf.Any message
+func isAny(sv reflect.Value) bool {
+ type wkt interface {
+ XXX_WellKnownType() string
+ }
+ t, ok := sv.Addr().Interface().(wkt)
+ return ok && t.XXX_WellKnownType() == "Any"
+}
+
+// writeProto3Any writes an expanded google.protobuf.Any message.
+//
+// It returns (false, nil) if sv value can't be unmarshaled (e.g. because
+// required messages are not linked in).
+//
+// It returns (true, error) when sv was written in expanded format or an error
+// was encountered.
+func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) {
+ turl := sv.FieldByName("TypeUrl")
+ val := sv.FieldByName("Value")
+ if !turl.IsValid() || !val.IsValid() {
+ return true, errors.New("proto: invalid google.protobuf.Any message")
+ }
+
+ b, ok := val.Interface().([]byte)
+ if !ok {
+ return true, errors.New("proto: invalid google.protobuf.Any message")
+ }
+
+ parts := strings.Split(turl.String(), "/")
+ mt := MessageType(parts[len(parts)-1])
+ if mt == nil {
+ return false, nil
+ }
+ m := reflect.New(mt.Elem())
+ if err := Unmarshal(b, m.Interface().(Message)); err != nil {
+ return false, nil
+ }
+ w.Write([]byte("["))
+ u := turl.String()
+ if requiresQuotes(u) {
+ writeString(w, u)
+ } else {
+ w.Write([]byte(u))
+ }
+ if w.compact {
+ w.Write([]byte("]:<"))
+ } else {
+ w.Write([]byte("]: <\n"))
+ w.ind++
+ }
+ if err := tm.writeStruct(w, m.Elem()); err != nil {
+ return true, err
+ }
+ if w.compact {
+ w.Write([]byte("> "))
+ } else {
+ w.ind--
+ w.Write([]byte(">\n"))
+ }
+ return true, nil
+}
+
+func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
+ if tm.ExpandAny && isAny(sv) {
+ if canExpand, err := tm.writeProto3Any(w, sv); canExpand {
+ return err
+ }
+ }
+ st := sv.Type()
+ sprops := GetProperties(st)
+ for i := 0; i < sv.NumField(); i++ {
+ fv := sv.Field(i)
+ props := sprops.Prop[i]
+ name := st.Field(i).Name
+
+ if strings.HasPrefix(name, "XXX_") {
+ // There are two XXX_ fields:
+ // XXX_unrecognized []byte
+ // XXX_extensions map[int32]proto.Extension
+ // The first is handled here;
+ // the second is handled at the bottom of this function.
+ if name == "XXX_unrecognized" && !fv.IsNil() {
+ if err := writeUnknownStruct(w, fv.Interface().([]byte)); err != nil {
+ return err
+ }
+ }
+ continue
+ }
+ if fv.Kind() == reflect.Ptr && fv.IsNil() {
+ // Field not filled in. This could be an optional field or
+ // a required field that wasn't filled in. Either way, there
+ // isn't anything we can show for it.
+ continue
+ }
+ if fv.Kind() == reflect.Slice && fv.IsNil() {
+ // Repeated field that is empty, or a bytes field that is unused.
+ continue
+ }
+
+ if props.Repeated && fv.Kind() == reflect.Slice {
+ // Repeated field.
+ for j := 0; j < fv.Len(); j++ {
+ if err := writeName(w, props); err != nil {
+ return err
+ }
+ if !w.compact {
+ if err := w.WriteByte(' '); err != nil {
+ return err
+ }
+ }
+ v := fv.Index(j)
+ if v.Kind() == reflect.Ptr && v.IsNil() {
+ // A nil message in a repeated field is not valid,
+ // but we can handle that more gracefully than panicking.
+ if _, err := w.Write([]byte("\n")); err != nil {
+ return err
+ }
+ continue
+ }
+ if err := tm.writeAny(w, v, props); err != nil {
+ return err
+ }
+ if err := w.WriteByte('\n'); err != nil {
+ return err
+ }
+ }
+ continue
+ }
+ if fv.Kind() == reflect.Map {
+ // Map fields are rendered as a repeated struct with key/value fields.
+ keys := fv.MapKeys()
+ sort.Sort(mapKeys(keys))
+ for _, key := range keys {
+ val := fv.MapIndex(key)
+ if err := writeName(w, props); err != nil {
+ return err
+ }
+ if !w.compact {
+ if err := w.WriteByte(' '); err != nil {
+ return err
+ }
+ }
+ // open struct
+ if err := w.WriteByte('<'); err != nil {
+ return err
+ }
+ if !w.compact {
+ if err := w.WriteByte('\n'); err != nil {
+ return err
+ }
+ }
+ w.indent()
+ // key
+ if _, err := w.WriteString("key:"); err != nil {
+ return err
+ }
+ if !w.compact {
+ if err := w.WriteByte(' '); err != nil {
+ return err
+ }
+ }
+ if err := tm.writeAny(w, key, props.mkeyprop); err != nil {
+ return err
+ }
+ if err := w.WriteByte('\n'); err != nil {
+ return err
+ }
+ // nil values aren't legal, but we can avoid panicking because of them.
+ if val.Kind() != reflect.Ptr || !val.IsNil() {
+ // value
+ if _, err := w.WriteString("value:"); err != nil {
+ return err
+ }
+ if !w.compact {
+ if err := w.WriteByte(' '); err != nil {
+ return err
+ }
+ }
+ if err := tm.writeAny(w, val, props.mvalprop); err != nil {
+ return err
+ }
+ if err := w.WriteByte('\n'); err != nil {
+ return err
+ }
+ }
+ // close struct
+ w.unindent()
+ if err := w.WriteByte('>'); err != nil {
+ return err
+ }
+ if err := w.WriteByte('\n'); err != nil {
+ return err
+ }
+ }
+ continue
+ }
+ if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 {
+ // empty bytes field
+ continue
+ }
+ if fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice {
+ // proto3 non-repeated scalar field; skip if zero value
+ if isProto3Zero(fv) {
+ continue
+ }
+ }
+
+ if fv.Kind() == reflect.Interface {
+ // Check if it is a oneof.
+ if st.Field(i).Tag.Get("protobuf_oneof") != "" {
+ // fv is nil, or holds a pointer to generated struct.
+ // That generated struct has exactly one field,
+ // which has a protobuf struct tag.
+ if fv.IsNil() {
+ continue
+ }
+ inner := fv.Elem().Elem() // interface -> *T -> T
+ tag := inner.Type().Field(0).Tag.Get("protobuf")
+ props = new(Properties) // Overwrite the outer props var, but not its pointee.
+ props.Parse(tag)
+ // Write the value in the oneof, not the oneof itself.
+ fv = inner.Field(0)
+
+ // Special case to cope with malformed messages gracefully:
+ // If the value in the oneof is a nil pointer, don't panic
+ // in writeAny.
+ if fv.Kind() == reflect.Ptr && fv.IsNil() {
+ // Use errors.New so writeAny won't render quotes.
+ msg := errors.New("/* nil */")
+ fv = reflect.ValueOf(&msg).Elem()
+ }
+ }
+ }
+
+ if err := writeName(w, props); err != nil {
+ return err
+ }
+ if !w.compact {
+ if err := w.WriteByte(' '); err != nil {
+ return err
+ }
+ }
+ if b, ok := fv.Interface().(raw); ok {
+ if err := writeRaw(w, b.Bytes()); err != nil {
+ return err
+ }
+ continue
+ }
+
+ // Enums have a String method, so writeAny will work fine.
+ if err := tm.writeAny(w, fv, props); err != nil {
+ return err
+ }
+
+ if err := w.WriteByte('\n'); err != nil {
+ return err
+ }
+ }
+
+ // Extensions (the XXX_extensions field).
+ pv := sv.Addr()
+ if _, ok := extendable(pv.Interface()); ok {
+ if err := tm.writeExtensions(w, pv); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// writeRaw writes an uninterpreted raw message.
+func writeRaw(w *textWriter, b []byte) error {
+ if err := w.WriteByte('<'); err != nil {
+ return err
+ }
+ if !w.compact {
+ if err := w.WriteByte('\n'); err != nil {
+ return err
+ }
+ }
+ w.indent()
+ if err := writeUnknownStruct(w, b); err != nil {
+ return err
+ }
+ w.unindent()
+ if err := w.WriteByte('>'); err != nil {
+ return err
+ }
+ return nil
+}
+
+// writeAny writes an arbitrary field.
+func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
+ v = reflect.Indirect(v)
+
+ // Floats have special cases.
+ if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 {
+ x := v.Float()
+ var b []byte
+ switch {
+ case math.IsInf(x, 1):
+ b = posInf
+ case math.IsInf(x, -1):
+ b = negInf
+ case math.IsNaN(x):
+ b = nan
+ }
+ if b != nil {
+ _, err := w.Write(b)
+ return err
+ }
+ // Other values are handled below.
+ }
+
+ // We don't attempt to serialise every possible value type; only those
+ // that can occur in protocol buffers.
+ switch v.Kind() {
+ case reflect.Slice:
+ // Should only be a []byte; repeated fields are handled in writeStruct.
+ if err := writeString(w, string(v.Bytes())); err != nil {
+ return err
+ }
+ case reflect.String:
+ if err := writeString(w, v.String()); err != nil {
+ return err
+ }
+ case reflect.Struct:
+ // Required/optional group/message.
+ var bra, ket byte = '<', '>'
+ if props != nil && props.Wire == "group" {
+ bra, ket = '{', '}'
+ }
+ if err := w.WriteByte(bra); err != nil {
+ return err
+ }
+ if !w.compact {
+ if err := w.WriteByte('\n'); err != nil {
+ return err
+ }
+ }
+ w.indent()
+ if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
+ text, err := etm.MarshalText()
+ if err != nil {
+ return err
+ }
+ if _, err = w.Write(text); err != nil {
+ return err
+ }
+ } else if err := tm.writeStruct(w, v); err != nil {
+ return err
+ }
+ w.unindent()
+ if err := w.WriteByte(ket); err != nil {
+ return err
+ }
+ default:
+ _, err := fmt.Fprint(w, v.Interface())
+ return err
+ }
+ return nil
+}
+
+// equivalent to C's isprint.
+func isprint(c byte) bool {
+ return c >= 0x20 && c < 0x7f
+}
+
+// writeString writes a string in the protocol buffer text format.
+// It is similar to strconv.Quote except we don't use Go escape sequences,
+// we treat the string as a byte sequence, and we use octal escapes.
+// These differences are to maintain interoperability with the other
+// languages' implementations of the text format.
+func writeString(w *textWriter, s string) error {
+ // use WriteByte here to get any needed indent
+ if err := w.WriteByte('"'); err != nil {
+ return err
+ }
+ // Loop over the bytes, not the runes.
+ for i := 0; i < len(s); i++ {
+ var err error
+ // Divergence from C++: we don't escape apostrophes.
+ // There's no need to escape them, and the C++ parser
+ // copes with a naked apostrophe.
+ switch c := s[i]; c {
+ case '\n':
+ _, err = w.w.Write(backslashN)
+ case '\r':
+ _, err = w.w.Write(backslashR)
+ case '\t':
+ _, err = w.w.Write(backslashT)
+ case '"':
+ _, err = w.w.Write(backslashDQ)
+ case '\\':
+ _, err = w.w.Write(backslashBS)
+ default:
+ if isprint(c) {
+ err = w.w.WriteByte(c)
+ } else {
+ _, err = fmt.Fprintf(w.w, "\\%03o", c)
+ }
+ }
+ if err != nil {
+ return err
+ }
+ }
+ return w.WriteByte('"')
+}
+
+func writeUnknownStruct(w *textWriter, data []byte) (err error) {
+ if !w.compact {
+ if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil {
+ return err
+ }
+ }
+ b := NewBuffer(data)
+ for b.index < len(b.buf) {
+ x, err := b.DecodeVarint()
+ if err != nil {
+ _, err := fmt.Fprintf(w, "/* %v */\n", err)
+ return err
+ }
+ wire, tag := x&7, x>>3
+ if wire == WireEndGroup {
+ w.unindent()
+ if _, err := w.Write(endBraceNewline); err != nil {
+ return err
+ }
+ continue
+ }
+ if _, err := fmt.Fprint(w, tag); err != nil {
+ return err
+ }
+ if wire != WireStartGroup {
+ if err := w.WriteByte(':'); err != nil {
+ return err
+ }
+ }
+ if !w.compact || wire == WireStartGroup {
+ if err := w.WriteByte(' '); err != nil {
+ return err
+ }
+ }
+ switch wire {
+ case WireBytes:
+ buf, e := b.DecodeRawBytes(false)
+ if e == nil {
+ _, err = fmt.Fprintf(w, "%q", buf)
+ } else {
+ _, err = fmt.Fprintf(w, "/* %v */", e)
+ }
+ case WireFixed32:
+ x, err = b.DecodeFixed32()
+ err = writeUnknownInt(w, x, err)
+ case WireFixed64:
+ x, err = b.DecodeFixed64()
+ err = writeUnknownInt(w, x, err)
+ case WireStartGroup:
+ err = w.WriteByte('{')
+ w.indent()
+ case WireVarint:
+ x, err = b.DecodeVarint()
+ err = writeUnknownInt(w, x, err)
+ default:
+ _, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire)
+ }
+ if err != nil {
+ return err
+ }
+ if err = w.WriteByte('\n'); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func writeUnknownInt(w *textWriter, x uint64, err error) error {
+ if err == nil {
+ _, err = fmt.Fprint(w, x)
+ } else {
+ _, err = fmt.Fprintf(w, "/* %v */", err)
+ }
+ return err
+}
+
+type int32Slice []int32
+
+func (s int32Slice) Len() int { return len(s) }
+func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] }
+func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// writeExtensions writes all the extensions in pv.
+// pv is assumed to be a pointer to a protocol message struct that is extendable.
+func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error {
+ emap := extensionMaps[pv.Type().Elem()]
+ ep, _ := extendable(pv.Interface())
+
+ // Order the extensions by ID.
+ // This isn't strictly necessary, but it will give us
+ // canonical output, which will also make testing easier.
+ m, mu := ep.extensionsRead()
+ if m == nil {
+ return nil
+ }
+ mu.Lock()
+ ids := make([]int32, 0, len(m))
+ for id := range m {
+ ids = append(ids, id)
+ }
+ sort.Sort(int32Slice(ids))
+ mu.Unlock()
+
+ for _, extNum := range ids {
+ ext := m[extNum]
+ var desc *ExtensionDesc
+ if emap != nil {
+ desc = emap[extNum]
+ }
+ if desc == nil {
+ // Unknown extension.
+ if err := writeUnknownStruct(w, ext.enc); err != nil {
+ return err
+ }
+ continue
+ }
+
+ pb, err := GetExtension(ep, desc)
+ if err != nil {
+ return fmt.Errorf("failed getting extension: %v", err)
+ }
+
+ // Repeated extensions will appear as a slice.
+ if !desc.repeated() {
+ if err := tm.writeExtension(w, desc.Name, pb); err != nil {
+ return err
+ }
+ } else {
+ v := reflect.ValueOf(pb)
+ for i := 0; i < v.Len(); i++ {
+ if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
+ return err
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error {
+ if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil {
+ return err
+ }
+ if !w.compact {
+ if err := w.WriteByte(' '); err != nil {
+ return err
+ }
+ }
+ if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil {
+ return err
+ }
+ if err := w.WriteByte('\n'); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (w *textWriter) writeIndent() {
+ if !w.complete {
+ return
+ }
+ remain := w.ind * 2
+ for remain > 0 {
+ n := remain
+ if n > len(spaces) {
+ n = len(spaces)
+ }
+ w.w.Write(spaces[:n])
+ remain -= n
+ }
+ w.complete = false
+}
+
+// TextMarshaler is a configurable text format marshaler.
+type TextMarshaler struct {
+ Compact bool // use compact text format (one line).
+ ExpandAny bool // expand google.protobuf.Any messages of known types
+}
+
+// Marshal writes a given protocol buffer in text format.
+// The only errors returned are from w.
+func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
+ val := reflect.ValueOf(pb)
+ if pb == nil || val.IsNil() {
+ w.Write([]byte(""))
+ return nil
+ }
+ var bw *bufio.Writer
+ ww, ok := w.(writer)
+ if !ok {
+ bw = bufio.NewWriter(w)
+ ww = bw
+ }
+ aw := &textWriter{
+ w: ww,
+ complete: true,
+ compact: tm.Compact,
+ }
+
+ if etm, ok := pb.(encoding.TextMarshaler); ok {
+ text, err := etm.MarshalText()
+ if err != nil {
+ return err
+ }
+ if _, err = aw.Write(text); err != nil {
+ return err
+ }
+ if bw != nil {
+ return bw.Flush()
+ }
+ return nil
+ }
+ // Dereference the received pointer so we don't have outer < and >.
+ v := reflect.Indirect(val)
+ if err := tm.writeStruct(aw, v); err != nil {
+ return err
+ }
+ if bw != nil {
+ return bw.Flush()
+ }
+ return nil
+}
+
+// Text is the same as Marshal, but returns the string directly.
+func (tm *TextMarshaler) Text(pb Message) string {
+ var buf bytes.Buffer
+ tm.Marshal(&buf, pb)
+ return buf.String()
+}
+
+var (
+ defaultTextMarshaler = TextMarshaler{}
+ compactTextMarshaler = TextMarshaler{Compact: true}
+)
+
+// TODO: consider removing some of the Marshal functions below.
+
+// MarshalText writes a given protocol buffer in text format.
+// The only errors returned are from w.
+func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) }
+
+// MarshalTextString is the same as MarshalText, but returns the string directly.
+func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) }
+
+// CompactText writes a given protocol buffer in compact text format (one line).
+func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) }
+
+// CompactTextString is the same as CompactText, but returns the string directly.
+func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) }
diff --git a/vendor/github.com/golang/protobuf/proto/text_parser.go b/vendor/github.com/golang/protobuf/proto/text_parser.go
new file mode 100644
index 00000000..5e14513f
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/text_parser.go
@@ -0,0 +1,895 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+package proto
+
+// Functions for parsing the Text protocol buffer format.
+// TODO: message sets.
+
+import (
+ "encoding"
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "unicode/utf8"
+)
+
+// Error string emitted when deserializing Any and fields are already set
+const anyRepeatedlyUnpacked = "Any message unpacked multiple times, or %q already set"
+
+type ParseError struct {
+ Message string
+ Line int // 1-based line number
+ Offset int // 0-based byte offset from start of input
+}
+
+func (p *ParseError) Error() string {
+ if p.Line == 1 {
+ // show offset only for first line
+ return fmt.Sprintf("line 1.%d: %v", p.Offset, p.Message)
+ }
+ return fmt.Sprintf("line %d: %v", p.Line, p.Message)
+}
+
+type token struct {
+ value string
+ err *ParseError
+ line int // line number
+ offset int // byte number from start of input, not start of line
+ unquoted string // the unquoted version of value, if it was a quoted string
+}
+
+func (t *token) String() string {
+ if t.err == nil {
+ return fmt.Sprintf("%q (line=%d, offset=%d)", t.value, t.line, t.offset)
+ }
+ return fmt.Sprintf("parse error: %v", t.err)
+}
+
+type textParser struct {
+ s string // remaining input
+ done bool // whether the parsing is finished (success or error)
+ backed bool // whether back() was called
+ offset, line int
+ cur token
+}
+
+func newTextParser(s string) *textParser {
+ p := new(textParser)
+ p.s = s
+ p.line = 1
+ p.cur.line = 1
+ return p
+}
+
+func (p *textParser) errorf(format string, a ...interface{}) *ParseError {
+ pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset}
+ p.cur.err = pe
+ p.done = true
+ return pe
+}
+
+// Numbers and identifiers are matched by [-+._A-Za-z0-9]
+func isIdentOrNumberChar(c byte) bool {
+ switch {
+ case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z':
+ return true
+ case '0' <= c && c <= '9':
+ return true
+ }
+ switch c {
+ case '-', '+', '.', '_':
+ return true
+ }
+ return false
+}
+
+func isWhitespace(c byte) bool {
+ switch c {
+ case ' ', '\t', '\n', '\r':
+ return true
+ }
+ return false
+}
+
+func isQuote(c byte) bool {
+ switch c {
+ case '"', '\'':
+ return true
+ }
+ return false
+}
+
+func (p *textParser) skipWhitespace() {
+ i := 0
+ for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') {
+ if p.s[i] == '#' {
+ // comment; skip to end of line or input
+ for i < len(p.s) && p.s[i] != '\n' {
+ i++
+ }
+ if i == len(p.s) {
+ break
+ }
+ }
+ if p.s[i] == '\n' {
+ p.line++
+ }
+ i++
+ }
+ p.offset += i
+ p.s = p.s[i:len(p.s)]
+ if len(p.s) == 0 {
+ p.done = true
+ }
+}
+
+func (p *textParser) advance() {
+ // Skip whitespace
+ p.skipWhitespace()
+ if p.done {
+ return
+ }
+
+ // Start of non-whitespace
+ p.cur.err = nil
+ p.cur.offset, p.cur.line = p.offset, p.line
+ p.cur.unquoted = ""
+ switch p.s[0] {
+ case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/':
+ // Single symbol
+ p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
+ case '"', '\'':
+ // Quoted string
+ i := 1
+ for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' {
+ if p.s[i] == '\\' && i+1 < len(p.s) {
+ // skip escaped char
+ i++
+ }
+ i++
+ }
+ if i >= len(p.s) || p.s[i] != p.s[0] {
+ p.errorf("unmatched quote")
+ return
+ }
+ unq, err := unquoteC(p.s[1:i], rune(p.s[0]))
+ if err != nil {
+ p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err)
+ return
+ }
+ p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)]
+ p.cur.unquoted = unq
+ default:
+ i := 0
+ for i < len(p.s) && isIdentOrNumberChar(p.s[i]) {
+ i++
+ }
+ if i == 0 {
+ p.errorf("unexpected byte %#x", p.s[0])
+ return
+ }
+ p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)]
+ }
+ p.offset += len(p.cur.value)
+}
+
+var (
+ errBadUTF8 = errors.New("proto: bad UTF-8")
+ errBadHex = errors.New("proto: bad hexadecimal")
+)
+
+func unquoteC(s string, quote rune) (string, error) {
+ // This is based on C++'s tokenizer.cc.
+ // Despite its name, this is *not* parsing C syntax.
+ // For instance, "\0" is an invalid quoted string.
+
+ // Avoid allocation in trivial cases.
+ simple := true
+ for _, r := range s {
+ if r == '\\' || r == quote {
+ simple = false
+ break
+ }
+ }
+ if simple {
+ return s, nil
+ }
+
+ buf := make([]byte, 0, 3*len(s)/2)
+ for len(s) > 0 {
+ r, n := utf8.DecodeRuneInString(s)
+ if r == utf8.RuneError && n == 1 {
+ return "", errBadUTF8
+ }
+ s = s[n:]
+ if r != '\\' {
+ if r < utf8.RuneSelf {
+ buf = append(buf, byte(r))
+ } else {
+ buf = append(buf, string(r)...)
+ }
+ continue
+ }
+
+ ch, tail, err := unescape(s)
+ if err != nil {
+ return "", err
+ }
+ buf = append(buf, ch...)
+ s = tail
+ }
+ return string(buf), nil
+}
+
+func unescape(s string) (ch string, tail string, err error) {
+ r, n := utf8.DecodeRuneInString(s)
+ if r == utf8.RuneError && n == 1 {
+ return "", "", errBadUTF8
+ }
+ s = s[n:]
+ switch r {
+ case 'a':
+ return "\a", s, nil
+ case 'b':
+ return "\b", s, nil
+ case 'f':
+ return "\f", s, nil
+ case 'n':
+ return "\n", s, nil
+ case 'r':
+ return "\r", s, nil
+ case 't':
+ return "\t", s, nil
+ case 'v':
+ return "\v", s, nil
+ case '?':
+ return "?", s, nil // trigraph workaround
+ case '\'', '"', '\\':
+ return string(r), s, nil
+ case '0', '1', '2', '3', '4', '5', '6', '7', 'x', 'X':
+ if len(s) < 2 {
+ return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
+ }
+ base := 8
+ ss := s[:2]
+ s = s[2:]
+ if r == 'x' || r == 'X' {
+ base = 16
+ } else {
+ ss = string(r) + ss
+ }
+ i, err := strconv.ParseUint(ss, base, 8)
+ if err != nil {
+ return "", "", err
+ }
+ return string([]byte{byte(i)}), s, nil
+ case 'u', 'U':
+ n := 4
+ if r == 'U' {
+ n = 8
+ }
+ if len(s) < n {
+ return "", "", fmt.Errorf(`\%c requires %d digits`, r, n)
+ }
+
+ bs := make([]byte, n/2)
+ for i := 0; i < n; i += 2 {
+ a, ok1 := unhex(s[i])
+ b, ok2 := unhex(s[i+1])
+ if !ok1 || !ok2 {
+ return "", "", errBadHex
+ }
+ bs[i/2] = a<<4 | b
+ }
+ s = s[n:]
+ return string(bs), s, nil
+ }
+ return "", "", fmt.Errorf(`unknown escape \%c`, r)
+}
+
+// Adapted from src/pkg/strconv/quote.go.
+func unhex(b byte) (v byte, ok bool) {
+ switch {
+ case '0' <= b && b <= '9':
+ return b - '0', true
+ case 'a' <= b && b <= 'f':
+ return b - 'a' + 10, true
+ case 'A' <= b && b <= 'F':
+ return b - 'A' + 10, true
+ }
+ return 0, false
+}
+
+// Back off the parser by one token. Can only be done between calls to next().
+// It makes the next advance() a no-op.
+func (p *textParser) back() { p.backed = true }
+
+// Advances the parser and returns the new current token.
+func (p *textParser) next() *token {
+ if p.backed || p.done {
+ p.backed = false
+ return &p.cur
+ }
+ p.advance()
+ if p.done {
+ p.cur.value = ""
+ } else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) {
+ // Look for multiple quoted strings separated by whitespace,
+ // and concatenate them.
+ cat := p.cur
+ for {
+ p.skipWhitespace()
+ if p.done || !isQuote(p.s[0]) {
+ break
+ }
+ p.advance()
+ if p.cur.err != nil {
+ return &p.cur
+ }
+ cat.value += " " + p.cur.value
+ cat.unquoted += p.cur.unquoted
+ }
+ p.done = false // parser may have seen EOF, but we want to return cat
+ p.cur = cat
+ }
+ return &p.cur
+}
+
+func (p *textParser) consumeToken(s string) error {
+ tok := p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ if tok.value != s {
+ p.back()
+ return p.errorf("expected %q, found %q", s, tok.value)
+ }
+ return nil
+}
+
+// Return a RequiredNotSetError indicating which required field was not set.
+func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError {
+ st := sv.Type()
+ sprops := GetProperties(st)
+ for i := 0; i < st.NumField(); i++ {
+ if !isNil(sv.Field(i)) {
+ continue
+ }
+
+ props := sprops.Prop[i]
+ if props.Required {
+ return &RequiredNotSetError{fmt.Sprintf("%v.%v", st, props.OrigName)}
+ }
+ }
+ return &RequiredNotSetError{fmt.Sprintf("%v.", st)} // should not happen
+}
+
+// Returns the index in the struct for the named field, as well as the parsed tag properties.
+func structFieldByName(sprops *StructProperties, name string) (int, *Properties, bool) {
+ i, ok := sprops.decoderOrigNames[name]
+ if ok {
+ return i, sprops.Prop[i], true
+ }
+ return -1, nil, false
+}
+
+// Consume a ':' from the input stream (if the next token is a colon),
+// returning an error if a colon is needed but not present.
+func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseError {
+ tok := p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ if tok.value != ":" {
+ // Colon is optional when the field is a group or message.
+ needColon := true
+ switch props.Wire {
+ case "group":
+ needColon = false
+ case "bytes":
+ // A "bytes" field is either a message, a string, or a repeated field;
+ // those three become *T, *string and []T respectively, so we can check for
+ // this field being a pointer to a non-string.
+ if typ.Kind() == reflect.Ptr {
+ // *T or *string
+ if typ.Elem().Kind() == reflect.String {
+ break
+ }
+ } else if typ.Kind() == reflect.Slice {
+ // []T or []*T
+ if typ.Elem().Kind() != reflect.Ptr {
+ break
+ }
+ } else if typ.Kind() == reflect.String {
+ // The proto3 exception is for a string field,
+ // which requires a colon.
+ break
+ }
+ needColon = false
+ }
+ if needColon {
+ return p.errorf("expected ':', found %q", tok.value)
+ }
+ p.back()
+ }
+ return nil
+}
+
+func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
+ st := sv.Type()
+ sprops := GetProperties(st)
+ reqCount := sprops.reqCount
+ var reqFieldErr error
+ fieldSet := make(map[string]bool)
+ // A struct is a sequence of "name: value", terminated by one of
+ // '>' or '}', or the end of the input. A name may also be
+ // "[extension]" or "[type/url]".
+ //
+ // The whole struct can also be an expanded Any message, like:
+ // [type/url] < ... struct contents ... >
+ for {
+ tok := p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ if tok.value == terminator {
+ break
+ }
+ if tok.value == "[" {
+ // Looks like an extension or an Any.
+ //
+ // TODO: Check whether we need to handle
+ // namespace rooted names (e.g. ".something.Foo").
+ extName, err := p.consumeExtName()
+ if err != nil {
+ return err
+ }
+
+ if s := strings.LastIndex(extName, "/"); s >= 0 {
+ // If it contains a slash, it's an Any type URL.
+ messageName := extName[s+1:]
+ mt := MessageType(messageName)
+ if mt == nil {
+ return p.errorf("unrecognized message %q in google.protobuf.Any", messageName)
+ }
+ tok = p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ // consume an optional colon
+ if tok.value == ":" {
+ tok = p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ }
+ var terminator string
+ switch tok.value {
+ case "<":
+ terminator = ">"
+ case "{":
+ terminator = "}"
+ default:
+ return p.errorf("expected '{' or '<', found %q", tok.value)
+ }
+ v := reflect.New(mt.Elem())
+ if pe := p.readStruct(v.Elem(), terminator); pe != nil {
+ return pe
+ }
+ b, err := Marshal(v.Interface().(Message))
+ if err != nil {
+ return p.errorf("failed to marshal message of type %q: %v", messageName, err)
+ }
+ if fieldSet["type_url"] {
+ return p.errorf(anyRepeatedlyUnpacked, "type_url")
+ }
+ if fieldSet["value"] {
+ return p.errorf(anyRepeatedlyUnpacked, "value")
+ }
+ sv.FieldByName("TypeUrl").SetString(extName)
+ sv.FieldByName("Value").SetBytes(b)
+ fieldSet["type_url"] = true
+ fieldSet["value"] = true
+ continue
+ }
+
+ var desc *ExtensionDesc
+ // This could be faster, but it's functional.
+ // TODO: Do something smarter than a linear scan.
+ for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) {
+ if d.Name == extName {
+ desc = d
+ break
+ }
+ }
+ if desc == nil {
+ return p.errorf("unrecognized extension %q", extName)
+ }
+
+ props := &Properties{}
+ props.Parse(desc.Tag)
+
+ typ := reflect.TypeOf(desc.ExtensionType)
+ if err := p.checkForColon(props, typ); err != nil {
+ return err
+ }
+
+ rep := desc.repeated()
+
+ // Read the extension structure, and set it in
+ // the value we're constructing.
+ var ext reflect.Value
+ if !rep {
+ ext = reflect.New(typ).Elem()
+ } else {
+ ext = reflect.New(typ.Elem()).Elem()
+ }
+ if err := p.readAny(ext, props); err != nil {
+ if _, ok := err.(*RequiredNotSetError); !ok {
+ return err
+ }
+ reqFieldErr = err
+ }
+ ep := sv.Addr().Interface().(Message)
+ if !rep {
+ SetExtension(ep, desc, ext.Interface())
+ } else {
+ old, err := GetExtension(ep, desc)
+ var sl reflect.Value
+ if err == nil {
+ sl = reflect.ValueOf(old) // existing slice
+ } else {
+ sl = reflect.MakeSlice(typ, 0, 1)
+ }
+ sl = reflect.Append(sl, ext)
+ SetExtension(ep, desc, sl.Interface())
+ }
+ if err := p.consumeOptionalSeparator(); err != nil {
+ return err
+ }
+ continue
+ }
+
+ // This is a normal, non-extension field.
+ name := tok.value
+ var dst reflect.Value
+ fi, props, ok := structFieldByName(sprops, name)
+ if ok {
+ dst = sv.Field(fi)
+ } else if oop, ok := sprops.OneofTypes[name]; ok {
+ // It is a oneof.
+ props = oop.Prop
+ nv := reflect.New(oop.Type.Elem())
+ dst = nv.Elem().Field(0)
+ field := sv.Field(oop.Field)
+ if !field.IsNil() {
+ return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, sv.Type().Field(oop.Field).Name)
+ }
+ field.Set(nv)
+ }
+ if !dst.IsValid() {
+ return p.errorf("unknown field name %q in %v", name, st)
+ }
+
+ if dst.Kind() == reflect.Map {
+ // Consume any colon.
+ if err := p.checkForColon(props, dst.Type()); err != nil {
+ return err
+ }
+
+ // Construct the map if it doesn't already exist.
+ if dst.IsNil() {
+ dst.Set(reflect.MakeMap(dst.Type()))
+ }
+ key := reflect.New(dst.Type().Key()).Elem()
+ val := reflect.New(dst.Type().Elem()).Elem()
+
+ // The map entry should be this sequence of tokens:
+ // < key : KEY value : VALUE >
+ // However, implementations may omit key or value, and technically
+ // we should support them in any order. See b/28924776 for a time
+ // this went wrong.
+
+ tok := p.next()
+ var terminator string
+ switch tok.value {
+ case "<":
+ terminator = ">"
+ case "{":
+ terminator = "}"
+ default:
+ return p.errorf("expected '{' or '<', found %q", tok.value)
+ }
+ for {
+ tok := p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ if tok.value == terminator {
+ break
+ }
+ switch tok.value {
+ case "key":
+ if err := p.consumeToken(":"); err != nil {
+ return err
+ }
+ if err := p.readAny(key, props.mkeyprop); err != nil {
+ return err
+ }
+ if err := p.consumeOptionalSeparator(); err != nil {
+ return err
+ }
+ case "value":
+ if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil {
+ return err
+ }
+ if err := p.readAny(val, props.mvalprop); err != nil {
+ return err
+ }
+ if err := p.consumeOptionalSeparator(); err != nil {
+ return err
+ }
+ default:
+ p.back()
+ return p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value)
+ }
+ }
+
+ dst.SetMapIndex(key, val)
+ continue
+ }
+
+ // Check that it's not already set if it's not a repeated field.
+ if !props.Repeated && fieldSet[name] {
+ return p.errorf("non-repeated field %q was repeated", name)
+ }
+
+ if err := p.checkForColon(props, dst.Type()); err != nil {
+ return err
+ }
+
+ // Parse into the field.
+ fieldSet[name] = true
+ if err := p.readAny(dst, props); err != nil {
+ if _, ok := err.(*RequiredNotSetError); !ok {
+ return err
+ }
+ reqFieldErr = err
+ }
+ if props.Required {
+ reqCount--
+ }
+
+ if err := p.consumeOptionalSeparator(); err != nil {
+ return err
+ }
+
+ }
+
+ if reqCount > 0 {
+ return p.missingRequiredFieldError(sv)
+ }
+ return reqFieldErr
+}
+
+// consumeExtName consumes extension name or expanded Any type URL and the
+// following ']'. It returns the name or URL consumed.
+func (p *textParser) consumeExtName() (string, error) {
+ tok := p.next()
+ if tok.err != nil {
+ return "", tok.err
+ }
+
+ // If extension name or type url is quoted, it's a single token.
+ if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] {
+ name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0]))
+ if err != nil {
+ return "", err
+ }
+ return name, p.consumeToken("]")
+ }
+
+ // Consume everything up to "]"
+ var parts []string
+ for tok.value != "]" {
+ parts = append(parts, tok.value)
+ tok = p.next()
+ if tok.err != nil {
+ return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
+ }
+ }
+ return strings.Join(parts, ""), nil
+}
+
+// consumeOptionalSeparator consumes an optional semicolon or comma.
+// It is used in readStruct to provide backward compatibility.
+func (p *textParser) consumeOptionalSeparator() error {
+ tok := p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ if tok.value != ";" && tok.value != "," {
+ p.back()
+ }
+ return nil
+}
+
+func (p *textParser) readAny(v reflect.Value, props *Properties) error {
+ tok := p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ if tok.value == "" {
+ return p.errorf("unexpected EOF")
+ }
+
+ switch fv := v; fv.Kind() {
+ case reflect.Slice:
+ at := v.Type()
+ if at.Elem().Kind() == reflect.Uint8 {
+ // Special case for []byte
+ if tok.value[0] != '"' && tok.value[0] != '\'' {
+ // Deliberately written out here, as the error after
+ // this switch statement would write "invalid []byte: ...",
+ // which is not as user-friendly.
+ return p.errorf("invalid string: %v", tok.value)
+ }
+ bytes := []byte(tok.unquoted)
+ fv.Set(reflect.ValueOf(bytes))
+ return nil
+ }
+ // Repeated field.
+ if tok.value == "[" {
+ // Repeated field with list notation, like [1,2,3].
+ for {
+ fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
+ err := p.readAny(fv.Index(fv.Len()-1), props)
+ if err != nil {
+ return err
+ }
+ tok := p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ if tok.value == "]" {
+ break
+ }
+ if tok.value != "," {
+ return p.errorf("Expected ']' or ',' found %q", tok.value)
+ }
+ }
+ return nil
+ }
+ // One value of the repeated field.
+ p.back()
+ fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
+ return p.readAny(fv.Index(fv.Len()-1), props)
+ case reflect.Bool:
+ // true/1/t/True or false/f/0/False.
+ switch tok.value {
+ case "true", "1", "t", "True":
+ fv.SetBool(true)
+ return nil
+ case "false", "0", "f", "False":
+ fv.SetBool(false)
+ return nil
+ }
+ case reflect.Float32, reflect.Float64:
+ v := tok.value
+ // Ignore 'f' for compatibility with output generated by C++, but don't
+ // remove 'f' when the value is "-inf" or "inf".
+ if strings.HasSuffix(v, "f") && tok.value != "-inf" && tok.value != "inf" {
+ v = v[:len(v)-1]
+ }
+ if f, err := strconv.ParseFloat(v, fv.Type().Bits()); err == nil {
+ fv.SetFloat(f)
+ return nil
+ }
+ case reflect.Int32:
+ if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
+ fv.SetInt(x)
+ return nil
+ }
+
+ if len(props.Enum) == 0 {
+ break
+ }
+ m, ok := enumValueMaps[props.Enum]
+ if !ok {
+ break
+ }
+ x, ok := m[tok.value]
+ if !ok {
+ break
+ }
+ fv.SetInt(int64(x))
+ return nil
+ case reflect.Int64:
+ if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil {
+ fv.SetInt(x)
+ return nil
+ }
+
+ case reflect.Ptr:
+ // A basic field (indirected through pointer), or a repeated message/group
+ p.back()
+ fv.Set(reflect.New(fv.Type().Elem()))
+ return p.readAny(fv.Elem(), props)
+ case reflect.String:
+ if tok.value[0] == '"' || tok.value[0] == '\'' {
+ fv.SetString(tok.unquoted)
+ return nil
+ }
+ case reflect.Struct:
+ var terminator string
+ switch tok.value {
+ case "{":
+ terminator = "}"
+ case "<":
+ terminator = ">"
+ default:
+ return p.errorf("expected '{' or '<', found %q", tok.value)
+ }
+ // TODO: Handle nested messages which implement encoding.TextUnmarshaler.
+ return p.readStruct(fv, terminator)
+ case reflect.Uint32:
+ if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
+ fv.SetUint(x)
+ return nil
+ }
+ case reflect.Uint64:
+ if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
+ fv.SetUint(x)
+ return nil
+ }
+ }
+ return p.errorf("invalid %v: %v", v.Type(), tok.value)
+}
+
+// UnmarshalText reads a protocol buffer in Text format. UnmarshalText resets pb
+// before starting to unmarshal, so any existing data in pb is always removed.
+// If a required field is not set and no other error occurs,
+// UnmarshalText returns *RequiredNotSetError.
+func UnmarshalText(s string, pb Message) error {
+ if um, ok := pb.(encoding.TextUnmarshaler); ok {
+ err := um.UnmarshalText([]byte(s))
+ return err
+ }
+ pb.Reset()
+ v := reflect.ValueOf(pb)
+ if pe := newTextParser(s).readStruct(v.Elem(), ""); pe != nil {
+ return pe
+ }
+ return nil
+}
diff --git a/vendor/github.com/golang/protobuf/ptypes/any.go b/vendor/github.com/golang/protobuf/ptypes/any.go
new file mode 100644
index 00000000..b2af97f4
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/ptypes/any.go
@@ -0,0 +1,139 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2016 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+package ptypes
+
+// This file implements functions to marshal proto.Message to/from
+// google.protobuf.Any message.
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+
+ "github.com/golang/protobuf/proto"
+ "github.com/golang/protobuf/ptypes/any"
+)
+
+const googleApis = "type.googleapis.com/"
+
+// AnyMessageName returns the name of the message contained in a google.protobuf.Any message.
+//
+// Note that regular type assertions should be done using the Is
+// function. AnyMessageName is provided for less common use cases like filtering a
+// sequence of Any messages based on a set of allowed message type names.
+func AnyMessageName(any *any.Any) (string, error) {
+ if any == nil {
+ return "", fmt.Errorf("message is nil")
+ }
+ slash := strings.LastIndex(any.TypeUrl, "/")
+ if slash < 0 {
+ return "", fmt.Errorf("message type url %q is invalid", any.TypeUrl)
+ }
+ return any.TypeUrl[slash+1:], nil
+}
+
+// MarshalAny takes the protocol buffer and encodes it into google.protobuf.Any.
+func MarshalAny(pb proto.Message) (*any.Any, error) {
+ value, err := proto.Marshal(pb)
+ if err != nil {
+ return nil, err
+ }
+ return &any.Any{TypeUrl: googleApis + proto.MessageName(pb), Value: value}, nil
+}
+
+// DynamicAny is a value that can be passed to UnmarshalAny to automatically
+// allocate a proto.Message for the type specified in a google.protobuf.Any
+// message. The allocated message is stored in the embedded proto.Message.
+//
+// Example:
+//
+// var x ptypes.DynamicAny
+// if err := ptypes.UnmarshalAny(a, &x); err != nil { ... }
+// fmt.Printf("unmarshaled message: %v", x.Message)
+type DynamicAny struct {
+ proto.Message
+}
+
+// Empty returns a new proto.Message of the type specified in a
+// google.protobuf.Any message. It returns an error if corresponding message
+// type isn't linked in.
+func Empty(any *any.Any) (proto.Message, error) {
+ aname, err := AnyMessageName(any)
+ if err != nil {
+ return nil, err
+ }
+
+ t := proto.MessageType(aname)
+ if t == nil {
+ return nil, fmt.Errorf("any: message type %q isn't linked in", aname)
+ }
+ return reflect.New(t.Elem()).Interface().(proto.Message), nil
+}
+
+// UnmarshalAny parses the protocol buffer representation in a google.protobuf.Any
+// message and places the decoded result in pb. It returns an error if type of
+// contents of Any message does not match type of pb message.
+//
+// pb can be a proto.Message, or a *DynamicAny.
+func UnmarshalAny(any *any.Any, pb proto.Message) error {
+ if d, ok := pb.(*DynamicAny); ok {
+ if d.Message == nil {
+ var err error
+ d.Message, err = Empty(any)
+ if err != nil {
+ return err
+ }
+ }
+ return UnmarshalAny(any, d.Message)
+ }
+
+ aname, err := AnyMessageName(any)
+ if err != nil {
+ return err
+ }
+
+ mname := proto.MessageName(pb)
+ if aname != mname {
+ return fmt.Errorf("mismatched message type: got %q want %q", aname, mname)
+ }
+ return proto.Unmarshal(any.Value, pb)
+}
+
+// Is returns true if any value contains a given message type.
+func Is(any *any.Any, pb proto.Message) bool {
+ aname, err := AnyMessageName(any)
+ if err != nil {
+ return false
+ }
+
+ return aname == proto.MessageName(pb)
+}
diff --git a/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go b/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go
new file mode 100644
index 00000000..f3460172
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go
@@ -0,0 +1,178 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: google/protobuf/any.proto
+
+/*
+Package any is a generated protocol buffer package.
+
+It is generated from these files:
+ google/protobuf/any.proto
+
+It has these top-level messages:
+ Any
+*/
+package any
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+// `Any` contains an arbitrary serialized protocol buffer message along with a
+// URL that describes the type of the serialized message.
+//
+// Protobuf library provides support to pack/unpack Any values in the form
+// of utility functions or additional generated methods of the Any type.
+//
+// Example 1: Pack and unpack a message in C++.
+//
+// Foo foo = ...;
+// Any any;
+// any.PackFrom(foo);
+// ...
+// if (any.UnpackTo(&foo)) {
+// ...
+// }
+//
+// Example 2: Pack and unpack a message in Java.
+//
+// Foo foo = ...;
+// Any any = Any.pack(foo);
+// ...
+// if (any.is(Foo.class)) {
+// foo = any.unpack(Foo.class);
+// }
+//
+// Example 3: Pack and unpack a message in Python.
+//
+// foo = Foo(...)
+// any = Any()
+// any.Pack(foo)
+// ...
+// if any.Is(Foo.DESCRIPTOR):
+// any.Unpack(foo)
+// ...
+//
+// Example 4: Pack and unpack a message in Go
+//
+// foo := &pb.Foo{...}
+// any, err := ptypes.MarshalAny(foo)
+// ...
+// foo := &pb.Foo{}
+// if err := ptypes.UnmarshalAny(any, foo); err != nil {
+// ...
+// }
+//
+// The pack methods provided by protobuf library will by default use
+// 'type.googleapis.com/full.type.name' as the type URL and the unpack
+// methods only use the fully qualified type name after the last '/'
+// in the type URL, for example "foo.bar.com/x/y.z" will yield type
+// name "y.z".
+//
+//
+// JSON
+// ====
+// The JSON representation of an `Any` value uses the regular
+// representation of the deserialized, embedded message, with an
+// additional field `@type` which contains the type URL. Example:
+//
+// package google.profile;
+// message Person {
+// string first_name = 1;
+// string last_name = 2;
+// }
+//
+// {
+// "@type": "type.googleapis.com/google.profile.Person",
+// "firstName": ,
+// "lastName":
+// }
+//
+// If the embedded message type is well-known and has a custom JSON
+// representation, that representation will be embedded adding a field
+// `value` which holds the custom JSON in addition to the `@type`
+// field. Example (for message [google.protobuf.Duration][]):
+//
+// {
+// "@type": "type.googleapis.com/google.protobuf.Duration",
+// "value": "1.212s"
+// }
+//
+type Any struct {
+ // A URL/resource name whose content describes the type of the
+ // serialized protocol buffer message.
+ //
+ // For URLs which use the scheme `http`, `https`, or no scheme, the
+ // following restrictions and interpretations apply:
+ //
+ // * If no scheme is provided, `https` is assumed.
+ // * The last segment of the URL's path must represent the fully
+ // qualified name of the type (as in `path/google.protobuf.Duration`).
+ // The name should be in a canonical form (e.g., leading "." is
+ // not accepted).
+ // * An HTTP GET on the URL must yield a [google.protobuf.Type][]
+ // value in binary format, or produce an error.
+ // * Applications are allowed to cache lookup results based on the
+ // URL, or have them precompiled into a binary to avoid any
+ // lookup. Therefore, binary compatibility needs to be preserved
+ // on changes to types. (Use versioned type names to manage
+ // breaking changes.)
+ //
+ // Schemes other than `http`, `https` (or the empty scheme) might be
+ // used with implementation specific semantics.
+ //
+ TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl" json:"type_url,omitempty"`
+ // Must be a valid serialized protocol buffer of the above specified type.
+ Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
+}
+
+func (m *Any) Reset() { *m = Any{} }
+func (m *Any) String() string { return proto.CompactTextString(m) }
+func (*Any) ProtoMessage() {}
+func (*Any) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (*Any) XXX_WellKnownType() string { return "Any" }
+
+func (m *Any) GetTypeUrl() string {
+ if m != nil {
+ return m.TypeUrl
+ }
+ return ""
+}
+
+func (m *Any) GetValue() []byte {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterType((*Any)(nil), "google.protobuf.Any")
+}
+
+func init() { proto.RegisterFile("google/protobuf/any.proto", fileDescriptor0) }
+
+var fileDescriptor0 = []byte{
+ // 185 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4c, 0xcf, 0xcf, 0x4f,
+ 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcc, 0xab, 0xd4,
+ 0x03, 0x73, 0x84, 0xf8, 0x21, 0x52, 0x7a, 0x30, 0x29, 0x25, 0x33, 0x2e, 0x66, 0xc7, 0xbc, 0x4a,
+ 0x21, 0x49, 0x2e, 0x8e, 0x92, 0xca, 0x82, 0xd4, 0xf8, 0xd2, 0xa2, 0x1c, 0x09, 0x46, 0x05, 0x46,
+ 0x0d, 0xce, 0x20, 0x76, 0x10, 0x3f, 0xb4, 0x28, 0x47, 0x48, 0x84, 0x8b, 0xb5, 0x2c, 0x31, 0xa7,
+ 0x34, 0x55, 0x82, 0x49, 0x81, 0x51, 0x83, 0x27, 0x08, 0xc2, 0x71, 0xca, 0xe7, 0x12, 0x4e, 0xce,
+ 0xcf, 0xd5, 0x43, 0x33, 0xce, 0x89, 0xc3, 0x31, 0xaf, 0x32, 0x00, 0xc4, 0x09, 0x60, 0x8c, 0x52,
+ 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc,
+ 0x4b, 0x47, 0xb8, 0xa8, 0x00, 0x64, 0x7a, 0x31, 0xc8, 0x61, 0x8b, 0x98, 0x98, 0xdd, 0x03, 0x9c,
+ 0x56, 0x31, 0xc9, 0xb9, 0x43, 0x8c, 0x0a, 0x80, 0x2a, 0xd1, 0x0b, 0x4f, 0xcd, 0xc9, 0xf1, 0xce,
+ 0xcb, 0x2f, 0xcf, 0x0b, 0x01, 0x29, 0x4d, 0x62, 0x03, 0xeb, 0x35, 0x06, 0x04, 0x00, 0x00, 0xff,
+ 0xff, 0x13, 0xf8, 0xe8, 0x42, 0xdd, 0x00, 0x00, 0x00,
+}
diff --git a/vendor/github.com/golang/protobuf/ptypes/any/any.proto b/vendor/github.com/golang/protobuf/ptypes/any/any.proto
new file mode 100644
index 00000000..c7486676
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/ptypes/any/any.proto
@@ -0,0 +1,149 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option go_package = "github.com/golang/protobuf/ptypes/any";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "AnyProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+// `Any` contains an arbitrary serialized protocol buffer message along with a
+// URL that describes the type of the serialized message.
+//
+// Protobuf library provides support to pack/unpack Any values in the form
+// of utility functions or additional generated methods of the Any type.
+//
+// Example 1: Pack and unpack a message in C++.
+//
+// Foo foo = ...;
+// Any any;
+// any.PackFrom(foo);
+// ...
+// if (any.UnpackTo(&foo)) {
+// ...
+// }
+//
+// Example 2: Pack and unpack a message in Java.
+//
+// Foo foo = ...;
+// Any any = Any.pack(foo);
+// ...
+// if (any.is(Foo.class)) {
+// foo = any.unpack(Foo.class);
+// }
+//
+// Example 3: Pack and unpack a message in Python.
+//
+// foo = Foo(...)
+// any = Any()
+// any.Pack(foo)
+// ...
+// if any.Is(Foo.DESCRIPTOR):
+// any.Unpack(foo)
+// ...
+//
+// Example 4: Pack and unpack a message in Go
+//
+// foo := &pb.Foo{...}
+// any, err := ptypes.MarshalAny(foo)
+// ...
+// foo := &pb.Foo{}
+// if err := ptypes.UnmarshalAny(any, foo); err != nil {
+// ...
+// }
+//
+// The pack methods provided by protobuf library will by default use
+// 'type.googleapis.com/full.type.name' as the type URL and the unpack
+// methods only use the fully qualified type name after the last '/'
+// in the type URL, for example "foo.bar.com/x/y.z" will yield type
+// name "y.z".
+//
+//
+// JSON
+// ====
+// The JSON representation of an `Any` value uses the regular
+// representation of the deserialized, embedded message, with an
+// additional field `@type` which contains the type URL. Example:
+//
+// package google.profile;
+// message Person {
+// string first_name = 1;
+// string last_name = 2;
+// }
+//
+// {
+// "@type": "type.googleapis.com/google.profile.Person",
+// "firstName": ,
+// "lastName":
+// }
+//
+// If the embedded message type is well-known and has a custom JSON
+// representation, that representation will be embedded adding a field
+// `value` which holds the custom JSON in addition to the `@type`
+// field. Example (for message [google.protobuf.Duration][]):
+//
+// {
+// "@type": "type.googleapis.com/google.protobuf.Duration",
+// "value": "1.212s"
+// }
+//
+message Any {
+ // A URL/resource name whose content describes the type of the
+ // serialized protocol buffer message.
+ //
+ // For URLs which use the scheme `http`, `https`, or no scheme, the
+ // following restrictions and interpretations apply:
+ //
+ // * If no scheme is provided, `https` is assumed.
+ // * The last segment of the URL's path must represent the fully
+ // qualified name of the type (as in `path/google.protobuf.Duration`).
+ // The name should be in a canonical form (e.g., leading "." is
+ // not accepted).
+ // * An HTTP GET on the URL must yield a [google.protobuf.Type][]
+ // value in binary format, or produce an error.
+ // * Applications are allowed to cache lookup results based on the
+ // URL, or have them precompiled into a binary to avoid any
+ // lookup. Therefore, binary compatibility needs to be preserved
+ // on changes to types. (Use versioned type names to manage
+ // breaking changes.)
+ //
+ // Schemes other than `http`, `https` (or the empty scheme) might be
+ // used with implementation specific semantics.
+ //
+ string type_url = 1;
+
+ // Must be a valid serialized protocol buffer of the above specified type.
+ bytes value = 2;
+}
diff --git a/vendor/github.com/golang/protobuf/ptypes/doc.go b/vendor/github.com/golang/protobuf/ptypes/doc.go
new file mode 100644
index 00000000..c0d595da
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/ptypes/doc.go
@@ -0,0 +1,35 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2016 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+/*
+Package ptypes contains code for interacting with well-known types.
+*/
+package ptypes
diff --git a/vendor/github.com/golang/protobuf/ptypes/duration.go b/vendor/github.com/golang/protobuf/ptypes/duration.go
new file mode 100644
index 00000000..65cb0f8e
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/ptypes/duration.go
@@ -0,0 +1,102 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2016 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+package ptypes
+
+// This file implements conversions between google.protobuf.Duration
+// and time.Duration.
+
+import (
+ "errors"
+ "fmt"
+ "time"
+
+ durpb "github.com/golang/protobuf/ptypes/duration"
+)
+
+const (
+ // Range of a durpb.Duration in seconds, as specified in
+ // google/protobuf/duration.proto. This is about 10,000 years in seconds.
+ maxSeconds = int64(10000 * 365.25 * 24 * 60 * 60)
+ minSeconds = -maxSeconds
+)
+
+// validateDuration determines whether the durpb.Duration is valid according to the
+// definition in google/protobuf/duration.proto. A valid durpb.Duration
+// may still be too large to fit into a time.Duration (the range of durpb.Duration
+// is about 10,000 years, and the range of time.Duration is about 290).
+func validateDuration(d *durpb.Duration) error {
+ if d == nil {
+ return errors.New("duration: nil Duration")
+ }
+ if d.Seconds < minSeconds || d.Seconds > maxSeconds {
+ return fmt.Errorf("duration: %v: seconds out of range", d)
+ }
+ if d.Nanos <= -1e9 || d.Nanos >= 1e9 {
+ return fmt.Errorf("duration: %v: nanos out of range", d)
+ }
+ // Seconds and Nanos must have the same sign, unless d.Nanos is zero.
+ if (d.Seconds < 0 && d.Nanos > 0) || (d.Seconds > 0 && d.Nanos < 0) {
+ return fmt.Errorf("duration: %v: seconds and nanos have different signs", d)
+ }
+ return nil
+}
+
+// Duration converts a durpb.Duration to a time.Duration. Duration
+// returns an error if the durpb.Duration is invalid or is too large to be
+// represented in a time.Duration.
+func Duration(p *durpb.Duration) (time.Duration, error) {
+ if err := validateDuration(p); err != nil {
+ return 0, err
+ }
+ d := time.Duration(p.Seconds) * time.Second
+ if int64(d/time.Second) != p.Seconds {
+ return 0, fmt.Errorf("duration: %v is out of range for time.Duration", p)
+ }
+ if p.Nanos != 0 {
+ d += time.Duration(p.Nanos)
+ if (d < 0) != (p.Nanos < 0) {
+ return 0, fmt.Errorf("duration: %v is out of range for time.Duration", p)
+ }
+ }
+ return d, nil
+}
+
+// DurationProto converts a time.Duration to a durpb.Duration.
+func DurationProto(d time.Duration) *durpb.Duration {
+ nanos := d.Nanoseconds()
+ secs := nanos / 1e9
+ nanos -= secs * 1e9
+ return &durpb.Duration{
+ Seconds: secs,
+ Nanos: int32(nanos),
+ }
+}
diff --git a/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go b/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go
new file mode 100644
index 00000000..b2410a09
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go
@@ -0,0 +1,144 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: google/protobuf/duration.proto
+
+/*
+Package duration is a generated protocol buffer package.
+
+It is generated from these files:
+ google/protobuf/duration.proto
+
+It has these top-level messages:
+ Duration
+*/
+package duration
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+// A Duration represents a signed, fixed-length span of time represented
+// as a count of seconds and fractions of seconds at nanosecond
+// resolution. It is independent of any calendar and concepts like "day"
+// or "month". It is related to Timestamp in that the difference between
+// two Timestamp values is a Duration and it can be added or subtracted
+// from a Timestamp. Range is approximately +-10,000 years.
+//
+// # Examples
+//
+// Example 1: Compute Duration from two Timestamps in pseudo code.
+//
+// Timestamp start = ...;
+// Timestamp end = ...;
+// Duration duration = ...;
+//
+// duration.seconds = end.seconds - start.seconds;
+// duration.nanos = end.nanos - start.nanos;
+//
+// if (duration.seconds < 0 && duration.nanos > 0) {
+// duration.seconds += 1;
+// duration.nanos -= 1000000000;
+// } else if (durations.seconds > 0 && duration.nanos < 0) {
+// duration.seconds -= 1;
+// duration.nanos += 1000000000;
+// }
+//
+// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code.
+//
+// Timestamp start = ...;
+// Duration duration = ...;
+// Timestamp end = ...;
+//
+// end.seconds = start.seconds + duration.seconds;
+// end.nanos = start.nanos + duration.nanos;
+//
+// if (end.nanos < 0) {
+// end.seconds -= 1;
+// end.nanos += 1000000000;
+// } else if (end.nanos >= 1000000000) {
+// end.seconds += 1;
+// end.nanos -= 1000000000;
+// }
+//
+// Example 3: Compute Duration from datetime.timedelta in Python.
+//
+// td = datetime.timedelta(days=3, minutes=10)
+// duration = Duration()
+// duration.FromTimedelta(td)
+//
+// # JSON Mapping
+//
+// In JSON format, the Duration type is encoded as a string rather than an
+// object, where the string ends in the suffix "s" (indicating seconds) and
+// is preceded by the number of seconds, with nanoseconds expressed as
+// fractional seconds. For example, 3 seconds with 0 nanoseconds should be
+// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should
+// be expressed in JSON format as "3.000000001s", and 3 seconds and 1
+// microsecond should be expressed in JSON format as "3.000001s".
+//
+//
+type Duration struct {
+ // Signed seconds of the span of time. Must be from -315,576,000,000
+ // to +315,576,000,000 inclusive. Note: these bounds are computed from:
+ // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
+ Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"`
+ // Signed fractions of a second at nanosecond resolution of the span
+ // of time. Durations less than one second are represented with a 0
+ // `seconds` field and a positive or negative `nanos` field. For durations
+ // of one second or more, a non-zero value for the `nanos` field must be
+ // of the same sign as the `seconds` field. Must be from -999,999,999
+ // to +999,999,999 inclusive.
+ Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"`
+}
+
+func (m *Duration) Reset() { *m = Duration{} }
+func (m *Duration) String() string { return proto.CompactTextString(m) }
+func (*Duration) ProtoMessage() {}
+func (*Duration) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (*Duration) XXX_WellKnownType() string { return "Duration" }
+
+func (m *Duration) GetSeconds() int64 {
+ if m != nil {
+ return m.Seconds
+ }
+ return 0
+}
+
+func (m *Duration) GetNanos() int32 {
+ if m != nil {
+ return m.Nanos
+ }
+ return 0
+}
+
+func init() {
+ proto.RegisterType((*Duration)(nil), "google.protobuf.Duration")
+}
+
+func init() { proto.RegisterFile("google/protobuf/duration.proto", fileDescriptor0) }
+
+var fileDescriptor0 = []byte{
+ // 190 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f,
+ 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0x29, 0x2d, 0x4a,
+ 0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0x56,
+ 0x5c, 0x1c, 0x2e, 0x50, 0x25, 0x42, 0x12, 0x5c, 0xec, 0xc5, 0xa9, 0xc9, 0xf9, 0x79, 0x29, 0xc5,
+ 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x30, 0xae, 0x90, 0x08, 0x17, 0x6b, 0x5e, 0x62, 0x5e,
+ 0x7e, 0xb1, 0x04, 0x93, 0x02, 0xa3, 0x06, 0x6b, 0x10, 0x84, 0xe3, 0x54, 0xc3, 0x25, 0x9c, 0x9c,
+ 0x9f, 0xab, 0x87, 0x66, 0xa4, 0x13, 0x2f, 0xcc, 0xc0, 0x00, 0x90, 0x48, 0x00, 0x63, 0x94, 0x56,
+ 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x7e, 0x7a, 0x7e, 0x4e, 0x62, 0x5e,
+ 0x3a, 0xc2, 0x7d, 0x05, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x70, 0x67, 0xfe, 0x60, 0x64, 0x5c, 0xc4,
+ 0xc4, 0xec, 0x1e, 0xe0, 0xb4, 0x8a, 0x49, 0xce, 0x1d, 0x62, 0x6e, 0x00, 0x54, 0xa9, 0x5e, 0x78,
+ 0x6a, 0x4e, 0x8e, 0x77, 0x5e, 0x7e, 0x79, 0x5e, 0x08, 0x48, 0x4b, 0x12, 0x1b, 0xd8, 0x0c, 0x63,
+ 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x84, 0x30, 0xff, 0xf3, 0x00, 0x00, 0x00,
+}
diff --git a/vendor/github.com/golang/protobuf/ptypes/duration/duration.proto b/vendor/github.com/golang/protobuf/ptypes/duration/duration.proto
new file mode 100644
index 00000000..975fce41
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/ptypes/duration/duration.proto
@@ -0,0 +1,117 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option go_package = "github.com/golang/protobuf/ptypes/duration";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "DurationProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+// A Duration represents a signed, fixed-length span of time represented
+// as a count of seconds and fractions of seconds at nanosecond
+// resolution. It is independent of any calendar and concepts like "day"
+// or "month". It is related to Timestamp in that the difference between
+// two Timestamp values is a Duration and it can be added or subtracted
+// from a Timestamp. Range is approximately +-10,000 years.
+//
+// # Examples
+//
+// Example 1: Compute Duration from two Timestamps in pseudo code.
+//
+// Timestamp start = ...;
+// Timestamp end = ...;
+// Duration duration = ...;
+//
+// duration.seconds = end.seconds - start.seconds;
+// duration.nanos = end.nanos - start.nanos;
+//
+// if (duration.seconds < 0 && duration.nanos > 0) {
+// duration.seconds += 1;
+// duration.nanos -= 1000000000;
+// } else if (durations.seconds > 0 && duration.nanos < 0) {
+// duration.seconds -= 1;
+// duration.nanos += 1000000000;
+// }
+//
+// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code.
+//
+// Timestamp start = ...;
+// Duration duration = ...;
+// Timestamp end = ...;
+//
+// end.seconds = start.seconds + duration.seconds;
+// end.nanos = start.nanos + duration.nanos;
+//
+// if (end.nanos < 0) {
+// end.seconds -= 1;
+// end.nanos += 1000000000;
+// } else if (end.nanos >= 1000000000) {
+// end.seconds += 1;
+// end.nanos -= 1000000000;
+// }
+//
+// Example 3: Compute Duration from datetime.timedelta in Python.
+//
+// td = datetime.timedelta(days=3, minutes=10)
+// duration = Duration()
+// duration.FromTimedelta(td)
+//
+// # JSON Mapping
+//
+// In JSON format, the Duration type is encoded as a string rather than an
+// object, where the string ends in the suffix "s" (indicating seconds) and
+// is preceded by the number of seconds, with nanoseconds expressed as
+// fractional seconds. For example, 3 seconds with 0 nanoseconds should be
+// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should
+// be expressed in JSON format as "3.000000001s", and 3 seconds and 1
+// microsecond should be expressed in JSON format as "3.000001s".
+//
+//
+message Duration {
+
+ // Signed seconds of the span of time. Must be from -315,576,000,000
+ // to +315,576,000,000 inclusive. Note: these bounds are computed from:
+ // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
+ int64 seconds = 1;
+
+ // Signed fractions of a second at nanosecond resolution of the span
+ // of time. Durations less than one second are represented with a 0
+ // `seconds` field and a positive or negative `nanos` field. For durations
+ // of one second or more, a non-zero value for the `nanos` field must be
+ // of the same sign as the `seconds` field. Must be from -999,999,999
+ // to +999,999,999 inclusive.
+ int32 nanos = 2;
+}
diff --git a/vendor/github.com/golang/protobuf/ptypes/regen.sh b/vendor/github.com/golang/protobuf/ptypes/regen.sh
new file mode 100644
index 00000000..b50a9414
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/ptypes/regen.sh
@@ -0,0 +1,43 @@
+#!/bin/bash -e
+#
+# This script fetches and rebuilds the "well-known types" protocol buffers.
+# To run this you will need protoc and goprotobuf installed;
+# see https://github.com/golang/protobuf for instructions.
+# You also need Go and Git installed.
+
+PKG=github.com/golang/protobuf/ptypes
+UPSTREAM=https://github.com/google/protobuf
+UPSTREAM_SUBDIR=src/google/protobuf
+PROTO_FILES=(any duration empty struct timestamp wrappers)
+
+function die() {
+ echo 1>&2 $*
+ exit 1
+}
+
+# Sanity check that the right tools are accessible.
+for tool in go git protoc protoc-gen-go; do
+ q=$(which $tool) || die "didn't find $tool"
+ echo 1>&2 "$tool: $q"
+done
+
+tmpdir=$(mktemp -d -t regen-wkt.XXXXXX)
+trap 'rm -rf $tmpdir' EXIT
+
+echo -n 1>&2 "finding package dir... "
+pkgdir=$(go list -f '{{.Dir}}' $PKG)
+echo 1>&2 $pkgdir
+base=$(echo $pkgdir | sed "s,/$PKG\$,,")
+echo 1>&2 "base: $base"
+cd "$base"
+
+echo 1>&2 "fetching latest protos... "
+git clone -q $UPSTREAM $tmpdir
+
+for file in ${PROTO_FILES[@]}; do
+ echo 1>&2 "* $file"
+ protoc --go_out=. -I$tmpdir/src $tmpdir/src/google/protobuf/$file.proto || die
+ cp $tmpdir/src/google/protobuf/$file.proto $PKG/$file
+done
+
+echo 1>&2 "All OK"
diff --git a/vendor/github.com/golang/protobuf/ptypes/timestamp.go b/vendor/github.com/golang/protobuf/ptypes/timestamp.go
new file mode 100644
index 00000000..47f10dbc
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/ptypes/timestamp.go
@@ -0,0 +1,134 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2016 The Go Authors. All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+package ptypes
+
+// This file implements operations on google.protobuf.Timestamp.
+
+import (
+ "errors"
+ "fmt"
+ "time"
+
+ tspb "github.com/golang/protobuf/ptypes/timestamp"
+)
+
+const (
+ // Seconds field of the earliest valid Timestamp.
+ // This is time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC).Unix().
+ minValidSeconds = -62135596800
+ // Seconds field just after the latest valid Timestamp.
+ // This is time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC).Unix().
+ maxValidSeconds = 253402300800
+)
+
+// validateTimestamp determines whether a Timestamp is valid.
+// A valid timestamp represents a time in the range
+// [0001-01-01, 10000-01-01) and has a Nanos field
+// in the range [0, 1e9).
+//
+// If the Timestamp is valid, validateTimestamp returns nil.
+// Otherwise, it returns an error that describes
+// the problem.
+//
+// Every valid Timestamp can be represented by a time.Time, but the converse is not true.
+func validateTimestamp(ts *tspb.Timestamp) error {
+ if ts == nil {
+ return errors.New("timestamp: nil Timestamp")
+ }
+ if ts.Seconds < minValidSeconds {
+ return fmt.Errorf("timestamp: %v before 0001-01-01", ts)
+ }
+ if ts.Seconds >= maxValidSeconds {
+ return fmt.Errorf("timestamp: %v after 10000-01-01", ts)
+ }
+ if ts.Nanos < 0 || ts.Nanos >= 1e9 {
+ return fmt.Errorf("timestamp: %v: nanos not in range [0, 1e9)", ts)
+ }
+ return nil
+}
+
+// Timestamp converts a google.protobuf.Timestamp proto to a time.Time.
+// It returns an error if the argument is invalid.
+//
+// Unlike most Go functions, if Timestamp returns an error, the first return value
+// is not the zero time.Time. Instead, it is the value obtained from the
+// time.Unix function when passed the contents of the Timestamp, in the UTC
+// locale. This may or may not be a meaningful time; many invalid Timestamps
+// do map to valid time.Times.
+//
+// A nil Timestamp returns an error. The first return value in that case is
+// undefined.
+func Timestamp(ts *tspb.Timestamp) (time.Time, error) {
+ // Don't return the zero value on error, because corresponds to a valid
+ // timestamp. Instead return whatever time.Unix gives us.
+ var t time.Time
+ if ts == nil {
+ t = time.Unix(0, 0).UTC() // treat nil like the empty Timestamp
+ } else {
+ t = time.Unix(ts.Seconds, int64(ts.Nanos)).UTC()
+ }
+ return t, validateTimestamp(ts)
+}
+
+// TimestampNow returns a google.protobuf.Timestamp for the current time.
+func TimestampNow() *tspb.Timestamp {
+ ts, err := TimestampProto(time.Now())
+ if err != nil {
+ panic("ptypes: time.Now() out of Timestamp range")
+ }
+ return ts
+}
+
+// TimestampProto converts the time.Time to a google.protobuf.Timestamp proto.
+// It returns an error if the resulting Timestamp is invalid.
+func TimestampProto(t time.Time) (*tspb.Timestamp, error) {
+ seconds := t.Unix()
+ nanos := int32(t.Sub(time.Unix(seconds, 0)))
+ ts := &tspb.Timestamp{
+ Seconds: seconds,
+ Nanos: nanos,
+ }
+ if err := validateTimestamp(ts); err != nil {
+ return nil, err
+ }
+ return ts, nil
+}
+
+// TimestampString returns the RFC 3339 string for valid Timestamps. For invalid
+// Timestamps, it returns an error message in parentheses.
+func TimestampString(ts *tspb.Timestamp) string {
+ t, err := Timestamp(ts)
+ if err != nil {
+ return fmt.Sprintf("(%v)", err)
+ }
+ return t.Format(time.RFC3339Nano)
+}
diff --git a/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go
new file mode 100644
index 00000000..e23e4a25
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go
@@ -0,0 +1,160 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: google/protobuf/timestamp.proto
+
+/*
+Package timestamp is a generated protocol buffer package.
+
+It is generated from these files:
+ google/protobuf/timestamp.proto
+
+It has these top-level messages:
+ Timestamp
+*/
+package timestamp
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+// A Timestamp represents a point in time independent of any time zone
+// or calendar, represented as seconds and fractions of seconds at
+// nanosecond resolution in UTC Epoch time. It is encoded using the
+// Proleptic Gregorian Calendar which extends the Gregorian calendar
+// backwards to year one. It is encoded assuming all minutes are 60
+// seconds long, i.e. leap seconds are "smeared" so that no leap second
+// table is needed for interpretation. Range is from
+// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
+// By restricting to that range, we ensure that we can convert to
+// and from RFC 3339 date strings.
+// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt).
+//
+// # Examples
+//
+// Example 1: Compute Timestamp from POSIX `time()`.
+//
+// Timestamp timestamp;
+// timestamp.set_seconds(time(NULL));
+// timestamp.set_nanos(0);
+//
+// Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+//
+// struct timeval tv;
+// gettimeofday(&tv, NULL);
+//
+// Timestamp timestamp;
+// timestamp.set_seconds(tv.tv_sec);
+// timestamp.set_nanos(tv.tv_usec * 1000);
+//
+// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+//
+// FILETIME ft;
+// GetSystemTimeAsFileTime(&ft);
+// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+//
+// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+// Timestamp timestamp;
+// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+//
+// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+//
+// long millis = System.currentTimeMillis();
+//
+// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+// .setNanos((int) ((millis % 1000) * 1000000)).build();
+//
+//
+// Example 5: Compute Timestamp from current time in Python.
+//
+// timestamp = Timestamp()
+// timestamp.GetCurrentTime()
+//
+// # JSON Mapping
+//
+// In JSON format, the Timestamp type is encoded as a string in the
+// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
+// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
+// where {year} is always expressed using four digits while {month}, {day},
+// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
+// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
+// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
+// is required, though only UTC (as indicated by "Z") is presently supported.
+//
+// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
+// 01:30 UTC on January 15, 2017.
+//
+// In JavaScript, one can convert a Date object to this format using the
+// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString]
+// method. In Python, a standard `datetime.datetime` object can be converted
+// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
+// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
+// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
+// http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime())
+// to obtain a formatter capable of generating timestamps in this format.
+//
+//
+type Timestamp struct {
+ // Represents seconds of UTC time since Unix epoch
+ // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
+ // 9999-12-31T23:59:59Z inclusive.
+ Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"`
+ // Non-negative fractions of a second at nanosecond resolution. Negative
+ // second values with fractions must still have non-negative nanos values
+ // that count forward in time. Must be from 0 to 999,999,999
+ // inclusive.
+ Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"`
+}
+
+func (m *Timestamp) Reset() { *m = Timestamp{} }
+func (m *Timestamp) String() string { return proto.CompactTextString(m) }
+func (*Timestamp) ProtoMessage() {}
+func (*Timestamp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (*Timestamp) XXX_WellKnownType() string { return "Timestamp" }
+
+func (m *Timestamp) GetSeconds() int64 {
+ if m != nil {
+ return m.Seconds
+ }
+ return 0
+}
+
+func (m *Timestamp) GetNanos() int32 {
+ if m != nil {
+ return m.Nanos
+ }
+ return 0
+}
+
+func init() {
+ proto.RegisterType((*Timestamp)(nil), "google.protobuf.Timestamp")
+}
+
+func init() { proto.RegisterFile("google/protobuf/timestamp.proto", fileDescriptor0) }
+
+var fileDescriptor0 = []byte{
+ // 191 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xcf, 0xcf, 0x4f,
+ 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0xc9, 0xcc, 0x4d,
+ 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0xd0, 0x03, 0x0b, 0x09, 0xf1, 0x43, 0x14, 0xe8, 0xc1, 0x14, 0x28,
+ 0x59, 0x73, 0x71, 0x86, 0xc0, 0xd4, 0x08, 0x49, 0x70, 0xb1, 0x17, 0xa7, 0x26, 0xe7, 0xe7, 0xa5,
+ 0x14, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0x30, 0x07, 0xc1, 0xb8, 0x42, 0x22, 0x5c, 0xac, 0x79, 0x89,
+ 0x79, 0xf9, 0xc5, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xac, 0x41, 0x10, 0x8e, 0x53, 0x1d, 0x97, 0x70,
+ 0x72, 0x7e, 0xae, 0x1e, 0x9a, 0x99, 0x4e, 0x7c, 0x70, 0x13, 0x03, 0x40, 0x42, 0x01, 0x8c, 0x51,
+ 0xda, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0xe9, 0xf9, 0x39, 0x89,
+ 0x79, 0xe9, 0x08, 0x27, 0x16, 0x94, 0x54, 0x16, 0xa4, 0x16, 0x23, 0x5c, 0xfa, 0x83, 0x91, 0x71,
+ 0x11, 0x13, 0xb3, 0x7b, 0x80, 0xd3, 0x2a, 0x26, 0x39, 0x77, 0x88, 0xc9, 0x01, 0x50, 0xb5, 0x7a,
+ 0xe1, 0xa9, 0x39, 0x39, 0xde, 0x79, 0xf9, 0xe5, 0x79, 0x21, 0x20, 0x3d, 0x49, 0x6c, 0x60, 0x43,
+ 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbc, 0x77, 0x4a, 0x07, 0xf7, 0x00, 0x00, 0x00,
+}
diff --git a/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto
new file mode 100644
index 00000000..b7cbd175
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto
@@ -0,0 +1,133 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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 Google Inc. 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 THE COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option go_package = "github.com/golang/protobuf/ptypes/timestamp";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "TimestampProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+// A Timestamp represents a point in time independent of any time zone
+// or calendar, represented as seconds and fractions of seconds at
+// nanosecond resolution in UTC Epoch time. It is encoded using the
+// Proleptic Gregorian Calendar which extends the Gregorian calendar
+// backwards to year one. It is encoded assuming all minutes are 60
+// seconds long, i.e. leap seconds are "smeared" so that no leap second
+// table is needed for interpretation. Range is from
+// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
+// By restricting to that range, we ensure that we can convert to
+// and from RFC 3339 date strings.
+// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt).
+//
+// # Examples
+//
+// Example 1: Compute Timestamp from POSIX `time()`.
+//
+// Timestamp timestamp;
+// timestamp.set_seconds(time(NULL));
+// timestamp.set_nanos(0);
+//
+// Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+//
+// struct timeval tv;
+// gettimeofday(&tv, NULL);
+//
+// Timestamp timestamp;
+// timestamp.set_seconds(tv.tv_sec);
+// timestamp.set_nanos(tv.tv_usec * 1000);
+//
+// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+//
+// FILETIME ft;
+// GetSystemTimeAsFileTime(&ft);
+// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+//
+// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+// Timestamp timestamp;
+// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+//
+// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+//
+// long millis = System.currentTimeMillis();
+//
+// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+// .setNanos((int) ((millis % 1000) * 1000000)).build();
+//
+//
+// Example 5: Compute Timestamp from current time in Python.
+//
+// timestamp = Timestamp()
+// timestamp.GetCurrentTime()
+//
+// # JSON Mapping
+//
+// In JSON format, the Timestamp type is encoded as a string in the
+// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
+// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
+// where {year} is always expressed using four digits while {month}, {day},
+// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
+// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
+// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
+// is required, though only UTC (as indicated by "Z") is presently supported.
+//
+// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
+// 01:30 UTC on January 15, 2017.
+//
+// In JavaScript, one can convert a Date object to this format using the
+// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString]
+// method. In Python, a standard `datetime.datetime` object can be converted
+// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
+// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
+// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
+// http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime())
+// to obtain a formatter capable of generating timestamps in this format.
+//
+//
+message Timestamp {
+
+ // Represents seconds of UTC time since Unix epoch
+ // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
+ // 9999-12-31T23:59:59Z inclusive.
+ int64 seconds = 1;
+
+ // Non-negative fractions of a second at nanosecond resolution. Negative
+ // second values with fractions must still have non-negative nanos values
+ // that count forward in time. Must be from 0 to 999,999,999
+ // inclusive.
+ int32 nanos = 2;
+}
diff --git a/vendor/github.com/golang/snappy/.gitignore b/vendor/github.com/golang/snappy/.gitignore
new file mode 100644
index 00000000..042091d9
--- /dev/null
+++ b/vendor/github.com/golang/snappy/.gitignore
@@ -0,0 +1,16 @@
+cmd/snappytool/snappytool
+testdata/bench
+
+# These explicitly listed benchmark data files are for an obsolete version of
+# snappy_test.go.
+testdata/alice29.txt
+testdata/asyoulik.txt
+testdata/fireworks.jpeg
+testdata/geo.protodata
+testdata/html
+testdata/html_x_4
+testdata/kppkn.gtb
+testdata/lcet10.txt
+testdata/paper-100k.pdf
+testdata/plrabn12.txt
+testdata/urls.10K
diff --git a/vendor/github.com/golang/snappy/AUTHORS b/vendor/github.com/golang/snappy/AUTHORS
new file mode 100644
index 00000000..bcfa1952
--- /dev/null
+++ b/vendor/github.com/golang/snappy/AUTHORS
@@ -0,0 +1,15 @@
+# This is the official list of Snappy-Go authors for copyright purposes.
+# This file is distinct from the CONTRIBUTORS files.
+# See the latter for an explanation.
+
+# Names should be added to this file as
+# Name or Organization
+# The email address is not required for organizations.
+
+# Please keep the list sorted.
+
+Damian Gryski
+Google Inc.
+Jan Mercl <0xjnml@gmail.com>
+Rodolfo Carvalho
+Sebastien Binet
diff --git a/vendor/github.com/golang/snappy/CONTRIBUTORS b/vendor/github.com/golang/snappy/CONTRIBUTORS
new file mode 100644
index 00000000..931ae316
--- /dev/null
+++ b/vendor/github.com/golang/snappy/CONTRIBUTORS
@@ -0,0 +1,37 @@
+# This is the official list of people who can contribute
+# (and typically have contributed) code to the Snappy-Go repository.
+# The AUTHORS file lists the copyright holders; this file
+# lists people. For example, Google employees are listed here
+# but not in AUTHORS, because Google holds the copyright.
+#
+# The submission process automatically checks to make sure
+# that people submitting code are listed in this file (by email address).
+#
+# Names should be added to this file only after verifying that
+# the individual or the individual's organization has agreed to
+# the appropriate Contributor License Agreement, found here:
+#
+# http://code.google.com/legal/individual-cla-v1.0.html
+# http://code.google.com/legal/corporate-cla-v1.0.html
+#
+# The agreement for individuals can be filled out on the web.
+#
+# When adding J Random Contributor's name to this file,
+# either J's name or J's organization's name should be
+# added to the AUTHORS file, depending on whether the
+# individual or corporate CLA was used.
+
+# Names should be added to this file like so:
+# Name
+
+# Please keep the list sorted.
+
+Damian Gryski
+Jan Mercl <0xjnml@gmail.com>
+Kai Backman
+Marc-Antoine Ruel
+Nigel Tao
+Rob Pike
+Rodolfo Carvalho
+Russ Cox
+Sebastien Binet
diff --git a/vendor/github.com/golang/snappy/LICENSE b/vendor/github.com/golang/snappy/LICENSE
new file mode 100644
index 00000000..6050c10f
--- /dev/null
+++ b/vendor/github.com/golang/snappy/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2011 The Snappy-Go Authors. 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 Google Inc. 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 THE COPYRIGHT
+OWNER OR CONTRIBUTORS 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.
diff --git a/vendor/github.com/golang/snappy/README b/vendor/github.com/golang/snappy/README
new file mode 100644
index 00000000..cea12879
--- /dev/null
+++ b/vendor/github.com/golang/snappy/README
@@ -0,0 +1,107 @@
+The Snappy compression format in the Go programming language.
+
+To download and install from source:
+$ go get github.com/golang/snappy
+
+Unless otherwise noted, the Snappy-Go source files are distributed
+under the BSD-style license found in the LICENSE file.
+
+
+
+Benchmarks.
+
+The golang/snappy benchmarks include compressing (Z) and decompressing (U) ten
+or so files, the same set used by the C++ Snappy code (github.com/google/snappy
+and note the "google", not "golang"). On an "Intel(R) Core(TM) i7-3770 CPU @
+3.40GHz", Go's GOARCH=amd64 numbers as of 2016-05-29:
+
+"go test -test.bench=."
+
+_UFlat0-8 2.19GB/s ± 0% html
+_UFlat1-8 1.41GB/s ± 0% urls
+_UFlat2-8 23.5GB/s ± 2% jpg
+_UFlat3-8 1.91GB/s ± 0% jpg_200
+_UFlat4-8 14.0GB/s ± 1% pdf
+_UFlat5-8 1.97GB/s ± 0% html4
+_UFlat6-8 814MB/s ± 0% txt1
+_UFlat7-8 785MB/s ± 0% txt2
+_UFlat8-8 857MB/s ± 0% txt3
+_UFlat9-8 719MB/s ± 1% txt4
+_UFlat10-8 2.84GB/s ± 0% pb
+_UFlat11-8 1.05GB/s ± 0% gaviota
+
+_ZFlat0-8 1.04GB/s ± 0% html
+_ZFlat1-8 534MB/s ± 0% urls
+_ZFlat2-8 15.7GB/s ± 1% jpg
+_ZFlat3-8 740MB/s ± 3% jpg_200
+_ZFlat4-8 9.20GB/s ± 1% pdf
+_ZFlat5-8 991MB/s ± 0% html4
+_ZFlat6-8 379MB/s ± 0% txt1
+_ZFlat7-8 352MB/s ± 0% txt2
+_ZFlat8-8 396MB/s ± 1% txt3
+_ZFlat9-8 327MB/s ± 1% txt4
+_ZFlat10-8 1.33GB/s ± 1% pb
+_ZFlat11-8 605MB/s ± 1% gaviota
+
+
+
+"go test -test.bench=. -tags=noasm"
+
+_UFlat0-8 621MB/s ± 2% html
+_UFlat1-8 494MB/s ± 1% urls
+_UFlat2-8 23.2GB/s ± 1% jpg
+_UFlat3-8 1.12GB/s ± 1% jpg_200
+_UFlat4-8 4.35GB/s ± 1% pdf
+_UFlat5-8 609MB/s ± 0% html4
+_UFlat6-8 296MB/s ± 0% txt1
+_UFlat7-8 288MB/s ± 0% txt2
+_UFlat8-8 309MB/s ± 1% txt3
+_UFlat9-8 280MB/s ± 1% txt4
+_UFlat10-8 753MB/s ± 0% pb
+_UFlat11-8 400MB/s ± 0% gaviota
+
+_ZFlat0-8 409MB/s ± 1% html
+_ZFlat1-8 250MB/s ± 1% urls
+_ZFlat2-8 12.3GB/s ± 1% jpg
+_ZFlat3-8 132MB/s ± 0% jpg_200
+_ZFlat4-8 2.92GB/s ± 0% pdf
+_ZFlat5-8 405MB/s ± 1% html4
+_ZFlat6-8 179MB/s ± 1% txt1
+_ZFlat7-8 170MB/s ± 1% txt2
+_ZFlat8-8 189MB/s ± 1% txt3
+_ZFlat9-8 164MB/s ± 1% txt4
+_ZFlat10-8 479MB/s ± 1% pb
+_ZFlat11-8 270MB/s ± 1% gaviota
+
+
+
+For comparison (Go's encoded output is byte-for-byte identical to C++'s), here
+are the numbers from C++ Snappy's
+
+make CXXFLAGS="-O2 -DNDEBUG -g" clean snappy_unittest.log && cat snappy_unittest.log
+
+BM_UFlat/0 2.4GB/s html
+BM_UFlat/1 1.4GB/s urls
+BM_UFlat/2 21.8GB/s jpg
+BM_UFlat/3 1.5GB/s jpg_200
+BM_UFlat/4 13.3GB/s pdf
+BM_UFlat/5 2.1GB/s html4
+BM_UFlat/6 1.0GB/s txt1
+BM_UFlat/7 959.4MB/s txt2
+BM_UFlat/8 1.0GB/s txt3
+BM_UFlat/9 864.5MB/s txt4
+BM_UFlat/10 2.9GB/s pb
+BM_UFlat/11 1.2GB/s gaviota
+
+BM_ZFlat/0 944.3MB/s html (22.31 %)
+BM_ZFlat/1 501.6MB/s urls (47.78 %)
+BM_ZFlat/2 14.3GB/s jpg (99.95 %)
+BM_ZFlat/3 538.3MB/s jpg_200 (73.00 %)
+BM_ZFlat/4 8.3GB/s pdf (83.30 %)
+BM_ZFlat/5 903.5MB/s html4 (22.52 %)
+BM_ZFlat/6 336.0MB/s txt1 (57.88 %)
+BM_ZFlat/7 312.3MB/s txt2 (61.91 %)
+BM_ZFlat/8 353.1MB/s txt3 (54.99 %)
+BM_ZFlat/9 289.9MB/s txt4 (66.26 %)
+BM_ZFlat/10 1.2GB/s pb (19.68 %)
+BM_ZFlat/11 527.4MB/s gaviota (37.72 %)
diff --git a/vendor/github.com/golang/snappy/decode.go b/vendor/github.com/golang/snappy/decode.go
new file mode 100644
index 00000000..72efb035
--- /dev/null
+++ b/vendor/github.com/golang/snappy/decode.go
@@ -0,0 +1,237 @@
+// Copyright 2011 The Snappy-Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package snappy
+
+import (
+ "encoding/binary"
+ "errors"
+ "io"
+)
+
+var (
+ // ErrCorrupt reports that the input is invalid.
+ ErrCorrupt = errors.New("snappy: corrupt input")
+ // ErrTooLarge reports that the uncompressed length is too large.
+ ErrTooLarge = errors.New("snappy: decoded block is too large")
+ // ErrUnsupported reports that the input isn't supported.
+ ErrUnsupported = errors.New("snappy: unsupported input")
+
+ errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length")
+)
+
+// DecodedLen returns the length of the decoded block.
+func DecodedLen(src []byte) (int, error) {
+ v, _, err := decodedLen(src)
+ return v, err
+}
+
+// decodedLen returns the length of the decoded block and the number of bytes
+// that the length header occupied.
+func decodedLen(src []byte) (blockLen, headerLen int, err error) {
+ v, n := binary.Uvarint(src)
+ if n <= 0 || v > 0xffffffff {
+ return 0, 0, ErrCorrupt
+ }
+
+ const wordSize = 32 << (^uint(0) >> 32 & 1)
+ if wordSize == 32 && v > 0x7fffffff {
+ return 0, 0, ErrTooLarge
+ }
+ return int(v), n, nil
+}
+
+const (
+ decodeErrCodeCorrupt = 1
+ decodeErrCodeUnsupportedLiteralLength = 2
+)
+
+// Decode returns the decoded form of src. The returned slice may be a sub-
+// slice of dst if dst was large enough to hold the entire decoded block.
+// Otherwise, a newly allocated slice will be returned.
+//
+// The dst and src must not overlap. It is valid to pass a nil dst.
+func Decode(dst, src []byte) ([]byte, error) {
+ dLen, s, err := decodedLen(src)
+ if err != nil {
+ return nil, err
+ }
+ if dLen <= len(dst) {
+ dst = dst[:dLen]
+ } else {
+ dst = make([]byte, dLen)
+ }
+ switch decode(dst, src[s:]) {
+ case 0:
+ return dst, nil
+ case decodeErrCodeUnsupportedLiteralLength:
+ return nil, errUnsupportedLiteralLength
+ }
+ return nil, ErrCorrupt
+}
+
+// NewReader returns a new Reader that decompresses from r, using the framing
+// format described at
+// https://github.com/google/snappy/blob/master/framing_format.txt
+func NewReader(r io.Reader) *Reader {
+ return &Reader{
+ r: r,
+ decoded: make([]byte, maxBlockSize),
+ buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize),
+ }
+}
+
+// Reader is an io.Reader that can read Snappy-compressed bytes.
+type Reader struct {
+ r io.Reader
+ err error
+ decoded []byte
+ buf []byte
+ // decoded[i:j] contains decoded bytes that have not yet been passed on.
+ i, j int
+ readHeader bool
+}
+
+// Reset discards any buffered data, resets all state, and switches the Snappy
+// reader to read from r. This permits reusing a Reader rather than allocating
+// a new one.
+func (r *Reader) Reset(reader io.Reader) {
+ r.r = reader
+ r.err = nil
+ r.i = 0
+ r.j = 0
+ r.readHeader = false
+}
+
+func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) {
+ if _, r.err = io.ReadFull(r.r, p); r.err != nil {
+ if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) {
+ r.err = ErrCorrupt
+ }
+ return false
+ }
+ return true
+}
+
+// Read satisfies the io.Reader interface.
+func (r *Reader) Read(p []byte) (int, error) {
+ if r.err != nil {
+ return 0, r.err
+ }
+ for {
+ if r.i < r.j {
+ n := copy(p, r.decoded[r.i:r.j])
+ r.i += n
+ return n, nil
+ }
+ if !r.readFull(r.buf[:4], true) {
+ return 0, r.err
+ }
+ chunkType := r.buf[0]
+ if !r.readHeader {
+ if chunkType != chunkTypeStreamIdentifier {
+ r.err = ErrCorrupt
+ return 0, r.err
+ }
+ r.readHeader = true
+ }
+ chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16
+ if chunkLen > len(r.buf) {
+ r.err = ErrUnsupported
+ return 0, r.err
+ }
+
+ // The chunk types are specified at
+ // https://github.com/google/snappy/blob/master/framing_format.txt
+ switch chunkType {
+ case chunkTypeCompressedData:
+ // Section 4.2. Compressed data (chunk type 0x00).
+ if chunkLen < checksumSize {
+ r.err = ErrCorrupt
+ return 0, r.err
+ }
+ buf := r.buf[:chunkLen]
+ if !r.readFull(buf, false) {
+ return 0, r.err
+ }
+ checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
+ buf = buf[checksumSize:]
+
+ n, err := DecodedLen(buf)
+ if err != nil {
+ r.err = err
+ return 0, r.err
+ }
+ if n > len(r.decoded) {
+ r.err = ErrCorrupt
+ return 0, r.err
+ }
+ if _, err := Decode(r.decoded, buf); err != nil {
+ r.err = err
+ return 0, r.err
+ }
+ if crc(r.decoded[:n]) != checksum {
+ r.err = ErrCorrupt
+ return 0, r.err
+ }
+ r.i, r.j = 0, n
+ continue
+
+ case chunkTypeUncompressedData:
+ // Section 4.3. Uncompressed data (chunk type 0x01).
+ if chunkLen < checksumSize {
+ r.err = ErrCorrupt
+ return 0, r.err
+ }
+ buf := r.buf[:checksumSize]
+ if !r.readFull(buf, false) {
+ return 0, r.err
+ }
+ checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
+ // Read directly into r.decoded instead of via r.buf.
+ n := chunkLen - checksumSize
+ if n > len(r.decoded) {
+ r.err = ErrCorrupt
+ return 0, r.err
+ }
+ if !r.readFull(r.decoded[:n], false) {
+ return 0, r.err
+ }
+ if crc(r.decoded[:n]) != checksum {
+ r.err = ErrCorrupt
+ return 0, r.err
+ }
+ r.i, r.j = 0, n
+ continue
+
+ case chunkTypeStreamIdentifier:
+ // Section 4.1. Stream identifier (chunk type 0xff).
+ if chunkLen != len(magicBody) {
+ r.err = ErrCorrupt
+ return 0, r.err
+ }
+ if !r.readFull(r.buf[:len(magicBody)], false) {
+ return 0, r.err
+ }
+ for i := 0; i < len(magicBody); i++ {
+ if r.buf[i] != magicBody[i] {
+ r.err = ErrCorrupt
+ return 0, r.err
+ }
+ }
+ continue
+ }
+
+ if chunkType <= 0x7f {
+ // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
+ r.err = ErrUnsupported
+ return 0, r.err
+ }
+ // Section 4.4 Padding (chunk type 0xfe).
+ // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
+ if !r.readFull(r.buf[:chunkLen], false) {
+ return 0, r.err
+ }
+ }
+}
diff --git a/vendor/github.com/golang/snappy/decode_amd64.go b/vendor/github.com/golang/snappy/decode_amd64.go
new file mode 100644
index 00000000..fcd192b8
--- /dev/null
+++ b/vendor/github.com/golang/snappy/decode_amd64.go
@@ -0,0 +1,14 @@
+// Copyright 2016 The Snappy-Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !appengine
+// +build gc
+// +build !noasm
+
+package snappy
+
+// decode has the same semantics as in decode_other.go.
+//
+//go:noescape
+func decode(dst, src []byte) int
diff --git a/vendor/github.com/golang/snappy/decode_amd64.s b/vendor/github.com/golang/snappy/decode_amd64.s
new file mode 100644
index 00000000..e6179f65
--- /dev/null
+++ b/vendor/github.com/golang/snappy/decode_amd64.s
@@ -0,0 +1,490 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !appengine
+// +build gc
+// +build !noasm
+
+#include "textflag.h"
+
+// The asm code generally follows the pure Go code in decode_other.go, except
+// where marked with a "!!!".
+
+// func decode(dst, src []byte) int
+//
+// All local variables fit into registers. The non-zero stack size is only to
+// spill registers and push args when issuing a CALL. The register allocation:
+// - AX scratch
+// - BX scratch
+// - CX length or x
+// - DX offset
+// - SI &src[s]
+// - DI &dst[d]
+// + R8 dst_base
+// + R9 dst_len
+// + R10 dst_base + dst_len
+// + R11 src_base
+// + R12 src_len
+// + R13 src_base + src_len
+// - R14 used by doCopy
+// - R15 used by doCopy
+//
+// The registers R8-R13 (marked with a "+") are set at the start of the
+// function, and after a CALL returns, and are not otherwise modified.
+//
+// The d variable is implicitly DI - R8, and len(dst)-d is R10 - DI.
+// The s variable is implicitly SI - R11, and len(src)-s is R13 - SI.
+TEXT ·decode(SB), NOSPLIT, $48-56
+ // Initialize SI, DI and R8-R13.
+ MOVQ dst_base+0(FP), R8
+ MOVQ dst_len+8(FP), R9
+ MOVQ R8, DI
+ MOVQ R8, R10
+ ADDQ R9, R10
+ MOVQ src_base+24(FP), R11
+ MOVQ src_len+32(FP), R12
+ MOVQ R11, SI
+ MOVQ R11, R13
+ ADDQ R12, R13
+
+loop:
+ // for s < len(src)
+ CMPQ SI, R13
+ JEQ end
+
+ // CX = uint32(src[s])
+ //
+ // switch src[s] & 0x03
+ MOVBLZX (SI), CX
+ MOVL CX, BX
+ ANDL $3, BX
+ CMPL BX, $1
+ JAE tagCopy
+
+ // ----------------------------------------
+ // The code below handles literal tags.
+
+ // case tagLiteral:
+ // x := uint32(src[s] >> 2)
+ // switch
+ SHRL $2, CX
+ CMPL CX, $60
+ JAE tagLit60Plus
+
+ // case x < 60:
+ // s++
+ INCQ SI
+
+doLit:
+ // This is the end of the inner "switch", when we have a literal tag.
+ //
+ // We assume that CX == x and x fits in a uint32, where x is the variable
+ // used in the pure Go decode_other.go code.
+
+ // length = int(x) + 1
+ //
+ // Unlike the pure Go code, we don't need to check if length <= 0 because
+ // CX can hold 64 bits, so the increment cannot overflow.
+ INCQ CX
+
+ // Prepare to check if copying length bytes will run past the end of dst or
+ // src.
+ //
+ // AX = len(dst) - d
+ // BX = len(src) - s
+ MOVQ R10, AX
+ SUBQ DI, AX
+ MOVQ R13, BX
+ SUBQ SI, BX
+
+ // !!! Try a faster technique for short (16 or fewer bytes) copies.
+ //
+ // if length > 16 || len(dst)-d < 16 || len(src)-s < 16 {
+ // goto callMemmove // Fall back on calling runtime·memmove.
+ // }
+ //
+ // The C++ snappy code calls this TryFastAppend. It also checks len(src)-s
+ // against 21 instead of 16, because it cannot assume that all of its input
+ // is contiguous in memory and so it needs to leave enough source bytes to
+ // read the next tag without refilling buffers, but Go's Decode assumes
+ // contiguousness (the src argument is a []byte).
+ CMPQ CX, $16
+ JGT callMemmove
+ CMPQ AX, $16
+ JLT callMemmove
+ CMPQ BX, $16
+ JLT callMemmove
+
+ // !!! Implement the copy from src to dst as a 16-byte load and store.
+ // (Decode's documentation says that dst and src must not overlap.)
+ //
+ // This always copies 16 bytes, instead of only length bytes, but that's
+ // OK. If the input is a valid Snappy encoding then subsequent iterations
+ // will fix up the overrun. Otherwise, Decode returns a nil []byte (and a
+ // non-nil error), so the overrun will be ignored.
+ //
+ // Note that on amd64, it is legal and cheap to issue unaligned 8-byte or
+ // 16-byte loads and stores. This technique probably wouldn't be as
+ // effective on architectures that are fussier about alignment.
+ MOVOU 0(SI), X0
+ MOVOU X0, 0(DI)
+
+ // d += length
+ // s += length
+ ADDQ CX, DI
+ ADDQ CX, SI
+ JMP loop
+
+callMemmove:
+ // if length > len(dst)-d || length > len(src)-s { etc }
+ CMPQ CX, AX
+ JGT errCorrupt
+ CMPQ CX, BX
+ JGT errCorrupt
+
+ // copy(dst[d:], src[s:s+length])
+ //
+ // This means calling runtime·memmove(&dst[d], &src[s], length), so we push
+ // DI, SI and CX as arguments. Coincidentally, we also need to spill those
+ // three registers to the stack, to save local variables across the CALL.
+ MOVQ DI, 0(SP)
+ MOVQ SI, 8(SP)
+ MOVQ CX, 16(SP)
+ MOVQ DI, 24(SP)
+ MOVQ SI, 32(SP)
+ MOVQ CX, 40(SP)
+ CALL runtime·memmove(SB)
+
+ // Restore local variables: unspill registers from the stack and
+ // re-calculate R8-R13.
+ MOVQ 24(SP), DI
+ MOVQ 32(SP), SI
+ MOVQ 40(SP), CX
+ MOVQ dst_base+0(FP), R8
+ MOVQ dst_len+8(FP), R9
+ MOVQ R8, R10
+ ADDQ R9, R10
+ MOVQ src_base+24(FP), R11
+ MOVQ src_len+32(FP), R12
+ MOVQ R11, R13
+ ADDQ R12, R13
+
+ // d += length
+ // s += length
+ ADDQ CX, DI
+ ADDQ CX, SI
+ JMP loop
+
+tagLit60Plus:
+ // !!! This fragment does the
+ //
+ // s += x - 58; if uint(s) > uint(len(src)) { etc }
+ //
+ // checks. In the asm version, we code it once instead of once per switch case.
+ ADDQ CX, SI
+ SUBQ $58, SI
+ MOVQ SI, BX
+ SUBQ R11, BX
+ CMPQ BX, R12
+ JA errCorrupt
+
+ // case x == 60:
+ CMPL CX, $61
+ JEQ tagLit61
+ JA tagLit62Plus
+
+ // x = uint32(src[s-1])
+ MOVBLZX -1(SI), CX
+ JMP doLit
+
+tagLit61:
+ // case x == 61:
+ // x = uint32(src[s-2]) | uint32(src[s-1])<<8
+ MOVWLZX -2(SI), CX
+ JMP doLit
+
+tagLit62Plus:
+ CMPL CX, $62
+ JA tagLit63
+
+ // case x == 62:
+ // x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
+ MOVWLZX -3(SI), CX
+ MOVBLZX -1(SI), BX
+ SHLL $16, BX
+ ORL BX, CX
+ JMP doLit
+
+tagLit63:
+ // case x == 63:
+ // x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
+ MOVL -4(SI), CX
+ JMP doLit
+
+// The code above handles literal tags.
+// ----------------------------------------
+// The code below handles copy tags.
+
+tagCopy4:
+ // case tagCopy4:
+ // s += 5
+ ADDQ $5, SI
+
+ // if uint(s) > uint(len(src)) { etc }
+ MOVQ SI, BX
+ SUBQ R11, BX
+ CMPQ BX, R12
+ JA errCorrupt
+
+ // length = 1 + int(src[s-5])>>2
+ SHRQ $2, CX
+ INCQ CX
+
+ // offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24)
+ MOVLQZX -4(SI), DX
+ JMP doCopy
+
+tagCopy2:
+ // case tagCopy2:
+ // s += 3
+ ADDQ $3, SI
+
+ // if uint(s) > uint(len(src)) { etc }
+ MOVQ SI, BX
+ SUBQ R11, BX
+ CMPQ BX, R12
+ JA errCorrupt
+
+ // length = 1 + int(src[s-3])>>2
+ SHRQ $2, CX
+ INCQ CX
+
+ // offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8)
+ MOVWQZX -2(SI), DX
+ JMP doCopy
+
+tagCopy:
+ // We have a copy tag. We assume that:
+ // - BX == src[s] & 0x03
+ // - CX == src[s]
+ CMPQ BX, $2
+ JEQ tagCopy2
+ JA tagCopy4
+
+ // case tagCopy1:
+ // s += 2
+ ADDQ $2, SI
+
+ // if uint(s) > uint(len(src)) { etc }
+ MOVQ SI, BX
+ SUBQ R11, BX
+ CMPQ BX, R12
+ JA errCorrupt
+
+ // offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]))
+ MOVQ CX, DX
+ ANDQ $0xe0, DX
+ SHLQ $3, DX
+ MOVBQZX -1(SI), BX
+ ORQ BX, DX
+
+ // length = 4 + int(src[s-2])>>2&0x7
+ SHRQ $2, CX
+ ANDQ $7, CX
+ ADDQ $4, CX
+
+doCopy:
+ // This is the end of the outer "switch", when we have a copy tag.
+ //
+ // We assume that:
+ // - CX == length && CX > 0
+ // - DX == offset
+
+ // if offset <= 0 { etc }
+ CMPQ DX, $0
+ JLE errCorrupt
+
+ // if d < offset { etc }
+ MOVQ DI, BX
+ SUBQ R8, BX
+ CMPQ BX, DX
+ JLT errCorrupt
+
+ // if length > len(dst)-d { etc }
+ MOVQ R10, BX
+ SUBQ DI, BX
+ CMPQ CX, BX
+ JGT errCorrupt
+
+ // forwardCopy(dst[d:d+length], dst[d-offset:]); d += length
+ //
+ // Set:
+ // - R14 = len(dst)-d
+ // - R15 = &dst[d-offset]
+ MOVQ R10, R14
+ SUBQ DI, R14
+ MOVQ DI, R15
+ SUBQ DX, R15
+
+ // !!! Try a faster technique for short (16 or fewer bytes) forward copies.
+ //
+ // First, try using two 8-byte load/stores, similar to the doLit technique
+ // above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is
+ // still OK if offset >= 8. Note that this has to be two 8-byte load/stores
+ // and not one 16-byte load/store, and the first store has to be before the
+ // second load, due to the overlap if offset is in the range [8, 16).
+ //
+ // if length > 16 || offset < 8 || len(dst)-d < 16 {
+ // goto slowForwardCopy
+ // }
+ // copy 16 bytes
+ // d += length
+ CMPQ CX, $16
+ JGT slowForwardCopy
+ CMPQ DX, $8
+ JLT slowForwardCopy
+ CMPQ R14, $16
+ JLT slowForwardCopy
+ MOVQ 0(R15), AX
+ MOVQ AX, 0(DI)
+ MOVQ 8(R15), BX
+ MOVQ BX, 8(DI)
+ ADDQ CX, DI
+ JMP loop
+
+slowForwardCopy:
+ // !!! If the forward copy is longer than 16 bytes, or if offset < 8, we
+ // can still try 8-byte load stores, provided we can overrun up to 10 extra
+ // bytes. As above, the overrun will be fixed up by subsequent iterations
+ // of the outermost loop.
+ //
+ // The C++ snappy code calls this technique IncrementalCopyFastPath. Its
+ // commentary says:
+ //
+ // ----
+ //
+ // The main part of this loop is a simple copy of eight bytes at a time
+ // until we've copied (at least) the requested amount of bytes. However,
+ // if d and d-offset are less than eight bytes apart (indicating a
+ // repeating pattern of length < 8), we first need to expand the pattern in
+ // order to get the correct results. For instance, if the buffer looks like
+ // this, with the eight-byte and patterns marked as
+ // intervals:
+ //
+ // abxxxxxxxxxxxx
+ // [------] d-offset
+ // [------] d
+ //
+ // a single eight-byte copy from to will repeat the pattern
+ // once, after which we can move two bytes without moving :
+ //
+ // ababxxxxxxxxxx
+ // [------] d-offset
+ // [------] d
+ //
+ // and repeat the exercise until the two no longer overlap.
+ //
+ // This allows us to do very well in the special case of one single byte
+ // repeated many times, without taking a big hit for more general cases.
+ //
+ // The worst case of extra writing past the end of the match occurs when
+ // offset == 1 and length == 1; the last copy will read from byte positions
+ // [0..7] and write to [4..11], whereas it was only supposed to write to
+ // position 1. Thus, ten excess bytes.
+ //
+ // ----
+ //
+ // That "10 byte overrun" worst case is confirmed by Go's
+ // TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy
+ // and finishSlowForwardCopy algorithm.
+ //
+ // if length > len(dst)-d-10 {
+ // goto verySlowForwardCopy
+ // }
+ SUBQ $10, R14
+ CMPQ CX, R14
+ JGT verySlowForwardCopy
+
+makeOffsetAtLeast8:
+ // !!! As above, expand the pattern so that offset >= 8 and we can use
+ // 8-byte load/stores.
+ //
+ // for offset < 8 {
+ // copy 8 bytes from dst[d-offset:] to dst[d:]
+ // length -= offset
+ // d += offset
+ // offset += offset
+ // // The two previous lines together means that d-offset, and therefore
+ // // R15, is unchanged.
+ // }
+ CMPQ DX, $8
+ JGE fixUpSlowForwardCopy
+ MOVQ (R15), BX
+ MOVQ BX, (DI)
+ SUBQ DX, CX
+ ADDQ DX, DI
+ ADDQ DX, DX
+ JMP makeOffsetAtLeast8
+
+fixUpSlowForwardCopy:
+ // !!! Add length (which might be negative now) to d (implied by DI being
+ // &dst[d]) so that d ends up at the right place when we jump back to the
+ // top of the loop. Before we do that, though, we save DI to AX so that, if
+ // length is positive, copying the remaining length bytes will write to the
+ // right place.
+ MOVQ DI, AX
+ ADDQ CX, DI
+
+finishSlowForwardCopy:
+ // !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative
+ // length means that we overrun, but as above, that will be fixed up by
+ // subsequent iterations of the outermost loop.
+ CMPQ CX, $0
+ JLE loop
+ MOVQ (R15), BX
+ MOVQ BX, (AX)
+ ADDQ $8, R15
+ ADDQ $8, AX
+ SUBQ $8, CX
+ JMP finishSlowForwardCopy
+
+verySlowForwardCopy:
+ // verySlowForwardCopy is a simple implementation of forward copy. In C
+ // parlance, this is a do/while loop instead of a while loop, since we know
+ // that length > 0. In Go syntax:
+ //
+ // for {
+ // dst[d] = dst[d - offset]
+ // d++
+ // length--
+ // if length == 0 {
+ // break
+ // }
+ // }
+ MOVB (R15), BX
+ MOVB BX, (DI)
+ INCQ R15
+ INCQ DI
+ DECQ CX
+ JNZ verySlowForwardCopy
+ JMP loop
+
+// The code above handles copy tags.
+// ----------------------------------------
+
+end:
+ // This is the end of the "for s < len(src)".
+ //
+ // if d != len(dst) { etc }
+ CMPQ DI, R10
+ JNE errCorrupt
+
+ // return 0
+ MOVQ $0, ret+48(FP)
+ RET
+
+errCorrupt:
+ // return decodeErrCodeCorrupt
+ MOVQ $1, ret+48(FP)
+ RET
diff --git a/vendor/github.com/golang/snappy/decode_other.go b/vendor/github.com/golang/snappy/decode_other.go
new file mode 100644
index 00000000..8c9f2049
--- /dev/null
+++ b/vendor/github.com/golang/snappy/decode_other.go
@@ -0,0 +1,101 @@
+// Copyright 2016 The Snappy-Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !amd64 appengine !gc noasm
+
+package snappy
+
+// decode writes the decoding of src to dst. It assumes that the varint-encoded
+// length of the decompressed bytes has already been read, and that len(dst)
+// equals that length.
+//
+// It returns 0 on success or a decodeErrCodeXxx error code on failure.
+func decode(dst, src []byte) int {
+ var d, s, offset, length int
+ for s < len(src) {
+ switch src[s] & 0x03 {
+ case tagLiteral:
+ x := uint32(src[s] >> 2)
+ switch {
+ case x < 60:
+ s++
+ case x == 60:
+ s += 2
+ if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
+ return decodeErrCodeCorrupt
+ }
+ x = uint32(src[s-1])
+ case x == 61:
+ s += 3
+ if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
+ return decodeErrCodeCorrupt
+ }
+ x = uint32(src[s-2]) | uint32(src[s-1])<<8
+ case x == 62:
+ s += 4
+ if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
+ return decodeErrCodeCorrupt
+ }
+ x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
+ case x == 63:
+ s += 5
+ if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
+ return decodeErrCodeCorrupt
+ }
+ x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
+ }
+ length = int(x) + 1
+ if length <= 0 {
+ return decodeErrCodeUnsupportedLiteralLength
+ }
+ if length > len(dst)-d || length > len(src)-s {
+ return decodeErrCodeCorrupt
+ }
+ copy(dst[d:], src[s:s+length])
+ d += length
+ s += length
+ continue
+
+ case tagCopy1:
+ s += 2
+ if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
+ return decodeErrCodeCorrupt
+ }
+ length = 4 + int(src[s-2])>>2&0x7
+ offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]))
+
+ case tagCopy2:
+ s += 3
+ if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
+ return decodeErrCodeCorrupt
+ }
+ length = 1 + int(src[s-3])>>2
+ offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8)
+
+ case tagCopy4:
+ s += 5
+ if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
+ return decodeErrCodeCorrupt
+ }
+ length = 1 + int(src[s-5])>>2
+ offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24)
+ }
+
+ if offset <= 0 || d < offset || length > len(dst)-d {
+ return decodeErrCodeCorrupt
+ }
+ // Copy from an earlier sub-slice of dst to a later sub-slice. Unlike
+ // the built-in copy function, this byte-by-byte copy always runs
+ // forwards, even if the slices overlap. Conceptually, this is:
+ //
+ // d += forwardCopy(dst[d:d+length], dst[d-offset:])
+ for end := d + length; d != end; d++ {
+ dst[d] = dst[d-offset]
+ }
+ }
+ if d != len(dst) {
+ return decodeErrCodeCorrupt
+ }
+ return 0
+}
diff --git a/vendor/github.com/golang/snappy/encode.go b/vendor/github.com/golang/snappy/encode.go
new file mode 100644
index 00000000..8d393e90
--- /dev/null
+++ b/vendor/github.com/golang/snappy/encode.go
@@ -0,0 +1,285 @@
+// Copyright 2011 The Snappy-Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package snappy
+
+import (
+ "encoding/binary"
+ "errors"
+ "io"
+)
+
+// Encode returns the encoded form of src. The returned slice may be a sub-
+// slice of dst if dst was large enough to hold the entire encoded block.
+// Otherwise, a newly allocated slice will be returned.
+//
+// The dst and src must not overlap. It is valid to pass a nil dst.
+func Encode(dst, src []byte) []byte {
+ if n := MaxEncodedLen(len(src)); n < 0 {
+ panic(ErrTooLarge)
+ } else if len(dst) < n {
+ dst = make([]byte, n)
+ }
+
+ // The block starts with the varint-encoded length of the decompressed bytes.
+ d := binary.PutUvarint(dst, uint64(len(src)))
+
+ for len(src) > 0 {
+ p := src
+ src = nil
+ if len(p) > maxBlockSize {
+ p, src = p[:maxBlockSize], p[maxBlockSize:]
+ }
+ if len(p) < minNonLiteralBlockSize {
+ d += emitLiteral(dst[d:], p)
+ } else {
+ d += encodeBlock(dst[d:], p)
+ }
+ }
+ return dst[:d]
+}
+
+// inputMargin is the minimum number of extra input bytes to keep, inside
+// encodeBlock's inner loop. On some architectures, this margin lets us
+// implement a fast path for emitLiteral, where the copy of short (<= 16 byte)
+// literals can be implemented as a single load to and store from a 16-byte
+// register. That literal's actual length can be as short as 1 byte, so this
+// can copy up to 15 bytes too much, but that's OK as subsequent iterations of
+// the encoding loop will fix up the copy overrun, and this inputMargin ensures
+// that we don't overrun the dst and src buffers.
+const inputMargin = 16 - 1
+
+// minNonLiteralBlockSize is the minimum size of the input to encodeBlock that
+// could be encoded with a copy tag. This is the minimum with respect to the
+// algorithm used by encodeBlock, not a minimum enforced by the file format.
+//
+// The encoded output must start with at least a 1 byte literal, as there are
+// no previous bytes to copy. A minimal (1 byte) copy after that, generated
+// from an emitCopy call in encodeBlock's main loop, would require at least
+// another inputMargin bytes, for the reason above: we want any emitLiteral
+// calls inside encodeBlock's main loop to use the fast path if possible, which
+// requires being able to overrun by inputMargin bytes. Thus,
+// minNonLiteralBlockSize equals 1 + 1 + inputMargin.
+//
+// The C++ code doesn't use this exact threshold, but it could, as discussed at
+// https://groups.google.com/d/topic/snappy-compression/oGbhsdIJSJ8/discussion
+// The difference between Go (2+inputMargin) and C++ (inputMargin) is purely an
+// optimization. It should not affect the encoded form. This is tested by
+// TestSameEncodingAsCppShortCopies.
+const minNonLiteralBlockSize = 1 + 1 + inputMargin
+
+// MaxEncodedLen returns the maximum length of a snappy block, given its
+// uncompressed length.
+//
+// It will return a negative value if srcLen is too large to encode.
+func MaxEncodedLen(srcLen int) int {
+ n := uint64(srcLen)
+ if n > 0xffffffff {
+ return -1
+ }
+ // Compressed data can be defined as:
+ // compressed := item* literal*
+ // item := literal* copy
+ //
+ // The trailing literal sequence has a space blowup of at most 62/60
+ // since a literal of length 60 needs one tag byte + one extra byte
+ // for length information.
+ //
+ // Item blowup is trickier to measure. Suppose the "copy" op copies
+ // 4 bytes of data. Because of a special check in the encoding code,
+ // we produce a 4-byte copy only if the offset is < 65536. Therefore
+ // the copy op takes 3 bytes to encode, and this type of item leads
+ // to at most the 62/60 blowup for representing literals.
+ //
+ // Suppose the "copy" op copies 5 bytes of data. If the offset is big
+ // enough, it will take 5 bytes to encode the copy op. Therefore the
+ // worst case here is a one-byte literal followed by a five-byte copy.
+ // That is, 6 bytes of input turn into 7 bytes of "compressed" data.
+ //
+ // This last factor dominates the blowup, so the final estimate is:
+ n = 32 + n + n/6
+ if n > 0xffffffff {
+ return -1
+ }
+ return int(n)
+}
+
+var errClosed = errors.New("snappy: Writer is closed")
+
+// NewWriter returns a new Writer that compresses to w.
+//
+// The Writer returned does not buffer writes. There is no need to Flush or
+// Close such a Writer.
+//
+// Deprecated: the Writer returned is not suitable for many small writes, only
+// for few large writes. Use NewBufferedWriter instead, which is efficient
+// regardless of the frequency and shape of the writes, and remember to Close
+// that Writer when done.
+func NewWriter(w io.Writer) *Writer {
+ return &Writer{
+ w: w,
+ obuf: make([]byte, obufLen),
+ }
+}
+
+// NewBufferedWriter returns a new Writer that compresses to w, using the
+// framing format described at
+// https://github.com/google/snappy/blob/master/framing_format.txt
+//
+// The Writer returned buffers writes. Users must call Close to guarantee all
+// data has been forwarded to the underlying io.Writer. They may also call
+// Flush zero or more times before calling Close.
+func NewBufferedWriter(w io.Writer) *Writer {
+ return &Writer{
+ w: w,
+ ibuf: make([]byte, 0, maxBlockSize),
+ obuf: make([]byte, obufLen),
+ }
+}
+
+// Writer is an io.Writer that can write Snappy-compressed bytes.
+type Writer struct {
+ w io.Writer
+ err error
+
+ // ibuf is a buffer for the incoming (uncompressed) bytes.
+ //
+ // Its use is optional. For backwards compatibility, Writers created by the
+ // NewWriter function have ibuf == nil, do not buffer incoming bytes, and
+ // therefore do not need to be Flush'ed or Close'd.
+ ibuf []byte
+
+ // obuf is a buffer for the outgoing (compressed) bytes.
+ obuf []byte
+
+ // wroteStreamHeader is whether we have written the stream header.
+ wroteStreamHeader bool
+}
+
+// Reset discards the writer's state and switches the Snappy writer to write to
+// w. This permits reusing a Writer rather than allocating a new one.
+func (w *Writer) Reset(writer io.Writer) {
+ w.w = writer
+ w.err = nil
+ if w.ibuf != nil {
+ w.ibuf = w.ibuf[:0]
+ }
+ w.wroteStreamHeader = false
+}
+
+// Write satisfies the io.Writer interface.
+func (w *Writer) Write(p []byte) (nRet int, errRet error) {
+ if w.ibuf == nil {
+ // Do not buffer incoming bytes. This does not perform or compress well
+ // if the caller of Writer.Write writes many small slices. This
+ // behavior is therefore deprecated, but still supported for backwards
+ // compatibility with code that doesn't explicitly Flush or Close.
+ return w.write(p)
+ }
+
+ // The remainder of this method is based on bufio.Writer.Write from the
+ // standard library.
+
+ for len(p) > (cap(w.ibuf)-len(w.ibuf)) && w.err == nil {
+ var n int
+ if len(w.ibuf) == 0 {
+ // Large write, empty buffer.
+ // Write directly from p to avoid copy.
+ n, _ = w.write(p)
+ } else {
+ n = copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p)
+ w.ibuf = w.ibuf[:len(w.ibuf)+n]
+ w.Flush()
+ }
+ nRet += n
+ p = p[n:]
+ }
+ if w.err != nil {
+ return nRet, w.err
+ }
+ n := copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p)
+ w.ibuf = w.ibuf[:len(w.ibuf)+n]
+ nRet += n
+ return nRet, nil
+}
+
+func (w *Writer) write(p []byte) (nRet int, errRet error) {
+ if w.err != nil {
+ return 0, w.err
+ }
+ for len(p) > 0 {
+ obufStart := len(magicChunk)
+ if !w.wroteStreamHeader {
+ w.wroteStreamHeader = true
+ copy(w.obuf, magicChunk)
+ obufStart = 0
+ }
+
+ var uncompressed []byte
+ if len(p) > maxBlockSize {
+ uncompressed, p = p[:maxBlockSize], p[maxBlockSize:]
+ } else {
+ uncompressed, p = p, nil
+ }
+ checksum := crc(uncompressed)
+
+ // Compress the buffer, discarding the result if the improvement
+ // isn't at least 12.5%.
+ compressed := Encode(w.obuf[obufHeaderLen:], uncompressed)
+ chunkType := uint8(chunkTypeCompressedData)
+ chunkLen := 4 + len(compressed)
+ obufEnd := obufHeaderLen + len(compressed)
+ if len(compressed) >= len(uncompressed)-len(uncompressed)/8 {
+ chunkType = chunkTypeUncompressedData
+ chunkLen = 4 + len(uncompressed)
+ obufEnd = obufHeaderLen
+ }
+
+ // Fill in the per-chunk header that comes before the body.
+ w.obuf[len(magicChunk)+0] = chunkType
+ w.obuf[len(magicChunk)+1] = uint8(chunkLen >> 0)
+ w.obuf[len(magicChunk)+2] = uint8(chunkLen >> 8)
+ w.obuf[len(magicChunk)+3] = uint8(chunkLen >> 16)
+ w.obuf[len(magicChunk)+4] = uint8(checksum >> 0)
+ w.obuf[len(magicChunk)+5] = uint8(checksum >> 8)
+ w.obuf[len(magicChunk)+6] = uint8(checksum >> 16)
+ w.obuf[len(magicChunk)+7] = uint8(checksum >> 24)
+
+ if _, err := w.w.Write(w.obuf[obufStart:obufEnd]); err != nil {
+ w.err = err
+ return nRet, err
+ }
+ if chunkType == chunkTypeUncompressedData {
+ if _, err := w.w.Write(uncompressed); err != nil {
+ w.err = err
+ return nRet, err
+ }
+ }
+ nRet += len(uncompressed)
+ }
+ return nRet, nil
+}
+
+// Flush flushes the Writer to its underlying io.Writer.
+func (w *Writer) Flush() error {
+ if w.err != nil {
+ return w.err
+ }
+ if len(w.ibuf) == 0 {
+ return nil
+ }
+ w.write(w.ibuf)
+ w.ibuf = w.ibuf[:0]
+ return w.err
+}
+
+// Close calls Flush and then closes the Writer.
+func (w *Writer) Close() error {
+ w.Flush()
+ ret := w.err
+ if w.err == nil {
+ w.err = errClosed
+ }
+ return ret
+}
diff --git a/vendor/github.com/golang/snappy/encode_amd64.go b/vendor/github.com/golang/snappy/encode_amd64.go
new file mode 100644
index 00000000..150d91bc
--- /dev/null
+++ b/vendor/github.com/golang/snappy/encode_amd64.go
@@ -0,0 +1,29 @@
+// Copyright 2016 The Snappy-Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !appengine
+// +build gc
+// +build !noasm
+
+package snappy
+
+// emitLiteral has the same semantics as in encode_other.go.
+//
+//go:noescape
+func emitLiteral(dst, lit []byte) int
+
+// emitCopy has the same semantics as in encode_other.go.
+//
+//go:noescape
+func emitCopy(dst []byte, offset, length int) int
+
+// extendMatch has the same semantics as in encode_other.go.
+//
+//go:noescape
+func extendMatch(src []byte, i, j int) int
+
+// encodeBlock has the same semantics as in encode_other.go.
+//
+//go:noescape
+func encodeBlock(dst, src []byte) (d int)
diff --git a/vendor/github.com/golang/snappy/encode_amd64.s b/vendor/github.com/golang/snappy/encode_amd64.s
new file mode 100644
index 00000000..adfd979f
--- /dev/null
+++ b/vendor/github.com/golang/snappy/encode_amd64.s
@@ -0,0 +1,730 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !appengine
+// +build gc
+// +build !noasm
+
+#include "textflag.h"
+
+// The XXX lines assemble on Go 1.4, 1.5 and 1.7, but not 1.6, due to a
+// Go toolchain regression. See https://github.com/golang/go/issues/15426 and
+// https://github.com/golang/snappy/issues/29
+//
+// As a workaround, the package was built with a known good assembler, and
+// those instructions were disassembled by "objdump -d" to yield the
+// 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15
+// style comments, in AT&T asm syntax. Note that rsp here is a physical
+// register, not Go/asm's SP pseudo-register (see https://golang.org/doc/asm).
+// The instructions were then encoded as "BYTE $0x.." sequences, which assemble
+// fine on Go 1.6.
+
+// The asm code generally follows the pure Go code in encode_other.go, except
+// where marked with a "!!!".
+
+// ----------------------------------------------------------------------------
+
+// func emitLiteral(dst, lit []byte) int
+//
+// All local variables fit into registers. The register allocation:
+// - AX len(lit)
+// - BX n
+// - DX return value
+// - DI &dst[i]
+// - R10 &lit[0]
+//
+// The 24 bytes of stack space is to call runtime·memmove.
+//
+// The unusual register allocation of local variables, such as R10 for the
+// source pointer, matches the allocation used at the call site in encodeBlock,
+// which makes it easier to manually inline this function.
+TEXT ·emitLiteral(SB), NOSPLIT, $24-56
+ MOVQ dst_base+0(FP), DI
+ MOVQ lit_base+24(FP), R10
+ MOVQ lit_len+32(FP), AX
+ MOVQ AX, DX
+ MOVL AX, BX
+ SUBL $1, BX
+
+ CMPL BX, $60
+ JLT oneByte
+ CMPL BX, $256
+ JLT twoBytes
+
+threeBytes:
+ MOVB $0xf4, 0(DI)
+ MOVW BX, 1(DI)
+ ADDQ $3, DI
+ ADDQ $3, DX
+ JMP memmove
+
+twoBytes:
+ MOVB $0xf0, 0(DI)
+ MOVB BX, 1(DI)
+ ADDQ $2, DI
+ ADDQ $2, DX
+ JMP memmove
+
+oneByte:
+ SHLB $2, BX
+ MOVB BX, 0(DI)
+ ADDQ $1, DI
+ ADDQ $1, DX
+
+memmove:
+ MOVQ DX, ret+48(FP)
+
+ // copy(dst[i:], lit)
+ //
+ // This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
+ // DI, R10 and AX as arguments.
+ MOVQ DI, 0(SP)
+ MOVQ R10, 8(SP)
+ MOVQ AX, 16(SP)
+ CALL runtime·memmove(SB)
+ RET
+
+// ----------------------------------------------------------------------------
+
+// func emitCopy(dst []byte, offset, length int) int
+//
+// All local variables fit into registers. The register allocation:
+// - AX length
+// - SI &dst[0]
+// - DI &dst[i]
+// - R11 offset
+//
+// The unusual register allocation of local variables, such as R11 for the
+// offset, matches the allocation used at the call site in encodeBlock, which
+// makes it easier to manually inline this function.
+TEXT ·emitCopy(SB), NOSPLIT, $0-48
+ MOVQ dst_base+0(FP), DI
+ MOVQ DI, SI
+ MOVQ offset+24(FP), R11
+ MOVQ length+32(FP), AX
+
+loop0:
+ // for length >= 68 { etc }
+ CMPL AX, $68
+ JLT step1
+
+ // Emit a length 64 copy, encoded as 3 bytes.
+ MOVB $0xfe, 0(DI)
+ MOVW R11, 1(DI)
+ ADDQ $3, DI
+ SUBL $64, AX
+ JMP loop0
+
+step1:
+ // if length > 64 { etc }
+ CMPL AX, $64
+ JLE step2
+
+ // Emit a length 60 copy, encoded as 3 bytes.
+ MOVB $0xee, 0(DI)
+ MOVW R11, 1(DI)
+ ADDQ $3, DI
+ SUBL $60, AX
+
+step2:
+ // if length >= 12 || offset >= 2048 { goto step3 }
+ CMPL AX, $12
+ JGE step3
+ CMPL R11, $2048
+ JGE step3
+
+ // Emit the remaining copy, encoded as 2 bytes.
+ MOVB R11, 1(DI)
+ SHRL $8, R11
+ SHLB $5, R11
+ SUBB $4, AX
+ SHLB $2, AX
+ ORB AX, R11
+ ORB $1, R11
+ MOVB R11, 0(DI)
+ ADDQ $2, DI
+
+ // Return the number of bytes written.
+ SUBQ SI, DI
+ MOVQ DI, ret+40(FP)
+ RET
+
+step3:
+ // Emit the remaining copy, encoded as 3 bytes.
+ SUBL $1, AX
+ SHLB $2, AX
+ ORB $2, AX
+ MOVB AX, 0(DI)
+ MOVW R11, 1(DI)
+ ADDQ $3, DI
+
+ // Return the number of bytes written.
+ SUBQ SI, DI
+ MOVQ DI, ret+40(FP)
+ RET
+
+// ----------------------------------------------------------------------------
+
+// func extendMatch(src []byte, i, j int) int
+//
+// All local variables fit into registers. The register allocation:
+// - DX &src[0]
+// - SI &src[j]
+// - R13 &src[len(src) - 8]
+// - R14 &src[len(src)]
+// - R15 &src[i]
+//
+// The unusual register allocation of local variables, such as R15 for a source
+// pointer, matches the allocation used at the call site in encodeBlock, which
+// makes it easier to manually inline this function.
+TEXT ·extendMatch(SB), NOSPLIT, $0-48
+ MOVQ src_base+0(FP), DX
+ MOVQ src_len+8(FP), R14
+ MOVQ i+24(FP), R15
+ MOVQ j+32(FP), SI
+ ADDQ DX, R14
+ ADDQ DX, R15
+ ADDQ DX, SI
+ MOVQ R14, R13
+ SUBQ $8, R13
+
+cmp8:
+ // As long as we are 8 or more bytes before the end of src, we can load and
+ // compare 8 bytes at a time. If those 8 bytes are equal, repeat.
+ CMPQ SI, R13
+ JA cmp1
+ MOVQ (R15), AX
+ MOVQ (SI), BX
+ CMPQ AX, BX
+ JNE bsf
+ ADDQ $8, R15
+ ADDQ $8, SI
+ JMP cmp8
+
+bsf:
+ // If those 8 bytes were not equal, XOR the two 8 byte values, and return
+ // the index of the first byte that differs. The BSF instruction finds the
+ // least significant 1 bit, the amd64 architecture is little-endian, and
+ // the shift by 3 converts a bit index to a byte index.
+ XORQ AX, BX
+ BSFQ BX, BX
+ SHRQ $3, BX
+ ADDQ BX, SI
+
+ // Convert from &src[ret] to ret.
+ SUBQ DX, SI
+ MOVQ SI, ret+40(FP)
+ RET
+
+cmp1:
+ // In src's tail, compare 1 byte at a time.
+ CMPQ SI, R14
+ JAE extendMatchEnd
+ MOVB (R15), AX
+ MOVB (SI), BX
+ CMPB AX, BX
+ JNE extendMatchEnd
+ ADDQ $1, R15
+ ADDQ $1, SI
+ JMP cmp1
+
+extendMatchEnd:
+ // Convert from &src[ret] to ret.
+ SUBQ DX, SI
+ MOVQ SI, ret+40(FP)
+ RET
+
+// ----------------------------------------------------------------------------
+
+// func encodeBlock(dst, src []byte) (d int)
+//
+// All local variables fit into registers, other than "var table". The register
+// allocation:
+// - AX . .
+// - BX . .
+// - CX 56 shift (note that amd64 shifts by non-immediates must use CX).
+// - DX 64 &src[0], tableSize
+// - SI 72 &src[s]
+// - DI 80 &dst[d]
+// - R9 88 sLimit
+// - R10 . &src[nextEmit]
+// - R11 96 prevHash, currHash, nextHash, offset
+// - R12 104 &src[base], skip
+// - R13 . &src[nextS], &src[len(src) - 8]
+// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x
+// - R15 112 candidate
+//
+// The second column (56, 64, etc) is the stack offset to spill the registers
+// when calling other functions. We could pack this slightly tighter, but it's
+// simpler to have a dedicated spill map independent of the function called.
+//
+// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An
+// extra 56 bytes, to call other functions, and an extra 64 bytes, to spill
+// local variables (registers) during calls gives 32768 + 56 + 64 = 32888.
+TEXT ·encodeBlock(SB), 0, $32888-56
+ MOVQ dst_base+0(FP), DI
+ MOVQ src_base+24(FP), SI
+ MOVQ src_len+32(FP), R14
+
+ // shift, tableSize := uint32(32-8), 1<<8
+ MOVQ $24, CX
+ MOVQ $256, DX
+
+calcShift:
+ // for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 {
+ // shift--
+ // }
+ CMPQ DX, $16384
+ JGE varTable
+ CMPQ DX, R14
+ JGE varTable
+ SUBQ $1, CX
+ SHLQ $1, DX
+ JMP calcShift
+
+varTable:
+ // var table [maxTableSize]uint16
+ //
+ // In the asm code, unlike the Go code, we can zero-initialize only the
+ // first tableSize elements. Each uint16 element is 2 bytes and each MOVOU
+ // writes 16 bytes, so we can do only tableSize/8 writes instead of the
+ // 2048 writes that would zero-initialize all of table's 32768 bytes.
+ SHRQ $3, DX
+ LEAQ table-32768(SP), BX
+ PXOR X0, X0
+
+memclr:
+ MOVOU X0, 0(BX)
+ ADDQ $16, BX
+ SUBQ $1, DX
+ JNZ memclr
+
+ // !!! DX = &src[0]
+ MOVQ SI, DX
+
+ // sLimit := len(src) - inputMargin
+ MOVQ R14, R9
+ SUBQ $15, R9
+
+ // !!! Pre-emptively spill CX, DX and R9 to the stack. Their values don't
+ // change for the rest of the function.
+ MOVQ CX, 56(SP)
+ MOVQ DX, 64(SP)
+ MOVQ R9, 88(SP)
+
+ // nextEmit := 0
+ MOVQ DX, R10
+
+ // s := 1
+ ADDQ $1, SI
+
+ // nextHash := hash(load32(src, s), shift)
+ MOVL 0(SI), R11
+ IMULL $0x1e35a7bd, R11
+ SHRL CX, R11
+
+outer:
+ // for { etc }
+
+ // skip := 32
+ MOVQ $32, R12
+
+ // nextS := s
+ MOVQ SI, R13
+
+ // candidate := 0
+ MOVQ $0, R15
+
+inner0:
+ // for { etc }
+
+ // s := nextS
+ MOVQ R13, SI
+
+ // bytesBetweenHashLookups := skip >> 5
+ MOVQ R12, R14
+ SHRQ $5, R14
+
+ // nextS = s + bytesBetweenHashLookups
+ ADDQ R14, R13
+
+ // skip += bytesBetweenHashLookups
+ ADDQ R14, R12
+
+ // if nextS > sLimit { goto emitRemainder }
+ MOVQ R13, AX
+ SUBQ DX, AX
+ CMPQ AX, R9
+ JA emitRemainder
+
+ // candidate = int(table[nextHash])
+ // XXX: MOVWQZX table-32768(SP)(R11*2), R15
+ // XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15
+ BYTE $0x4e
+ BYTE $0x0f
+ BYTE $0xb7
+ BYTE $0x7c
+ BYTE $0x5c
+ BYTE $0x78
+
+ // table[nextHash] = uint16(s)
+ MOVQ SI, AX
+ SUBQ DX, AX
+
+ // XXX: MOVW AX, table-32768(SP)(R11*2)
+ // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2)
+ BYTE $0x66
+ BYTE $0x42
+ BYTE $0x89
+ BYTE $0x44
+ BYTE $0x5c
+ BYTE $0x78
+
+ // nextHash = hash(load32(src, nextS), shift)
+ MOVL 0(R13), R11
+ IMULL $0x1e35a7bd, R11
+ SHRL CX, R11
+
+ // if load32(src, s) != load32(src, candidate) { continue } break
+ MOVL 0(SI), AX
+ MOVL (DX)(R15*1), BX
+ CMPL AX, BX
+ JNE inner0
+
+fourByteMatch:
+ // As per the encode_other.go code:
+ //
+ // A 4-byte match has been found. We'll later see etc.
+
+ // !!! Jump to a fast path for short (<= 16 byte) literals. See the comment
+ // on inputMargin in encode.go.
+ MOVQ SI, AX
+ SUBQ R10, AX
+ CMPQ AX, $16
+ JLE emitLiteralFastPath
+
+ // ----------------------------------------
+ // Begin inline of the emitLiteral call.
+ //
+ // d += emitLiteral(dst[d:], src[nextEmit:s])
+
+ MOVL AX, BX
+ SUBL $1, BX
+
+ CMPL BX, $60
+ JLT inlineEmitLiteralOneByte
+ CMPL BX, $256
+ JLT inlineEmitLiteralTwoBytes
+
+inlineEmitLiteralThreeBytes:
+ MOVB $0xf4, 0(DI)
+ MOVW BX, 1(DI)
+ ADDQ $3, DI
+ JMP inlineEmitLiteralMemmove
+
+inlineEmitLiteralTwoBytes:
+ MOVB $0xf0, 0(DI)
+ MOVB BX, 1(DI)
+ ADDQ $2, DI
+ JMP inlineEmitLiteralMemmove
+
+inlineEmitLiteralOneByte:
+ SHLB $2, BX
+ MOVB BX, 0(DI)
+ ADDQ $1, DI
+
+inlineEmitLiteralMemmove:
+ // Spill local variables (registers) onto the stack; call; unspill.
+ //
+ // copy(dst[i:], lit)
+ //
+ // This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
+ // DI, R10 and AX as arguments.
+ MOVQ DI, 0(SP)
+ MOVQ R10, 8(SP)
+ MOVQ AX, 16(SP)
+ ADDQ AX, DI // Finish the "d +=" part of "d += emitLiteral(etc)".
+ MOVQ SI, 72(SP)
+ MOVQ DI, 80(SP)
+ MOVQ R15, 112(SP)
+ CALL runtime·memmove(SB)
+ MOVQ 56(SP), CX
+ MOVQ 64(SP), DX
+ MOVQ 72(SP), SI
+ MOVQ 80(SP), DI
+ MOVQ 88(SP), R9
+ MOVQ 112(SP), R15
+ JMP inner1
+
+inlineEmitLiteralEnd:
+ // End inline of the emitLiteral call.
+ // ----------------------------------------
+
+emitLiteralFastPath:
+ // !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2".
+ MOVB AX, BX
+ SUBB $1, BX
+ SHLB $2, BX
+ MOVB BX, (DI)
+ ADDQ $1, DI
+
+ // !!! Implement the copy from lit to dst as a 16-byte load and store.
+ // (Encode's documentation says that dst and src must not overlap.)
+ //
+ // This always copies 16 bytes, instead of only len(lit) bytes, but that's
+ // OK. Subsequent iterations will fix up the overrun.
+ //
+ // Note that on amd64, it is legal and cheap to issue unaligned 8-byte or
+ // 16-byte loads and stores. This technique probably wouldn't be as
+ // effective on architectures that are fussier about alignment.
+ MOVOU 0(R10), X0
+ MOVOU X0, 0(DI)
+ ADDQ AX, DI
+
+inner1:
+ // for { etc }
+
+ // base := s
+ MOVQ SI, R12
+
+ // !!! offset := base - candidate
+ MOVQ R12, R11
+ SUBQ R15, R11
+ SUBQ DX, R11
+
+ // ----------------------------------------
+ // Begin inline of the extendMatch call.
+ //
+ // s = extendMatch(src, candidate+4, s+4)
+
+ // !!! R14 = &src[len(src)]
+ MOVQ src_len+32(FP), R14
+ ADDQ DX, R14
+
+ // !!! R13 = &src[len(src) - 8]
+ MOVQ R14, R13
+ SUBQ $8, R13
+
+ // !!! R15 = &src[candidate + 4]
+ ADDQ $4, R15
+ ADDQ DX, R15
+
+ // !!! s += 4
+ ADDQ $4, SI
+
+inlineExtendMatchCmp8:
+ // As long as we are 8 or more bytes before the end of src, we can load and
+ // compare 8 bytes at a time. If those 8 bytes are equal, repeat.
+ CMPQ SI, R13
+ JA inlineExtendMatchCmp1
+ MOVQ (R15), AX
+ MOVQ (SI), BX
+ CMPQ AX, BX
+ JNE inlineExtendMatchBSF
+ ADDQ $8, R15
+ ADDQ $8, SI
+ JMP inlineExtendMatchCmp8
+
+inlineExtendMatchBSF:
+ // If those 8 bytes were not equal, XOR the two 8 byte values, and return
+ // the index of the first byte that differs. The BSF instruction finds the
+ // least significant 1 bit, the amd64 architecture is little-endian, and
+ // the shift by 3 converts a bit index to a byte index.
+ XORQ AX, BX
+ BSFQ BX, BX
+ SHRQ $3, BX
+ ADDQ BX, SI
+ JMP inlineExtendMatchEnd
+
+inlineExtendMatchCmp1:
+ // In src's tail, compare 1 byte at a time.
+ CMPQ SI, R14
+ JAE inlineExtendMatchEnd
+ MOVB (R15), AX
+ MOVB (SI), BX
+ CMPB AX, BX
+ JNE inlineExtendMatchEnd
+ ADDQ $1, R15
+ ADDQ $1, SI
+ JMP inlineExtendMatchCmp1
+
+inlineExtendMatchEnd:
+ // End inline of the extendMatch call.
+ // ----------------------------------------
+
+ // ----------------------------------------
+ // Begin inline of the emitCopy call.
+ //
+ // d += emitCopy(dst[d:], base-candidate, s-base)
+
+ // !!! length := s - base
+ MOVQ SI, AX
+ SUBQ R12, AX
+
+inlineEmitCopyLoop0:
+ // for length >= 68 { etc }
+ CMPL AX, $68
+ JLT inlineEmitCopyStep1
+
+ // Emit a length 64 copy, encoded as 3 bytes.
+ MOVB $0xfe, 0(DI)
+ MOVW R11, 1(DI)
+ ADDQ $3, DI
+ SUBL $64, AX
+ JMP inlineEmitCopyLoop0
+
+inlineEmitCopyStep1:
+ // if length > 64 { etc }
+ CMPL AX, $64
+ JLE inlineEmitCopyStep2
+
+ // Emit a length 60 copy, encoded as 3 bytes.
+ MOVB $0xee, 0(DI)
+ MOVW R11, 1(DI)
+ ADDQ $3, DI
+ SUBL $60, AX
+
+inlineEmitCopyStep2:
+ // if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 }
+ CMPL AX, $12
+ JGE inlineEmitCopyStep3
+ CMPL R11, $2048
+ JGE inlineEmitCopyStep3
+
+ // Emit the remaining copy, encoded as 2 bytes.
+ MOVB R11, 1(DI)
+ SHRL $8, R11
+ SHLB $5, R11
+ SUBB $4, AX
+ SHLB $2, AX
+ ORB AX, R11
+ ORB $1, R11
+ MOVB R11, 0(DI)
+ ADDQ $2, DI
+ JMP inlineEmitCopyEnd
+
+inlineEmitCopyStep3:
+ // Emit the remaining copy, encoded as 3 bytes.
+ SUBL $1, AX
+ SHLB $2, AX
+ ORB $2, AX
+ MOVB AX, 0(DI)
+ MOVW R11, 1(DI)
+ ADDQ $3, DI
+
+inlineEmitCopyEnd:
+ // End inline of the emitCopy call.
+ // ----------------------------------------
+
+ // nextEmit = s
+ MOVQ SI, R10
+
+ // if s >= sLimit { goto emitRemainder }
+ MOVQ SI, AX
+ SUBQ DX, AX
+ CMPQ AX, R9
+ JAE emitRemainder
+
+ // As per the encode_other.go code:
+ //
+ // We could immediately etc.
+
+ // x := load64(src, s-1)
+ MOVQ -1(SI), R14
+
+ // prevHash := hash(uint32(x>>0), shift)
+ MOVL R14, R11
+ IMULL $0x1e35a7bd, R11
+ SHRL CX, R11
+
+ // table[prevHash] = uint16(s-1)
+ MOVQ SI, AX
+ SUBQ DX, AX
+ SUBQ $1, AX
+
+ // XXX: MOVW AX, table-32768(SP)(R11*2)
+ // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2)
+ BYTE $0x66
+ BYTE $0x42
+ BYTE $0x89
+ BYTE $0x44
+ BYTE $0x5c
+ BYTE $0x78
+
+ // currHash := hash(uint32(x>>8), shift)
+ SHRQ $8, R14
+ MOVL R14, R11
+ IMULL $0x1e35a7bd, R11
+ SHRL CX, R11
+
+ // candidate = int(table[currHash])
+ // XXX: MOVWQZX table-32768(SP)(R11*2), R15
+ // XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15
+ BYTE $0x4e
+ BYTE $0x0f
+ BYTE $0xb7
+ BYTE $0x7c
+ BYTE $0x5c
+ BYTE $0x78
+
+ // table[currHash] = uint16(s)
+ ADDQ $1, AX
+
+ // XXX: MOVW AX, table-32768(SP)(R11*2)
+ // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2)
+ BYTE $0x66
+ BYTE $0x42
+ BYTE $0x89
+ BYTE $0x44
+ BYTE $0x5c
+ BYTE $0x78
+
+ // if uint32(x>>8) == load32(src, candidate) { continue }
+ MOVL (DX)(R15*1), BX
+ CMPL R14, BX
+ JEQ inner1
+
+ // nextHash = hash(uint32(x>>16), shift)
+ SHRQ $8, R14
+ MOVL R14, R11
+ IMULL $0x1e35a7bd, R11
+ SHRL CX, R11
+
+ // s++
+ ADDQ $1, SI
+
+ // break out of the inner1 for loop, i.e. continue the outer loop.
+ JMP outer
+
+emitRemainder:
+ // if nextEmit < len(src) { etc }
+ MOVQ src_len+32(FP), AX
+ ADDQ DX, AX
+ CMPQ R10, AX
+ JEQ encodeBlockEnd
+
+ // d += emitLiteral(dst[d:], src[nextEmit:])
+ //
+ // Push args.
+ MOVQ DI, 0(SP)
+ MOVQ $0, 8(SP) // Unnecessary, as the callee ignores it, but conservative.
+ MOVQ $0, 16(SP) // Unnecessary, as the callee ignores it, but conservative.
+ MOVQ R10, 24(SP)
+ SUBQ R10, AX
+ MOVQ AX, 32(SP)
+ MOVQ AX, 40(SP) // Unnecessary, as the callee ignores it, but conservative.
+
+ // Spill local variables (registers) onto the stack; call; unspill.
+ MOVQ DI, 80(SP)
+ CALL ·emitLiteral(SB)
+ MOVQ 80(SP), DI
+
+ // Finish the "d +=" part of "d += emitLiteral(etc)".
+ ADDQ 48(SP), DI
+
+encodeBlockEnd:
+ MOVQ dst_base+0(FP), AX
+ SUBQ AX, DI
+ MOVQ DI, d+48(FP)
+ RET
diff --git a/vendor/github.com/golang/snappy/encode_other.go b/vendor/github.com/golang/snappy/encode_other.go
new file mode 100644
index 00000000..dbcae905
--- /dev/null
+++ b/vendor/github.com/golang/snappy/encode_other.go
@@ -0,0 +1,238 @@
+// Copyright 2016 The Snappy-Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !amd64 appengine !gc noasm
+
+package snappy
+
+func load32(b []byte, i int) uint32 {
+ b = b[i : i+4 : len(b)] // Help the compiler eliminate bounds checks on the next line.
+ return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
+}
+
+func load64(b []byte, i int) uint64 {
+ b = b[i : i+8 : len(b)] // Help the compiler eliminate bounds checks on the next line.
+ return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
+ uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
+}
+
+// emitLiteral writes a literal chunk and returns the number of bytes written.
+//
+// It assumes that:
+// dst is long enough to hold the encoded bytes
+// 1 <= len(lit) && len(lit) <= 65536
+func emitLiteral(dst, lit []byte) int {
+ i, n := 0, uint(len(lit)-1)
+ switch {
+ case n < 60:
+ dst[0] = uint8(n)<<2 | tagLiteral
+ i = 1
+ case n < 1<<8:
+ dst[0] = 60<<2 | tagLiteral
+ dst[1] = uint8(n)
+ i = 2
+ default:
+ dst[0] = 61<<2 | tagLiteral
+ dst[1] = uint8(n)
+ dst[2] = uint8(n >> 8)
+ i = 3
+ }
+ return i + copy(dst[i:], lit)
+}
+
+// emitCopy writes a copy chunk and returns the number of bytes written.
+//
+// It assumes that:
+// dst is long enough to hold the encoded bytes
+// 1 <= offset && offset <= 65535
+// 4 <= length && length <= 65535
+func emitCopy(dst []byte, offset, length int) int {
+ i := 0
+ // The maximum length for a single tagCopy1 or tagCopy2 op is 64 bytes. The
+ // threshold for this loop is a little higher (at 68 = 64 + 4), and the
+ // length emitted down below is is a little lower (at 60 = 64 - 4), because
+ // it's shorter to encode a length 67 copy as a length 60 tagCopy2 followed
+ // by a length 7 tagCopy1 (which encodes as 3+2 bytes) than to encode it as
+ // a length 64 tagCopy2 followed by a length 3 tagCopy2 (which encodes as
+ // 3+3 bytes). The magic 4 in the 64±4 is because the minimum length for a
+ // tagCopy1 op is 4 bytes, which is why a length 3 copy has to be an
+ // encodes-as-3-bytes tagCopy2 instead of an encodes-as-2-bytes tagCopy1.
+ for length >= 68 {
+ // Emit a length 64 copy, encoded as 3 bytes.
+ dst[i+0] = 63<<2 | tagCopy2
+ dst[i+1] = uint8(offset)
+ dst[i+2] = uint8(offset >> 8)
+ i += 3
+ length -= 64
+ }
+ if length > 64 {
+ // Emit a length 60 copy, encoded as 3 bytes.
+ dst[i+0] = 59<<2 | tagCopy2
+ dst[i+1] = uint8(offset)
+ dst[i+2] = uint8(offset >> 8)
+ i += 3
+ length -= 60
+ }
+ if length >= 12 || offset >= 2048 {
+ // Emit the remaining copy, encoded as 3 bytes.
+ dst[i+0] = uint8(length-1)<<2 | tagCopy2
+ dst[i+1] = uint8(offset)
+ dst[i+2] = uint8(offset >> 8)
+ return i + 3
+ }
+ // Emit the remaining copy, encoded as 2 bytes.
+ dst[i+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1
+ dst[i+1] = uint8(offset)
+ return i + 2
+}
+
+// extendMatch returns the largest k such that k <= len(src) and that
+// src[i:i+k-j] and src[j:k] have the same contents.
+//
+// It assumes that:
+// 0 <= i && i < j && j <= len(src)
+func extendMatch(src []byte, i, j int) int {
+ for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 {
+ }
+ return j
+}
+
+func hash(u, shift uint32) uint32 {
+ return (u * 0x1e35a7bd) >> shift
+}
+
+// encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It
+// assumes that the varint-encoded length of the decompressed bytes has already
+// been written.
+//
+// It also assumes that:
+// len(dst) >= MaxEncodedLen(len(src)) &&
+// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize
+func encodeBlock(dst, src []byte) (d int) {
+ // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive.
+ // The table element type is uint16, as s < sLimit and sLimit < len(src)
+ // and len(src) <= maxBlockSize and maxBlockSize == 65536.
+ const (
+ maxTableSize = 1 << 14
+ // tableMask is redundant, but helps the compiler eliminate bounds
+ // checks.
+ tableMask = maxTableSize - 1
+ )
+ shift := uint32(32 - 8)
+ for tableSize := 1 << 8; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 {
+ shift--
+ }
+ // In Go, all array elements are zero-initialized, so there is no advantage
+ // to a smaller tableSize per se. However, it matches the C++ algorithm,
+ // and in the asm versions of this code, we can get away with zeroing only
+ // the first tableSize elements.
+ var table [maxTableSize]uint16
+
+ // sLimit is when to stop looking for offset/length copies. The inputMargin
+ // lets us use a fast path for emitLiteral in the main loop, while we are
+ // looking for copies.
+ sLimit := len(src) - inputMargin
+
+ // nextEmit is where in src the next emitLiteral should start from.
+ nextEmit := 0
+
+ // The encoded form must start with a literal, as there are no previous
+ // bytes to copy, so we start looking for hash matches at s == 1.
+ s := 1
+ nextHash := hash(load32(src, s), shift)
+
+ for {
+ // Copied from the C++ snappy implementation:
+ //
+ // Heuristic match skipping: If 32 bytes are scanned with no matches
+ // found, start looking only at every other byte. If 32 more bytes are
+ // scanned (or skipped), look at every third byte, etc.. When a match
+ // is found, immediately go back to looking at every byte. This is a
+ // small loss (~5% performance, ~0.1% density) for compressible data
+ // due to more bookkeeping, but for non-compressible data (such as
+ // JPEG) it's a huge win since the compressor quickly "realizes" the
+ // data is incompressible and doesn't bother looking for matches
+ // everywhere.
+ //
+ // The "skip" variable keeps track of how many bytes there are since
+ // the last match; dividing it by 32 (ie. right-shifting by five) gives
+ // the number of bytes to move ahead for each iteration.
+ skip := 32
+
+ nextS := s
+ candidate := 0
+ for {
+ s = nextS
+ bytesBetweenHashLookups := skip >> 5
+ nextS = s + bytesBetweenHashLookups
+ skip += bytesBetweenHashLookups
+ if nextS > sLimit {
+ goto emitRemainder
+ }
+ candidate = int(table[nextHash&tableMask])
+ table[nextHash&tableMask] = uint16(s)
+ nextHash = hash(load32(src, nextS), shift)
+ if load32(src, s) == load32(src, candidate) {
+ break
+ }
+ }
+
+ // A 4-byte match has been found. We'll later see if more than 4 bytes
+ // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit
+ // them as literal bytes.
+ d += emitLiteral(dst[d:], src[nextEmit:s])
+
+ // Call emitCopy, and then see if another emitCopy could be our next
+ // move. Repeat until we find no match for the input immediately after
+ // what was consumed by the last emitCopy call.
+ //
+ // If we exit this loop normally then we need to call emitLiteral next,
+ // though we don't yet know how big the literal will be. We handle that
+ // by proceeding to the next iteration of the main loop. We also can
+ // exit this loop via goto if we get close to exhausting the input.
+ for {
+ // Invariant: we have a 4-byte match at s, and no need to emit any
+ // literal bytes prior to s.
+ base := s
+
+ // Extend the 4-byte match as long as possible.
+ //
+ // This is an inlined version of:
+ // s = extendMatch(src, candidate+4, s+4)
+ s += 4
+ for i := candidate + 4; s < len(src) && src[i] == src[s]; i, s = i+1, s+1 {
+ }
+
+ d += emitCopy(dst[d:], base-candidate, s-base)
+ nextEmit = s
+ if s >= sLimit {
+ goto emitRemainder
+ }
+
+ // We could immediately start working at s now, but to improve
+ // compression we first update the hash table at s-1 and at s. If
+ // another emitCopy is not our next move, also calculate nextHash
+ // at s+1. At least on GOARCH=amd64, these three hash calculations
+ // are faster as one load64 call (with some shifts) instead of
+ // three load32 calls.
+ x := load64(src, s-1)
+ prevHash := hash(uint32(x>>0), shift)
+ table[prevHash&tableMask] = uint16(s - 1)
+ currHash := hash(uint32(x>>8), shift)
+ candidate = int(table[currHash&tableMask])
+ table[currHash&tableMask] = uint16(s)
+ if uint32(x>>8) != load32(src, candidate) {
+ nextHash = hash(uint32(x>>16), shift)
+ s++
+ break
+ }
+ }
+ }
+
+emitRemainder:
+ if nextEmit < len(src) {
+ d += emitLiteral(dst[d:], src[nextEmit:])
+ }
+ return d
+}
diff --git a/vendor/github.com/golang/snappy/snappy.go b/vendor/github.com/golang/snappy/snappy.go
new file mode 100644
index 00000000..0cf5e379
--- /dev/null
+++ b/vendor/github.com/golang/snappy/snappy.go
@@ -0,0 +1,87 @@
+// Copyright 2011 The Snappy-Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package snappy implements the snappy block-based compression format.
+// It aims for very high speeds and reasonable compression.
+//
+// The C++ snappy implementation is at https://github.com/google/snappy
+package snappy // import "github.com/golang/snappy"
+
+import (
+ "hash/crc32"
+)
+
+/*
+Each encoded block begins with the varint-encoded length of the decoded data,
+followed by a sequence of chunks. Chunks begin and end on byte boundaries. The
+first byte of each chunk is broken into its 2 least and 6 most significant bits
+called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag.
+Zero means a literal tag. All other values mean a copy tag.
+
+For literal tags:
+ - If m < 60, the next 1 + m bytes are literal bytes.
+ - Otherwise, let n be the little-endian unsigned integer denoted by the next
+ m - 59 bytes. The next 1 + n bytes after that are literal bytes.
+
+For copy tags, length bytes are copied from offset bytes ago, in the style of
+Lempel-Ziv compression algorithms. In particular:
+ - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12).
+ The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10
+ of the offset. The next byte is bits 0-7 of the offset.
+ - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65).
+ The length is 1 + m. The offset is the little-endian unsigned integer
+ denoted by the next 2 bytes.
+ - For l == 3, this tag is a legacy format that is no longer issued by most
+ encoders. Nonetheless, the offset ranges in [0, 1<<32) and the length in
+ [1, 65). The length is 1 + m. The offset is the little-endian unsigned
+ integer denoted by the next 4 bytes.
+*/
+const (
+ tagLiteral = 0x00
+ tagCopy1 = 0x01
+ tagCopy2 = 0x02
+ tagCopy4 = 0x03
+)
+
+const (
+ checksumSize = 4
+ chunkHeaderSize = 4
+ magicChunk = "\xff\x06\x00\x00" + magicBody
+ magicBody = "sNaPpY"
+
+ // maxBlockSize is the maximum size of the input to encodeBlock. It is not
+ // part of the wire format per se, but some parts of the encoder assume
+ // that an offset fits into a uint16.
+ //
+ // Also, for the framing format (Writer type instead of Encode function),
+ // https://github.com/google/snappy/blob/master/framing_format.txt says
+ // that "the uncompressed data in a chunk must be no longer than 65536
+ // bytes".
+ maxBlockSize = 65536
+
+ // maxEncodedLenOfMaxBlockSize equals MaxEncodedLen(maxBlockSize), but is
+ // hard coded to be a const instead of a variable, so that obufLen can also
+ // be a const. Their equivalence is confirmed by
+ // TestMaxEncodedLenOfMaxBlockSize.
+ maxEncodedLenOfMaxBlockSize = 76490
+
+ obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize
+ obufLen = obufHeaderLen + maxEncodedLenOfMaxBlockSize
+)
+
+const (
+ chunkTypeCompressedData = 0x00
+ chunkTypeUncompressedData = 0x01
+ chunkTypePadding = 0xfe
+ chunkTypeStreamIdentifier = 0xff
+)
+
+var crcTable = crc32.MakeTable(crc32.Castagnoli)
+
+// crc implements the checksum specified in section 3 of
+// https://github.com/google/snappy/blob/master/framing_format.txt
+func crc(b []byte) uint32 {
+ c := crc32.Update(0, crcTable, b)
+ return uint32(c>>15|c<<17) + 0xa282ead8
+}
diff --git a/vendor/github.com/gomodule/redigo/LICENSE b/vendor/github.com/gomodule/redigo/LICENSE
new file mode 100644
index 00000000..67db8588
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/LICENSE
@@ -0,0 +1,175 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
diff --git a/vendor/github.com/gomodule/redigo/redis/commandinfo.go b/vendor/github.com/gomodule/redigo/redis/commandinfo.go
new file mode 100644
index 00000000..b6df6a25
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/redis/commandinfo.go
@@ -0,0 +1,55 @@
+// Copyright 2014 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "strings"
+)
+
+const (
+ connectionWatchState = 1 << iota
+ connectionMultiState
+ connectionSubscribeState
+ connectionMonitorState
+)
+
+type commandInfo struct {
+ // Set or Clear these states on connection.
+ Set, Clear int
+}
+
+var commandInfos = map[string]commandInfo{
+ "WATCH": {Set: connectionWatchState},
+ "UNWATCH": {Clear: connectionWatchState},
+ "MULTI": {Set: connectionMultiState},
+ "EXEC": {Clear: connectionWatchState | connectionMultiState},
+ "DISCARD": {Clear: connectionWatchState | connectionMultiState},
+ "PSUBSCRIBE": {Set: connectionSubscribeState},
+ "SUBSCRIBE": {Set: connectionSubscribeState},
+ "MONITOR": {Set: connectionMonitorState},
+}
+
+func init() {
+ for n, ci := range commandInfos {
+ commandInfos[strings.ToLower(n)] = ci
+ }
+}
+
+func lookupCommandInfo(commandName string) commandInfo {
+ if ci, ok := commandInfos[commandName]; ok {
+ return ci
+ }
+ return commandInfos[strings.ToUpper(commandName)]
+}
diff --git a/vendor/github.com/gomodule/redigo/redis/conn.go b/vendor/github.com/gomodule/redigo/redis/conn.go
new file mode 100644
index 00000000..5aa0f32f
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/redis/conn.go
@@ -0,0 +1,673 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "bufio"
+ "bytes"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "net/url"
+ "regexp"
+ "strconv"
+ "sync"
+ "time"
+)
+
+var (
+ _ ConnWithTimeout = (*conn)(nil)
+)
+
+// conn is the low-level implementation of Conn
+type conn struct {
+ // Shared
+ mu sync.Mutex
+ pending int
+ err error
+ conn net.Conn
+
+ // Read
+ readTimeout time.Duration
+ br *bufio.Reader
+
+ // Write
+ writeTimeout time.Duration
+ bw *bufio.Writer
+
+ // Scratch space for formatting argument length.
+ // '*' or '$', length, "\r\n"
+ lenScratch [32]byte
+
+ // Scratch space for formatting integers and floats.
+ numScratch [40]byte
+}
+
+// DialTimeout acts like Dial but takes timeouts for establishing the
+// connection to the server, writing a command and reading a reply.
+//
+// Deprecated: Use Dial with options instead.
+func DialTimeout(network, address string, connectTimeout, readTimeout, writeTimeout time.Duration) (Conn, error) {
+ return Dial(network, address,
+ DialConnectTimeout(connectTimeout),
+ DialReadTimeout(readTimeout),
+ DialWriteTimeout(writeTimeout))
+}
+
+// DialOption specifies an option for dialing a Redis server.
+type DialOption struct {
+ f func(*dialOptions)
+}
+
+type dialOptions struct {
+ readTimeout time.Duration
+ writeTimeout time.Duration
+ dialer *net.Dialer
+ dial func(network, addr string) (net.Conn, error)
+ db int
+ password string
+ useTLS bool
+ skipVerify bool
+ tlsConfig *tls.Config
+}
+
+// DialReadTimeout specifies the timeout for reading a single command reply.
+func DialReadTimeout(d time.Duration) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.readTimeout = d
+ }}
+}
+
+// DialWriteTimeout specifies the timeout for writing a single command.
+func DialWriteTimeout(d time.Duration) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.writeTimeout = d
+ }}
+}
+
+// DialConnectTimeout specifies the timeout for connecting to the Redis server when
+// no DialNetDial option is specified.
+func DialConnectTimeout(d time.Duration) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.dialer.Timeout = d
+ }}
+}
+
+// DialKeepAlive specifies the keep-alive period for TCP connections to the Redis server
+// when no DialNetDial option is specified.
+// If zero, keep-alives are not enabled. If no DialKeepAlive option is specified then
+// the default of 5 minutes is used to ensure that half-closed TCP sessions are detected.
+func DialKeepAlive(d time.Duration) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.dialer.KeepAlive = d
+ }}
+}
+
+// DialNetDial specifies a custom dial function for creating TCP
+// connections, otherwise a net.Dialer customized via the other options is used.
+// DialNetDial overrides DialConnectTimeout and DialKeepAlive.
+func DialNetDial(dial func(network, addr string) (net.Conn, error)) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.dial = dial
+ }}
+}
+
+// DialDatabase specifies the database to select when dialing a connection.
+func DialDatabase(db int) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.db = db
+ }}
+}
+
+// DialPassword specifies the password to use when connecting to
+// the Redis server.
+func DialPassword(password string) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.password = password
+ }}
+}
+
+// DialTLSConfig specifies the config to use when a TLS connection is dialed.
+// Has no effect when not dialing a TLS connection.
+func DialTLSConfig(c *tls.Config) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.tlsConfig = c
+ }}
+}
+
+// DialTLSSkipVerify disables server name verification when connecting over
+// TLS. Has no effect when not dialing a TLS connection.
+func DialTLSSkipVerify(skip bool) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.skipVerify = skip
+ }}
+}
+
+// DialUseTLS specifies whether TLS should be used when connecting to the
+// server. This option is ignore by DialURL.
+func DialUseTLS(useTLS bool) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.useTLS = useTLS
+ }}
+}
+
+// Dial connects to the Redis server at the given network and
+// address using the specified options.
+func Dial(network, address string, options ...DialOption) (Conn, error) {
+ do := dialOptions{
+ dialer: &net.Dialer{
+ KeepAlive: time.Minute * 5,
+ },
+ }
+ for _, option := range options {
+ option.f(&do)
+ }
+ if do.dial == nil {
+ do.dial = do.dialer.Dial
+ }
+
+ netConn, err := do.dial(network, address)
+ if err != nil {
+ return nil, err
+ }
+
+ if do.useTLS {
+ var tlsConfig *tls.Config
+ if do.tlsConfig == nil {
+ tlsConfig = &tls.Config{InsecureSkipVerify: do.skipVerify}
+ } else {
+ tlsConfig = cloneTLSConfig(do.tlsConfig)
+ }
+ if tlsConfig.ServerName == "" {
+ host, _, err := net.SplitHostPort(address)
+ if err != nil {
+ netConn.Close()
+ return nil, err
+ }
+ tlsConfig.ServerName = host
+ }
+
+ tlsConn := tls.Client(netConn, tlsConfig)
+ if err := tlsConn.Handshake(); err != nil {
+ netConn.Close()
+ return nil, err
+ }
+ netConn = tlsConn
+ }
+
+ c := &conn{
+ conn: netConn,
+ bw: bufio.NewWriter(netConn),
+ br: bufio.NewReader(netConn),
+ readTimeout: do.readTimeout,
+ writeTimeout: do.writeTimeout,
+ }
+
+ if do.password != "" {
+ if _, err := c.Do("AUTH", do.password); err != nil {
+ netConn.Close()
+ return nil, err
+ }
+ }
+
+ if do.db != 0 {
+ if _, err := c.Do("SELECT", do.db); err != nil {
+ netConn.Close()
+ return nil, err
+ }
+ }
+
+ return c, nil
+}
+
+var pathDBRegexp = regexp.MustCompile(`/(\d*)\z`)
+
+// DialURL connects to a Redis server at the given URL using the Redis
+// URI scheme. URLs should follow the draft IANA specification for the
+// scheme (https://www.iana.org/assignments/uri-schemes/prov/redis).
+func DialURL(rawurl string, options ...DialOption) (Conn, error) {
+ u, err := url.Parse(rawurl)
+ if err != nil {
+ return nil, err
+ }
+
+ if u.Scheme != "redis" && u.Scheme != "rediss" {
+ return nil, fmt.Errorf("invalid redis URL scheme: %s", u.Scheme)
+ }
+
+ // As per the IANA draft spec, the host defaults to localhost and
+ // the port defaults to 6379.
+ host, port, err := net.SplitHostPort(u.Host)
+ if err != nil {
+ // assume port is missing
+ host = u.Host
+ port = "6379"
+ }
+ if host == "" {
+ host = "localhost"
+ }
+ address := net.JoinHostPort(host, port)
+
+ if u.User != nil {
+ password, isSet := u.User.Password()
+ if isSet {
+ options = append(options, DialPassword(password))
+ }
+ }
+
+ match := pathDBRegexp.FindStringSubmatch(u.Path)
+ if len(match) == 2 {
+ db := 0
+ if len(match[1]) > 0 {
+ db, err = strconv.Atoi(match[1])
+ if err != nil {
+ return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
+ }
+ }
+ if db != 0 {
+ options = append(options, DialDatabase(db))
+ }
+ } else if u.Path != "" {
+ return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
+ }
+
+ options = append(options, DialUseTLS(u.Scheme == "rediss"))
+
+ return Dial("tcp", address, options...)
+}
+
+// NewConn returns a new Redigo connection for the given net connection.
+func NewConn(netConn net.Conn, readTimeout, writeTimeout time.Duration) Conn {
+ return &conn{
+ conn: netConn,
+ bw: bufio.NewWriter(netConn),
+ br: bufio.NewReader(netConn),
+ readTimeout: readTimeout,
+ writeTimeout: writeTimeout,
+ }
+}
+
+func (c *conn) Close() error {
+ c.mu.Lock()
+ err := c.err
+ if c.err == nil {
+ c.err = errors.New("redigo: closed")
+ err = c.conn.Close()
+ }
+ c.mu.Unlock()
+ return err
+}
+
+func (c *conn) fatal(err error) error {
+ c.mu.Lock()
+ if c.err == nil {
+ c.err = err
+ // Close connection to force errors on subsequent calls and to unblock
+ // other reader or writer.
+ c.conn.Close()
+ }
+ c.mu.Unlock()
+ return err
+}
+
+func (c *conn) Err() error {
+ c.mu.Lock()
+ err := c.err
+ c.mu.Unlock()
+ return err
+}
+
+func (c *conn) writeLen(prefix byte, n int) error {
+ c.lenScratch[len(c.lenScratch)-1] = '\n'
+ c.lenScratch[len(c.lenScratch)-2] = '\r'
+ i := len(c.lenScratch) - 3
+ for {
+ c.lenScratch[i] = byte('0' + n%10)
+ i -= 1
+ n = n / 10
+ if n == 0 {
+ break
+ }
+ }
+ c.lenScratch[i] = prefix
+ _, err := c.bw.Write(c.lenScratch[i:])
+ return err
+}
+
+func (c *conn) writeString(s string) error {
+ c.writeLen('$', len(s))
+ c.bw.WriteString(s)
+ _, err := c.bw.WriteString("\r\n")
+ return err
+}
+
+func (c *conn) writeBytes(p []byte) error {
+ c.writeLen('$', len(p))
+ c.bw.Write(p)
+ _, err := c.bw.WriteString("\r\n")
+ return err
+}
+
+func (c *conn) writeInt64(n int64) error {
+ return c.writeBytes(strconv.AppendInt(c.numScratch[:0], n, 10))
+}
+
+func (c *conn) writeFloat64(n float64) error {
+ return c.writeBytes(strconv.AppendFloat(c.numScratch[:0], n, 'g', -1, 64))
+}
+
+func (c *conn) writeCommand(cmd string, args []interface{}) error {
+ c.writeLen('*', 1+len(args))
+ if err := c.writeString(cmd); err != nil {
+ return err
+ }
+ for _, arg := range args {
+ if err := c.writeArg(arg, true); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (c *conn) writeArg(arg interface{}, argumentTypeOK bool) (err error) {
+ switch arg := arg.(type) {
+ case string:
+ return c.writeString(arg)
+ case []byte:
+ return c.writeBytes(arg)
+ case int:
+ return c.writeInt64(int64(arg))
+ case int64:
+ return c.writeInt64(arg)
+ case float64:
+ return c.writeFloat64(arg)
+ case bool:
+ if arg {
+ return c.writeString("1")
+ } else {
+ return c.writeString("0")
+ }
+ case nil:
+ return c.writeString("")
+ case Argument:
+ if argumentTypeOK {
+ return c.writeArg(arg.RedisArg(), false)
+ }
+ // See comment in default clause below.
+ var buf bytes.Buffer
+ fmt.Fprint(&buf, arg)
+ return c.writeBytes(buf.Bytes())
+ default:
+ // This default clause is intended to handle builtin numeric types.
+ // The function should return an error for other types, but this is not
+ // done for compatibility with previous versions of the package.
+ var buf bytes.Buffer
+ fmt.Fprint(&buf, arg)
+ return c.writeBytes(buf.Bytes())
+ }
+}
+
+type protocolError string
+
+func (pe protocolError) Error() string {
+ return fmt.Sprintf("redigo: %s (possible server error or unsupported concurrent read by application)", string(pe))
+}
+
+func (c *conn) readLine() ([]byte, error) {
+ p, err := c.br.ReadSlice('\n')
+ if err == bufio.ErrBufferFull {
+ return nil, protocolError("long response line")
+ }
+ if err != nil {
+ return nil, err
+ }
+ i := len(p) - 2
+ if i < 0 || p[i] != '\r' {
+ return nil, protocolError("bad response line terminator")
+ }
+ return p[:i], nil
+}
+
+// parseLen parses bulk string and array lengths.
+func parseLen(p []byte) (int, error) {
+ if len(p) == 0 {
+ return -1, protocolError("malformed length")
+ }
+
+ if p[0] == '-' && len(p) == 2 && p[1] == '1' {
+ // handle $-1 and $-1 null replies.
+ return -1, nil
+ }
+
+ var n int
+ for _, b := range p {
+ n *= 10
+ if b < '0' || b > '9' {
+ return -1, protocolError("illegal bytes in length")
+ }
+ n += int(b - '0')
+ }
+
+ return n, nil
+}
+
+// parseInt parses an integer reply.
+func parseInt(p []byte) (interface{}, error) {
+ if len(p) == 0 {
+ return 0, protocolError("malformed integer")
+ }
+
+ var negate bool
+ if p[0] == '-' {
+ negate = true
+ p = p[1:]
+ if len(p) == 0 {
+ return 0, protocolError("malformed integer")
+ }
+ }
+
+ var n int64
+ for _, b := range p {
+ n *= 10
+ if b < '0' || b > '9' {
+ return 0, protocolError("illegal bytes in length")
+ }
+ n += int64(b - '0')
+ }
+
+ if negate {
+ n = -n
+ }
+ return n, nil
+}
+
+var (
+ okReply interface{} = "OK"
+ pongReply interface{} = "PONG"
+)
+
+func (c *conn) readReply() (interface{}, error) {
+ line, err := c.readLine()
+ if err != nil {
+ return nil, err
+ }
+ if len(line) == 0 {
+ return nil, protocolError("short response line")
+ }
+ switch line[0] {
+ case '+':
+ switch {
+ case len(line) == 3 && line[1] == 'O' && line[2] == 'K':
+ // Avoid allocation for frequent "+OK" response.
+ return okReply, nil
+ case len(line) == 5 && line[1] == 'P' && line[2] == 'O' && line[3] == 'N' && line[4] == 'G':
+ // Avoid allocation in PING command benchmarks :)
+ return pongReply, nil
+ default:
+ return string(line[1:]), nil
+ }
+ case '-':
+ return Error(string(line[1:])), nil
+ case ':':
+ return parseInt(line[1:])
+ case '$':
+ n, err := parseLen(line[1:])
+ if n < 0 || err != nil {
+ return nil, err
+ }
+ p := make([]byte, n)
+ _, err = io.ReadFull(c.br, p)
+ if err != nil {
+ return nil, err
+ }
+ if line, err := c.readLine(); err != nil {
+ return nil, err
+ } else if len(line) != 0 {
+ return nil, protocolError("bad bulk string format")
+ }
+ return p, nil
+ case '*':
+ n, err := parseLen(line[1:])
+ if n < 0 || err != nil {
+ return nil, err
+ }
+ r := make([]interface{}, n)
+ for i := range r {
+ r[i], err = c.readReply()
+ if err != nil {
+ return nil, err
+ }
+ }
+ return r, nil
+ }
+ return nil, protocolError("unexpected response line")
+}
+
+func (c *conn) Send(cmd string, args ...interface{}) error {
+ c.mu.Lock()
+ c.pending += 1
+ c.mu.Unlock()
+ if c.writeTimeout != 0 {
+ c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
+ }
+ if err := c.writeCommand(cmd, args); err != nil {
+ return c.fatal(err)
+ }
+ return nil
+}
+
+func (c *conn) Flush() error {
+ if c.writeTimeout != 0 {
+ c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
+ }
+ if err := c.bw.Flush(); err != nil {
+ return c.fatal(err)
+ }
+ return nil
+}
+
+func (c *conn) Receive() (interface{}, error) {
+ return c.ReceiveWithTimeout(c.readTimeout)
+}
+
+func (c *conn) ReceiveWithTimeout(timeout time.Duration) (reply interface{}, err error) {
+ var deadline time.Time
+ if timeout != 0 {
+ deadline = time.Now().Add(timeout)
+ }
+ c.conn.SetReadDeadline(deadline)
+
+ if reply, err = c.readReply(); err != nil {
+ return nil, c.fatal(err)
+ }
+ // When using pub/sub, the number of receives can be greater than the
+ // number of sends. To enable normal use of the connection after
+ // unsubscribing from all channels, we do not decrement pending to a
+ // negative value.
+ //
+ // The pending field is decremented after the reply is read to handle the
+ // case where Receive is called before Send.
+ c.mu.Lock()
+ if c.pending > 0 {
+ c.pending -= 1
+ }
+ c.mu.Unlock()
+ if err, ok := reply.(Error); ok {
+ return nil, err
+ }
+ return
+}
+
+func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) {
+ return c.DoWithTimeout(c.readTimeout, cmd, args...)
+}
+
+func (c *conn) DoWithTimeout(readTimeout time.Duration, cmd string, args ...interface{}) (interface{}, error) {
+ c.mu.Lock()
+ pending := c.pending
+ c.pending = 0
+ c.mu.Unlock()
+
+ if cmd == "" && pending == 0 {
+ return nil, nil
+ }
+
+ if c.writeTimeout != 0 {
+ c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
+ }
+
+ if cmd != "" {
+ if err := c.writeCommand(cmd, args); err != nil {
+ return nil, c.fatal(err)
+ }
+ }
+
+ if err := c.bw.Flush(); err != nil {
+ return nil, c.fatal(err)
+ }
+
+ var deadline time.Time
+ if readTimeout != 0 {
+ deadline = time.Now().Add(readTimeout)
+ }
+ c.conn.SetReadDeadline(deadline)
+
+ if cmd == "" {
+ reply := make([]interface{}, pending)
+ for i := range reply {
+ r, e := c.readReply()
+ if e != nil {
+ return nil, c.fatal(e)
+ }
+ reply[i] = r
+ }
+ return reply, nil
+ }
+
+ var err error
+ var reply interface{}
+ for i := 0; i <= pending; i++ {
+ var e error
+ if reply, e = c.readReply(); e != nil {
+ return nil, c.fatal(e)
+ }
+ if e, ok := reply.(Error); ok && err == nil {
+ err = e
+ }
+ }
+ return reply, err
+}
diff --git a/vendor/github.com/gomodule/redigo/redis/doc.go b/vendor/github.com/gomodule/redigo/redis/doc.go
new file mode 100644
index 00000000..4e3dc438
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/redis/doc.go
@@ -0,0 +1,177 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+// Package redis is a client for the Redis database.
+//
+// The Redigo FAQ (https://github.com/gomodule/redigo/wiki/FAQ) contains more
+// documentation about this package.
+//
+// Connections
+//
+// The Conn interface is the primary interface for working with Redis.
+// Applications create connections by calling the Dial, DialWithTimeout or
+// NewConn functions. In the future, functions will be added for creating
+// sharded and other types of connections.
+//
+// The application must call the connection Close method when the application
+// is done with the connection.
+//
+// Executing Commands
+//
+// The Conn interface has a generic method for executing Redis commands:
+//
+// Do(commandName string, args ...interface{}) (reply interface{}, err error)
+//
+// The Redis command reference (http://redis.io/commands) lists the available
+// commands. An example of using the Redis APPEND command is:
+//
+// n, err := conn.Do("APPEND", "key", "value")
+//
+// The Do method converts command arguments to bulk strings for transmission
+// to the server as follows:
+//
+// Go Type Conversion
+// []byte Sent as is
+// string Sent as is
+// int, int64 strconv.FormatInt(v)
+// float64 strconv.FormatFloat(v, 'g', -1, 64)
+// bool true -> "1", false -> "0"
+// nil ""
+// all other types fmt.Fprint(w, v)
+//
+// Redis command reply types are represented using the following Go types:
+//
+// Redis type Go type
+// error redis.Error
+// integer int64
+// simple string string
+// bulk string []byte or nil if value not present.
+// array []interface{} or nil if value not present.
+//
+// Use type assertions or the reply helper functions to convert from
+// interface{} to the specific Go type for the command result.
+//
+// Pipelining
+//
+// Connections support pipelining using the Send, Flush and Receive methods.
+//
+// Send(commandName string, args ...interface{}) error
+// Flush() error
+// Receive() (reply interface{}, err error)
+//
+// Send writes the command to the connection's output buffer. Flush flushes the
+// connection's output buffer to the server. Receive reads a single reply from
+// the server. The following example shows a simple pipeline.
+//
+// c.Send("SET", "foo", "bar")
+// c.Send("GET", "foo")
+// c.Flush()
+// c.Receive() // reply from SET
+// v, err = c.Receive() // reply from GET
+//
+// The Do method combines the functionality of the Send, Flush and Receive
+// methods. The Do method starts by writing the command and flushing the output
+// buffer. Next, the Do method receives all pending replies including the reply
+// for the command just sent by Do. If any of the received replies is an error,
+// then Do returns the error. If there are no errors, then Do returns the last
+// reply. If the command argument to the Do method is "", then the Do method
+// will flush the output buffer and receive pending replies without sending a
+// command.
+//
+// Use the Send and Do methods to implement pipelined transactions.
+//
+// c.Send("MULTI")
+// c.Send("INCR", "foo")
+// c.Send("INCR", "bar")
+// r, err := c.Do("EXEC")
+// fmt.Println(r) // prints [1, 1]
+//
+// Concurrency
+//
+// Connections support one concurrent caller to the Receive method and one
+// concurrent caller to the Send and Flush methods. No other concurrency is
+// supported including concurrent calls to the Do method.
+//
+// For full concurrent access to Redis, use the thread-safe Pool to get, use
+// and release a connection from within a goroutine. Connections returned from
+// a Pool have the concurrency restrictions described in the previous
+// paragraph.
+//
+// Publish and Subscribe
+//
+// Use the Send, Flush and Receive methods to implement Pub/Sub subscribers.
+//
+// c.Send("SUBSCRIBE", "example")
+// c.Flush()
+// for {
+// reply, err := c.Receive()
+// if err != nil {
+// return err
+// }
+// // process pushed message
+// }
+//
+// The PubSubConn type wraps a Conn with convenience methods for implementing
+// subscribers. The Subscribe, PSubscribe, Unsubscribe and PUnsubscribe methods
+// send and flush a subscription management command. The receive method
+// converts a pushed message to convenient types for use in a type switch.
+//
+// psc := redis.PubSubConn{Conn: c}
+// psc.Subscribe("example")
+// for {
+// switch v := psc.Receive().(type) {
+// case redis.Message:
+// fmt.Printf("%s: message: %s\n", v.Channel, v.Data)
+// case redis.Subscription:
+// fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
+// case error:
+// return v
+// }
+// }
+//
+// Reply Helpers
+//
+// The Bool, Int, Bytes, String, Strings and Values functions convert a reply
+// to a value of a specific type. To allow convenient wrapping of calls to the
+// connection Do and Receive methods, the functions take a second argument of
+// type error. If the error is non-nil, then the helper function returns the
+// error. If the error is nil, the function converts the reply to the specified
+// type:
+//
+// exists, err := redis.Bool(c.Do("EXISTS", "foo"))
+// if err != nil {
+// // handle error return from c.Do or type conversion error.
+// }
+//
+// The Scan function converts elements of a array reply to Go types:
+//
+// var value1 int
+// var value2 string
+// reply, err := redis.Values(c.Do("MGET", "key1", "key2"))
+// if err != nil {
+// // handle error
+// }
+// if _, err := redis.Scan(reply, &value1, &value2); err != nil {
+// // handle error
+// }
+//
+// Errors
+//
+// Connection methods return error replies from the server as type redis.Error.
+//
+// Call the connection Err() method to determine if the connection encountered
+// non-recoverable error such as a network error or protocol parsing error. If
+// Err() returns a non-nil value, then the connection is not usable and should
+// be closed.
+package redis
diff --git a/vendor/github.com/gomodule/redigo/redis/go16.go b/vendor/github.com/gomodule/redigo/redis/go16.go
new file mode 100644
index 00000000..f6b1a7cc
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/redis/go16.go
@@ -0,0 +1,27 @@
+// +build !go1.7
+
+package redis
+
+import "crypto/tls"
+
+func cloneTLSConfig(cfg *tls.Config) *tls.Config {
+ return &tls.Config{
+ Rand: cfg.Rand,
+ Time: cfg.Time,
+ Certificates: cfg.Certificates,
+ NameToCertificate: cfg.NameToCertificate,
+ GetCertificate: cfg.GetCertificate,
+ RootCAs: cfg.RootCAs,
+ NextProtos: cfg.NextProtos,
+ ServerName: cfg.ServerName,
+ ClientAuth: cfg.ClientAuth,
+ ClientCAs: cfg.ClientCAs,
+ InsecureSkipVerify: cfg.InsecureSkipVerify,
+ CipherSuites: cfg.CipherSuites,
+ PreferServerCipherSuites: cfg.PreferServerCipherSuites,
+ ClientSessionCache: cfg.ClientSessionCache,
+ MinVersion: cfg.MinVersion,
+ MaxVersion: cfg.MaxVersion,
+ CurvePreferences: cfg.CurvePreferences,
+ }
+}
diff --git a/vendor/github.com/gomodule/redigo/redis/go17.go b/vendor/github.com/gomodule/redigo/redis/go17.go
new file mode 100644
index 00000000..5f363791
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/redis/go17.go
@@ -0,0 +1,29 @@
+// +build go1.7,!go1.8
+
+package redis
+
+import "crypto/tls"
+
+func cloneTLSConfig(cfg *tls.Config) *tls.Config {
+ return &tls.Config{
+ Rand: cfg.Rand,
+ Time: cfg.Time,
+ Certificates: cfg.Certificates,
+ NameToCertificate: cfg.NameToCertificate,
+ GetCertificate: cfg.GetCertificate,
+ RootCAs: cfg.RootCAs,
+ NextProtos: cfg.NextProtos,
+ ServerName: cfg.ServerName,
+ ClientAuth: cfg.ClientAuth,
+ ClientCAs: cfg.ClientCAs,
+ InsecureSkipVerify: cfg.InsecureSkipVerify,
+ CipherSuites: cfg.CipherSuites,
+ PreferServerCipherSuites: cfg.PreferServerCipherSuites,
+ ClientSessionCache: cfg.ClientSessionCache,
+ MinVersion: cfg.MinVersion,
+ MaxVersion: cfg.MaxVersion,
+ CurvePreferences: cfg.CurvePreferences,
+ DynamicRecordSizingDisabled: cfg.DynamicRecordSizingDisabled,
+ Renegotiation: cfg.Renegotiation,
+ }
+}
diff --git a/vendor/github.com/gomodule/redigo/redis/go18.go b/vendor/github.com/gomodule/redigo/redis/go18.go
new file mode 100644
index 00000000..558363be
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/redis/go18.go
@@ -0,0 +1,9 @@
+// +build go1.8
+
+package redis
+
+import "crypto/tls"
+
+func cloneTLSConfig(cfg *tls.Config) *tls.Config {
+ return cfg.Clone()
+}
diff --git a/vendor/github.com/gomodule/redigo/redis/log.go b/vendor/github.com/gomodule/redigo/redis/log.go
new file mode 100644
index 00000000..a06db9d6
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/redis/log.go
@@ -0,0 +1,146 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+ "time"
+)
+
+var (
+ _ ConnWithTimeout = (*loggingConn)(nil)
+)
+
+// NewLoggingConn returns a logging wrapper around a connection.
+func NewLoggingConn(conn Conn, logger *log.Logger, prefix string) Conn {
+ if prefix != "" {
+ prefix = prefix + "."
+ }
+ return &loggingConn{conn, logger, prefix, nil}
+}
+
+//NewLoggingConnFilter returns a logging wrapper around a connection and a filter function.
+func NewLoggingConnFilter(conn Conn, logger *log.Logger, prefix string, skip func(cmdName string) bool) Conn {
+ if prefix != "" {
+ prefix = prefix + "."
+ }
+ return &loggingConn{conn, logger, prefix, skip}
+}
+
+type loggingConn struct {
+ Conn
+ logger *log.Logger
+ prefix string
+ skip func(cmdName string) bool
+}
+
+func (c *loggingConn) Close() error {
+ err := c.Conn.Close()
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "%sClose() -> (%v)", c.prefix, err)
+ c.logger.Output(2, buf.String())
+ return err
+}
+
+func (c *loggingConn) printValue(buf *bytes.Buffer, v interface{}) {
+ const chop = 32
+ switch v := v.(type) {
+ case []byte:
+ if len(v) > chop {
+ fmt.Fprintf(buf, "%q...", v[:chop])
+ } else {
+ fmt.Fprintf(buf, "%q", v)
+ }
+ case string:
+ if len(v) > chop {
+ fmt.Fprintf(buf, "%q...", v[:chop])
+ } else {
+ fmt.Fprintf(buf, "%q", v)
+ }
+ case []interface{}:
+ if len(v) == 0 {
+ buf.WriteString("[]")
+ } else {
+ sep := "["
+ fin := "]"
+ if len(v) > chop {
+ v = v[:chop]
+ fin = "...]"
+ }
+ for _, vv := range v {
+ buf.WriteString(sep)
+ c.printValue(buf, vv)
+ sep = ", "
+ }
+ buf.WriteString(fin)
+ }
+ default:
+ fmt.Fprint(buf, v)
+ }
+}
+
+func (c *loggingConn) print(method, commandName string, args []interface{}, reply interface{}, err error) {
+ if c.skip != nil && c.skip(commandName) {
+ return
+ }
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "%s%s(", c.prefix, method)
+ if method != "Receive" {
+ buf.WriteString(commandName)
+ for _, arg := range args {
+ buf.WriteString(", ")
+ c.printValue(&buf, arg)
+ }
+ }
+ buf.WriteString(") -> (")
+ if method != "Send" {
+ c.printValue(&buf, reply)
+ buf.WriteString(", ")
+ }
+ fmt.Fprintf(&buf, "%v)", err)
+ c.logger.Output(3, buf.String())
+}
+
+func (c *loggingConn) Do(commandName string, args ...interface{}) (interface{}, error) {
+ reply, err := c.Conn.Do(commandName, args...)
+ c.print("Do", commandName, args, reply, err)
+ return reply, err
+}
+
+func (c *loggingConn) DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (interface{}, error) {
+ reply, err := DoWithTimeout(c.Conn, timeout, commandName, args...)
+ c.print("DoWithTimeout", commandName, args, reply, err)
+ return reply, err
+}
+
+func (c *loggingConn) Send(commandName string, args ...interface{}) error {
+ err := c.Conn.Send(commandName, args...)
+ c.print("Send", commandName, args, nil, err)
+ return err
+}
+
+func (c *loggingConn) Receive() (interface{}, error) {
+ reply, err := c.Conn.Receive()
+ c.print("Receive", "", nil, reply, err)
+ return reply, err
+}
+
+func (c *loggingConn) ReceiveWithTimeout(timeout time.Duration) (interface{}, error) {
+ reply, err := ReceiveWithTimeout(c.Conn, timeout)
+ c.print("ReceiveWithTimeout", "", nil, reply, err)
+ return reply, err
+}
diff --git a/vendor/github.com/gomodule/redigo/redis/pool.go b/vendor/github.com/gomodule/redigo/redis/pool.go
new file mode 100644
index 00000000..a833f85c
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/redis/pool.go
@@ -0,0 +1,560 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "bytes"
+ "crypto/rand"
+ "crypto/sha1"
+ "errors"
+ "io"
+ "strconv"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+var (
+ _ ConnWithTimeout = (*activeConn)(nil)
+ _ ConnWithTimeout = (*errorConn)(nil)
+)
+
+var nowFunc = time.Now // for testing
+
+// ErrPoolExhausted is returned from a pool connection method (Do, Send,
+// Receive, Flush, Err) when the maximum number of database connections in the
+// pool has been reached.
+var ErrPoolExhausted = errors.New("redigo: connection pool exhausted")
+
+var (
+ errPoolClosed = errors.New("redigo: connection pool closed")
+ errConnClosed = errors.New("redigo: connection closed")
+)
+
+// Pool maintains a pool of connections. The application calls the Get method
+// to get a connection from the pool and the connection's Close method to
+// return the connection's resources to the pool.
+//
+// The following example shows how to use a pool in a web application. The
+// application creates a pool at application startup and makes it available to
+// request handlers using a package level variable. The pool configuration used
+// here is an example, not a recommendation.
+//
+// func newPool(addr string) *redis.Pool {
+// return &redis.Pool{
+// MaxIdle: 3,
+// IdleTimeout: 240 * time.Second,
+// Dial: func () (redis.Conn, error) { return redis.Dial("tcp", addr) },
+// }
+// }
+//
+// var (
+// pool *redis.Pool
+// redisServer = flag.String("redisServer", ":6379", "")
+// )
+//
+// func main() {
+// flag.Parse()
+// pool = newPool(*redisServer)
+// ...
+// }
+//
+// A request handler gets a connection from the pool and closes the connection
+// when the handler is done:
+//
+// func serveHome(w http.ResponseWriter, r *http.Request) {
+// conn := pool.Get()
+// defer conn.Close()
+// ...
+// }
+//
+// Use the Dial function to authenticate connections with the AUTH command or
+// select a database with the SELECT command:
+//
+// pool := &redis.Pool{
+// // Other pool configuration not shown in this example.
+// Dial: func () (redis.Conn, error) {
+// c, err := redis.Dial("tcp", server)
+// if err != nil {
+// return nil, err
+// }
+// if _, err := c.Do("AUTH", password); err != nil {
+// c.Close()
+// return nil, err
+// }
+// if _, err := c.Do("SELECT", db); err != nil {
+// c.Close()
+// return nil, err
+// }
+// return c, nil
+// },
+// }
+//
+// Use the TestOnBorrow function to check the health of an idle connection
+// before the connection is returned to the application. This example PINGs
+// connections that have been idle more than a minute:
+//
+// pool := &redis.Pool{
+// // Other pool configuration not shown in this example.
+// TestOnBorrow: func(c redis.Conn, t time.Time) error {
+// if time.Since(t) < time.Minute {
+// return nil
+// }
+// _, err := c.Do("PING")
+// return err
+// },
+// }
+//
+type Pool struct {
+ // Dial is an application supplied function for creating and configuring a
+ // connection.
+ //
+ // The connection returned from Dial must not be in a special state
+ // (subscribed to pubsub channel, transaction started, ...).
+ Dial func() (Conn, error)
+
+ // TestOnBorrow is an optional application supplied function for checking
+ // the health of an idle connection before the connection is used again by
+ // the application. Argument t is the time that the connection was returned
+ // to the pool. If the function returns an error, then the connection is
+ // closed.
+ TestOnBorrow func(c Conn, t time.Time) error
+
+ // Maximum number of idle connections in the pool.
+ MaxIdle int
+
+ // Maximum number of connections allocated by the pool at a given time.
+ // When zero, there is no limit on the number of connections in the pool.
+ MaxActive int
+
+ // Close connections after remaining idle for this duration. If the value
+ // is zero, then idle connections are not closed. Applications should set
+ // the timeout to a value less than the server's timeout.
+ IdleTimeout time.Duration
+
+ // If Wait is true and the pool is at the MaxActive limit, then Get() waits
+ // for a connection to be returned to the pool before returning.
+ Wait bool
+
+ // Close connections older than this duration. If the value is zero, then
+ // the pool does not close connections based on age.
+ MaxConnLifetime time.Duration
+
+ chInitialized uint32 // set to 1 when field ch is initialized
+
+ mu sync.Mutex // mu protects the following fields
+ closed bool // set to true when the pool is closed.
+ active int // the number of open connections in the pool
+ ch chan struct{} // limits open connections when p.Wait is true
+ idle idleList // idle connections
+}
+
+// NewPool creates a new pool.
+//
+// Deprecated: Initialize the Pool directory as shown in the example.
+func NewPool(newFn func() (Conn, error), maxIdle int) *Pool {
+ return &Pool{Dial: newFn, MaxIdle: maxIdle}
+}
+
+// Get gets a connection. The application must close the returned connection.
+// This method always returns a valid connection so that applications can defer
+// error handling to the first use of the connection. If there is an error
+// getting an underlying connection, then the connection Err, Do, Send, Flush
+// and Receive methods return that error.
+func (p *Pool) Get() Conn {
+ pc, err := p.get(nil)
+ if err != nil {
+ return errorConn{err}
+ }
+ return &activeConn{p: p, pc: pc}
+}
+
+// PoolStats contains pool statistics.
+type PoolStats struct {
+ // ActiveCount is the number of connections in the pool. The count includes
+ // idle connections and connections in use.
+ ActiveCount int
+ // IdleCount is the number of idle connections in the pool.
+ IdleCount int
+}
+
+// Stats returns pool's statistics.
+func (p *Pool) Stats() PoolStats {
+ p.mu.Lock()
+ stats := PoolStats{
+ ActiveCount: p.active,
+ IdleCount: p.idle.count,
+ }
+ p.mu.Unlock()
+
+ return stats
+}
+
+// ActiveCount returns the number of connections in the pool. The count
+// includes idle connections and connections in use.
+func (p *Pool) ActiveCount() int {
+ p.mu.Lock()
+ active := p.active
+ p.mu.Unlock()
+ return active
+}
+
+// IdleCount returns the number of idle connections in the pool.
+func (p *Pool) IdleCount() int {
+ p.mu.Lock()
+ idle := p.idle.count
+ p.mu.Unlock()
+ return idle
+}
+
+// Close releases the resources used by the pool.
+func (p *Pool) Close() error {
+ p.mu.Lock()
+ if p.closed {
+ p.mu.Unlock()
+ return nil
+ }
+ p.closed = true
+ p.active -= p.idle.count
+ pc := p.idle.front
+ p.idle.count = 0
+ p.idle.front, p.idle.back = nil, nil
+ if p.ch != nil {
+ close(p.ch)
+ }
+ p.mu.Unlock()
+ for ; pc != nil; pc = pc.next {
+ pc.c.Close()
+ }
+ return nil
+}
+
+func (p *Pool) lazyInit() {
+ // Fast path.
+ if atomic.LoadUint32(&p.chInitialized) == 1 {
+ return
+ }
+ // Slow path.
+ p.mu.Lock()
+ if p.chInitialized == 0 {
+ p.ch = make(chan struct{}, p.MaxActive)
+ if p.closed {
+ close(p.ch)
+ } else {
+ for i := 0; i < p.MaxActive; i++ {
+ p.ch <- struct{}{}
+ }
+ }
+ atomic.StoreUint32(&p.chInitialized, 1)
+ }
+ p.mu.Unlock()
+}
+
+// get prunes stale connections and returns a connection from the idle list or
+// creates a new connection.
+func (p *Pool) get(ctx interface {
+ Done() <-chan struct{}
+ Err() error
+}) (*poolConn, error) {
+
+ // Handle limit for p.Wait == true.
+ if p.Wait && p.MaxActive > 0 {
+ p.lazyInit()
+ if ctx == nil {
+ <-p.ch
+ } else {
+ select {
+ case <-p.ch:
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ }
+ }
+ }
+
+ p.mu.Lock()
+
+ // Prune stale connections at the back of the idle list.
+ if p.IdleTimeout > 0 {
+ n := p.idle.count
+ for i := 0; i < n && p.idle.back != nil && p.idle.back.t.Add(p.IdleTimeout).Before(nowFunc()); i++ {
+ pc := p.idle.back
+ p.idle.popBack()
+ p.mu.Unlock()
+ pc.c.Close()
+ p.mu.Lock()
+ p.active--
+ }
+ }
+
+ // Get idle connection from the front of idle list.
+ for p.idle.front != nil {
+ pc := p.idle.front
+ p.idle.popFront()
+ p.mu.Unlock()
+ if (p.TestOnBorrow == nil || p.TestOnBorrow(pc.c, pc.t) == nil) &&
+ (p.MaxConnLifetime == 0 || nowFunc().Sub(pc.created) < p.MaxConnLifetime) {
+ return pc, nil
+ }
+ pc.c.Close()
+ p.mu.Lock()
+ p.active--
+ }
+
+ // Check for pool closed before dialing a new connection.
+ if p.closed {
+ p.mu.Unlock()
+ return nil, errors.New("redigo: get on closed pool")
+ }
+
+ // Handle limit for p.Wait == false.
+ if !p.Wait && p.MaxActive > 0 && p.active >= p.MaxActive {
+ p.mu.Unlock()
+ return nil, ErrPoolExhausted
+ }
+
+ p.active++
+ p.mu.Unlock()
+ c, err := p.Dial()
+ if err != nil {
+ c = nil
+ p.mu.Lock()
+ p.active--
+ if p.ch != nil && !p.closed {
+ p.ch <- struct{}{}
+ }
+ p.mu.Unlock()
+ }
+ return &poolConn{c: c, created: nowFunc()}, err
+}
+
+func (p *Pool) put(pc *poolConn, forceClose bool) error {
+ p.mu.Lock()
+ if !p.closed && !forceClose {
+ pc.t = nowFunc()
+ p.idle.pushFront(pc)
+ if p.idle.count > p.MaxIdle {
+ pc = p.idle.back
+ p.idle.popBack()
+ } else {
+ pc = nil
+ }
+ }
+
+ if pc != nil {
+ p.mu.Unlock()
+ pc.c.Close()
+ p.mu.Lock()
+ p.active--
+ }
+
+ if p.ch != nil && !p.closed {
+ p.ch <- struct{}{}
+ }
+ p.mu.Unlock()
+ return nil
+}
+
+type activeConn struct {
+ p *Pool
+ pc *poolConn
+ state int
+}
+
+var (
+ sentinel []byte
+ sentinelOnce sync.Once
+)
+
+func initSentinel() {
+ p := make([]byte, 64)
+ if _, err := rand.Read(p); err == nil {
+ sentinel = p
+ } else {
+ h := sha1.New()
+ io.WriteString(h, "Oops, rand failed. Use time instead.")
+ io.WriteString(h, strconv.FormatInt(time.Now().UnixNano(), 10))
+ sentinel = h.Sum(nil)
+ }
+}
+
+func (ac *activeConn) Close() error {
+ pc := ac.pc
+ if pc == nil {
+ return nil
+ }
+ ac.pc = nil
+
+ if ac.state&connectionMultiState != 0 {
+ pc.c.Send("DISCARD")
+ ac.state &^= (connectionMultiState | connectionWatchState)
+ } else if ac.state&connectionWatchState != 0 {
+ pc.c.Send("UNWATCH")
+ ac.state &^= connectionWatchState
+ }
+ if ac.state&connectionSubscribeState != 0 {
+ pc.c.Send("UNSUBSCRIBE")
+ pc.c.Send("PUNSUBSCRIBE")
+ // To detect the end of the message stream, ask the server to echo
+ // a sentinel value and read until we see that value.
+ sentinelOnce.Do(initSentinel)
+ pc.c.Send("ECHO", sentinel)
+ pc.c.Flush()
+ for {
+ p, err := pc.c.Receive()
+ if err != nil {
+ break
+ }
+ if p, ok := p.([]byte); ok && bytes.Equal(p, sentinel) {
+ ac.state &^= connectionSubscribeState
+ break
+ }
+ }
+ }
+ pc.c.Do("")
+ ac.p.put(pc, ac.state != 0 || pc.c.Err() != nil)
+ return nil
+}
+
+func (ac *activeConn) Err() error {
+ pc := ac.pc
+ if pc == nil {
+ return errConnClosed
+ }
+ return pc.c.Err()
+}
+
+func (ac *activeConn) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
+ pc := ac.pc
+ if pc == nil {
+ return nil, errConnClosed
+ }
+ ci := lookupCommandInfo(commandName)
+ ac.state = (ac.state | ci.Set) &^ ci.Clear
+ return pc.c.Do(commandName, args...)
+}
+
+func (ac *activeConn) DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (reply interface{}, err error) {
+ pc := ac.pc
+ if pc == nil {
+ return nil, errConnClosed
+ }
+ cwt, ok := pc.c.(ConnWithTimeout)
+ if !ok {
+ return nil, errTimeoutNotSupported
+ }
+ ci := lookupCommandInfo(commandName)
+ ac.state = (ac.state | ci.Set) &^ ci.Clear
+ return cwt.DoWithTimeout(timeout, commandName, args...)
+}
+
+func (ac *activeConn) Send(commandName string, args ...interface{}) error {
+ pc := ac.pc
+ if pc == nil {
+ return errConnClosed
+ }
+ ci := lookupCommandInfo(commandName)
+ ac.state = (ac.state | ci.Set) &^ ci.Clear
+ return pc.c.Send(commandName, args...)
+}
+
+func (ac *activeConn) Flush() error {
+ pc := ac.pc
+ if pc == nil {
+ return errConnClosed
+ }
+ return pc.c.Flush()
+}
+
+func (ac *activeConn) Receive() (reply interface{}, err error) {
+ pc := ac.pc
+ if pc == nil {
+ return nil, errConnClosed
+ }
+ return pc.c.Receive()
+}
+
+func (ac *activeConn) ReceiveWithTimeout(timeout time.Duration) (reply interface{}, err error) {
+ pc := ac.pc
+ if pc == nil {
+ return nil, errConnClosed
+ }
+ cwt, ok := pc.c.(ConnWithTimeout)
+ if !ok {
+ return nil, errTimeoutNotSupported
+ }
+ return cwt.ReceiveWithTimeout(timeout)
+}
+
+type errorConn struct{ err error }
+
+func (ec errorConn) Do(string, ...interface{}) (interface{}, error) { return nil, ec.err }
+func (ec errorConn) DoWithTimeout(time.Duration, string, ...interface{}) (interface{}, error) {
+ return nil, ec.err
+}
+func (ec errorConn) Send(string, ...interface{}) error { return ec.err }
+func (ec errorConn) Err() error { return ec.err }
+func (ec errorConn) Close() error { return nil }
+func (ec errorConn) Flush() error { return ec.err }
+func (ec errorConn) Receive() (interface{}, error) { return nil, ec.err }
+func (ec errorConn) ReceiveWithTimeout(time.Duration) (interface{}, error) { return nil, ec.err }
+
+type idleList struct {
+ count int
+ front, back *poolConn
+}
+
+type poolConn struct {
+ c Conn
+ t time.Time
+ created time.Time
+ next, prev *poolConn
+}
+
+func (l *idleList) pushFront(pc *poolConn) {
+ pc.next = l.front
+ pc.prev = nil
+ if l.count == 0 {
+ l.back = pc
+ } else {
+ l.front.prev = pc
+ }
+ l.front = pc
+ l.count++
+ return
+}
+
+func (l *idleList) popFront() {
+ pc := l.front
+ l.count--
+ if l.count == 0 {
+ l.front, l.back = nil, nil
+ } else {
+ pc.next.prev = nil
+ l.front = pc.next
+ }
+ pc.next, pc.prev = nil, nil
+}
+
+func (l *idleList) popBack() {
+ pc := l.back
+ l.count--
+ if l.count == 0 {
+ l.front, l.back = nil, nil
+ } else {
+ pc.prev.next = nil
+ l.back = pc.prev
+ }
+ pc.next, pc.prev = nil, nil
+}
diff --git a/vendor/github.com/gomodule/redigo/redis/pool17.go b/vendor/github.com/gomodule/redigo/redis/pool17.go
new file mode 100644
index 00000000..c1ea18ee
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/redis/pool17.go
@@ -0,0 +1,35 @@
+// Copyright 2018 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+// +build go1.7
+
+package redis
+
+import "context"
+
+// GetContext gets a connection using the provided context.
+//
+// The provided Context must be non-nil. If the context expires before the
+// connection is complete, an error is returned. Any expiration on the context
+// will not affect the returned connection.
+//
+// If the function completes without error, then the application must close the
+// returned connection.
+func (p *Pool) GetContext(ctx context.Context) (Conn, error) {
+ pc, err := p.get(ctx)
+ if err != nil {
+ return errorConn{err}, err
+ }
+ return &activeConn{p: p, pc: pc}, nil
+}
diff --git a/vendor/github.com/gomodule/redigo/redis/pubsub.go b/vendor/github.com/gomodule/redigo/redis/pubsub.go
new file mode 100644
index 00000000..2da60211
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/redis/pubsub.go
@@ -0,0 +1,148 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "errors"
+ "time"
+)
+
+// Subscription represents a subscribe or unsubscribe notification.
+type Subscription struct {
+ // Kind is "subscribe", "unsubscribe", "psubscribe" or "punsubscribe"
+ Kind string
+
+ // The channel that was changed.
+ Channel string
+
+ // The current number of subscriptions for connection.
+ Count int
+}
+
+// Message represents a message notification.
+type Message struct {
+ // The originating channel.
+ Channel string
+
+ // The matched pattern, if any
+ Pattern string
+
+ // The message data.
+ Data []byte
+}
+
+// Pong represents a pubsub pong notification.
+type Pong struct {
+ Data string
+}
+
+// PubSubConn wraps a Conn with convenience methods for subscribers.
+type PubSubConn struct {
+ Conn Conn
+}
+
+// Close closes the connection.
+func (c PubSubConn) Close() error {
+ return c.Conn.Close()
+}
+
+// Subscribe subscribes the connection to the specified channels.
+func (c PubSubConn) Subscribe(channel ...interface{}) error {
+ c.Conn.Send("SUBSCRIBE", channel...)
+ return c.Conn.Flush()
+}
+
+// PSubscribe subscribes the connection to the given patterns.
+func (c PubSubConn) PSubscribe(channel ...interface{}) error {
+ c.Conn.Send("PSUBSCRIBE", channel...)
+ return c.Conn.Flush()
+}
+
+// Unsubscribe unsubscribes the connection from the given channels, or from all
+// of them if none is given.
+func (c PubSubConn) Unsubscribe(channel ...interface{}) error {
+ c.Conn.Send("UNSUBSCRIBE", channel...)
+ return c.Conn.Flush()
+}
+
+// PUnsubscribe unsubscribes the connection from the given patterns, or from all
+// of them if none is given.
+func (c PubSubConn) PUnsubscribe(channel ...interface{}) error {
+ c.Conn.Send("PUNSUBSCRIBE", channel...)
+ return c.Conn.Flush()
+}
+
+// Ping sends a PING to the server with the specified data.
+//
+// The connection must be subscribed to at least one channel or pattern when
+// calling this method.
+func (c PubSubConn) Ping(data string) error {
+ c.Conn.Send("PING", data)
+ return c.Conn.Flush()
+}
+
+// Receive returns a pushed message as a Subscription, Message, Pong or error.
+// The return value is intended to be used directly in a type switch as
+// illustrated in the PubSubConn example.
+func (c PubSubConn) Receive() interface{} {
+ return c.receiveInternal(c.Conn.Receive())
+}
+
+// ReceiveWithTimeout is like Receive, but it allows the application to
+// override the connection's default timeout.
+func (c PubSubConn) ReceiveWithTimeout(timeout time.Duration) interface{} {
+ return c.receiveInternal(ReceiveWithTimeout(c.Conn, timeout))
+}
+
+func (c PubSubConn) receiveInternal(replyArg interface{}, errArg error) interface{} {
+ reply, err := Values(replyArg, errArg)
+ if err != nil {
+ return err
+ }
+
+ var kind string
+ reply, err = Scan(reply, &kind)
+ if err != nil {
+ return err
+ }
+
+ switch kind {
+ case "message":
+ var m Message
+ if _, err := Scan(reply, &m.Channel, &m.Data); err != nil {
+ return err
+ }
+ return m
+ case "pmessage":
+ var m Message
+ if _, err := Scan(reply, &m.Pattern, &m.Channel, &m.Data); err != nil {
+ return err
+ }
+ return m
+ case "subscribe", "psubscribe", "unsubscribe", "punsubscribe":
+ s := Subscription{Kind: kind}
+ if _, err := Scan(reply, &s.Channel, &s.Count); err != nil {
+ return err
+ }
+ return s
+ case "pong":
+ var p Pong
+ if _, err := Scan(reply, &p.Data); err != nil {
+ return err
+ }
+ return p
+ }
+ return errors.New("redigo: unknown pubsub notification")
+}
diff --git a/vendor/github.com/gomodule/redigo/redis/redis.go b/vendor/github.com/gomodule/redigo/redis/redis.go
new file mode 100644
index 00000000..141fa4a9
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/redis/redis.go
@@ -0,0 +1,117 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "errors"
+ "time"
+)
+
+// Error represents an error returned in a command reply.
+type Error string
+
+func (err Error) Error() string { return string(err) }
+
+// Conn represents a connection to a Redis server.
+type Conn interface {
+ // Close closes the connection.
+ Close() error
+
+ // Err returns a non-nil value when the connection is not usable.
+ Err() error
+
+ // Do sends a command to the server and returns the received reply.
+ Do(commandName string, args ...interface{}) (reply interface{}, err error)
+
+ // Send writes the command to the client's output buffer.
+ Send(commandName string, args ...interface{}) error
+
+ // Flush flushes the output buffer to the Redis server.
+ Flush() error
+
+ // Receive receives a single reply from the Redis server
+ Receive() (reply interface{}, err error)
+}
+
+// Argument is the interface implemented by an object which wants to control how
+// the object is converted to Redis bulk strings.
+type Argument interface {
+ // RedisArg returns a value to be encoded as a bulk string per the
+ // conversions listed in the section 'Executing Commands'.
+ // Implementations should typically return a []byte or string.
+ RedisArg() interface{}
+}
+
+// Scanner is implemented by an object which wants to control its value is
+// interpreted when read from Redis.
+type Scanner interface {
+ // RedisScan assigns a value from a Redis value. The argument src is one of
+ // the reply types listed in the section `Executing Commands`.
+ //
+ // An error should be returned if the value cannot be stored without
+ // loss of information.
+ RedisScan(src interface{}) error
+}
+
+// ConnWithTimeout is an optional interface that allows the caller to override
+// a connection's default read timeout. This interface is useful for executing
+// the BLPOP, BRPOP, BRPOPLPUSH, XREAD and other commands that block at the
+// server.
+//
+// A connection's default read timeout is set with the DialReadTimeout dial
+// option. Applications should rely on the default timeout for commands that do
+// not block at the server.
+//
+// All of the Conn implementations in this package satisfy the ConnWithTimeout
+// interface.
+//
+// Use the DoWithTimeout and ReceiveWithTimeout helper functions to simplify
+// use of this interface.
+type ConnWithTimeout interface {
+ Conn
+
+ // Do sends a command to the server and returns the received reply.
+ // The timeout overrides the read timeout set when dialing the
+ // connection.
+ DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (reply interface{}, err error)
+
+ // Receive receives a single reply from the Redis server. The timeout
+ // overrides the read timeout set when dialing the connection.
+ ReceiveWithTimeout(timeout time.Duration) (reply interface{}, err error)
+}
+
+var errTimeoutNotSupported = errors.New("redis: connection does not support ConnWithTimeout")
+
+// DoWithTimeout executes a Redis command with the specified read timeout. If
+// the connection does not satisfy the ConnWithTimeout interface, then an error
+// is returned.
+func DoWithTimeout(c Conn, timeout time.Duration, cmd string, args ...interface{}) (interface{}, error) {
+ cwt, ok := c.(ConnWithTimeout)
+ if !ok {
+ return nil, errTimeoutNotSupported
+ }
+ return cwt.DoWithTimeout(timeout, cmd, args...)
+}
+
+// ReceiveWithTimeout receives a reply with the specified read timeout. If the
+// connection does not satisfy the ConnWithTimeout interface, then an error is
+// returned.
+func ReceiveWithTimeout(c Conn, timeout time.Duration) (interface{}, error) {
+ cwt, ok := c.(ConnWithTimeout)
+ if !ok {
+ return nil, errTimeoutNotSupported
+ }
+ return cwt.ReceiveWithTimeout(timeout)
+}
diff --git a/vendor/github.com/gomodule/redigo/redis/reply.go b/vendor/github.com/gomodule/redigo/redis/reply.go
new file mode 100644
index 00000000..c2b3b2b6
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/redis/reply.go
@@ -0,0 +1,479 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+)
+
+// ErrNil indicates that a reply value is nil.
+var ErrNil = errors.New("redigo: nil returned")
+
+// Int is a helper that converts a command reply to an integer. If err is not
+// equal to nil, then Int returns 0, err. Otherwise, Int converts the
+// reply to an int as follows:
+//
+// Reply type Result
+// integer int(reply), nil
+// bulk string parsed reply, nil
+// nil 0, ErrNil
+// other 0, error
+func Int(reply interface{}, err error) (int, error) {
+ if err != nil {
+ return 0, err
+ }
+ switch reply := reply.(type) {
+ case int64:
+ x := int(reply)
+ if int64(x) != reply {
+ return 0, strconv.ErrRange
+ }
+ return x, nil
+ case []byte:
+ n, err := strconv.ParseInt(string(reply), 10, 0)
+ return int(n), err
+ case nil:
+ return 0, ErrNil
+ case Error:
+ return 0, reply
+ }
+ return 0, fmt.Errorf("redigo: unexpected type for Int, got type %T", reply)
+}
+
+// Int64 is a helper that converts a command reply to 64 bit integer. If err is
+// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
+// reply to an int64 as follows:
+//
+// Reply type Result
+// integer reply, nil
+// bulk string parsed reply, nil
+// nil 0, ErrNil
+// other 0, error
+func Int64(reply interface{}, err error) (int64, error) {
+ if err != nil {
+ return 0, err
+ }
+ switch reply := reply.(type) {
+ case int64:
+ return reply, nil
+ case []byte:
+ n, err := strconv.ParseInt(string(reply), 10, 64)
+ return n, err
+ case nil:
+ return 0, ErrNil
+ case Error:
+ return 0, reply
+ }
+ return 0, fmt.Errorf("redigo: unexpected type for Int64, got type %T", reply)
+}
+
+var errNegativeInt = errors.New("redigo: unexpected value for Uint64")
+
+// Uint64 is a helper that converts a command reply to 64 bit integer. If err is
+// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
+// reply to an int64 as follows:
+//
+// Reply type Result
+// integer reply, nil
+// bulk string parsed reply, nil
+// nil 0, ErrNil
+// other 0, error
+func Uint64(reply interface{}, err error) (uint64, error) {
+ if err != nil {
+ return 0, err
+ }
+ switch reply := reply.(type) {
+ case int64:
+ if reply < 0 {
+ return 0, errNegativeInt
+ }
+ return uint64(reply), nil
+ case []byte:
+ n, err := strconv.ParseUint(string(reply), 10, 64)
+ return n, err
+ case nil:
+ return 0, ErrNil
+ case Error:
+ return 0, reply
+ }
+ return 0, fmt.Errorf("redigo: unexpected type for Uint64, got type %T", reply)
+}
+
+// Float64 is a helper that converts a command reply to 64 bit float. If err is
+// not equal to nil, then Float64 returns 0, err. Otherwise, Float64 converts
+// the reply to an int as follows:
+//
+// Reply type Result
+// bulk string parsed reply, nil
+// nil 0, ErrNil
+// other 0, error
+func Float64(reply interface{}, err error) (float64, error) {
+ if err != nil {
+ return 0, err
+ }
+ switch reply := reply.(type) {
+ case []byte:
+ n, err := strconv.ParseFloat(string(reply), 64)
+ return n, err
+ case nil:
+ return 0, ErrNil
+ case Error:
+ return 0, reply
+ }
+ return 0, fmt.Errorf("redigo: unexpected type for Float64, got type %T", reply)
+}
+
+// String is a helper that converts a command reply to a string. If err is not
+// equal to nil, then String returns "", err. Otherwise String converts the
+// reply to a string as follows:
+//
+// Reply type Result
+// bulk string string(reply), nil
+// simple string reply, nil
+// nil "", ErrNil
+// other "", error
+func String(reply interface{}, err error) (string, error) {
+ if err != nil {
+ return "", err
+ }
+ switch reply := reply.(type) {
+ case []byte:
+ return string(reply), nil
+ case string:
+ return reply, nil
+ case nil:
+ return "", ErrNil
+ case Error:
+ return "", reply
+ }
+ return "", fmt.Errorf("redigo: unexpected type for String, got type %T", reply)
+}
+
+// Bytes is a helper that converts a command reply to a slice of bytes. If err
+// is not equal to nil, then Bytes returns nil, err. Otherwise Bytes converts
+// the reply to a slice of bytes as follows:
+//
+// Reply type Result
+// bulk string reply, nil
+// simple string []byte(reply), nil
+// nil nil, ErrNil
+// other nil, error
+func Bytes(reply interface{}, err error) ([]byte, error) {
+ if err != nil {
+ return nil, err
+ }
+ switch reply := reply.(type) {
+ case []byte:
+ return reply, nil
+ case string:
+ return []byte(reply), nil
+ case nil:
+ return nil, ErrNil
+ case Error:
+ return nil, reply
+ }
+ return nil, fmt.Errorf("redigo: unexpected type for Bytes, got type %T", reply)
+}
+
+// Bool is a helper that converts a command reply to a boolean. If err is not
+// equal to nil, then Bool returns false, err. Otherwise Bool converts the
+// reply to boolean as follows:
+//
+// Reply type Result
+// integer value != 0, nil
+// bulk string strconv.ParseBool(reply)
+// nil false, ErrNil
+// other false, error
+func Bool(reply interface{}, err error) (bool, error) {
+ if err != nil {
+ return false, err
+ }
+ switch reply := reply.(type) {
+ case int64:
+ return reply != 0, nil
+ case []byte:
+ return strconv.ParseBool(string(reply))
+ case nil:
+ return false, ErrNil
+ case Error:
+ return false, reply
+ }
+ return false, fmt.Errorf("redigo: unexpected type for Bool, got type %T", reply)
+}
+
+// MultiBulk is a helper that converts an array command reply to a []interface{}.
+//
+// Deprecated: Use Values instead.
+func MultiBulk(reply interface{}, err error) ([]interface{}, error) { return Values(reply, err) }
+
+// Values is a helper that converts an array command reply to a []interface{}.
+// If err is not equal to nil, then Values returns nil, err. Otherwise, Values
+// converts the reply as follows:
+//
+// Reply type Result
+// array reply, nil
+// nil nil, ErrNil
+// other nil, error
+func Values(reply interface{}, err error) ([]interface{}, error) {
+ if err != nil {
+ return nil, err
+ }
+ switch reply := reply.(type) {
+ case []interface{}:
+ return reply, nil
+ case nil:
+ return nil, ErrNil
+ case Error:
+ return nil, reply
+ }
+ return nil, fmt.Errorf("redigo: unexpected type for Values, got type %T", reply)
+}
+
+func sliceHelper(reply interface{}, err error, name string, makeSlice func(int), assign func(int, interface{}) error) error {
+ if err != nil {
+ return err
+ }
+ switch reply := reply.(type) {
+ case []interface{}:
+ makeSlice(len(reply))
+ for i := range reply {
+ if reply[i] == nil {
+ continue
+ }
+ if err := assign(i, reply[i]); err != nil {
+ return err
+ }
+ }
+ return nil
+ case nil:
+ return ErrNil
+ case Error:
+ return reply
+ }
+ return fmt.Errorf("redigo: unexpected type for %s, got type %T", name, reply)
+}
+
+// Float64s is a helper that converts an array command reply to a []float64. If
+// err is not equal to nil, then Float64s returns nil, err. Nil array items are
+// converted to 0 in the output slice. Floats64 returns an error if an array
+// item is not a bulk string or nil.
+func Float64s(reply interface{}, err error) ([]float64, error) {
+ var result []float64
+ err = sliceHelper(reply, err, "Float64s", func(n int) { result = make([]float64, n) }, func(i int, v interface{}) error {
+ p, ok := v.([]byte)
+ if !ok {
+ return fmt.Errorf("redigo: unexpected element type for Floats64, got type %T", v)
+ }
+ f, err := strconv.ParseFloat(string(p), 64)
+ result[i] = f
+ return err
+ })
+ return result, err
+}
+
+// Strings is a helper that converts an array command reply to a []string. If
+// err is not equal to nil, then Strings returns nil, err. Nil array items are
+// converted to "" in the output slice. Strings returns an error if an array
+// item is not a bulk string or nil.
+func Strings(reply interface{}, err error) ([]string, error) {
+ var result []string
+ err = sliceHelper(reply, err, "Strings", func(n int) { result = make([]string, n) }, func(i int, v interface{}) error {
+ switch v := v.(type) {
+ case string:
+ result[i] = v
+ return nil
+ case []byte:
+ result[i] = string(v)
+ return nil
+ default:
+ return fmt.Errorf("redigo: unexpected element type for Strings, got type %T", v)
+ }
+ })
+ return result, err
+}
+
+// ByteSlices is a helper that converts an array command reply to a [][]byte.
+// If err is not equal to nil, then ByteSlices returns nil, err. Nil array
+// items are stay nil. ByteSlices returns an error if an array item is not a
+// bulk string or nil.
+func ByteSlices(reply interface{}, err error) ([][]byte, error) {
+ var result [][]byte
+ err = sliceHelper(reply, err, "ByteSlices", func(n int) { result = make([][]byte, n) }, func(i int, v interface{}) error {
+ p, ok := v.([]byte)
+ if !ok {
+ return fmt.Errorf("redigo: unexpected element type for ByteSlices, got type %T", v)
+ }
+ result[i] = p
+ return nil
+ })
+ return result, err
+}
+
+// Int64s is a helper that converts an array command reply to a []int64.
+// If err is not equal to nil, then Int64s returns nil, err. Nil array
+// items are stay nil. Int64s returns an error if an array item is not a
+// bulk string or nil.
+func Int64s(reply interface{}, err error) ([]int64, error) {
+ var result []int64
+ err = sliceHelper(reply, err, "Int64s", func(n int) { result = make([]int64, n) }, func(i int, v interface{}) error {
+ switch v := v.(type) {
+ case int64:
+ result[i] = v
+ return nil
+ case []byte:
+ n, err := strconv.ParseInt(string(v), 10, 64)
+ result[i] = n
+ return err
+ default:
+ return fmt.Errorf("redigo: unexpected element type for Int64s, got type %T", v)
+ }
+ })
+ return result, err
+}
+
+// Ints is a helper that converts an array command reply to a []in.
+// If err is not equal to nil, then Ints returns nil, err. Nil array
+// items are stay nil. Ints returns an error if an array item is not a
+// bulk string or nil.
+func Ints(reply interface{}, err error) ([]int, error) {
+ var result []int
+ err = sliceHelper(reply, err, "Ints", func(n int) { result = make([]int, n) }, func(i int, v interface{}) error {
+ switch v := v.(type) {
+ case int64:
+ n := int(v)
+ if int64(n) != v {
+ return strconv.ErrRange
+ }
+ result[i] = n
+ return nil
+ case []byte:
+ n, err := strconv.Atoi(string(v))
+ result[i] = n
+ return err
+ default:
+ return fmt.Errorf("redigo: unexpected element type for Ints, got type %T", v)
+ }
+ })
+ return result, err
+}
+
+// StringMap is a helper that converts an array of strings (alternating key, value)
+// into a map[string]string. The HGETALL and CONFIG GET commands return replies in this format.
+// Requires an even number of values in result.
+func StringMap(result interface{}, err error) (map[string]string, error) {
+ values, err := Values(result, err)
+ if err != nil {
+ return nil, err
+ }
+ if len(values)%2 != 0 {
+ return nil, errors.New("redigo: StringMap expects even number of values result")
+ }
+ m := make(map[string]string, len(values)/2)
+ for i := 0; i < len(values); i += 2 {
+ key, okKey := values[i].([]byte)
+ value, okValue := values[i+1].([]byte)
+ if !okKey || !okValue {
+ return nil, errors.New("redigo: StringMap key not a bulk string value")
+ }
+ m[string(key)] = string(value)
+ }
+ return m, nil
+}
+
+// IntMap is a helper that converts an array of strings (alternating key, value)
+// into a map[string]int. The HGETALL commands return replies in this format.
+// Requires an even number of values in result.
+func IntMap(result interface{}, err error) (map[string]int, error) {
+ values, err := Values(result, err)
+ if err != nil {
+ return nil, err
+ }
+ if len(values)%2 != 0 {
+ return nil, errors.New("redigo: IntMap expects even number of values result")
+ }
+ m := make(map[string]int, len(values)/2)
+ for i := 0; i < len(values); i += 2 {
+ key, ok := values[i].([]byte)
+ if !ok {
+ return nil, errors.New("redigo: IntMap key not a bulk string value")
+ }
+ value, err := Int(values[i+1], nil)
+ if err != nil {
+ return nil, err
+ }
+ m[string(key)] = value
+ }
+ return m, nil
+}
+
+// Int64Map is a helper that converts an array of strings (alternating key, value)
+// into a map[string]int64. The HGETALL commands return replies in this format.
+// Requires an even number of values in result.
+func Int64Map(result interface{}, err error) (map[string]int64, error) {
+ values, err := Values(result, err)
+ if err != nil {
+ return nil, err
+ }
+ if len(values)%2 != 0 {
+ return nil, errors.New("redigo: Int64Map expects even number of values result")
+ }
+ m := make(map[string]int64, len(values)/2)
+ for i := 0; i < len(values); i += 2 {
+ key, ok := values[i].([]byte)
+ if !ok {
+ return nil, errors.New("redigo: Int64Map key not a bulk string value")
+ }
+ value, err := Int64(values[i+1], nil)
+ if err != nil {
+ return nil, err
+ }
+ m[string(key)] = value
+ }
+ return m, nil
+}
+
+// Positions is a helper that converts an array of positions (lat, long)
+// into a [][2]float64. The GEOPOS command returns replies in this format.
+func Positions(result interface{}, err error) ([]*[2]float64, error) {
+ values, err := Values(result, err)
+ if err != nil {
+ return nil, err
+ }
+ positions := make([]*[2]float64, len(values))
+ for i := range values {
+ if values[i] == nil {
+ continue
+ }
+ p, ok := values[i].([]interface{})
+ if !ok {
+ return nil, fmt.Errorf("redigo: unexpected element type for interface slice, got type %T", values[i])
+ }
+ if len(p) != 2 {
+ return nil, fmt.Errorf("redigo: unexpected number of values for a member position, got %d", len(p))
+ }
+ lat, err := Float64(p[0], nil)
+ if err != nil {
+ return nil, err
+ }
+ long, err := Float64(p[1], nil)
+ if err != nil {
+ return nil, err
+ }
+ positions[i] = &[2]float64{lat, long}
+ }
+ return positions, nil
+}
diff --git a/vendor/github.com/gomodule/redigo/redis/scan.go b/vendor/github.com/gomodule/redigo/redis/scan.go
new file mode 100644
index 00000000..ef9551bd
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/redis/scan.go
@@ -0,0 +1,585 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+func ensureLen(d reflect.Value, n int) {
+ if n > d.Cap() {
+ d.Set(reflect.MakeSlice(d.Type(), n, n))
+ } else {
+ d.SetLen(n)
+ }
+}
+
+func cannotConvert(d reflect.Value, s interface{}) error {
+ var sname string
+ switch s.(type) {
+ case string:
+ sname = "Redis simple string"
+ case Error:
+ sname = "Redis error"
+ case int64:
+ sname = "Redis integer"
+ case []byte:
+ sname = "Redis bulk string"
+ case []interface{}:
+ sname = "Redis array"
+ default:
+ sname = reflect.TypeOf(s).String()
+ }
+ return fmt.Errorf("cannot convert from %s to %s", sname, d.Type())
+}
+
+func convertAssignBulkString(d reflect.Value, s []byte) (err error) {
+ switch d.Type().Kind() {
+ case reflect.Float32, reflect.Float64:
+ var x float64
+ x, err = strconv.ParseFloat(string(s), d.Type().Bits())
+ d.SetFloat(x)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ var x int64
+ x, err = strconv.ParseInt(string(s), 10, d.Type().Bits())
+ d.SetInt(x)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ var x uint64
+ x, err = strconv.ParseUint(string(s), 10, d.Type().Bits())
+ d.SetUint(x)
+ case reflect.Bool:
+ var x bool
+ x, err = strconv.ParseBool(string(s))
+ d.SetBool(x)
+ case reflect.String:
+ d.SetString(string(s))
+ case reflect.Slice:
+ if d.Type().Elem().Kind() != reflect.Uint8 {
+ err = cannotConvert(d, s)
+ } else {
+ d.SetBytes(s)
+ }
+ default:
+ err = cannotConvert(d, s)
+ }
+ return
+}
+
+func convertAssignInt(d reflect.Value, s int64) (err error) {
+ switch d.Type().Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ d.SetInt(s)
+ if d.Int() != s {
+ err = strconv.ErrRange
+ d.SetInt(0)
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ if s < 0 {
+ err = strconv.ErrRange
+ } else {
+ x := uint64(s)
+ d.SetUint(x)
+ if d.Uint() != x {
+ err = strconv.ErrRange
+ d.SetUint(0)
+ }
+ }
+ case reflect.Bool:
+ d.SetBool(s != 0)
+ default:
+ err = cannotConvert(d, s)
+ }
+ return
+}
+
+func convertAssignValue(d reflect.Value, s interface{}) (err error) {
+ if d.Kind() != reflect.Ptr {
+ if d.CanAddr() {
+ d2 := d.Addr()
+ if d2.CanInterface() {
+ if scanner, ok := d2.Interface().(Scanner); ok {
+ return scanner.RedisScan(s)
+ }
+ }
+ }
+ } else if d.CanInterface() {
+ // Already a reflect.Ptr
+ if d.IsNil() {
+ d.Set(reflect.New(d.Type().Elem()))
+ }
+ if scanner, ok := d.Interface().(Scanner); ok {
+ return scanner.RedisScan(s)
+ }
+ }
+
+ switch s := s.(type) {
+ case []byte:
+ err = convertAssignBulkString(d, s)
+ case int64:
+ err = convertAssignInt(d, s)
+ default:
+ err = cannotConvert(d, s)
+ }
+ return err
+}
+
+func convertAssignArray(d reflect.Value, s []interface{}) error {
+ if d.Type().Kind() != reflect.Slice {
+ return cannotConvert(d, s)
+ }
+ ensureLen(d, len(s))
+ for i := 0; i < len(s); i++ {
+ if err := convertAssignValue(d.Index(i), s[i]); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func convertAssign(d interface{}, s interface{}) (err error) {
+ if scanner, ok := d.(Scanner); ok {
+ return scanner.RedisScan(s)
+ }
+
+ // Handle the most common destination types using type switches and
+ // fall back to reflection for all other types.
+ switch s := s.(type) {
+ case nil:
+ // ignore
+ case []byte:
+ switch d := d.(type) {
+ case *string:
+ *d = string(s)
+ case *int:
+ *d, err = strconv.Atoi(string(s))
+ case *bool:
+ *d, err = strconv.ParseBool(string(s))
+ case *[]byte:
+ *d = s
+ case *interface{}:
+ *d = s
+ case nil:
+ // skip value
+ default:
+ if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
+ err = cannotConvert(d, s)
+ } else {
+ err = convertAssignBulkString(d.Elem(), s)
+ }
+ }
+ case int64:
+ switch d := d.(type) {
+ case *int:
+ x := int(s)
+ if int64(x) != s {
+ err = strconv.ErrRange
+ x = 0
+ }
+ *d = x
+ case *bool:
+ *d = s != 0
+ case *interface{}:
+ *d = s
+ case nil:
+ // skip value
+ default:
+ if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
+ err = cannotConvert(d, s)
+ } else {
+ err = convertAssignInt(d.Elem(), s)
+ }
+ }
+ case string:
+ switch d := d.(type) {
+ case *string:
+ *d = s
+ case *interface{}:
+ *d = s
+ case nil:
+ // skip value
+ default:
+ err = cannotConvert(reflect.ValueOf(d), s)
+ }
+ case []interface{}:
+ switch d := d.(type) {
+ case *[]interface{}:
+ *d = s
+ case *interface{}:
+ *d = s
+ case nil:
+ // skip value
+ default:
+ if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
+ err = cannotConvert(d, s)
+ } else {
+ err = convertAssignArray(d.Elem(), s)
+ }
+ }
+ case Error:
+ err = s
+ default:
+ err = cannotConvert(reflect.ValueOf(d), s)
+ }
+ return
+}
+
+// Scan copies from src to the values pointed at by dest.
+//
+// Scan uses RedisScan if available otherwise:
+//
+// The values pointed at by dest must be an integer, float, boolean, string,
+// []byte, interface{} or slices of these types. Scan uses the standard strconv
+// package to convert bulk strings to numeric and boolean types.
+//
+// If a dest value is nil, then the corresponding src value is skipped.
+//
+// If a src element is nil, then the corresponding dest value is not modified.
+//
+// To enable easy use of Scan in a loop, Scan returns the slice of src
+// following the copied values.
+func Scan(src []interface{}, dest ...interface{}) ([]interface{}, error) {
+ if len(src) < len(dest) {
+ return nil, errors.New("redigo.Scan: array short")
+ }
+ var err error
+ for i, d := range dest {
+ err = convertAssign(d, src[i])
+ if err != nil {
+ err = fmt.Errorf("redigo.Scan: cannot assign to dest %d: %v", i, err)
+ break
+ }
+ }
+ return src[len(dest):], err
+}
+
+type fieldSpec struct {
+ name string
+ index []int
+ omitEmpty bool
+}
+
+type structSpec struct {
+ m map[string]*fieldSpec
+ l []*fieldSpec
+}
+
+func (ss *structSpec) fieldSpec(name []byte) *fieldSpec {
+ return ss.m[string(name)]
+}
+
+func compileStructSpec(t reflect.Type, depth map[string]int, index []int, ss *structSpec) {
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ switch {
+ case f.PkgPath != "" && !f.Anonymous:
+ // Ignore unexported fields.
+ case f.Anonymous:
+ // TODO: Handle pointers. Requires change to decoder and
+ // protection against infinite recursion.
+ if f.Type.Kind() == reflect.Struct {
+ compileStructSpec(f.Type, depth, append(index, i), ss)
+ }
+ default:
+ fs := &fieldSpec{name: f.Name}
+ tag := f.Tag.Get("redis")
+ p := strings.Split(tag, ",")
+ if len(p) > 0 {
+ if p[0] == "-" {
+ continue
+ }
+ if len(p[0]) > 0 {
+ fs.name = p[0]
+ }
+ for _, s := range p[1:] {
+ switch s {
+ case "omitempty":
+ fs.omitEmpty = true
+ default:
+ panic(fmt.Errorf("redigo: unknown field tag %s for type %s", s, t.Name()))
+ }
+ }
+ }
+ d, found := depth[fs.name]
+ if !found {
+ d = 1 << 30
+ }
+ switch {
+ case len(index) == d:
+ // At same depth, remove from result.
+ delete(ss.m, fs.name)
+ j := 0
+ for i := 0; i < len(ss.l); i++ {
+ if fs.name != ss.l[i].name {
+ ss.l[j] = ss.l[i]
+ j += 1
+ }
+ }
+ ss.l = ss.l[:j]
+ case len(index) < d:
+ fs.index = make([]int, len(index)+1)
+ copy(fs.index, index)
+ fs.index[len(index)] = i
+ depth[fs.name] = len(index)
+ ss.m[fs.name] = fs
+ ss.l = append(ss.l, fs)
+ }
+ }
+ }
+}
+
+var (
+ structSpecMutex sync.RWMutex
+ structSpecCache = make(map[reflect.Type]*structSpec)
+ defaultFieldSpec = &fieldSpec{}
+)
+
+func structSpecForType(t reflect.Type) *structSpec {
+
+ structSpecMutex.RLock()
+ ss, found := structSpecCache[t]
+ structSpecMutex.RUnlock()
+ if found {
+ return ss
+ }
+
+ structSpecMutex.Lock()
+ defer structSpecMutex.Unlock()
+ ss, found = structSpecCache[t]
+ if found {
+ return ss
+ }
+
+ ss = &structSpec{m: make(map[string]*fieldSpec)}
+ compileStructSpec(t, make(map[string]int), nil, ss)
+ structSpecCache[t] = ss
+ return ss
+}
+
+var errScanStructValue = errors.New("redigo.ScanStruct: value must be non-nil pointer to a struct")
+
+// ScanStruct scans alternating names and values from src to a struct. The
+// HGETALL and CONFIG GET commands return replies in this format.
+//
+// ScanStruct uses exported field names to match values in the response. Use
+// 'redis' field tag to override the name:
+//
+// Field int `redis:"myName"`
+//
+// Fields with the tag redis:"-" are ignored.
+//
+// Each field uses RedisScan if available otherwise:
+// Integer, float, boolean, string and []byte fields are supported. Scan uses the
+// standard strconv package to convert bulk string values to numeric and
+// boolean types.
+//
+// If a src element is nil, then the corresponding field is not modified.
+func ScanStruct(src []interface{}, dest interface{}) error {
+ d := reflect.ValueOf(dest)
+ if d.Kind() != reflect.Ptr || d.IsNil() {
+ return errScanStructValue
+ }
+ d = d.Elem()
+ if d.Kind() != reflect.Struct {
+ return errScanStructValue
+ }
+ ss := structSpecForType(d.Type())
+
+ if len(src)%2 != 0 {
+ return errors.New("redigo.ScanStruct: number of values not a multiple of 2")
+ }
+
+ for i := 0; i < len(src); i += 2 {
+ s := src[i+1]
+ if s == nil {
+ continue
+ }
+ name, ok := src[i].([]byte)
+ if !ok {
+ return fmt.Errorf("redigo.ScanStruct: key %d not a bulk string value", i)
+ }
+ fs := ss.fieldSpec(name)
+ if fs == nil {
+ continue
+ }
+ if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
+ return fmt.Errorf("redigo.ScanStruct: cannot assign field %s: %v", fs.name, err)
+ }
+ }
+ return nil
+}
+
+var (
+ errScanSliceValue = errors.New("redigo.ScanSlice: dest must be non-nil pointer to a struct")
+)
+
+// ScanSlice scans src to the slice pointed to by dest. The elements the dest
+// slice must be integer, float, boolean, string, struct or pointer to struct
+// values.
+//
+// Struct fields must be integer, float, boolean or string values. All struct
+// fields are used unless a subset is specified using fieldNames.
+func ScanSlice(src []interface{}, dest interface{}, fieldNames ...string) error {
+ d := reflect.ValueOf(dest)
+ if d.Kind() != reflect.Ptr || d.IsNil() {
+ return errScanSliceValue
+ }
+ d = d.Elem()
+ if d.Kind() != reflect.Slice {
+ return errScanSliceValue
+ }
+
+ isPtr := false
+ t := d.Type().Elem()
+ if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
+ isPtr = true
+ t = t.Elem()
+ }
+
+ if t.Kind() != reflect.Struct {
+ ensureLen(d, len(src))
+ for i, s := range src {
+ if s == nil {
+ continue
+ }
+ if err := convertAssignValue(d.Index(i), s); err != nil {
+ return fmt.Errorf("redigo.ScanSlice: cannot assign element %d: %v", i, err)
+ }
+ }
+ return nil
+ }
+
+ ss := structSpecForType(t)
+ fss := ss.l
+ if len(fieldNames) > 0 {
+ fss = make([]*fieldSpec, len(fieldNames))
+ for i, name := range fieldNames {
+ fss[i] = ss.m[name]
+ if fss[i] == nil {
+ return fmt.Errorf("redigo.ScanSlice: ScanSlice bad field name %s", name)
+ }
+ }
+ }
+
+ if len(fss) == 0 {
+ return errors.New("redigo.ScanSlice: no struct fields")
+ }
+
+ n := len(src) / len(fss)
+ if n*len(fss) != len(src) {
+ return errors.New("redigo.ScanSlice: length not a multiple of struct field count")
+ }
+
+ ensureLen(d, n)
+ for i := 0; i < n; i++ {
+ d := d.Index(i)
+ if isPtr {
+ if d.IsNil() {
+ d.Set(reflect.New(t))
+ }
+ d = d.Elem()
+ }
+ for j, fs := range fss {
+ s := src[i*len(fss)+j]
+ if s == nil {
+ continue
+ }
+ if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
+ return fmt.Errorf("redigo.ScanSlice: cannot assign element %d to field %s: %v", i*len(fss)+j, fs.name, err)
+ }
+ }
+ }
+ return nil
+}
+
+// Args is a helper for constructing command arguments from structured values.
+type Args []interface{}
+
+// Add returns the result of appending value to args.
+func (args Args) Add(value ...interface{}) Args {
+ return append(args, value...)
+}
+
+// AddFlat returns the result of appending the flattened value of v to args.
+//
+// Maps are flattened by appending the alternating keys and map values to args.
+//
+// Slices are flattened by appending the slice elements to args.
+//
+// Structs are flattened by appending the alternating names and values of
+// exported fields to args. If v is a nil struct pointer, then nothing is
+// appended. The 'redis' field tag overrides struct field names. See ScanStruct
+// for more information on the use of the 'redis' field tag.
+//
+// Other types are appended to args as is.
+func (args Args) AddFlat(v interface{}) Args {
+ rv := reflect.ValueOf(v)
+ switch rv.Kind() {
+ case reflect.Struct:
+ args = flattenStruct(args, rv)
+ case reflect.Slice:
+ for i := 0; i < rv.Len(); i++ {
+ args = append(args, rv.Index(i).Interface())
+ }
+ case reflect.Map:
+ for _, k := range rv.MapKeys() {
+ args = append(args, k.Interface(), rv.MapIndex(k).Interface())
+ }
+ case reflect.Ptr:
+ if rv.Type().Elem().Kind() == reflect.Struct {
+ if !rv.IsNil() {
+ args = flattenStruct(args, rv.Elem())
+ }
+ } else {
+ args = append(args, v)
+ }
+ default:
+ args = append(args, v)
+ }
+ return args
+}
+
+func flattenStruct(args Args, v reflect.Value) Args {
+ ss := structSpecForType(v.Type())
+ for _, fs := range ss.l {
+ fv := v.FieldByIndex(fs.index)
+ if fs.omitEmpty {
+ var empty = false
+ switch fv.Kind() {
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+ empty = fv.Len() == 0
+ case reflect.Bool:
+ empty = !fv.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ empty = fv.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ empty = fv.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ empty = fv.Float() == 0
+ case reflect.Interface, reflect.Ptr:
+ empty = fv.IsNil()
+ }
+ if empty {
+ continue
+ }
+ }
+ args = append(args, fs.name, fv.Interface())
+ }
+ return args
+}
diff --git a/vendor/github.com/gomodule/redigo/redis/script.go b/vendor/github.com/gomodule/redigo/redis/script.go
new file mode 100644
index 00000000..0ef1c821
--- /dev/null
+++ b/vendor/github.com/gomodule/redigo/redis/script.go
@@ -0,0 +1,91 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "crypto/sha1"
+ "encoding/hex"
+ "io"
+ "strings"
+)
+
+// Script encapsulates the source, hash and key count for a Lua script. See
+// http://redis.io/commands/eval for information on scripts in Redis.
+type Script struct {
+ keyCount int
+ src string
+ hash string
+}
+
+// NewScript returns a new script object. If keyCount is greater than or equal
+// to zero, then the count is automatically inserted in the EVAL command
+// argument list. If keyCount is less than zero, then the application supplies
+// the count as the first value in the keysAndArgs argument to the Do, Send and
+// SendHash methods.
+func NewScript(keyCount int, src string) *Script {
+ h := sha1.New()
+ io.WriteString(h, src)
+ return &Script{keyCount, src, hex.EncodeToString(h.Sum(nil))}
+}
+
+func (s *Script) args(spec string, keysAndArgs []interface{}) []interface{} {
+ var args []interface{}
+ if s.keyCount < 0 {
+ args = make([]interface{}, 1+len(keysAndArgs))
+ args[0] = spec
+ copy(args[1:], keysAndArgs)
+ } else {
+ args = make([]interface{}, 2+len(keysAndArgs))
+ args[0] = spec
+ args[1] = s.keyCount
+ copy(args[2:], keysAndArgs)
+ }
+ return args
+}
+
+// Hash returns the script hash.
+func (s *Script) Hash() string {
+ return s.hash
+}
+
+// Do evaluates the script. Under the covers, Do optimistically evaluates the
+// script using the EVALSHA command. If the command fails because the script is
+// not loaded, then Do evaluates the script using the EVAL command (thus
+// causing the script to load).
+func (s *Script) Do(c Conn, keysAndArgs ...interface{}) (interface{}, error) {
+ v, err := c.Do("EVALSHA", s.args(s.hash, keysAndArgs)...)
+ if e, ok := err.(Error); ok && strings.HasPrefix(string(e), "NOSCRIPT ") {
+ v, err = c.Do("EVAL", s.args(s.src, keysAndArgs)...)
+ }
+ return v, err
+}
+
+// SendHash evaluates the script without waiting for the reply. The script is
+// evaluated with the EVALSHA command. The application must ensure that the
+// script is loaded by a previous call to Send, Do or Load methods.
+func (s *Script) SendHash(c Conn, keysAndArgs ...interface{}) error {
+ return c.Send("EVALSHA", s.args(s.hash, keysAndArgs)...)
+}
+
+// Send evaluates the script without waiting for the reply.
+func (s *Script) Send(c Conn, keysAndArgs ...interface{}) error {
+ return c.Send("EVAL", s.args(s.src, keysAndArgs)...)
+}
+
+// Load loads the script without evaluating it.
+func (s *Script) Load(c Conn) error {
+ _, err := c.Do("SCRIPT", "LOAD", s.src)
+ return err
+}
diff --git a/vendor/github.com/jmespath/go-jmespath/.gitignore b/vendor/github.com/jmespath/go-jmespath/.gitignore
new file mode 100644
index 00000000..5091fb07
--- /dev/null
+++ b/vendor/github.com/jmespath/go-jmespath/.gitignore
@@ -0,0 +1,4 @@
+/jpgo
+jmespath-fuzz.zip
+cpu.out
+go-jmespath.test
diff --git a/vendor/github.com/jmespath/go-jmespath/.travis.yml b/vendor/github.com/jmespath/go-jmespath/.travis.yml
new file mode 100644
index 00000000..1f980775
--- /dev/null
+++ b/vendor/github.com/jmespath/go-jmespath/.travis.yml
@@ -0,0 +1,9 @@
+language: go
+
+sudo: false
+
+go:
+ - 1.4
+
+install: go get -v -t ./...
+script: make test
diff --git a/vendor/github.com/jmespath/go-jmespath/LICENSE b/vendor/github.com/jmespath/go-jmespath/LICENSE
new file mode 100644
index 00000000..b03310a9
--- /dev/null
+++ b/vendor/github.com/jmespath/go-jmespath/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2015 James Saryerwinnie
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vendor/github.com/jmespath/go-jmespath/Makefile b/vendor/github.com/jmespath/go-jmespath/Makefile
new file mode 100644
index 00000000..a828d284
--- /dev/null
+++ b/vendor/github.com/jmespath/go-jmespath/Makefile
@@ -0,0 +1,44 @@
+
+CMD = jpgo
+
+help:
+ @echo "Please use \`make ' where is one of"
+ @echo " test to run all the tests"
+ @echo " build to build the library and jp executable"
+ @echo " generate to run codegen"
+
+
+generate:
+ go generate ./...
+
+build:
+ rm -f $(CMD)
+ go build ./...
+ rm -f cmd/$(CMD)/$(CMD) && cd cmd/$(CMD)/ && go build ./...
+ mv cmd/$(CMD)/$(CMD) .
+
+test:
+ go test -v ./...
+
+check:
+ go vet ./...
+ @echo "golint ./..."
+ @lint=`golint ./...`; \
+ lint=`echo "$$lint" | grep -v "astnodetype_string.go" | grep -v "toktype_string.go"`; \
+ echo "$$lint"; \
+ if [ "$$lint" != "" ]; then exit 1; fi
+
+htmlc:
+ go test -coverprofile="/tmp/jpcov" && go tool cover -html="/tmp/jpcov" && unlink /tmp/jpcov
+
+buildfuzz:
+ go-fuzz-build github.com/jmespath/go-jmespath/fuzz
+
+fuzz: buildfuzz
+ go-fuzz -bin=./jmespath-fuzz.zip -workdir=fuzz/testdata
+
+bench:
+ go test -bench . -cpuprofile cpu.out
+
+pprof-cpu:
+ go tool pprof ./go-jmespath.test ./cpu.out
diff --git a/vendor/github.com/jmespath/go-jmespath/README.md b/vendor/github.com/jmespath/go-jmespath/README.md
new file mode 100644
index 00000000..187ef676
--- /dev/null
+++ b/vendor/github.com/jmespath/go-jmespath/README.md
@@ -0,0 +1,7 @@
+# go-jmespath - A JMESPath implementation in Go
+
+[](https://travis-ci.org/jmespath/go-jmespath)
+
+
+
+See http://jmespath.org for more info.
diff --git a/vendor/github.com/jmespath/go-jmespath/api.go b/vendor/github.com/jmespath/go-jmespath/api.go
new file mode 100644
index 00000000..8e26ffee
--- /dev/null
+++ b/vendor/github.com/jmespath/go-jmespath/api.go
@@ -0,0 +1,49 @@
+package jmespath
+
+import "strconv"
+
+// JMESPath is the epresentation of a compiled JMES path query. A JMESPath is
+// safe for concurrent use by multiple goroutines.
+type JMESPath struct {
+ ast ASTNode
+ intr *treeInterpreter
+}
+
+// Compile parses a JMESPath expression and returns, if successful, a JMESPath
+// object that can be used to match against data.
+func Compile(expression string) (*JMESPath, error) {
+ parser := NewParser()
+ ast, err := parser.Parse(expression)
+ if err != nil {
+ return nil, err
+ }
+ jmespath := &JMESPath{ast: ast, intr: newInterpreter()}
+ return jmespath, nil
+}
+
+// MustCompile is like Compile but panics if the expression cannot be parsed.
+// It simplifies safe initialization of global variables holding compiled
+// JMESPaths.
+func MustCompile(expression string) *JMESPath {
+ jmespath, err := Compile(expression)
+ if err != nil {
+ panic(`jmespath: Compile(` + strconv.Quote(expression) + `): ` + err.Error())
+ }
+ return jmespath
+}
+
+// Search evaluates a JMESPath expression against input data and returns the result.
+func (jp *JMESPath) Search(data interface{}) (interface{}, error) {
+ return jp.intr.Execute(jp.ast, data)
+}
+
+// Search evaluates a JMESPath expression against input data and returns the result.
+func Search(expression string, data interface{}) (interface{}, error) {
+ intr := newInterpreter()
+ parser := NewParser()
+ ast, err := parser.Parse(expression)
+ if err != nil {
+ return nil, err
+ }
+ return intr.Execute(ast, data)
+}
diff --git a/vendor/github.com/jmespath/go-jmespath/astnodetype_string.go b/vendor/github.com/jmespath/go-jmespath/astnodetype_string.go
new file mode 100644
index 00000000..1cd2d239
--- /dev/null
+++ b/vendor/github.com/jmespath/go-jmespath/astnodetype_string.go
@@ -0,0 +1,16 @@
+// generated by stringer -type astNodeType; DO NOT EDIT
+
+package jmespath
+
+import "fmt"
+
+const _astNodeType_name = "ASTEmptyASTComparatorASTCurrentNodeASTExpRefASTFunctionExpressionASTFieldASTFilterProjectionASTFlattenASTIdentityASTIndexASTIndexExpressionASTKeyValPairASTLiteralASTMultiSelectHashASTMultiSelectListASTOrExpressionASTAndExpressionASTNotExpressionASTPipeASTProjectionASTSubexpressionASTSliceASTValueProjection"
+
+var _astNodeType_index = [...]uint16{0, 8, 21, 35, 44, 65, 73, 92, 102, 113, 121, 139, 152, 162, 180, 198, 213, 229, 245, 252, 265, 281, 289, 307}
+
+func (i astNodeType) String() string {
+ if i < 0 || i >= astNodeType(len(_astNodeType_index)-1) {
+ return fmt.Sprintf("astNodeType(%d)", i)
+ }
+ return _astNodeType_name[_astNodeType_index[i]:_astNodeType_index[i+1]]
+}
diff --git a/vendor/github.com/jmespath/go-jmespath/functions.go b/vendor/github.com/jmespath/go-jmespath/functions.go
new file mode 100644
index 00000000..9b7cd89b
--- /dev/null
+++ b/vendor/github.com/jmespath/go-jmespath/functions.go
@@ -0,0 +1,842 @@
+package jmespath
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "unicode/utf8"
+)
+
+type jpFunction func(arguments []interface{}) (interface{}, error)
+
+type jpType string
+
+const (
+ jpUnknown jpType = "unknown"
+ jpNumber jpType = "number"
+ jpString jpType = "string"
+ jpArray jpType = "array"
+ jpObject jpType = "object"
+ jpArrayNumber jpType = "array[number]"
+ jpArrayString jpType = "array[string]"
+ jpExpref jpType = "expref"
+ jpAny jpType = "any"
+)
+
+type functionEntry struct {
+ name string
+ arguments []argSpec
+ handler jpFunction
+ hasExpRef bool
+}
+
+type argSpec struct {
+ types []jpType
+ variadic bool
+}
+
+type byExprString struct {
+ intr *treeInterpreter
+ node ASTNode
+ items []interface{}
+ hasError bool
+}
+
+func (a *byExprString) Len() int {
+ return len(a.items)
+}
+func (a *byExprString) Swap(i, j int) {
+ a.items[i], a.items[j] = a.items[j], a.items[i]
+}
+func (a *byExprString) Less(i, j int) bool {
+ first, err := a.intr.Execute(a.node, a.items[i])
+ if err != nil {
+ a.hasError = true
+ // Return a dummy value.
+ return true
+ }
+ ith, ok := first.(string)
+ if !ok {
+ a.hasError = true
+ return true
+ }
+ second, err := a.intr.Execute(a.node, a.items[j])
+ if err != nil {
+ a.hasError = true
+ // Return a dummy value.
+ return true
+ }
+ jth, ok := second.(string)
+ if !ok {
+ a.hasError = true
+ return true
+ }
+ return ith < jth
+}
+
+type byExprFloat struct {
+ intr *treeInterpreter
+ node ASTNode
+ items []interface{}
+ hasError bool
+}
+
+func (a *byExprFloat) Len() int {
+ return len(a.items)
+}
+func (a *byExprFloat) Swap(i, j int) {
+ a.items[i], a.items[j] = a.items[j], a.items[i]
+}
+func (a *byExprFloat) Less(i, j int) bool {
+ first, err := a.intr.Execute(a.node, a.items[i])
+ if err != nil {
+ a.hasError = true
+ // Return a dummy value.
+ return true
+ }
+ ith, ok := first.(float64)
+ if !ok {
+ a.hasError = true
+ return true
+ }
+ second, err := a.intr.Execute(a.node, a.items[j])
+ if err != nil {
+ a.hasError = true
+ // Return a dummy value.
+ return true
+ }
+ jth, ok := second.(float64)
+ if !ok {
+ a.hasError = true
+ return true
+ }
+ return ith < jth
+}
+
+type functionCaller struct {
+ functionTable map[string]functionEntry
+}
+
+func newFunctionCaller() *functionCaller {
+ caller := &functionCaller{}
+ caller.functionTable = map[string]functionEntry{
+ "length": {
+ name: "length",
+ arguments: []argSpec{
+ {types: []jpType{jpString, jpArray, jpObject}},
+ },
+ handler: jpfLength,
+ },
+ "starts_with": {
+ name: "starts_with",
+ arguments: []argSpec{
+ {types: []jpType{jpString}},
+ {types: []jpType{jpString}},
+ },
+ handler: jpfStartsWith,
+ },
+ "abs": {
+ name: "abs",
+ arguments: []argSpec{
+ {types: []jpType{jpNumber}},
+ },
+ handler: jpfAbs,
+ },
+ "avg": {
+ name: "avg",
+ arguments: []argSpec{
+ {types: []jpType{jpArrayNumber}},
+ },
+ handler: jpfAvg,
+ },
+ "ceil": {
+ name: "ceil",
+ arguments: []argSpec{
+ {types: []jpType{jpNumber}},
+ },
+ handler: jpfCeil,
+ },
+ "contains": {
+ name: "contains",
+ arguments: []argSpec{
+ {types: []jpType{jpArray, jpString}},
+ {types: []jpType{jpAny}},
+ },
+ handler: jpfContains,
+ },
+ "ends_with": {
+ name: "ends_with",
+ arguments: []argSpec{
+ {types: []jpType{jpString}},
+ {types: []jpType{jpString}},
+ },
+ handler: jpfEndsWith,
+ },
+ "floor": {
+ name: "floor",
+ arguments: []argSpec{
+ {types: []jpType{jpNumber}},
+ },
+ handler: jpfFloor,
+ },
+ "map": {
+ name: "amp",
+ arguments: []argSpec{
+ {types: []jpType{jpExpref}},
+ {types: []jpType{jpArray}},
+ },
+ handler: jpfMap,
+ hasExpRef: true,
+ },
+ "max": {
+ name: "max",
+ arguments: []argSpec{
+ {types: []jpType{jpArrayNumber, jpArrayString}},
+ },
+ handler: jpfMax,
+ },
+ "merge": {
+ name: "merge",
+ arguments: []argSpec{
+ {types: []jpType{jpObject}, variadic: true},
+ },
+ handler: jpfMerge,
+ },
+ "max_by": {
+ name: "max_by",
+ arguments: []argSpec{
+ {types: []jpType{jpArray}},
+ {types: []jpType{jpExpref}},
+ },
+ handler: jpfMaxBy,
+ hasExpRef: true,
+ },
+ "sum": {
+ name: "sum",
+ arguments: []argSpec{
+ {types: []jpType{jpArrayNumber}},
+ },
+ handler: jpfSum,
+ },
+ "min": {
+ name: "min",
+ arguments: []argSpec{
+ {types: []jpType{jpArrayNumber, jpArrayString}},
+ },
+ handler: jpfMin,
+ },
+ "min_by": {
+ name: "min_by",
+ arguments: []argSpec{
+ {types: []jpType{jpArray}},
+ {types: []jpType{jpExpref}},
+ },
+ handler: jpfMinBy,
+ hasExpRef: true,
+ },
+ "type": {
+ name: "type",
+ arguments: []argSpec{
+ {types: []jpType{jpAny}},
+ },
+ handler: jpfType,
+ },
+ "keys": {
+ name: "keys",
+ arguments: []argSpec{
+ {types: []jpType{jpObject}},
+ },
+ handler: jpfKeys,
+ },
+ "values": {
+ name: "values",
+ arguments: []argSpec{
+ {types: []jpType{jpObject}},
+ },
+ handler: jpfValues,
+ },
+ "sort": {
+ name: "sort",
+ arguments: []argSpec{
+ {types: []jpType{jpArrayString, jpArrayNumber}},
+ },
+ handler: jpfSort,
+ },
+ "sort_by": {
+ name: "sort_by",
+ arguments: []argSpec{
+ {types: []jpType{jpArray}},
+ {types: []jpType{jpExpref}},
+ },
+ handler: jpfSortBy,
+ hasExpRef: true,
+ },
+ "join": {
+ name: "join",
+ arguments: []argSpec{
+ {types: []jpType{jpString}},
+ {types: []jpType{jpArrayString}},
+ },
+ handler: jpfJoin,
+ },
+ "reverse": {
+ name: "reverse",
+ arguments: []argSpec{
+ {types: []jpType{jpArray, jpString}},
+ },
+ handler: jpfReverse,
+ },
+ "to_array": {
+ name: "to_array",
+ arguments: []argSpec{
+ {types: []jpType{jpAny}},
+ },
+ handler: jpfToArray,
+ },
+ "to_string": {
+ name: "to_string",
+ arguments: []argSpec{
+ {types: []jpType{jpAny}},
+ },
+ handler: jpfToString,
+ },
+ "to_number": {
+ name: "to_number",
+ arguments: []argSpec{
+ {types: []jpType{jpAny}},
+ },
+ handler: jpfToNumber,
+ },
+ "not_null": {
+ name: "not_null",
+ arguments: []argSpec{
+ {types: []jpType{jpAny}, variadic: true},
+ },
+ handler: jpfNotNull,
+ },
+ }
+ return caller
+}
+
+func (e *functionEntry) resolveArgs(arguments []interface{}) ([]interface{}, error) {
+ if len(e.arguments) == 0 {
+ return arguments, nil
+ }
+ if !e.arguments[len(e.arguments)-1].variadic {
+ if len(e.arguments) != len(arguments) {
+ return nil, errors.New("incorrect number of args")
+ }
+ for i, spec := range e.arguments {
+ userArg := arguments[i]
+ err := spec.typeCheck(userArg)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return arguments, nil
+ }
+ if len(arguments) < len(e.arguments) {
+ return nil, errors.New("Invalid arity.")
+ }
+ return arguments, nil
+}
+
+func (a *argSpec) typeCheck(arg interface{}) error {
+ for _, t := range a.types {
+ switch t {
+ case jpNumber:
+ if _, ok := arg.(float64); ok {
+ return nil
+ }
+ case jpString:
+ if _, ok := arg.(string); ok {
+ return nil
+ }
+ case jpArray:
+ if isSliceType(arg) {
+ return nil
+ }
+ case jpObject:
+ if _, ok := arg.(map[string]interface{}); ok {
+ return nil
+ }
+ case jpArrayNumber:
+ if _, ok := toArrayNum(arg); ok {
+ return nil
+ }
+ case jpArrayString:
+ if _, ok := toArrayStr(arg); ok {
+ return nil
+ }
+ case jpAny:
+ return nil
+ case jpExpref:
+ if _, ok := arg.(expRef); ok {
+ return nil
+ }
+ }
+ }
+ return fmt.Errorf("Invalid type for: %v, expected: %#v", arg, a.types)
+}
+
+func (f *functionCaller) CallFunction(name string, arguments []interface{}, intr *treeInterpreter) (interface{}, error) {
+ entry, ok := f.functionTable[name]
+ if !ok {
+ return nil, errors.New("unknown function: " + name)
+ }
+ resolvedArgs, err := entry.resolveArgs(arguments)
+ if err != nil {
+ return nil, err
+ }
+ if entry.hasExpRef {
+ var extra []interface{}
+ extra = append(extra, intr)
+ resolvedArgs = append(extra, resolvedArgs...)
+ }
+ return entry.handler(resolvedArgs)
+}
+
+func jpfAbs(arguments []interface{}) (interface{}, error) {
+ num := arguments[0].(float64)
+ return math.Abs(num), nil
+}
+
+func jpfLength(arguments []interface{}) (interface{}, error) {
+ arg := arguments[0]
+ if c, ok := arg.(string); ok {
+ return float64(utf8.RuneCountInString(c)), nil
+ } else if isSliceType(arg) {
+ v := reflect.ValueOf(arg)
+ return float64(v.Len()), nil
+ } else if c, ok := arg.(map[string]interface{}); ok {
+ return float64(len(c)), nil
+ }
+ return nil, errors.New("could not compute length()")
+}
+
+func jpfStartsWith(arguments []interface{}) (interface{}, error) {
+ search := arguments[0].(string)
+ prefix := arguments[1].(string)
+ return strings.HasPrefix(search, prefix), nil
+}
+
+func jpfAvg(arguments []interface{}) (interface{}, error) {
+ // We've already type checked the value so we can safely use
+ // type assertions.
+ args := arguments[0].([]interface{})
+ length := float64(len(args))
+ numerator := 0.0
+ for _, n := range args {
+ numerator += n.(float64)
+ }
+ return numerator / length, nil
+}
+func jpfCeil(arguments []interface{}) (interface{}, error) {
+ val := arguments[0].(float64)
+ return math.Ceil(val), nil
+}
+func jpfContains(arguments []interface{}) (interface{}, error) {
+ search := arguments[0]
+ el := arguments[1]
+ if searchStr, ok := search.(string); ok {
+ if elStr, ok := el.(string); ok {
+ return strings.Index(searchStr, elStr) != -1, nil
+ }
+ return false, nil
+ }
+ // Otherwise this is a generic contains for []interface{}
+ general := search.([]interface{})
+ for _, item := range general {
+ if item == el {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+func jpfEndsWith(arguments []interface{}) (interface{}, error) {
+ search := arguments[0].(string)
+ suffix := arguments[1].(string)
+ return strings.HasSuffix(search, suffix), nil
+}
+func jpfFloor(arguments []interface{}) (interface{}, error) {
+ val := arguments[0].(float64)
+ return math.Floor(val), nil
+}
+func jpfMap(arguments []interface{}) (interface{}, error) {
+ intr := arguments[0].(*treeInterpreter)
+ exp := arguments[1].(expRef)
+ node := exp.ref
+ arr := arguments[2].([]interface{})
+ mapped := make([]interface{}, 0, len(arr))
+ for _, value := range arr {
+ current, err := intr.Execute(node, value)
+ if err != nil {
+ return nil, err
+ }
+ mapped = append(mapped, current)
+ }
+ return mapped, nil
+}
+func jpfMax(arguments []interface{}) (interface{}, error) {
+ if items, ok := toArrayNum(arguments[0]); ok {
+ if len(items) == 0 {
+ return nil, nil
+ }
+ if len(items) == 1 {
+ return items[0], nil
+ }
+ best := items[0]
+ for _, item := range items[1:] {
+ if item > best {
+ best = item
+ }
+ }
+ return best, nil
+ }
+ // Otherwise we're dealing with a max() of strings.
+ items, _ := toArrayStr(arguments[0])
+ if len(items) == 0 {
+ return nil, nil
+ }
+ if len(items) == 1 {
+ return items[0], nil
+ }
+ best := items[0]
+ for _, item := range items[1:] {
+ if item > best {
+ best = item
+ }
+ }
+ return best, nil
+}
+func jpfMerge(arguments []interface{}) (interface{}, error) {
+ final := make(map[string]interface{})
+ for _, m := range arguments {
+ mapped := m.(map[string]interface{})
+ for key, value := range mapped {
+ final[key] = value
+ }
+ }
+ return final, nil
+}
+func jpfMaxBy(arguments []interface{}) (interface{}, error) {
+ intr := arguments[0].(*treeInterpreter)
+ arr := arguments[1].([]interface{})
+ exp := arguments[2].(expRef)
+ node := exp.ref
+ if len(arr) == 0 {
+ return nil, nil
+ } else if len(arr) == 1 {
+ return arr[0], nil
+ }
+ start, err := intr.Execute(node, arr[0])
+ if err != nil {
+ return nil, err
+ }
+ switch t := start.(type) {
+ case float64:
+ bestVal := t
+ bestItem := arr[0]
+ for _, item := range arr[1:] {
+ result, err := intr.Execute(node, item)
+ if err != nil {
+ return nil, err
+ }
+ current, ok := result.(float64)
+ if !ok {
+ return nil, errors.New("invalid type, must be number")
+ }
+ if current > bestVal {
+ bestVal = current
+ bestItem = item
+ }
+ }
+ return bestItem, nil
+ case string:
+ bestVal := t
+ bestItem := arr[0]
+ for _, item := range arr[1:] {
+ result, err := intr.Execute(node, item)
+ if err != nil {
+ return nil, err
+ }
+ current, ok := result.(string)
+ if !ok {
+ return nil, errors.New("invalid type, must be string")
+ }
+ if current > bestVal {
+ bestVal = current
+ bestItem = item
+ }
+ }
+ return bestItem, nil
+ default:
+ return nil, errors.New("invalid type, must be number of string")
+ }
+}
+func jpfSum(arguments []interface{}) (interface{}, error) {
+ items, _ := toArrayNum(arguments[0])
+ sum := 0.0
+ for _, item := range items {
+ sum += item
+ }
+ return sum, nil
+}
+
+func jpfMin(arguments []interface{}) (interface{}, error) {
+ if items, ok := toArrayNum(arguments[0]); ok {
+ if len(items) == 0 {
+ return nil, nil
+ }
+ if len(items) == 1 {
+ return items[0], nil
+ }
+ best := items[0]
+ for _, item := range items[1:] {
+ if item < best {
+ best = item
+ }
+ }
+ return best, nil
+ }
+ items, _ := toArrayStr(arguments[0])
+ if len(items) == 0 {
+ return nil, nil
+ }
+ if len(items) == 1 {
+ return items[0], nil
+ }
+ best := items[0]
+ for _, item := range items[1:] {
+ if item < best {
+ best = item
+ }
+ }
+ return best, nil
+}
+
+func jpfMinBy(arguments []interface{}) (interface{}, error) {
+ intr := arguments[0].(*treeInterpreter)
+ arr := arguments[1].([]interface{})
+ exp := arguments[2].(expRef)
+ node := exp.ref
+ if len(arr) == 0 {
+ return nil, nil
+ } else if len(arr) == 1 {
+ return arr[0], nil
+ }
+ start, err := intr.Execute(node, arr[0])
+ if err != nil {
+ return nil, err
+ }
+ if t, ok := start.(float64); ok {
+ bestVal := t
+ bestItem := arr[0]
+ for _, item := range arr[1:] {
+ result, err := intr.Execute(node, item)
+ if err != nil {
+ return nil, err
+ }
+ current, ok := result.(float64)
+ if !ok {
+ return nil, errors.New("invalid type, must be number")
+ }
+ if current < bestVal {
+ bestVal = current
+ bestItem = item
+ }
+ }
+ return bestItem, nil
+ } else if t, ok := start.(string); ok {
+ bestVal := t
+ bestItem := arr[0]
+ for _, item := range arr[1:] {
+ result, err := intr.Execute(node, item)
+ if err != nil {
+ return nil, err
+ }
+ current, ok := result.(string)
+ if !ok {
+ return nil, errors.New("invalid type, must be string")
+ }
+ if current < bestVal {
+ bestVal = current
+ bestItem = item
+ }
+ }
+ return bestItem, nil
+ } else {
+ return nil, errors.New("invalid type, must be number of string")
+ }
+}
+func jpfType(arguments []interface{}) (interface{}, error) {
+ arg := arguments[0]
+ if _, ok := arg.(float64); ok {
+ return "number", nil
+ }
+ if _, ok := arg.(string); ok {
+ return "string", nil
+ }
+ if _, ok := arg.([]interface{}); ok {
+ return "array", nil
+ }
+ if _, ok := arg.(map[string]interface{}); ok {
+ return "object", nil
+ }
+ if arg == nil {
+ return "null", nil
+ }
+ if arg == true || arg == false {
+ return "boolean", nil
+ }
+ return nil, errors.New("unknown type")
+}
+func jpfKeys(arguments []interface{}) (interface{}, error) {
+ arg := arguments[0].(map[string]interface{})
+ collected := make([]interface{}, 0, len(arg))
+ for key := range arg {
+ collected = append(collected, key)
+ }
+ return collected, nil
+}
+func jpfValues(arguments []interface{}) (interface{}, error) {
+ arg := arguments[0].(map[string]interface{})
+ collected := make([]interface{}, 0, len(arg))
+ for _, value := range arg {
+ collected = append(collected, value)
+ }
+ return collected, nil
+}
+func jpfSort(arguments []interface{}) (interface{}, error) {
+ if items, ok := toArrayNum(arguments[0]); ok {
+ d := sort.Float64Slice(items)
+ sort.Stable(d)
+ final := make([]interface{}, len(d))
+ for i, val := range d {
+ final[i] = val
+ }
+ return final, nil
+ }
+ // Otherwise we're dealing with sort()'ing strings.
+ items, _ := toArrayStr(arguments[0])
+ d := sort.StringSlice(items)
+ sort.Stable(d)
+ final := make([]interface{}, len(d))
+ for i, val := range d {
+ final[i] = val
+ }
+ return final, nil
+}
+func jpfSortBy(arguments []interface{}) (interface{}, error) {
+ intr := arguments[0].(*treeInterpreter)
+ arr := arguments[1].([]interface{})
+ exp := arguments[2].(expRef)
+ node := exp.ref
+ if len(arr) == 0 {
+ return arr, nil
+ } else if len(arr) == 1 {
+ return arr, nil
+ }
+ start, err := intr.Execute(node, arr[0])
+ if err != nil {
+ return nil, err
+ }
+ if _, ok := start.(float64); ok {
+ sortable := &byExprFloat{intr, node, arr, false}
+ sort.Stable(sortable)
+ if sortable.hasError {
+ return nil, errors.New("error in sort_by comparison")
+ }
+ return arr, nil
+ } else if _, ok := start.(string); ok {
+ sortable := &byExprString{intr, node, arr, false}
+ sort.Stable(sortable)
+ if sortable.hasError {
+ return nil, errors.New("error in sort_by comparison")
+ }
+ return arr, nil
+ } else {
+ return nil, errors.New("invalid type, must be number of string")
+ }
+}
+func jpfJoin(arguments []interface{}) (interface{}, error) {
+ sep := arguments[0].(string)
+ // We can't just do arguments[1].([]string), we have to
+ // manually convert each item to a string.
+ arrayStr := []string{}
+ for _, item := range arguments[1].([]interface{}) {
+ arrayStr = append(arrayStr, item.(string))
+ }
+ return strings.Join(arrayStr, sep), nil
+}
+func jpfReverse(arguments []interface{}) (interface{}, error) {
+ if s, ok := arguments[0].(string); ok {
+ r := []rune(s)
+ for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
+ r[i], r[j] = r[j], r[i]
+ }
+ return string(r), nil
+ }
+ items := arguments[0].([]interface{})
+ length := len(items)
+ reversed := make([]interface{}, length)
+ for i, item := range items {
+ reversed[length-(i+1)] = item
+ }
+ return reversed, nil
+}
+func jpfToArray(arguments []interface{}) (interface{}, error) {
+ if _, ok := arguments[0].([]interface{}); ok {
+ return arguments[0], nil
+ }
+ return arguments[:1:1], nil
+}
+func jpfToString(arguments []interface{}) (interface{}, error) {
+ if v, ok := arguments[0].(string); ok {
+ return v, nil
+ }
+ result, err := json.Marshal(arguments[0])
+ if err != nil {
+ return nil, err
+ }
+ return string(result), nil
+}
+func jpfToNumber(arguments []interface{}) (interface{}, error) {
+ arg := arguments[0]
+ if v, ok := arg.(float64); ok {
+ return v, nil
+ }
+ if v, ok := arg.(string); ok {
+ conv, err := strconv.ParseFloat(v, 64)
+ if err != nil {
+ return nil, nil
+ }
+ return conv, nil
+ }
+ if _, ok := arg.([]interface{}); ok {
+ return nil, nil
+ }
+ if _, ok := arg.(map[string]interface{}); ok {
+ return nil, nil
+ }
+ if arg == nil {
+ return nil, nil
+ }
+ if arg == true || arg == false {
+ return nil, nil
+ }
+ return nil, errors.New("unknown type")
+}
+func jpfNotNull(arguments []interface{}) (interface{}, error) {
+ for _, arg := range arguments {
+ if arg != nil {
+ return arg, nil
+ }
+ }
+ return nil, nil
+}
diff --git a/vendor/github.com/jmespath/go-jmespath/interpreter.go b/vendor/github.com/jmespath/go-jmespath/interpreter.go
new file mode 100644
index 00000000..13c74604
--- /dev/null
+++ b/vendor/github.com/jmespath/go-jmespath/interpreter.go
@@ -0,0 +1,418 @@
+package jmespath
+
+import (
+ "errors"
+ "reflect"
+ "unicode"
+ "unicode/utf8"
+)
+
+/* This is a tree based interpreter. It walks the AST and directly
+ interprets the AST to search through a JSON document.
+*/
+
+type treeInterpreter struct {
+ fCall *functionCaller
+}
+
+func newInterpreter() *treeInterpreter {
+ interpreter := treeInterpreter{}
+ interpreter.fCall = newFunctionCaller()
+ return &interpreter
+}
+
+type expRef struct {
+ ref ASTNode
+}
+
+// Execute takes an ASTNode and input data and interprets the AST directly.
+// It will produce the result of applying the JMESPath expression associated
+// with the ASTNode to the input data "value".
+func (intr *treeInterpreter) Execute(node ASTNode, value interface{}) (interface{}, error) {
+ switch node.nodeType {
+ case ASTComparator:
+ left, err := intr.Execute(node.children[0], value)
+ if err != nil {
+ return nil, err
+ }
+ right, err := intr.Execute(node.children[1], value)
+ if err != nil {
+ return nil, err
+ }
+ switch node.value {
+ case tEQ:
+ return objsEqual(left, right), nil
+ case tNE:
+ return !objsEqual(left, right), nil
+ }
+ leftNum, ok := left.(float64)
+ if !ok {
+ return nil, nil
+ }
+ rightNum, ok := right.(float64)
+ if !ok {
+ return nil, nil
+ }
+ switch node.value {
+ case tGT:
+ return leftNum > rightNum, nil
+ case tGTE:
+ return leftNum >= rightNum, nil
+ case tLT:
+ return leftNum < rightNum, nil
+ case tLTE:
+ return leftNum <= rightNum, nil
+ }
+ case ASTExpRef:
+ return expRef{ref: node.children[0]}, nil
+ case ASTFunctionExpression:
+ resolvedArgs := []interface{}{}
+ for _, arg := range node.children {
+ current, err := intr.Execute(arg, value)
+ if err != nil {
+ return nil, err
+ }
+ resolvedArgs = append(resolvedArgs, current)
+ }
+ return intr.fCall.CallFunction(node.value.(string), resolvedArgs, intr)
+ case ASTField:
+ if m, ok := value.(map[string]interface{}); ok {
+ key := node.value.(string)
+ return m[key], nil
+ }
+ return intr.fieldFromStruct(node.value.(string), value)
+ case ASTFilterProjection:
+ left, err := intr.Execute(node.children[0], value)
+ if err != nil {
+ return nil, nil
+ }
+ sliceType, ok := left.([]interface{})
+ if !ok {
+ if isSliceType(left) {
+ return intr.filterProjectionWithReflection(node, left)
+ }
+ return nil, nil
+ }
+ compareNode := node.children[2]
+ collected := []interface{}{}
+ for _, element := range sliceType {
+ result, err := intr.Execute(compareNode, element)
+ if err != nil {
+ return nil, err
+ }
+ if !isFalse(result) {
+ current, err := intr.Execute(node.children[1], element)
+ if err != nil {
+ return nil, err
+ }
+ if current != nil {
+ collected = append(collected, current)
+ }
+ }
+ }
+ return collected, nil
+ case ASTFlatten:
+ left, err := intr.Execute(node.children[0], value)
+ if err != nil {
+ return nil, nil
+ }
+ sliceType, ok := left.([]interface{})
+ if !ok {
+ // If we can't type convert to []interface{}, there's
+ // a chance this could still work via reflection if we're
+ // dealing with user provided types.
+ if isSliceType(left) {
+ return intr.flattenWithReflection(left)
+ }
+ return nil, nil
+ }
+ flattened := []interface{}{}
+ for _, element := range sliceType {
+ if elementSlice, ok := element.([]interface{}); ok {
+ flattened = append(flattened, elementSlice...)
+ } else if isSliceType(element) {
+ reflectFlat := []interface{}{}
+ v := reflect.ValueOf(element)
+ for i := 0; i < v.Len(); i++ {
+ reflectFlat = append(reflectFlat, v.Index(i).Interface())
+ }
+ flattened = append(flattened, reflectFlat...)
+ } else {
+ flattened = append(flattened, element)
+ }
+ }
+ return flattened, nil
+ case ASTIdentity, ASTCurrentNode:
+ return value, nil
+ case ASTIndex:
+ if sliceType, ok := value.([]interface{}); ok {
+ index := node.value.(int)
+ if index < 0 {
+ index += len(sliceType)
+ }
+ if index < len(sliceType) && index >= 0 {
+ return sliceType[index], nil
+ }
+ return nil, nil
+ }
+ // Otherwise try via reflection.
+ rv := reflect.ValueOf(value)
+ if rv.Kind() == reflect.Slice {
+ index := node.value.(int)
+ if index < 0 {
+ index += rv.Len()
+ }
+ if index < rv.Len() && index >= 0 {
+ v := rv.Index(index)
+ return v.Interface(), nil
+ }
+ }
+ return nil, nil
+ case ASTKeyValPair:
+ return intr.Execute(node.children[0], value)
+ case ASTLiteral:
+ return node.value, nil
+ case ASTMultiSelectHash:
+ if value == nil {
+ return nil, nil
+ }
+ collected := make(map[string]interface{})
+ for _, child := range node.children {
+ current, err := intr.Execute(child, value)
+ if err != nil {
+ return nil, err
+ }
+ key := child.value.(string)
+ collected[key] = current
+ }
+ return collected, nil
+ case ASTMultiSelectList:
+ if value == nil {
+ return nil, nil
+ }
+ collected := []interface{}{}
+ for _, child := range node.children {
+ current, err := intr.Execute(child, value)
+ if err != nil {
+ return nil, err
+ }
+ collected = append(collected, current)
+ }
+ return collected, nil
+ case ASTOrExpression:
+ matched, err := intr.Execute(node.children[0], value)
+ if err != nil {
+ return nil, err
+ }
+ if isFalse(matched) {
+ matched, err = intr.Execute(node.children[1], value)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return matched, nil
+ case ASTAndExpression:
+ matched, err := intr.Execute(node.children[0], value)
+ if err != nil {
+ return nil, err
+ }
+ if isFalse(matched) {
+ return matched, nil
+ }
+ return intr.Execute(node.children[1], value)
+ case ASTNotExpression:
+ matched, err := intr.Execute(node.children[0], value)
+ if err != nil {
+ return nil, err
+ }
+ if isFalse(matched) {
+ return true, nil
+ }
+ return false, nil
+ case ASTPipe:
+ result := value
+ var err error
+ for _, child := range node.children {
+ result, err = intr.Execute(child, result)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return result, nil
+ case ASTProjection:
+ left, err := intr.Execute(node.children[0], value)
+ if err != nil {
+ return nil, err
+ }
+ sliceType, ok := left.([]interface{})
+ if !ok {
+ if isSliceType(left) {
+ return intr.projectWithReflection(node, left)
+ }
+ return nil, nil
+ }
+ collected := []interface{}{}
+ var current interface{}
+ for _, element := range sliceType {
+ current, err = intr.Execute(node.children[1], element)
+ if err != nil {
+ return nil, err
+ }
+ if current != nil {
+ collected = append(collected, current)
+ }
+ }
+ return collected, nil
+ case ASTSubexpression, ASTIndexExpression:
+ left, err := intr.Execute(node.children[0], value)
+ if err != nil {
+ return nil, err
+ }
+ return intr.Execute(node.children[1], left)
+ case ASTSlice:
+ sliceType, ok := value.([]interface{})
+ if !ok {
+ if isSliceType(value) {
+ return intr.sliceWithReflection(node, value)
+ }
+ return nil, nil
+ }
+ parts := node.value.([]*int)
+ sliceParams := make([]sliceParam, 3)
+ for i, part := range parts {
+ if part != nil {
+ sliceParams[i].Specified = true
+ sliceParams[i].N = *part
+ }
+ }
+ return slice(sliceType, sliceParams)
+ case ASTValueProjection:
+ left, err := intr.Execute(node.children[0], value)
+ if err != nil {
+ return nil, nil
+ }
+ mapType, ok := left.(map[string]interface{})
+ if !ok {
+ return nil, nil
+ }
+ values := make([]interface{}, len(mapType))
+ for _, value := range mapType {
+ values = append(values, value)
+ }
+ collected := []interface{}{}
+ for _, element := range values {
+ current, err := intr.Execute(node.children[1], element)
+ if err != nil {
+ return nil, err
+ }
+ if current != nil {
+ collected = append(collected, current)
+ }
+ }
+ return collected, nil
+ }
+ return nil, errors.New("Unknown AST node: " + node.nodeType.String())
+}
+
+func (intr *treeInterpreter) fieldFromStruct(key string, value interface{}) (interface{}, error) {
+ rv := reflect.ValueOf(value)
+ first, n := utf8.DecodeRuneInString(key)
+ fieldName := string(unicode.ToUpper(first)) + key[n:]
+ if rv.Kind() == reflect.Struct {
+ v := rv.FieldByName(fieldName)
+ if !v.IsValid() {
+ return nil, nil
+ }
+ return v.Interface(), nil
+ } else if rv.Kind() == reflect.Ptr {
+ // Handle multiple levels of indirection?
+ if rv.IsNil() {
+ return nil, nil
+ }
+ rv = rv.Elem()
+ v := rv.FieldByName(fieldName)
+ if !v.IsValid() {
+ return nil, nil
+ }
+ return v.Interface(), nil
+ }
+ return nil, nil
+}
+
+func (intr *treeInterpreter) flattenWithReflection(value interface{}) (interface{}, error) {
+ v := reflect.ValueOf(value)
+ flattened := []interface{}{}
+ for i := 0; i < v.Len(); i++ {
+ element := v.Index(i).Interface()
+ if reflect.TypeOf(element).Kind() == reflect.Slice {
+ // Then insert the contents of the element
+ // slice into the flattened slice,
+ // i.e flattened = append(flattened, mySlice...)
+ elementV := reflect.ValueOf(element)
+ for j := 0; j < elementV.Len(); j++ {
+ flattened = append(
+ flattened, elementV.Index(j).Interface())
+ }
+ } else {
+ flattened = append(flattened, element)
+ }
+ }
+ return flattened, nil
+}
+
+func (intr *treeInterpreter) sliceWithReflection(node ASTNode, value interface{}) (interface{}, error) {
+ v := reflect.ValueOf(value)
+ parts := node.value.([]*int)
+ sliceParams := make([]sliceParam, 3)
+ for i, part := range parts {
+ if part != nil {
+ sliceParams[i].Specified = true
+ sliceParams[i].N = *part
+ }
+ }
+ final := []interface{}{}
+ for i := 0; i < v.Len(); i++ {
+ element := v.Index(i).Interface()
+ final = append(final, element)
+ }
+ return slice(final, sliceParams)
+}
+
+func (intr *treeInterpreter) filterProjectionWithReflection(node ASTNode, value interface{}) (interface{}, error) {
+ compareNode := node.children[2]
+ collected := []interface{}{}
+ v := reflect.ValueOf(value)
+ for i := 0; i < v.Len(); i++ {
+ element := v.Index(i).Interface()
+ result, err := intr.Execute(compareNode, element)
+ if err != nil {
+ return nil, err
+ }
+ if !isFalse(result) {
+ current, err := intr.Execute(node.children[1], element)
+ if err != nil {
+ return nil, err
+ }
+ if current != nil {
+ collected = append(collected, current)
+ }
+ }
+ }
+ return collected, nil
+}
+
+func (intr *treeInterpreter) projectWithReflection(node ASTNode, value interface{}) (interface{}, error) {
+ collected := []interface{}{}
+ v := reflect.ValueOf(value)
+ for i := 0; i < v.Len(); i++ {
+ element := v.Index(i).Interface()
+ result, err := intr.Execute(node.children[1], element)
+ if err != nil {
+ return nil, err
+ }
+ if result != nil {
+ collected = append(collected, result)
+ }
+ }
+ return collected, nil
+}
diff --git a/vendor/github.com/jmespath/go-jmespath/lexer.go b/vendor/github.com/jmespath/go-jmespath/lexer.go
new file mode 100644
index 00000000..817900c8
--- /dev/null
+++ b/vendor/github.com/jmespath/go-jmespath/lexer.go
@@ -0,0 +1,420 @@
+package jmespath
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "strconv"
+ "strings"
+ "unicode/utf8"
+)
+
+type token struct {
+ tokenType tokType
+ value string
+ position int
+ length int
+}
+
+type tokType int
+
+const eof = -1
+
+// Lexer contains information about the expression being tokenized.
+type Lexer struct {
+ expression string // The expression provided by the user.
+ currentPos int // The current position in the string.
+ lastWidth int // The width of the current rune. This
+ buf bytes.Buffer // Internal buffer used for building up values.
+}
+
+// SyntaxError is the main error used whenever a lexing or parsing error occurs.
+type SyntaxError struct {
+ msg string // Error message displayed to user
+ Expression string // Expression that generated a SyntaxError
+ Offset int // The location in the string where the error occurred
+}
+
+func (e SyntaxError) Error() string {
+ // In the future, it would be good to underline the specific
+ // location where the error occurred.
+ return "SyntaxError: " + e.msg
+}
+
+// HighlightLocation will show where the syntax error occurred.
+// It will place a "^" character on a line below the expression
+// at the point where the syntax error occurred.
+func (e SyntaxError) HighlightLocation() string {
+ return e.Expression + "\n" + strings.Repeat(" ", e.Offset) + "^"
+}
+
+//go:generate stringer -type=tokType
+const (
+ tUnknown tokType = iota
+ tStar
+ tDot
+ tFilter
+ tFlatten
+ tLparen
+ tRparen
+ tLbracket
+ tRbracket
+ tLbrace
+ tRbrace
+ tOr
+ tPipe
+ tNumber
+ tUnquotedIdentifier
+ tQuotedIdentifier
+ tComma
+ tColon
+ tLT
+ tLTE
+ tGT
+ tGTE
+ tEQ
+ tNE
+ tJSONLiteral
+ tStringLiteral
+ tCurrent
+ tExpref
+ tAnd
+ tNot
+ tEOF
+)
+
+var basicTokens = map[rune]tokType{
+ '.': tDot,
+ '*': tStar,
+ ',': tComma,
+ ':': tColon,
+ '{': tLbrace,
+ '}': tRbrace,
+ ']': tRbracket, // tLbracket not included because it could be "[]"
+ '(': tLparen,
+ ')': tRparen,
+ '@': tCurrent,
+}
+
+// Bit mask for [a-zA-Z_] shifted down 64 bits to fit in a single uint64.
+// When using this bitmask just be sure to shift the rune down 64 bits
+// before checking against identifierStartBits.
+const identifierStartBits uint64 = 576460745995190270
+
+// Bit mask for [a-zA-Z0-9], 128 bits -> 2 uint64s.
+var identifierTrailingBits = [2]uint64{287948901175001088, 576460745995190270}
+
+var whiteSpace = map[rune]bool{
+ ' ': true, '\t': true, '\n': true, '\r': true,
+}
+
+func (t token) String() string {
+ return fmt.Sprintf("Token{%+v, %s, %d, %d}",
+ t.tokenType, t.value, t.position, t.length)
+}
+
+// NewLexer creates a new JMESPath lexer.
+func NewLexer() *Lexer {
+ lexer := Lexer{}
+ return &lexer
+}
+
+func (lexer *Lexer) next() rune {
+ if lexer.currentPos >= len(lexer.expression) {
+ lexer.lastWidth = 0
+ return eof
+ }
+ r, w := utf8.DecodeRuneInString(lexer.expression[lexer.currentPos:])
+ lexer.lastWidth = w
+ lexer.currentPos += w
+ return r
+}
+
+func (lexer *Lexer) back() {
+ lexer.currentPos -= lexer.lastWidth
+}
+
+func (lexer *Lexer) peek() rune {
+ t := lexer.next()
+ lexer.back()
+ return t
+}
+
+// tokenize takes an expression and returns corresponding tokens.
+func (lexer *Lexer) tokenize(expression string) ([]token, error) {
+ var tokens []token
+ lexer.expression = expression
+ lexer.currentPos = 0
+ lexer.lastWidth = 0
+loop:
+ for {
+ r := lexer.next()
+ if identifierStartBits&(1<<(uint64(r)-64)) > 0 {
+ t := lexer.consumeUnquotedIdentifier()
+ tokens = append(tokens, t)
+ } else if val, ok := basicTokens[r]; ok {
+ // Basic single char token.
+ t := token{
+ tokenType: val,
+ value: string(r),
+ position: lexer.currentPos - lexer.lastWidth,
+ length: 1,
+ }
+ tokens = append(tokens, t)
+ } else if r == '-' || (r >= '0' && r <= '9') {
+ t := lexer.consumeNumber()
+ tokens = append(tokens, t)
+ } else if r == '[' {
+ t := lexer.consumeLBracket()
+ tokens = append(tokens, t)
+ } else if r == '"' {
+ t, err := lexer.consumeQuotedIdentifier()
+ if err != nil {
+ return tokens, err
+ }
+ tokens = append(tokens, t)
+ } else if r == '\'' {
+ t, err := lexer.consumeRawStringLiteral()
+ if err != nil {
+ return tokens, err
+ }
+ tokens = append(tokens, t)
+ } else if r == '`' {
+ t, err := lexer.consumeLiteral()
+ if err != nil {
+ return tokens, err
+ }
+ tokens = append(tokens, t)
+ } else if r == '|' {
+ t := lexer.matchOrElse(r, '|', tOr, tPipe)
+ tokens = append(tokens, t)
+ } else if r == '<' {
+ t := lexer.matchOrElse(r, '=', tLTE, tLT)
+ tokens = append(tokens, t)
+ } else if r == '>' {
+ t := lexer.matchOrElse(r, '=', tGTE, tGT)
+ tokens = append(tokens, t)
+ } else if r == '!' {
+ t := lexer.matchOrElse(r, '=', tNE, tNot)
+ tokens = append(tokens, t)
+ } else if r == '=' {
+ t := lexer.matchOrElse(r, '=', tEQ, tUnknown)
+ tokens = append(tokens, t)
+ } else if r == '&' {
+ t := lexer.matchOrElse(r, '&', tAnd, tExpref)
+ tokens = append(tokens, t)
+ } else if r == eof {
+ break loop
+ } else if _, ok := whiteSpace[r]; ok {
+ // Ignore whitespace
+ } else {
+ return tokens, lexer.syntaxError(fmt.Sprintf("Unknown char: %s", strconv.QuoteRuneToASCII(r)))
+ }
+ }
+ tokens = append(tokens, token{tEOF, "", len(lexer.expression), 0})
+ return tokens, nil
+}
+
+// Consume characters until the ending rune "r" is reached.
+// If the end of the expression is reached before seeing the
+// terminating rune "r", then an error is returned.
+// If no error occurs then the matching substring is returned.
+// The returned string will not include the ending rune.
+func (lexer *Lexer) consumeUntil(end rune) (string, error) {
+ start := lexer.currentPos
+ current := lexer.next()
+ for current != end && current != eof {
+ if current == '\\' && lexer.peek() != eof {
+ lexer.next()
+ }
+ current = lexer.next()
+ }
+ if lexer.lastWidth == 0 {
+ // Then we hit an EOF so we never reached the closing
+ // delimiter.
+ return "", SyntaxError{
+ msg: "Unclosed delimiter: " + string(end),
+ Expression: lexer.expression,
+ Offset: len(lexer.expression),
+ }
+ }
+ return lexer.expression[start : lexer.currentPos-lexer.lastWidth], nil
+}
+
+func (lexer *Lexer) consumeLiteral() (token, error) {
+ start := lexer.currentPos
+ value, err := lexer.consumeUntil('`')
+ if err != nil {
+ return token{}, err
+ }
+ value = strings.Replace(value, "\\`", "`", -1)
+ return token{
+ tokenType: tJSONLiteral,
+ value: value,
+ position: start,
+ length: len(value),
+ }, nil
+}
+
+func (lexer *Lexer) consumeRawStringLiteral() (token, error) {
+ start := lexer.currentPos
+ currentIndex := start
+ current := lexer.next()
+ for current != '\'' && lexer.peek() != eof {
+ if current == '\\' && lexer.peek() == '\'' {
+ chunk := lexer.expression[currentIndex : lexer.currentPos-1]
+ lexer.buf.WriteString(chunk)
+ lexer.buf.WriteString("'")
+ lexer.next()
+ currentIndex = lexer.currentPos
+ }
+ current = lexer.next()
+ }
+ if lexer.lastWidth == 0 {
+ // Then we hit an EOF so we never reached the closing
+ // delimiter.
+ return token{}, SyntaxError{
+ msg: "Unclosed delimiter: '",
+ Expression: lexer.expression,
+ Offset: len(lexer.expression),
+ }
+ }
+ if currentIndex < lexer.currentPos {
+ lexer.buf.WriteString(lexer.expression[currentIndex : lexer.currentPos-1])
+ }
+ value := lexer.buf.String()
+ // Reset the buffer so it can reused again.
+ lexer.buf.Reset()
+ return token{
+ tokenType: tStringLiteral,
+ value: value,
+ position: start,
+ length: len(value),
+ }, nil
+}
+
+func (lexer *Lexer) syntaxError(msg string) SyntaxError {
+ return SyntaxError{
+ msg: msg,
+ Expression: lexer.expression,
+ Offset: lexer.currentPos - 1,
+ }
+}
+
+// Checks for a two char token, otherwise matches a single character
+// token. This is used whenever a two char token overlaps a single
+// char token, e.g. "||" -> tPipe, "|" -> tOr.
+func (lexer *Lexer) matchOrElse(first rune, second rune, matchedType tokType, singleCharType tokType) token {
+ start := lexer.currentPos - lexer.lastWidth
+ nextRune := lexer.next()
+ var t token
+ if nextRune == second {
+ t = token{
+ tokenType: matchedType,
+ value: string(first) + string(second),
+ position: start,
+ length: 2,
+ }
+ } else {
+ lexer.back()
+ t = token{
+ tokenType: singleCharType,
+ value: string(first),
+ position: start,
+ length: 1,
+ }
+ }
+ return t
+}
+
+func (lexer *Lexer) consumeLBracket() token {
+ // There's three options here:
+ // 1. A filter expression "[?"
+ // 2. A flatten operator "[]"
+ // 3. A bare rbracket "["
+ start := lexer.currentPos - lexer.lastWidth
+ nextRune := lexer.next()
+ var t token
+ if nextRune == '?' {
+ t = token{
+ tokenType: tFilter,
+ value: "[?",
+ position: start,
+ length: 2,
+ }
+ } else if nextRune == ']' {
+ t = token{
+ tokenType: tFlatten,
+ value: "[]",
+ position: start,
+ length: 2,
+ }
+ } else {
+ t = token{
+ tokenType: tLbracket,
+ value: "[",
+ position: start,
+ length: 1,
+ }
+ lexer.back()
+ }
+ return t
+}
+
+func (lexer *Lexer) consumeQuotedIdentifier() (token, error) {
+ start := lexer.currentPos
+ value, err := lexer.consumeUntil('"')
+ if err != nil {
+ return token{}, err
+ }
+ var decoded string
+ asJSON := []byte("\"" + value + "\"")
+ if err := json.Unmarshal([]byte(asJSON), &decoded); err != nil {
+ return token{}, err
+ }
+ return token{
+ tokenType: tQuotedIdentifier,
+ value: decoded,
+ position: start - 1,
+ length: len(decoded),
+ }, nil
+}
+
+func (lexer *Lexer) consumeUnquotedIdentifier() token {
+ // Consume runes until we reach the end of an unquoted
+ // identifier.
+ start := lexer.currentPos - lexer.lastWidth
+ for {
+ r := lexer.next()
+ if r < 0 || r > 128 || identifierTrailingBits[uint64(r)/64]&(1<<(uint64(r)%64)) == 0 {
+ lexer.back()
+ break
+ }
+ }
+ value := lexer.expression[start:lexer.currentPos]
+ return token{
+ tokenType: tUnquotedIdentifier,
+ value: value,
+ position: start,
+ length: lexer.currentPos - start,
+ }
+}
+
+func (lexer *Lexer) consumeNumber() token {
+ // Consume runes until we reach something that's not a number.
+ start := lexer.currentPos - lexer.lastWidth
+ for {
+ r := lexer.next()
+ if r < '0' || r > '9' {
+ lexer.back()
+ break
+ }
+ }
+ value := lexer.expression[start:lexer.currentPos]
+ return token{
+ tokenType: tNumber,
+ value: value,
+ position: start,
+ length: lexer.currentPos - start,
+ }
+}
diff --git a/vendor/github.com/jmespath/go-jmespath/parser.go b/vendor/github.com/jmespath/go-jmespath/parser.go
new file mode 100644
index 00000000..1240a175
--- /dev/null
+++ b/vendor/github.com/jmespath/go-jmespath/parser.go
@@ -0,0 +1,603 @@
+package jmespath
+
+import (
+ "encoding/json"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+type astNodeType int
+
+//go:generate stringer -type astNodeType
+const (
+ ASTEmpty astNodeType = iota
+ ASTComparator
+ ASTCurrentNode
+ ASTExpRef
+ ASTFunctionExpression
+ ASTField
+ ASTFilterProjection
+ ASTFlatten
+ ASTIdentity
+ ASTIndex
+ ASTIndexExpression
+ ASTKeyValPair
+ ASTLiteral
+ ASTMultiSelectHash
+ ASTMultiSelectList
+ ASTOrExpression
+ ASTAndExpression
+ ASTNotExpression
+ ASTPipe
+ ASTProjection
+ ASTSubexpression
+ ASTSlice
+ ASTValueProjection
+)
+
+// ASTNode represents the abstract syntax tree of a JMESPath expression.
+type ASTNode struct {
+ nodeType astNodeType
+ value interface{}
+ children []ASTNode
+}
+
+func (node ASTNode) String() string {
+ return node.PrettyPrint(0)
+}
+
+// PrettyPrint will pretty print the parsed AST.
+// The AST is an implementation detail and this pretty print
+// function is provided as a convenience method to help with
+// debugging. You should not rely on its output as the internal
+// structure of the AST may change at any time.
+func (node ASTNode) PrettyPrint(indent int) string {
+ spaces := strings.Repeat(" ", indent)
+ output := fmt.Sprintf("%s%s {\n", spaces, node.nodeType)
+ nextIndent := indent + 2
+ if node.value != nil {
+ if converted, ok := node.value.(fmt.Stringer); ok {
+ // Account for things like comparator nodes
+ // that are enums with a String() method.
+ output += fmt.Sprintf("%svalue: %s\n", strings.Repeat(" ", nextIndent), converted.String())
+ } else {
+ output += fmt.Sprintf("%svalue: %#v\n", strings.Repeat(" ", nextIndent), node.value)
+ }
+ }
+ lastIndex := len(node.children)
+ if lastIndex > 0 {
+ output += fmt.Sprintf("%schildren: {\n", strings.Repeat(" ", nextIndent))
+ childIndent := nextIndent + 2
+ for _, elem := range node.children {
+ output += elem.PrettyPrint(childIndent)
+ }
+ }
+ output += fmt.Sprintf("%s}\n", spaces)
+ return output
+}
+
+var bindingPowers = map[tokType]int{
+ tEOF: 0,
+ tUnquotedIdentifier: 0,
+ tQuotedIdentifier: 0,
+ tRbracket: 0,
+ tRparen: 0,
+ tComma: 0,
+ tRbrace: 0,
+ tNumber: 0,
+ tCurrent: 0,
+ tExpref: 0,
+ tColon: 0,
+ tPipe: 1,
+ tOr: 2,
+ tAnd: 3,
+ tEQ: 5,
+ tLT: 5,
+ tLTE: 5,
+ tGT: 5,
+ tGTE: 5,
+ tNE: 5,
+ tFlatten: 9,
+ tStar: 20,
+ tFilter: 21,
+ tDot: 40,
+ tNot: 45,
+ tLbrace: 50,
+ tLbracket: 55,
+ tLparen: 60,
+}
+
+// Parser holds state about the current expression being parsed.
+type Parser struct {
+ expression string
+ tokens []token
+ index int
+}
+
+// NewParser creates a new JMESPath parser.
+func NewParser() *Parser {
+ p := Parser{}
+ return &p
+}
+
+// Parse will compile a JMESPath expression.
+func (p *Parser) Parse(expression string) (ASTNode, error) {
+ lexer := NewLexer()
+ p.expression = expression
+ p.index = 0
+ tokens, err := lexer.tokenize(expression)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ p.tokens = tokens
+ parsed, err := p.parseExpression(0)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ if p.current() != tEOF {
+ return ASTNode{}, p.syntaxError(fmt.Sprintf(
+ "Unexpected token at the end of the expresssion: %s", p.current()))
+ }
+ return parsed, nil
+}
+
+func (p *Parser) parseExpression(bindingPower int) (ASTNode, error) {
+ var err error
+ leftToken := p.lookaheadToken(0)
+ p.advance()
+ leftNode, err := p.nud(leftToken)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ currentToken := p.current()
+ for bindingPower < bindingPowers[currentToken] {
+ p.advance()
+ leftNode, err = p.led(currentToken, leftNode)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ currentToken = p.current()
+ }
+ return leftNode, nil
+}
+
+func (p *Parser) parseIndexExpression() (ASTNode, error) {
+ if p.lookahead(0) == tColon || p.lookahead(1) == tColon {
+ return p.parseSliceExpression()
+ }
+ indexStr := p.lookaheadToken(0).value
+ parsedInt, err := strconv.Atoi(indexStr)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ indexNode := ASTNode{nodeType: ASTIndex, value: parsedInt}
+ p.advance()
+ if err := p.match(tRbracket); err != nil {
+ return ASTNode{}, err
+ }
+ return indexNode, nil
+}
+
+func (p *Parser) parseSliceExpression() (ASTNode, error) {
+ parts := []*int{nil, nil, nil}
+ index := 0
+ current := p.current()
+ for current != tRbracket && index < 3 {
+ if current == tColon {
+ index++
+ p.advance()
+ } else if current == tNumber {
+ parsedInt, err := strconv.Atoi(p.lookaheadToken(0).value)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ parts[index] = &parsedInt
+ p.advance()
+ } else {
+ return ASTNode{}, p.syntaxError(
+ "Expected tColon or tNumber" + ", received: " + p.current().String())
+ }
+ current = p.current()
+ }
+ if err := p.match(tRbracket); err != nil {
+ return ASTNode{}, err
+ }
+ return ASTNode{
+ nodeType: ASTSlice,
+ value: parts,
+ }, nil
+}
+
+func (p *Parser) match(tokenType tokType) error {
+ if p.current() == tokenType {
+ p.advance()
+ return nil
+ }
+ return p.syntaxError("Expected " + tokenType.String() + ", received: " + p.current().String())
+}
+
+func (p *Parser) led(tokenType tokType, node ASTNode) (ASTNode, error) {
+ switch tokenType {
+ case tDot:
+ if p.current() != tStar {
+ right, err := p.parseDotRHS(bindingPowers[tDot])
+ return ASTNode{
+ nodeType: ASTSubexpression,
+ children: []ASTNode{node, right},
+ }, err
+ }
+ p.advance()
+ right, err := p.parseProjectionRHS(bindingPowers[tDot])
+ return ASTNode{
+ nodeType: ASTValueProjection,
+ children: []ASTNode{node, right},
+ }, err
+ case tPipe:
+ right, err := p.parseExpression(bindingPowers[tPipe])
+ return ASTNode{nodeType: ASTPipe, children: []ASTNode{node, right}}, err
+ case tOr:
+ right, err := p.parseExpression(bindingPowers[tOr])
+ return ASTNode{nodeType: ASTOrExpression, children: []ASTNode{node, right}}, err
+ case tAnd:
+ right, err := p.parseExpression(bindingPowers[tAnd])
+ return ASTNode{nodeType: ASTAndExpression, children: []ASTNode{node, right}}, err
+ case tLparen:
+ name := node.value
+ var args []ASTNode
+ for p.current() != tRparen {
+ expression, err := p.parseExpression(0)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ if p.current() == tComma {
+ if err := p.match(tComma); err != nil {
+ return ASTNode{}, err
+ }
+ }
+ args = append(args, expression)
+ }
+ if err := p.match(tRparen); err != nil {
+ return ASTNode{}, err
+ }
+ return ASTNode{
+ nodeType: ASTFunctionExpression,
+ value: name,
+ children: args,
+ }, nil
+ case tFilter:
+ return p.parseFilter(node)
+ case tFlatten:
+ left := ASTNode{nodeType: ASTFlatten, children: []ASTNode{node}}
+ right, err := p.parseProjectionRHS(bindingPowers[tFlatten])
+ return ASTNode{
+ nodeType: ASTProjection,
+ children: []ASTNode{left, right},
+ }, err
+ case tEQ, tNE, tGT, tGTE, tLT, tLTE:
+ right, err := p.parseExpression(bindingPowers[tokenType])
+ if err != nil {
+ return ASTNode{}, err
+ }
+ return ASTNode{
+ nodeType: ASTComparator,
+ value: tokenType,
+ children: []ASTNode{node, right},
+ }, nil
+ case tLbracket:
+ tokenType := p.current()
+ var right ASTNode
+ var err error
+ if tokenType == tNumber || tokenType == tColon {
+ right, err = p.parseIndexExpression()
+ if err != nil {
+ return ASTNode{}, err
+ }
+ return p.projectIfSlice(node, right)
+ }
+ // Otherwise this is a projection.
+ if err := p.match(tStar); err != nil {
+ return ASTNode{}, err
+ }
+ if err := p.match(tRbracket); err != nil {
+ return ASTNode{}, err
+ }
+ right, err = p.parseProjectionRHS(bindingPowers[tStar])
+ if err != nil {
+ return ASTNode{}, err
+ }
+ return ASTNode{
+ nodeType: ASTProjection,
+ children: []ASTNode{node, right},
+ }, nil
+ }
+ return ASTNode{}, p.syntaxError("Unexpected token: " + tokenType.String())
+}
+
+func (p *Parser) nud(token token) (ASTNode, error) {
+ switch token.tokenType {
+ case tJSONLiteral:
+ var parsed interface{}
+ err := json.Unmarshal([]byte(token.value), &parsed)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ return ASTNode{nodeType: ASTLiteral, value: parsed}, nil
+ case tStringLiteral:
+ return ASTNode{nodeType: ASTLiteral, value: token.value}, nil
+ case tUnquotedIdentifier:
+ return ASTNode{
+ nodeType: ASTField,
+ value: token.value,
+ }, nil
+ case tQuotedIdentifier:
+ node := ASTNode{nodeType: ASTField, value: token.value}
+ if p.current() == tLparen {
+ return ASTNode{}, p.syntaxErrorToken("Can't have quoted identifier as function name.", token)
+ }
+ return node, nil
+ case tStar:
+ left := ASTNode{nodeType: ASTIdentity}
+ var right ASTNode
+ var err error
+ if p.current() == tRbracket {
+ right = ASTNode{nodeType: ASTIdentity}
+ } else {
+ right, err = p.parseProjectionRHS(bindingPowers[tStar])
+ }
+ return ASTNode{nodeType: ASTValueProjection, children: []ASTNode{left, right}}, err
+ case tFilter:
+ return p.parseFilter(ASTNode{nodeType: ASTIdentity})
+ case tLbrace:
+ return p.parseMultiSelectHash()
+ case tFlatten:
+ left := ASTNode{
+ nodeType: ASTFlatten,
+ children: []ASTNode{{nodeType: ASTIdentity}},
+ }
+ right, err := p.parseProjectionRHS(bindingPowers[tFlatten])
+ if err != nil {
+ return ASTNode{}, err
+ }
+ return ASTNode{nodeType: ASTProjection, children: []ASTNode{left, right}}, nil
+ case tLbracket:
+ tokenType := p.current()
+ //var right ASTNode
+ if tokenType == tNumber || tokenType == tColon {
+ right, err := p.parseIndexExpression()
+ if err != nil {
+ return ASTNode{}, nil
+ }
+ return p.projectIfSlice(ASTNode{nodeType: ASTIdentity}, right)
+ } else if tokenType == tStar && p.lookahead(1) == tRbracket {
+ p.advance()
+ p.advance()
+ right, err := p.parseProjectionRHS(bindingPowers[tStar])
+ if err != nil {
+ return ASTNode{}, err
+ }
+ return ASTNode{
+ nodeType: ASTProjection,
+ children: []ASTNode{{nodeType: ASTIdentity}, right},
+ }, nil
+ } else {
+ return p.parseMultiSelectList()
+ }
+ case tCurrent:
+ return ASTNode{nodeType: ASTCurrentNode}, nil
+ case tExpref:
+ expression, err := p.parseExpression(bindingPowers[tExpref])
+ if err != nil {
+ return ASTNode{}, err
+ }
+ return ASTNode{nodeType: ASTExpRef, children: []ASTNode{expression}}, nil
+ case tNot:
+ expression, err := p.parseExpression(bindingPowers[tNot])
+ if err != nil {
+ return ASTNode{}, err
+ }
+ return ASTNode{nodeType: ASTNotExpression, children: []ASTNode{expression}}, nil
+ case tLparen:
+ expression, err := p.parseExpression(0)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ if err := p.match(tRparen); err != nil {
+ return ASTNode{}, err
+ }
+ return expression, nil
+ case tEOF:
+ return ASTNode{}, p.syntaxErrorToken("Incomplete expression", token)
+ }
+
+ return ASTNode{}, p.syntaxErrorToken("Invalid token: "+token.tokenType.String(), token)
+}
+
+func (p *Parser) parseMultiSelectList() (ASTNode, error) {
+ var expressions []ASTNode
+ for {
+ expression, err := p.parseExpression(0)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ expressions = append(expressions, expression)
+ if p.current() == tRbracket {
+ break
+ }
+ err = p.match(tComma)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ }
+ err := p.match(tRbracket)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ return ASTNode{
+ nodeType: ASTMultiSelectList,
+ children: expressions,
+ }, nil
+}
+
+func (p *Parser) parseMultiSelectHash() (ASTNode, error) {
+ var children []ASTNode
+ for {
+ keyToken := p.lookaheadToken(0)
+ if err := p.match(tUnquotedIdentifier); err != nil {
+ if err := p.match(tQuotedIdentifier); err != nil {
+ return ASTNode{}, p.syntaxError("Expected tQuotedIdentifier or tUnquotedIdentifier")
+ }
+ }
+ keyName := keyToken.value
+ err := p.match(tColon)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ value, err := p.parseExpression(0)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ node := ASTNode{
+ nodeType: ASTKeyValPair,
+ value: keyName,
+ children: []ASTNode{value},
+ }
+ children = append(children, node)
+ if p.current() == tComma {
+ err := p.match(tComma)
+ if err != nil {
+ return ASTNode{}, nil
+ }
+ } else if p.current() == tRbrace {
+ err := p.match(tRbrace)
+ if err != nil {
+ return ASTNode{}, nil
+ }
+ break
+ }
+ }
+ return ASTNode{
+ nodeType: ASTMultiSelectHash,
+ children: children,
+ }, nil
+}
+
+func (p *Parser) projectIfSlice(left ASTNode, right ASTNode) (ASTNode, error) {
+ indexExpr := ASTNode{
+ nodeType: ASTIndexExpression,
+ children: []ASTNode{left, right},
+ }
+ if right.nodeType == ASTSlice {
+ right, err := p.parseProjectionRHS(bindingPowers[tStar])
+ return ASTNode{
+ nodeType: ASTProjection,
+ children: []ASTNode{indexExpr, right},
+ }, err
+ }
+ return indexExpr, nil
+}
+func (p *Parser) parseFilter(node ASTNode) (ASTNode, error) {
+ var right, condition ASTNode
+ var err error
+ condition, err = p.parseExpression(0)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ if err := p.match(tRbracket); err != nil {
+ return ASTNode{}, err
+ }
+ if p.current() == tFlatten {
+ right = ASTNode{nodeType: ASTIdentity}
+ } else {
+ right, err = p.parseProjectionRHS(bindingPowers[tFilter])
+ if err != nil {
+ return ASTNode{}, err
+ }
+ }
+
+ return ASTNode{
+ nodeType: ASTFilterProjection,
+ children: []ASTNode{node, right, condition},
+ }, nil
+}
+
+func (p *Parser) parseDotRHS(bindingPower int) (ASTNode, error) {
+ lookahead := p.current()
+ if tokensOneOf([]tokType{tQuotedIdentifier, tUnquotedIdentifier, tStar}, lookahead) {
+ return p.parseExpression(bindingPower)
+ } else if lookahead == tLbracket {
+ if err := p.match(tLbracket); err != nil {
+ return ASTNode{}, err
+ }
+ return p.parseMultiSelectList()
+ } else if lookahead == tLbrace {
+ if err := p.match(tLbrace); err != nil {
+ return ASTNode{}, err
+ }
+ return p.parseMultiSelectHash()
+ }
+ return ASTNode{}, p.syntaxError("Expected identifier, lbracket, or lbrace")
+}
+
+func (p *Parser) parseProjectionRHS(bindingPower int) (ASTNode, error) {
+ current := p.current()
+ if bindingPowers[current] < 10 {
+ return ASTNode{nodeType: ASTIdentity}, nil
+ } else if current == tLbracket {
+ return p.parseExpression(bindingPower)
+ } else if current == tFilter {
+ return p.parseExpression(bindingPower)
+ } else if current == tDot {
+ err := p.match(tDot)
+ if err != nil {
+ return ASTNode{}, err
+ }
+ return p.parseDotRHS(bindingPower)
+ } else {
+ return ASTNode{}, p.syntaxError("Error")
+ }
+}
+
+func (p *Parser) lookahead(number int) tokType {
+ return p.lookaheadToken(number).tokenType
+}
+
+func (p *Parser) current() tokType {
+ return p.lookahead(0)
+}
+
+func (p *Parser) lookaheadToken(number int) token {
+ return p.tokens[p.index+number]
+}
+
+func (p *Parser) advance() {
+ p.index++
+}
+
+func tokensOneOf(elements []tokType, token tokType) bool {
+ for _, elem := range elements {
+ if elem == token {
+ return true
+ }
+ }
+ return false
+}
+
+func (p *Parser) syntaxError(msg string) SyntaxError {
+ return SyntaxError{
+ msg: msg,
+ Expression: p.expression,
+ Offset: p.lookaheadToken(0).position,
+ }
+}
+
+// Create a SyntaxError based on the provided token.
+// This differs from syntaxError() which creates a SyntaxError
+// based on the current lookahead token.
+func (p *Parser) syntaxErrorToken(msg string, t token) SyntaxError {
+ return SyntaxError{
+ msg: msg,
+ Expression: p.expression,
+ Offset: t.position,
+ }
+}
diff --git a/vendor/github.com/jmespath/go-jmespath/toktype_string.go b/vendor/github.com/jmespath/go-jmespath/toktype_string.go
new file mode 100644
index 00000000..dae79cbd
--- /dev/null
+++ b/vendor/github.com/jmespath/go-jmespath/toktype_string.go
@@ -0,0 +1,16 @@
+// generated by stringer -type=tokType; DO NOT EDIT
+
+package jmespath
+
+import "fmt"
+
+const _tokType_name = "tUnknowntStartDottFiltertFlattentLparentRparentLbrackettRbrackettLbracetRbracetOrtPipetNumbertUnquotedIdentifiertQuotedIdentifiertCommatColontLTtLTEtGTtGTEtEQtNEtJSONLiteraltStringLiteraltCurrenttExpreftAndtNottEOF"
+
+var _tokType_index = [...]uint8{0, 8, 13, 17, 24, 32, 39, 46, 55, 64, 71, 78, 81, 86, 93, 112, 129, 135, 141, 144, 148, 151, 155, 158, 161, 173, 187, 195, 202, 206, 210, 214}
+
+func (i tokType) String() string {
+ if i < 0 || i >= tokType(len(_tokType_index)-1) {
+ return fmt.Sprintf("tokType(%d)", i)
+ }
+ return _tokType_name[_tokType_index[i]:_tokType_index[i+1]]
+}
diff --git a/vendor/github.com/jmespath/go-jmespath/util.go b/vendor/github.com/jmespath/go-jmespath/util.go
new file mode 100644
index 00000000..ddc1b7d7
--- /dev/null
+++ b/vendor/github.com/jmespath/go-jmespath/util.go
@@ -0,0 +1,185 @@
+package jmespath
+
+import (
+ "errors"
+ "reflect"
+)
+
+// IsFalse determines if an object is false based on the JMESPath spec.
+// JMESPath defines false values to be any of:
+// - An empty string array, or hash.
+// - The boolean value false.
+// - nil
+func isFalse(value interface{}) bool {
+ switch v := value.(type) {
+ case bool:
+ return !v
+ case []interface{}:
+ return len(v) == 0
+ case map[string]interface{}:
+ return len(v) == 0
+ case string:
+ return len(v) == 0
+ case nil:
+ return true
+ }
+ // Try the reflection cases before returning false.
+ rv := reflect.ValueOf(value)
+ switch rv.Kind() {
+ case reflect.Struct:
+ // A struct type will never be false, even if
+ // all of its values are the zero type.
+ return false
+ case reflect.Slice, reflect.Map:
+ return rv.Len() == 0
+ case reflect.Ptr:
+ if rv.IsNil() {
+ return true
+ }
+ // If it's a pointer type, we'll try to deref the pointer
+ // and evaluate the pointer value for isFalse.
+ element := rv.Elem()
+ return isFalse(element.Interface())
+ }
+ return false
+}
+
+// ObjsEqual is a generic object equality check.
+// It will take two arbitrary objects and recursively determine
+// if they are equal.
+func objsEqual(left interface{}, right interface{}) bool {
+ return reflect.DeepEqual(left, right)
+}
+
+// SliceParam refers to a single part of a slice.
+// A slice consists of a start, a stop, and a step, similar to
+// python slices.
+type sliceParam struct {
+ N int
+ Specified bool
+}
+
+// Slice supports [start:stop:step] style slicing that's supported in JMESPath.
+func slice(slice []interface{}, parts []sliceParam) ([]interface{}, error) {
+ computed, err := computeSliceParams(len(slice), parts)
+ if err != nil {
+ return nil, err
+ }
+ start, stop, step := computed[0], computed[1], computed[2]
+ result := []interface{}{}
+ if step > 0 {
+ for i := start; i < stop; i += step {
+ result = append(result, slice[i])
+ }
+ } else {
+ for i := start; i > stop; i += step {
+ result = append(result, slice[i])
+ }
+ }
+ return result, nil
+}
+
+func computeSliceParams(length int, parts []sliceParam) ([]int, error) {
+ var start, stop, step int
+ if !parts[2].Specified {
+ step = 1
+ } else if parts[2].N == 0 {
+ return nil, errors.New("Invalid slice, step cannot be 0")
+ } else {
+ step = parts[2].N
+ }
+ var stepValueNegative bool
+ if step < 0 {
+ stepValueNegative = true
+ } else {
+ stepValueNegative = false
+ }
+
+ if !parts[0].Specified {
+ if stepValueNegative {
+ start = length - 1
+ } else {
+ start = 0
+ }
+ } else {
+ start = capSlice(length, parts[0].N, step)
+ }
+
+ if !parts[1].Specified {
+ if stepValueNegative {
+ stop = -1
+ } else {
+ stop = length
+ }
+ } else {
+ stop = capSlice(length, parts[1].N, step)
+ }
+ return []int{start, stop, step}, nil
+}
+
+func capSlice(length int, actual int, step int) int {
+ if actual < 0 {
+ actual += length
+ if actual < 0 {
+ if step < 0 {
+ actual = -1
+ } else {
+ actual = 0
+ }
+ }
+ } else if actual >= length {
+ if step < 0 {
+ actual = length - 1
+ } else {
+ actual = length
+ }
+ }
+ return actual
+}
+
+// ToArrayNum converts an empty interface type to a slice of float64.
+// If any element in the array cannot be converted, then nil is returned
+// along with a second value of false.
+func toArrayNum(data interface{}) ([]float64, bool) {
+ // Is there a better way to do this with reflect?
+ if d, ok := data.([]interface{}); ok {
+ result := make([]float64, len(d))
+ for i, el := range d {
+ item, ok := el.(float64)
+ if !ok {
+ return nil, false
+ }
+ result[i] = item
+ }
+ return result, true
+ }
+ return nil, false
+}
+
+// ToArrayStr converts an empty interface type to a slice of strings.
+// If any element in the array cannot be converted, then nil is returned
+// along with a second value of false. If the input data could be entirely
+// converted, then the converted data, along with a second value of true,
+// will be returned.
+func toArrayStr(data interface{}) ([]string, bool) {
+ // Is there a better way to do this with reflect?
+ if d, ok := data.([]interface{}); ok {
+ result := make([]string, len(d))
+ for i, el := range d {
+ item, ok := el.(string)
+ if !ok {
+ return nil, false
+ }
+ result[i] = item
+ }
+ return result, true
+ }
+ return nil, false
+}
+
+func isSliceType(v interface{}) bool {
+ if v == nil {
+ return false
+ }
+ return reflect.TypeOf(v).Kind() == reflect.Slice
+}
diff --git a/vendor/github.com/mmcloughlin/geohash/.appveyor.yml b/vendor/github.com/mmcloughlin/geohash/.appveyor.yml
new file mode 100644
index 00000000..aecdaf0d
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/geohash/.appveyor.yml
@@ -0,0 +1,15 @@
+version: "{build}"
+
+clone_folder: c:\gopath\src\github.com\mmcloughlin\geohash
+
+environment:
+ GOPATH: c:\gopath
+
+install:
+ - echo %PATH%
+ - echo %GOPATH%
+ - go version
+ - go env
+
+build_script:
+ - go test -v
diff --git a/vendor/github.com/mmcloughlin/geohash/.gitignore b/vendor/github.com/mmcloughlin/geohash/.gitignore
new file mode 100644
index 00000000..13e34578
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/geohash/.gitignore
@@ -0,0 +1,26 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+
+coverage.out
diff --git a/vendor/github.com/mmcloughlin/geohash/.travis.yml b/vendor/github.com/mmcloughlin/geohash/.travis.yml
new file mode 100644
index 00000000..0332114c
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/geohash/.travis.yml
@@ -0,0 +1,22 @@
+language: go
+go:
+- 1.x
+- 1.3.x
+- 1.4.x
+- 1.5.x
+- 1.6.x
+- 1.7.x
+- 1.8.x
+- 1.9.x
+- 1.10.x
+- 1.11.x
+before_install:
+- go get github.com/axw/gocov/gocov
+- go get github.com/mattn/goveralls
+- go get golang.org/x/tools/cmd/cover
+- go get github.com/klauspost/asmfmt/cmd/asmfmt
+script:
+- test -z "$(asmfmt -l *.s)"
+- go test -v -covermode=count -coverprofile=profile.cov
+after_success:
+- goveralls -coverprofile=profile.cov -service=travis-ci
diff --git a/vendor/github.com/mmcloughlin/geohash/LICENSE b/vendor/github.com/mmcloughlin/geohash/LICENSE
new file mode 100644
index 00000000..c0190c9a
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/geohash/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Michael McLoughlin
+
+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.
+
diff --git a/vendor/github.com/mmcloughlin/geohash/README.md b/vendor/github.com/mmcloughlin/geohash/README.md
new file mode 100644
index 00000000..0a34e3eb
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/geohash/README.md
@@ -0,0 +1,214 @@
+# geohash
+
+Go [geohash](https://en.wikipedia.org/wiki/Geohash) library offering encoding
+and decoding for string and integer geohashes.
+
+[](http://godoc.org/github.com/mmcloughlin/geohash)
+[](https://travis-ci.org/mmcloughlin/geohash)
+[](https://coveralls.io/r/mmcloughlin/geohash)
+[](https://goreportcard.com/report/github.com/mmcloughlin/geohash)
+
+## Install
+
+Fetch the package with
+
+```
+go get github.com/mmcloughlin/geohash
+```
+
+And import it into your programs with
+
+```go
+import "github.com/mmcloughlin/geohash"
+```
+
+## Usage
+
+#### func Decode
+
+```go
+func Decode(hash string) (lat, lng float64)
+```
+Decode the string geohash to a (lat, lng) point.
+
+#### func DecodeCenter
+
+```go
+func DecodeCenter(hash string) (lat, lng float64)
+```
+DecodeCenter decodes the string geohash to the central point of the bounding
+box.
+
+#### func DecodeInt
+
+```go
+func DecodeInt(hash uint64) (lat, lng float64)
+```
+DecodeInt decodes the provided 64-bit integer geohash to a (lat, lng) point.
+
+#### func DecodeIntWithPrecision
+
+```go
+func DecodeIntWithPrecision(hash uint64, bits uint) (lat, lng float64)
+```
+DecodeIntWithPrecision decodes the provided integer geohash with bits of
+precision to a (lat, lng) point.
+
+#### func Encode
+
+```go
+func Encode(lat, lng float64) string
+```
+Encode the point (lat, lng) as a string geohash with the standard 12 characters
+of precision.
+
+#### func EncodeInt
+
+```go
+func EncodeInt(lat, lng float64) uint64
+```
+EncodeInt encodes the point (lat, lng) to a 64-bit integer geohash.
+
+#### func EncodeIntWithPrecision
+
+```go
+func EncodeIntWithPrecision(lat, lng float64, bits uint) uint64
+```
+EncodeIntWithPrecision encodes the point (lat, lng) to an integer with the
+specified number of bits.
+
+#### func EncodeWithPrecision
+
+```go
+func EncodeWithPrecision(lat, lng float64, chars uint) string
+```
+EncodeWithPrecision encodes the point (lat, lng) as a string geohash with the
+specified number of characters of precision (max 12).
+
+#### func Neighbor
+
+```go
+func Neighbor(hash string, direction Direction) string
+```
+Neighbor returns a geohash string that corresponds to the provided geohash's
+neighbor in the provided direction
+
+#### func NeighborInt
+
+```go
+func NeighborInt(hash uint64, direction Direction) uint64
+```
+NeighborInt returns a uint64 that corresponds to the provided hash's neighbor in
+the provided direction at 64-bit precision.
+
+#### func NeighborIntWithPrecision
+
+```go
+func NeighborIntWithPrecision(hash uint64, bits uint, direction Direction) uint64
+```
+NeighborIntWithPrecision returns a uint64s that corresponds to the provided
+hash's neighbor in the provided direction at the given precision.
+
+#### func Neighbors
+
+```go
+func Neighbors(hash string) []string
+```
+Neighbors returns a slice of geohash strings that correspond to the provided
+geohash's neighbors.
+
+#### func NeighborsInt
+
+```go
+func NeighborsInt(hash uint64) []uint64
+```
+NeighborsInt returns a slice of uint64s that correspond to the provided hash's
+neighbors at 64-bit precision.
+
+#### func NeighborsIntWithPrecision
+
+```go
+func NeighborsIntWithPrecision(hash uint64, bits uint) []uint64
+```
+NeighborsIntWithPrecision returns a slice of uint64s that correspond to the
+provided hash's neighbors at the given precision.
+
+#### type Box
+
+```go
+type Box struct {
+ MinLat float64
+ MaxLat float64
+ MinLng float64
+ MaxLng float64
+}
+```
+
+Box represents a rectangle in latitude/longitude space.
+
+#### func BoundingBox
+
+```go
+func BoundingBox(hash string) Box
+```
+BoundingBox returns the region encoded by the given string geohash.
+
+#### func BoundingBoxInt
+
+```go
+func BoundingBoxInt(hash uint64) Box
+```
+BoundingBoxInt returns the region encoded by the given 64-bit integer geohash.
+
+#### func BoundingBoxIntWithPrecision
+
+```go
+func BoundingBoxIntWithPrecision(hash uint64, bits uint) Box
+```
+BoundingBoxIntWithPrecision returns the region encoded by the integer geohash
+with the specified precision.
+
+#### func (Box) Center
+
+```go
+func (b Box) Center() (lat, lng float64)
+```
+Center returns the center of the box.
+
+#### func (Box) Contains
+
+```go
+func (b Box) Contains(lat, lng float64) bool
+```
+Contains decides whether (lat, lng) is contained in the box. The containment
+test is inclusive of the edges and corners.
+
+#### func (Box) Round
+
+```go
+func (b Box) Round() (lat, lng float64)
+```
+Round returns a point inside the box, making an effort to round to minimal
+precision.
+
+#### type Direction
+
+```go
+type Direction int
+```
+
+Direction represents directions in the latitute/longitude space.
+
+```go
+const (
+ North Direction = iota
+ NorthEast
+ East
+ SouthEast
+ South
+ SouthWest
+ West
+ NorthWest
+)
+```
+Cardinal and intercardinal directions
diff --git a/vendor/github.com/mmcloughlin/geohash/README.tmpl b/vendor/github.com/mmcloughlin/geohash/README.tmpl
new file mode 100644
index 00000000..6f0fe4b8
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/geohash/README.tmpl
@@ -0,0 +1,25 @@
+# geohash
+
+Go [geohash](https://en.wikipedia.org/wiki/Geohash) library offering encoding
+and decoding for string and integer geohashes.
+
+[](http://godoc.org/github.com/mmcloughlin/geohash)
+[](https://travis-ci.org/mmcloughlin/geohash)
+[](https://coveralls.io/r/mmcloughlin/geohash)
+[](https://goreportcard.com/report/github.com/mmcloughlin/geohash)
+
+## Install
+
+Fetch the package with
+
+```
+go get {{ .ImportPath }}
+```
+
+And import it into your programs with
+
+```go
+import "{{ .ImportPath }}"
+```
+
+{{ .EmitUsage }}
diff --git a/vendor/github.com/mmcloughlin/geohash/asm_x86.s b/vendor/github.com/mmcloughlin/geohash/asm_x86.s
new file mode 100644
index 00000000..0d2d80c0
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/geohash/asm_x86.s
@@ -0,0 +1,56 @@
+// +build amd64,go1.6
+
+#include "textflag.h"
+
+// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
+TEXT ·cpuid(SB), NOSPLIT, $0-24
+ MOVL eaxArg+0(FP), AX
+ MOVL ecxArg+4(FP), CX
+ CPUID
+ MOVL AX, eax+8(FP)
+ MOVL BX, ebx+12(FP)
+ MOVL CX, ecx+16(FP)
+ MOVL DX, edx+20(FP)
+ RET
+
+// func EncodeInt(lat, lng float64) uint64
+TEXT ·EncodeInt(SB), NOSPLIT, $0
+ CMPB ·useAsm(SB), $1
+ JNE fallback
+
+#define LATF X0
+#define LATI R8
+#define LNGF X1
+#define LNGI R9
+#define TEMP R10
+#define GHSH R11
+#define MASK BX
+
+ MOVSD lat+0(FP), LATF
+ MOVSD lng+8(FP), LNGF
+
+ MOVQ $0x5555555555555555, MASK
+
+ MULSD $(0.005555555555555556), LATF
+ ADDSD $(1.5), LATF
+
+ MULSD $(0.002777777777777778), LNGF
+ ADDSD $(1.5), LNGF
+
+ MOVQ LNGF, LNGI
+ SHRQ $20, LNGI
+
+ MOVQ LATF, LATI
+ SHRQ $20, LATI
+ PDEPQ MASK, LATI, GHSH
+
+ PDEPQ MASK, LNGI, TEMP
+
+ SHLQ $1, TEMP
+ XORQ TEMP, GHSH
+
+ MOVQ GHSH, ret+16(FP)
+ RET
+
+fallback:
+ JMP ·encodeInt(SB)
diff --git a/vendor/github.com/mmcloughlin/geohash/base32.go b/vendor/github.com/mmcloughlin/geohash/base32.go
new file mode 100644
index 00000000..916b272b
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/geohash/base32.go
@@ -0,0 +1,44 @@
+package geohash
+
+// encoding encapsulates an encoding defined by a given base32 alphabet.
+type encoding struct {
+ encode string
+ decode [256]byte
+}
+
+// newEncoding constructs a new encoding defined by the given alphabet,
+// which must be a 32-byte string.
+func newEncoding(encoder string) *encoding {
+ e := new(encoding)
+ e.encode = encoder
+ for i := 0; i < len(e.decode); i++ {
+ e.decode[i] = 0xff
+ }
+ for i := 0; i < len(encoder); i++ {
+ e.decode[encoder[i]] = byte(i)
+ }
+ return e
+}
+
+// Decode string into bits of a 64-bit word. The string s may be at most 12
+// characters.
+func (e *encoding) Decode(s string) uint64 {
+ x := uint64(0)
+ for i := 0; i < len(s); i++ {
+ x = (x << 5) | uint64(e.decode[s[i]])
+ }
+ return x
+}
+
+// Encode bits of 64-bit word into a string.
+func (e *encoding) Encode(x uint64) string {
+ b := [12]byte{}
+ for i := 0; i < 12; i++ {
+ b[11-i] = e.encode[x&0x1f]
+ x >>= 5
+ }
+ return string(b[:])
+}
+
+// Base32Encoding with the Geohash alphabet.
+var base32encoding = newEncoding("0123456789bcdefghjkmnpqrstuvwxyz")
diff --git a/vendor/github.com/mmcloughlin/geohash/geohash.go b/vendor/github.com/mmcloughlin/geohash/geohash.go
new file mode 100644
index 00000000..85633049
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/geohash/geohash.go
@@ -0,0 +1,292 @@
+// Package geohash provides encoding and decoding of string and integer
+// geohashes.
+package geohash
+
+import (
+ "math"
+)
+
+// Direction represents directions in the latitute/longitude space.
+type Direction int
+
+// Cardinal and intercardinal directions
+const (
+ North Direction = iota
+ NorthEast
+ East
+ SouthEast
+ South
+ SouthWest
+ West
+ NorthWest
+)
+
+// Encode the point (lat, lng) as a string geohash with the standard 12
+// characters of precision.
+func Encode(lat, lng float64) string {
+ return EncodeWithPrecision(lat, lng, 12)
+}
+
+// EncodeWithPrecision encodes the point (lat, lng) as a string geohash with
+// the specified number of characters of precision (max 12).
+func EncodeWithPrecision(lat, lng float64, chars uint) string {
+ bits := 5 * chars
+ inthash := EncodeIntWithPrecision(lat, lng, bits)
+ enc := base32encoding.Encode(inthash)
+ return enc[12-chars:]
+}
+
+// EncodeInt encodes the point (lat, lng) to a 64-bit integer geohash.
+func EncodeInt(lat, lng float64) uint64
+
+// encodeInt provides a Go implementation of integer geohash. This is the
+// default implementation of EncodeInt, but optimized versions are provided
+// for certain architectures.
+func encodeInt(lat, lng float64) uint64 {
+ latInt := encodeRange(lat, 90)
+ lngInt := encodeRange(lng, 180)
+ return interleave(latInt, lngInt)
+}
+
+// EncodeIntWithPrecision encodes the point (lat, lng) to an integer with the
+// specified number of bits.
+func EncodeIntWithPrecision(lat, lng float64, bits uint) uint64 {
+ hash := EncodeInt(lat, lng)
+ return hash >> (64 - bits)
+}
+
+// Box represents a rectangle in latitude/longitude space.
+type Box struct {
+ MinLat float64
+ MaxLat float64
+ MinLng float64
+ MaxLng float64
+}
+
+// Center returns the center of the box.
+func (b Box) Center() (lat, lng float64) {
+ lat = (b.MinLat + b.MaxLat) / 2.0
+ lng = (b.MinLng + b.MaxLng) / 2.0
+ return
+}
+
+// Contains decides whether (lat, lng) is contained in the box. The
+// containment test is inclusive of the edges and corners.
+func (b Box) Contains(lat, lng float64) bool {
+ return (b.MinLat <= lat && lat <= b.MaxLat &&
+ b.MinLng <= lng && lng <= b.MaxLng)
+}
+
+// minDecimalPlaces returns the minimum number of decimal places such that
+// there must exist an number with that many places within any range of width
+// r. This is intended for returning minimal precision coordinates inside a
+// box.
+func maxDecimalPower(r float64) float64 {
+ m := int(math.Floor(math.Log10(r)))
+ return math.Pow10(m)
+}
+
+// Round returns a point inside the box, making an effort to round to minimal
+// precision.
+func (b Box) Round() (lat, lng float64) {
+ x := maxDecimalPower(b.MaxLat - b.MinLat)
+ lat = math.Ceil(b.MinLat/x) * x
+ x = maxDecimalPower(b.MaxLng - b.MinLng)
+ lng = math.Ceil(b.MinLng/x) * x
+ return
+}
+
+// errorWithPrecision returns the error range in latitude and longitude for in
+// integer geohash with bits of precision.
+func errorWithPrecision(bits uint) (latErr, lngErr float64) {
+ b := int(bits)
+ latBits := b / 2
+ lngBits := b - latBits
+ latErr = math.Ldexp(180.0, -latBits)
+ lngErr = math.Ldexp(360.0, -lngBits)
+ return
+}
+
+// BoundingBox returns the region encoded by the given string geohash.
+func BoundingBox(hash string) Box {
+ bits := uint(5 * len(hash))
+ inthash := base32encoding.Decode(hash)
+ return BoundingBoxIntWithPrecision(inthash, bits)
+}
+
+// BoundingBoxIntWithPrecision returns the region encoded by the integer
+// geohash with the specified precision.
+func BoundingBoxIntWithPrecision(hash uint64, bits uint) Box {
+ fullHash := hash << (64 - bits)
+ latInt, lngInt := deinterleave(fullHash)
+ lat := decodeRange(latInt, 90)
+ lng := decodeRange(lngInt, 180)
+ latErr, lngErr := errorWithPrecision(bits)
+ return Box{
+ MinLat: lat,
+ MaxLat: lat + latErr,
+ MinLng: lng,
+ MaxLng: lng + lngErr,
+ }
+}
+
+// BoundingBoxInt returns the region encoded by the given 64-bit integer
+// geohash.
+func BoundingBoxInt(hash uint64) Box {
+ return BoundingBoxIntWithPrecision(hash, 64)
+}
+
+// Decode the string geohash to a (lat, lng) point.
+func Decode(hash string) (lat, lng float64) {
+ box := BoundingBox(hash)
+ return box.Round()
+}
+
+// DecodeCenter decodes the string geohash to the central point of the bounding box.
+func DecodeCenter(hash string) (lat, lng float64) {
+ box := BoundingBox(hash)
+ return box.Center()
+}
+
+// DecodeIntWithPrecision decodes the provided integer geohash with bits of
+// precision to a (lat, lng) point.
+func DecodeIntWithPrecision(hash uint64, bits uint) (lat, lng float64) {
+ box := BoundingBoxIntWithPrecision(hash, bits)
+ return box.Round()
+}
+
+// DecodeInt decodes the provided 64-bit integer geohash to a (lat, lng) point.
+func DecodeInt(hash uint64) (lat, lng float64) {
+ return DecodeIntWithPrecision(hash, 64)
+}
+
+// Neighbors returns a slice of geohash strings that correspond to the provided
+// geohash's neighbors.
+func Neighbors(hash string) []string {
+ box := BoundingBox(hash)
+ lat, lng := box.Center()
+ latDelta := box.MaxLat - box.MinLat
+ lngDelta := box.MaxLng - box.MinLng
+ precision := uint(len(hash))
+ return []string{
+ // N
+ EncodeWithPrecision(lat+latDelta, lng, precision),
+ // NE,
+ EncodeWithPrecision(lat+latDelta, lng+lngDelta, precision),
+ // E,
+ EncodeWithPrecision(lat, lng+lngDelta, precision),
+ // SE,
+ EncodeWithPrecision(lat-latDelta, lng+lngDelta, precision),
+ // S,
+ EncodeWithPrecision(lat-latDelta, lng, precision),
+ // SW,
+ EncodeWithPrecision(lat-latDelta, lng-lngDelta, precision),
+ // W,
+ EncodeWithPrecision(lat, lng-lngDelta, precision),
+ // NW
+ EncodeWithPrecision(lat+latDelta, lng-lngDelta, precision),
+ }
+}
+
+// NeighborsInt returns a slice of uint64s that correspond to the provided hash's
+// neighbors at 64-bit precision.
+func NeighborsInt(hash uint64) []uint64 {
+ return NeighborsIntWithPrecision(hash, 64)
+}
+
+// NeighborsIntWithPrecision returns a slice of uint64s that correspond to the
+// provided hash's neighbors at the given precision.
+func NeighborsIntWithPrecision(hash uint64, bits uint) []uint64 {
+ box := BoundingBoxIntWithPrecision(hash, bits)
+ lat, lng := box.Center()
+ latDelta := box.MaxLat - box.MinLat
+ lngDelta := box.MaxLng - box.MinLng
+ return []uint64{
+ // N
+ EncodeIntWithPrecision(lat+latDelta, lng, bits),
+ // NE,
+ EncodeIntWithPrecision(lat+latDelta, lng+lngDelta, bits),
+ // E,
+ EncodeIntWithPrecision(lat, lng+lngDelta, bits),
+ // SE,
+ EncodeIntWithPrecision(lat-latDelta, lng+lngDelta, bits),
+ // S,
+ EncodeIntWithPrecision(lat-latDelta, lng, bits),
+ // SW,
+ EncodeIntWithPrecision(lat-latDelta, lng-lngDelta, bits),
+ // W,
+ EncodeIntWithPrecision(lat, lng-lngDelta, bits),
+ // NW
+ EncodeIntWithPrecision(lat+latDelta, lng-lngDelta, bits),
+ }
+}
+
+// Neighbor returns a geohash string that corresponds to the provided
+// geohash's neighbor in the provided direction
+func Neighbor(hash string, direction Direction) string {
+ return Neighbors(hash)[direction]
+}
+
+// NeighborInt returns a uint64 that corresponds to the provided hash's
+// neighbor in the provided direction at 64-bit precision.
+func NeighborInt(hash uint64, direction Direction) uint64 {
+ return NeighborsIntWithPrecision(hash, 64)[direction]
+}
+
+// NeighborIntWithPrecision returns a uint64s that corresponds to the
+// provided hash's neighbor in the provided direction at the given precision.
+func NeighborIntWithPrecision(hash uint64, bits uint, direction Direction) uint64 {
+ return NeighborsIntWithPrecision(hash, bits)[direction]
+}
+
+// precalculated for performance
+var exp232 = math.Exp2(32)
+
+// Encode the position of x within the range -r to +r as a 32-bit integer.
+func encodeRange(x, r float64) uint32 {
+ p := (x + r) / (2 * r)
+ return uint32(p * exp232)
+}
+
+// Decode the 32-bit range encoding X back to a value in the range -r to +r.
+func decodeRange(X uint32, r float64) float64 {
+ p := float64(X) / exp232
+ x := 2*r*p - r
+ return x
+}
+
+// Spread out the 32 bits of x into 64 bits, where the bits of x occupy even
+// bit positions.
+func spread(x uint32) uint64 {
+ X := uint64(x)
+ X = (X | (X << 16)) & 0x0000ffff0000ffff
+ X = (X | (X << 8)) & 0x00ff00ff00ff00ff
+ X = (X | (X << 4)) & 0x0f0f0f0f0f0f0f0f
+ X = (X | (X << 2)) & 0x3333333333333333
+ X = (X | (X << 1)) & 0x5555555555555555
+ return X
+}
+
+// Interleave the bits of x and y. In the result, x and y occupy even and odd
+// bitlevels, respectively.
+func interleave(x, y uint32) uint64 {
+ return spread(x) | (spread(y) << 1)
+}
+
+// Squash the even bitlevels of X into a 32-bit word. Odd bitlevels of X are
+// ignored, and may take any value.
+func squash(X uint64) uint32 {
+ X &= 0x5555555555555555
+ X = (X | (X >> 1)) & 0x3333333333333333
+ X = (X | (X >> 2)) & 0x0f0f0f0f0f0f0f0f
+ X = (X | (X >> 4)) & 0x00ff00ff00ff00ff
+ X = (X | (X >> 8)) & 0x0000ffff0000ffff
+ X = (X | (X >> 16)) & 0x00000000ffffffff
+ return uint32(X)
+}
+
+// Deinterleave the bits of X into 32-bit words containing the even and odd
+// bitlevels of X, respectively.
+func deinterleave(X uint64) (uint32, uint32) {
+ return squash(X), squash(X >> 1)
+}
diff --git a/vendor/github.com/mmcloughlin/geohash/geohash_x86.go b/vendor/github.com/mmcloughlin/geohash/geohash_x86.go
new file mode 100644
index 00000000..68cfb18d
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/geohash/geohash_x86.go
@@ -0,0 +1,24 @@
+// +build amd64,go1.6
+
+package geohash
+
+// useAsm flag determines whether the assembly version of EncodeInt will be
+// used. By Default we fall back to encodeInt.
+var useAsm bool
+
+// cpuid executes the CPUID instruction to obtain processor identification and
+// feature information.
+func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
+
+// hasBMI2 returns whether the CPU supports Bit Manipulation Instruction Set
+// 2.
+func hasBMI2() bool {
+ _, ebx, _, _ := cpuid(7, 0)
+ return ebx&(1<<8) != 0
+}
+
+// init determines whether to use assembly version by performing CPU feature
+// check.
+func init() {
+ useAsm = hasBMI2()
+}
diff --git a/vendor/github.com/mmcloughlin/geohash/stubs.s b/vendor/github.com/mmcloughlin/geohash/stubs.s
new file mode 100644
index 00000000..97c046c9
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/geohash/stubs.s
@@ -0,0 +1,7 @@
+// +build !amd64 !go1.6
+
+// Define NOSPLIT ourselves since "textflag.h" is missing in old Go versions.
+#define NOSPLIT 4
+
+TEXT ·EncodeInt(SB), NOSPLIT, $0
+ JMP ·encodeInt(SB)
diff --git a/vendor/github.com/nats-io/go-nats/.gitignore b/vendor/github.com/nats-io/go-nats/.gitignore
new file mode 100644
index 00000000..3d5981fa
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/.gitignore
@@ -0,0 +1,39 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+
+# Emacs
+*~
+\#*\#
+.\#*
+
+# vi/vim
+.??*.swp
+
+# Mac
+.DS_Store
+
+# Eclipse
+.project
+.settings/
+
+# bin
diff --git a/vendor/github.com/nats-io/go-nats/.travis.yml b/vendor/github.com/nats-io/go-nats/.travis.yml
new file mode 100644
index 00000000..732e4753
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/.travis.yml
@@ -0,0 +1,21 @@
+language: go
+sudo: false
+go:
+- 1.11.x
+- 1.10.x
+- 1.9.x
+install:
+- go get -t ./...
+- go get github.com/nats-io/gnatsd
+- go get github.com/mattn/goveralls
+- go get github.com/wadey/gocovmerge
+- go get -u honnef.co/go/tools/cmd/megacheck
+- go get -u github.com/client9/misspell/cmd/misspell
+before_script:
+- $(exit $(go fmt ./... | wc -l))
+- go vet ./...
+- misspell -error -locale US .
+- megacheck -ignore "$(cat staticcheck.ignore)" ./...
+script:
+- go test -i -race ./...
+- if [[ "$TRAVIS_GO_VERSION" == 1.11.* ]]; then ./scripts/cov.sh TRAVIS; else go test -v -race ./...; fi
diff --git a/vendor/github.com/nats-io/go-nats/GOVERNANCE.md b/vendor/github.com/nats-io/go-nats/GOVERNANCE.md
new file mode 100644
index 00000000..1d5a7be3
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/GOVERNANCE.md
@@ -0,0 +1,3 @@
+# NATS Go Client Governance
+
+NATS Go Client (go-nats) is part of the NATS project and is subject to the [NATS Governance](https://github.com/nats-io/nats-general/blob/master/GOVERNANCE.md).
\ No newline at end of file
diff --git a/vendor/github.com/nats-io/go-nats/LICENSE b/vendor/github.com/nats-io/go-nats/LICENSE
new file mode 100644
index 00000000..261eeb9e
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/nats-io/go-nats/MAINTAINERS.md b/vendor/github.com/nats-io/go-nats/MAINTAINERS.md
new file mode 100644
index 00000000..323faa8e
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/MAINTAINERS.md
@@ -0,0 +1,10 @@
+# Maintainers
+
+Maintainership is on a per project basis.
+
+### Core-maintainers
+ - Derek Collison [@derekcollison](https://github.com/derekcollison)
+ - Ivan Kozlovic [@kozlovic](https://github.com/kozlovic)
+
+### Maintainers
+ - Waldemar Quevedo [@wallyqs](https://github.com/wallyqs)
\ No newline at end of file
diff --git a/vendor/github.com/nats-io/go-nats/README.md b/vendor/github.com/nats-io/go-nats/README.md
new file mode 100644
index 00000000..933a1ef7
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/README.md
@@ -0,0 +1,341 @@
+# NATS - Go Client
+A [Go](http://golang.org) client for the [NATS messaging system](https://nats.io).
+
+[](https://www.apache.org/licenses/LICENSE-2.0)
+[](https://app.fossa.io/projects/git%2Bgithub.com%2Fnats-io%2Fgo-nats?ref=badge_shield)
+[](https://goreportcard.com/report/github.com/nats-io/go-nats) [](http://travis-ci.org/nats-io/go-nats) [](http://godoc.org/github.com/nats-io/go-nats) [](https://coveralls.io/r/nats-io/go-nats?branch=master)
+
+## Installation
+
+```bash
+# Go client
+go get github.com/nats-io/go-nats
+
+# Server
+go get github.com/nats-io/gnatsd
+```
+
+## Basic Usage
+
+```go
+
+// Connect to a server
+nc, _ := nats.Connect(nats.DefaultURL)
+
+// Simple Publisher
+nc.Publish("foo", []byte("Hello World"))
+
+// Simple Async Subscriber
+nc.Subscribe("foo", func(m *nats.Msg) {
+ fmt.Printf("Received a message: %s\n", string(m.Data))
+})
+
+// Simple Sync Subscriber
+sub, err := nc.SubscribeSync("foo")
+m, err := sub.NextMsg(timeout)
+
+// Channel Subscriber
+ch := make(chan *nats.Msg, 64)
+sub, err := nc.ChanSubscribe("foo", ch)
+msg := <- ch
+
+// Unsubscribe
+sub.Unsubscribe()
+
+// Drain
+sub.Drain()
+
+// Requests
+msg, err := nc.Request("help", []byte("help me"), 10*time.Millisecond)
+
+// Replies
+nc.Subscribe("help", func(m *Msg) {
+ nc.Publish(m.Reply, []byte("I can help!"))
+})
+
+// Drain connection (Preferred for responders)
+// Close() not needed if this is called.
+nc.Drain()
+
+// Close connection
+nc.Close()
+```
+
+## Encoded Connections
+
+```go
+
+nc, _ := nats.Connect(nats.DefaultURL)
+c, _ := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
+defer c.Close()
+
+// Simple Publisher
+c.Publish("foo", "Hello World")
+
+// Simple Async Subscriber
+c.Subscribe("foo", func(s string) {
+ fmt.Printf("Received a message: %s\n", s)
+})
+
+// EncodedConn can Publish any raw Go type using the registered Encoder
+type person struct {
+ Name string
+ Address string
+ Age int
+}
+
+// Go type Subscriber
+c.Subscribe("hello", func(p *person) {
+ fmt.Printf("Received a person: %+v\n", p)
+})
+
+me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery Street, San Francisco, CA"}
+
+// Go type Publisher
+c.Publish("hello", me)
+
+// Unsubscribe
+sub, err := c.Subscribe("foo", nil)
+...
+sub.Unsubscribe()
+
+// Requests
+var response string
+err := c.Request("help", "help me", &response, 10*time.Millisecond)
+if err != nil {
+ fmt.Printf("Request failed: %v\n", err)
+}
+
+// Replying
+c.Subscribe("help", func(subj, reply string, msg string) {
+ c.Publish(reply, "I can help!")
+})
+
+// Close connection
+c.Close();
+```
+
+## TLS
+
+```go
+// tls as a scheme will enable secure connections by default. This will also verify the server name.
+nc, err := nats.Connect("tls://nats.demo.io:4443")
+
+// If you are using a self-signed certificate, you need to have a tls.Config with RootCAs setup.
+// We provide a helper method to make this case easier.
+nc, err = nats.Connect("tls://localhost:4443", nats.RootCAs("./configs/certs/ca.pem"))
+
+// If the server requires client certificate, there is an helper function for that too:
+cert := nats.ClientCert("./configs/certs/client-cert.pem", "./configs/certs/client-key.pem")
+nc, err = nats.Connect("tls://localhost:4443", cert)
+
+// You can also supply a complete tls.Config
+
+certFile := "./configs/certs/client-cert.pem"
+keyFile := "./configs/certs/client-key.pem"
+cert, err := tls.LoadX509KeyPair(certFile, keyFile)
+if err != nil {
+ t.Fatalf("error parsing X509 certificate/key pair: %v", err)
+}
+
+config := &tls.Config{
+ ServerName: opts.Host,
+ Certificates: []tls.Certificate{cert},
+ RootCAs: pool,
+ MinVersion: tls.VersionTLS12,
+}
+
+nc, err = nats.Connect("nats://localhost:4443", nats.Secure(config))
+if err != nil {
+ t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err)
+}
+
+```
+
+## Using Go Channels (netchan)
+
+```go
+nc, _ := nats.Connect(nats.DefaultURL)
+ec, _ := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
+defer ec.Close()
+
+type person struct {
+ Name string
+ Address string
+ Age int
+}
+
+recvCh := make(chan *person)
+ec.BindRecvChan("hello", recvCh)
+
+sendCh := make(chan *person)
+ec.BindSendChan("hello", sendCh)
+
+me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery Street"}
+
+// Send via Go channels
+sendCh <- me
+
+// Receive via Go channels
+who := <- recvCh
+```
+
+## Wildcard Subscriptions
+
+```go
+
+// "*" matches any token, at any level of the subject.
+nc.Subscribe("foo.*.baz", func(m *Msg) {
+ fmt.Printf("Msg received on [%s] : %s\n", m.Subject, string(m.Data));
+})
+
+nc.Subscribe("foo.bar.*", func(m *Msg) {
+ fmt.Printf("Msg received on [%s] : %s\n", m.Subject, string(m.Data));
+})
+
+// ">" matches any length of the tail of a subject, and can only be the last token
+// E.g. 'foo.>' will match 'foo.bar', 'foo.bar.baz', 'foo.foo.bar.bax.22'
+nc.Subscribe("foo.>", func(m *Msg) {
+ fmt.Printf("Msg received on [%s] : %s\n", m.Subject, string(m.Data));
+})
+
+// Matches all of the above
+nc.Publish("foo.bar.baz", []byte("Hello World"))
+
+```
+
+## Queue Groups
+
+```go
+// All subscriptions with the same queue name will form a queue group.
+// Each message will be delivered to only one subscriber per queue group,
+// using queuing semantics. You can have as many queue groups as you wish.
+// Normal subscribers will continue to work as expected.
+
+nc.QueueSubscribe("foo", "job_workers", func(_ *Msg) {
+ received += 1;
+})
+
+```
+
+## Advanced Usage
+
+```go
+
+// Flush connection to server, returns when all messages have been processed.
+nc.Flush()
+fmt.Println("All clear!")
+
+// FlushTimeout specifies a timeout value as well.
+err := nc.FlushTimeout(1*time.Second)
+if err != nil {
+ fmt.Println("All clear!")
+} else {
+ fmt.Println("Flushed timed out!")
+}
+
+// Auto-unsubscribe after MAX_WANTED messages received
+const MAX_WANTED = 10
+sub, err := nc.Subscribe("foo")
+sub.AutoUnsubscribe(MAX_WANTED)
+
+// Multiple connections
+nc1 := nats.Connect("nats://host1:4222")
+nc2 := nats.Connect("nats://host2:4222")
+
+nc1.Subscribe("foo", func(m *Msg) {
+ fmt.Printf("Received a message: %s\n", string(m.Data))
+})
+
+nc2.Publish("foo", []byte("Hello World!"));
+
+```
+
+## Clustered Usage
+
+```go
+
+var servers = "nats://localhost:1222, nats://localhost:1223, nats://localhost:1224"
+
+nc, err := nats.Connect(servers)
+
+// Optionally set ReconnectWait and MaxReconnect attempts.
+// This example means 10 seconds total per backend.
+nc, err = nats.Connect(servers, nats.MaxReconnects(5), nats.ReconnectWait(2 * time.Second))
+
+// Optionally disable randomization of the server pool
+nc, err = nats.Connect(servers, nats.DontRandomize())
+
+// Setup callbacks to be notified on disconnects, reconnects and connection closed.
+nc, err = nats.Connect(servers,
+ nats.DisconnectHandler(func(nc *nats.Conn) {
+ fmt.Printf("Got disconnected!\n")
+ }),
+ nats.ReconnectHandler(func(_ *nats.Conn) {
+ fmt.Printf("Got reconnected to %v!\n", nc.ConnectedUrl())
+ }),
+ nats.ClosedHandler(func(nc *nats.Conn) {
+ fmt.Printf("Connection closed. Reason: %q\n", nc.LastError())
+ })
+)
+
+// When connecting to a mesh of servers with auto-discovery capabilities,
+// you may need to provide a username/password or token in order to connect
+// to any server in that mesh when authentication is required.
+// Instead of providing the credentials in the initial URL, you will use
+// new option setters:
+nc, err = nats.Connect("nats://localhost:4222", nats.UserInfo("foo", "bar"))
+
+// For token based authentication:
+nc, err = nats.Connect("nats://localhost:4222", nats.Token("S3cretT0ken"))
+
+// You can even pass the two at the same time in case one of the server
+// in the mesh requires token instead of user name and password.
+nc, err = nats.Connect("nats://localhost:4222",
+ nats.UserInfo("foo", "bar"),
+ nats.Token("S3cretT0ken"))
+
+// Note that if credentials are specified in the initial URLs, they take
+// precedence on the credentials specfied through the options.
+// For instance, in the connect call below, the client library will use
+// the user "my" and password "pwd" to connect to locahost:4222, however,
+// it will use username "foo" and password "bar" when (re)connecting to
+// a different server URL that it got as part of the auto-discovery.
+nc, err = nats.Connect("nats://my:pwd@localhost:4222", nats.UserInfo("foo", "bar"))
+
+```
+
+## Context support (+Go 1.7)
+
+```go
+ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+defer cancel()
+
+nc, err := nats.Connect(nats.DefaultURL)
+
+// Request with context
+msg, err := nc.RequestWithContext(ctx, "foo", []byte("bar"))
+
+// Synchronous subscriber with context
+sub, err := nc.SubscribeSync("foo")
+msg, err := sub.NextMsgWithContext(ctx)
+
+// Encoded Request with context
+c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
+type request struct {
+ Message string `json:"message"`
+}
+type response struct {
+ Code int `json:"code"`
+}
+req := &request{Message: "Hello"}
+resp := &response{}
+err := c.RequestWithContext(ctx, "foo", req, resp)
+```
+
+## License
+
+Unless otherwise noted, the NATS source files are distributed
+under the Apache Version 2.0 license found in the LICENSE file.
+
+[](https://app.fossa.io/projects/git%2Bgithub.com%2Fnats-io%2Fgo-nats?ref=badge_large)
diff --git a/vendor/github.com/nats-io/go-nats/TODO.md b/vendor/github.com/nats-io/go-nats/TODO.md
new file mode 100644
index 00000000..213aaeca
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/TODO.md
@@ -0,0 +1,26 @@
+
+- [ ] Better constructors, options handling
+- [ ] Functions for callback settings after connection created.
+- [ ] Better options for subscriptions. Slow Consumer state settable, Go routines vs Inline.
+- [ ] Move off of channels for subscribers, use syncPool linkedLists, etc with highwater.
+- [ ] Test for valid subjects on publish and subscribe?
+- [ ] SyncSubscriber and Next for EncodedConn
+- [ ] Fast Publisher?
+- [ ] pooling for structs used? leaky bucket?
+- [ ] Timeout 0 should work as no timeout
+- [x] Ping timer
+- [x] Name in Connect for gnatsd
+- [x] Asynchronous error handling
+- [x] Parser rewrite
+- [x] Reconnect
+- [x] Hide Lock
+- [x] Easier encoder interface
+- [x] QueueSubscribeSync
+- [x] Make nats specific errors prefixed with 'nats:'
+- [x] API test for closed connection
+- [x] TLS/SSL
+- [x] Stats collection
+- [x] Disconnect detection
+- [x] Optimized Publish (coalescing)
+- [x] Do Examples via Go style
+- [x] Standardized Errors
diff --git a/vendor/github.com/nats-io/go-nats/context.go b/vendor/github.com/nats-io/go-nats/context.go
new file mode 100644
index 00000000..3d753564
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/context.go
@@ -0,0 +1,184 @@
+// Copyright 2016-2018 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build go1.7
+
+// A Go client for the NATS messaging system (https://nats.io).
+package nats
+
+import (
+ "context"
+ "fmt"
+ "reflect"
+)
+
+// RequestWithContext takes a context, a subject and payload
+// in bytes and request expecting a single response.
+func (nc *Conn) RequestWithContext(ctx context.Context, subj string, data []byte) (*Msg, error) {
+ if ctx == nil {
+ return nil, ErrInvalidContext
+ }
+ if nc == nil {
+ return nil, ErrInvalidConnection
+ }
+ // Check whether the context is done already before making
+ // the request.
+ if ctx.Err() != nil {
+ return nil, ctx.Err()
+ }
+
+ nc.mu.Lock()
+ // If user wants the old style.
+ if nc.Opts.UseOldRequestStyle {
+ nc.mu.Unlock()
+ return nc.oldRequestWithContext(ctx, subj, data)
+ }
+
+ // Do setup for the new style.
+ if nc.respMap == nil {
+ // _INBOX wildcard
+ nc.respSub = fmt.Sprintf("%s.*", NewInbox())
+ nc.respMap = make(map[string]chan *Msg)
+ }
+ // Create literal Inbox and map to a chan msg.
+ mch := make(chan *Msg, RequestChanLen)
+ respInbox := nc.newRespInbox()
+ token := respToken(respInbox)
+ nc.respMap[token] = mch
+ createSub := nc.respMux == nil
+ ginbox := nc.respSub
+ nc.mu.Unlock()
+
+ if createSub {
+ // Make sure scoped subscription is setup only once.
+ var err error
+ nc.respSetup.Do(func() { err = nc.createRespMux(ginbox) })
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ err := nc.PublishRequest(subj, respInbox, data)
+ if err != nil {
+ return nil, err
+ }
+
+ var ok bool
+ var msg *Msg
+
+ select {
+ case msg, ok = <-mch:
+ if !ok {
+ return nil, ErrConnectionClosed
+ }
+ case <-ctx.Done():
+ nc.mu.Lock()
+ delete(nc.respMap, token)
+ nc.mu.Unlock()
+ return nil, ctx.Err()
+ }
+
+ return msg, nil
+}
+
+// oldRequestWithContext utilizes inbox and subscription per request.
+func (nc *Conn) oldRequestWithContext(ctx context.Context, subj string, data []byte) (*Msg, error) {
+ inbox := NewInbox()
+ ch := make(chan *Msg, RequestChanLen)
+
+ s, err := nc.subscribe(inbox, _EMPTY_, nil, ch)
+ if err != nil {
+ return nil, err
+ }
+ s.AutoUnsubscribe(1)
+ defer s.Unsubscribe()
+
+ err = nc.PublishRequest(subj, inbox, data)
+ if err != nil {
+ return nil, err
+ }
+
+ return s.NextMsgWithContext(ctx)
+}
+
+// NextMsgWithContext takes a context and returns the next message
+// available to a synchronous subscriber, blocking until it is delivered
+// or context gets canceled.
+func (s *Subscription) NextMsgWithContext(ctx context.Context) (*Msg, error) {
+ if ctx == nil {
+ return nil, ErrInvalidContext
+ }
+ if s == nil {
+ return nil, ErrBadSubscription
+ }
+ if ctx.Err() != nil {
+ return nil, ctx.Err()
+ }
+
+ s.mu.Lock()
+ err := s.validateNextMsgState()
+ if err != nil {
+ s.mu.Unlock()
+ return nil, err
+ }
+
+ mch := s.mch
+ s.mu.Unlock()
+
+ var ok bool
+ var msg *Msg
+
+ select {
+ case msg, ok = <-mch:
+ if !ok {
+ return nil, ErrConnectionClosed
+ }
+ err := s.processNextMsgDelivered(msg)
+ if err != nil {
+ return nil, err
+ }
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ }
+
+ return msg, nil
+}
+
+// RequestWithContext will create an Inbox and perform a Request
+// using the provided cancellation context with the Inbox reply
+// for the data v. A response will be decoded into the vPtrResponse.
+func (c *EncodedConn) RequestWithContext(ctx context.Context, subject string, v interface{}, vPtr interface{}) error {
+ if ctx == nil {
+ return ErrInvalidContext
+ }
+
+ b, err := c.Enc.Encode(subject, v)
+ if err != nil {
+ return err
+ }
+ m, err := c.Conn.RequestWithContext(ctx, subject, b)
+ if err != nil {
+ return err
+ }
+ if reflect.TypeOf(vPtr) == emptyMsgType {
+ mPtr := vPtr.(*Msg)
+ *mPtr = *m
+ } else {
+ err := c.Enc.Decode(m.Subject, m.Data, vPtr)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/nats-io/go-nats/enc.go b/vendor/github.com/nats-io/go-nats/enc.go
new file mode 100644
index 00000000..0f06acc1
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/enc.go
@@ -0,0 +1,269 @@
+// Copyright 2012-2018 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package nats
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "sync"
+ "time"
+
+ // Default Encoders
+ . "github.com/nats-io/go-nats/encoders/builtin"
+)
+
+// Encoder interface is for all register encoders
+type Encoder interface {
+ Encode(subject string, v interface{}) ([]byte, error)
+ Decode(subject string, data []byte, vPtr interface{}) error
+}
+
+var encMap map[string]Encoder
+var encLock sync.Mutex
+
+// Indexe names into the Registered Encoders.
+const (
+ JSON_ENCODER = "json"
+ GOB_ENCODER = "gob"
+ DEFAULT_ENCODER = "default"
+)
+
+func init() {
+ encMap = make(map[string]Encoder)
+ // Register json, gob and default encoder
+ RegisterEncoder(JSON_ENCODER, &JsonEncoder{})
+ RegisterEncoder(GOB_ENCODER, &GobEncoder{})
+ RegisterEncoder(DEFAULT_ENCODER, &DefaultEncoder{})
+}
+
+// EncodedConn are the preferred way to interface with NATS. They wrap a bare connection to
+// a nats server and have an extendable encoder system that will encode and decode messages
+// from raw Go types.
+type EncodedConn struct {
+ Conn *Conn
+ Enc Encoder
+}
+
+// NewEncodedConn will wrap an existing Connection and utilize the appropriate registered
+// encoder.
+func NewEncodedConn(c *Conn, encType string) (*EncodedConn, error) {
+ if c == nil {
+ return nil, errors.New("nats: Nil Connection")
+ }
+ if c.IsClosed() {
+ return nil, ErrConnectionClosed
+ }
+ ec := &EncodedConn{Conn: c, Enc: EncoderForType(encType)}
+ if ec.Enc == nil {
+ return nil, fmt.Errorf("No encoder registered for '%s'", encType)
+ }
+ return ec, nil
+}
+
+// RegisterEncoder will register the encType with the given Encoder. Useful for customization.
+func RegisterEncoder(encType string, enc Encoder) {
+ encLock.Lock()
+ defer encLock.Unlock()
+ encMap[encType] = enc
+}
+
+// EncoderForType will return the registered Encoder for the encType.
+func EncoderForType(encType string) Encoder {
+ encLock.Lock()
+ defer encLock.Unlock()
+ return encMap[encType]
+}
+
+// Publish publishes the data argument to the given subject. The data argument
+// will be encoded using the associated encoder.
+func (c *EncodedConn) Publish(subject string, v interface{}) error {
+ b, err := c.Enc.Encode(subject, v)
+ if err != nil {
+ return err
+ }
+ return c.Conn.publish(subject, _EMPTY_, b)
+}
+
+// PublishRequest will perform a Publish() expecting a response on the
+// reply subject. Use Request() for automatically waiting for a response
+// inline.
+func (c *EncodedConn) PublishRequest(subject, reply string, v interface{}) error {
+ b, err := c.Enc.Encode(subject, v)
+ if err != nil {
+ return err
+ }
+ return c.Conn.publish(subject, reply, b)
+}
+
+// Request will create an Inbox and perform a Request() call
+// with the Inbox reply for the data v. A response will be
+// decoded into the vPtrResponse.
+func (c *EncodedConn) Request(subject string, v interface{}, vPtr interface{}, timeout time.Duration) error {
+ b, err := c.Enc.Encode(subject, v)
+ if err != nil {
+ return err
+ }
+ m, err := c.Conn.Request(subject, b, timeout)
+ if err != nil {
+ return err
+ }
+ if reflect.TypeOf(vPtr) == emptyMsgType {
+ mPtr := vPtr.(*Msg)
+ *mPtr = *m
+ } else {
+ err = c.Enc.Decode(m.Subject, m.Data, vPtr)
+ }
+ return err
+}
+
+// Handler is a specific callback used for Subscribe. It is generalized to
+// an interface{}, but we will discover its format and arguments at runtime
+// and perform the correct callback, including de-marshaling JSON strings
+// back into the appropriate struct based on the signature of the Handler.
+//
+// Handlers are expected to have one of four signatures.
+//
+// type person struct {
+// Name string `json:"name,omitempty"`
+// Age uint `json:"age,omitempty"`
+// }
+//
+// handler := func(m *Msg)
+// handler := func(p *person)
+// handler := func(subject string, o *obj)
+// handler := func(subject, reply string, o *obj)
+//
+// These forms allow a callback to request a raw Msg ptr, where the processing
+// of the message from the wire is untouched. Process a JSON representation
+// and demarshal it into the given struct, e.g. person.
+// There are also variants where the callback wants either the subject, or the
+// subject and the reply subject.
+type Handler interface{}
+
+// Dissect the cb Handler's signature
+func argInfo(cb Handler) (reflect.Type, int) {
+ cbType := reflect.TypeOf(cb)
+ if cbType.Kind() != reflect.Func {
+ panic("nats: Handler needs to be a func")
+ }
+ numArgs := cbType.NumIn()
+ if numArgs == 0 {
+ return nil, numArgs
+ }
+ return cbType.In(numArgs - 1), numArgs
+}
+
+var emptyMsgType = reflect.TypeOf(&Msg{})
+
+// Subscribe will create a subscription on the given subject and process incoming
+// messages using the specified Handler. The Handler should be a func that matches
+// a signature from the description of Handler from above.
+func (c *EncodedConn) Subscribe(subject string, cb Handler) (*Subscription, error) {
+ return c.subscribe(subject, _EMPTY_, cb)
+}
+
+// QueueSubscribe will create a queue subscription on the given subject and process
+// incoming messages using the specified Handler. The Handler should be a func that
+// matches a signature from the description of Handler from above.
+func (c *EncodedConn) QueueSubscribe(subject, queue string, cb Handler) (*Subscription, error) {
+ return c.subscribe(subject, queue, cb)
+}
+
+// Internal implementation that all public functions will use.
+func (c *EncodedConn) subscribe(subject, queue string, cb Handler) (*Subscription, error) {
+ if cb == nil {
+ return nil, errors.New("nats: Handler required for EncodedConn Subscription")
+ }
+ argType, numArgs := argInfo(cb)
+ if argType == nil {
+ return nil, errors.New("nats: Handler requires at least one argument")
+ }
+
+ cbValue := reflect.ValueOf(cb)
+ wantsRaw := (argType == emptyMsgType)
+
+ natsCB := func(m *Msg) {
+ var oV []reflect.Value
+ if wantsRaw {
+ oV = []reflect.Value{reflect.ValueOf(m)}
+ } else {
+ var oPtr reflect.Value
+ if argType.Kind() != reflect.Ptr {
+ oPtr = reflect.New(argType)
+ } else {
+ oPtr = reflect.New(argType.Elem())
+ }
+ if err := c.Enc.Decode(m.Subject, m.Data, oPtr.Interface()); err != nil {
+ if c.Conn.Opts.AsyncErrorCB != nil {
+ c.Conn.ach.push(func() {
+ c.Conn.Opts.AsyncErrorCB(c.Conn, m.Sub, errors.New("nats: Got an error trying to unmarshal: "+err.Error()))
+ })
+ }
+ return
+ }
+ if argType.Kind() != reflect.Ptr {
+ oPtr = reflect.Indirect(oPtr)
+ }
+
+ // Callback Arity
+ switch numArgs {
+ case 1:
+ oV = []reflect.Value{oPtr}
+ case 2:
+ subV := reflect.ValueOf(m.Subject)
+ oV = []reflect.Value{subV, oPtr}
+ case 3:
+ subV := reflect.ValueOf(m.Subject)
+ replyV := reflect.ValueOf(m.Reply)
+ oV = []reflect.Value{subV, replyV, oPtr}
+ }
+
+ }
+ cbValue.Call(oV)
+ }
+
+ return c.Conn.subscribe(subject, queue, natsCB, nil)
+}
+
+// FlushTimeout allows a Flush operation to have an associated timeout.
+func (c *EncodedConn) FlushTimeout(timeout time.Duration) (err error) {
+ return c.Conn.FlushTimeout(timeout)
+}
+
+// Flush will perform a round trip to the server and return when it
+// receives the internal reply.
+func (c *EncodedConn) Flush() error {
+ return c.Conn.Flush()
+}
+
+// Close will close the connection to the server. This call will release
+// all blocking calls, such as Flush(), etc.
+func (c *EncodedConn) Close() {
+ c.Conn.Close()
+}
+
+// Drain will put a connection into a drain state. All subscriptions will
+// immediately be put into a drain state. Upon completion, the publishers
+// will be drained and can not publish any additional messages. Upon draining
+// of the publishers, the connection will be closed. Use the ClosedCB()
+// option to know when the connection has moved from draining to closed.
+func (c *EncodedConn) Drain() error {
+ return c.Conn.Drain()
+}
+
+// LastError reports the last error encountered via the Connection.
+func (c *EncodedConn) LastError() error {
+ return c.Conn.err
+}
diff --git a/vendor/github.com/nats-io/go-nats/encoders/builtin/default_enc.go b/vendor/github.com/nats-io/go-nats/encoders/builtin/default_enc.go
new file mode 100644
index 00000000..46d918ee
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/encoders/builtin/default_enc.go
@@ -0,0 +1,117 @@
+// Copyright 2012-2018 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package builtin
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "strconv"
+ "unsafe"
+)
+
+// DefaultEncoder implementation for EncodedConn.
+// This encoder will leave []byte and string untouched, but will attempt to
+// turn numbers into appropriate strings that can be decoded. It will also
+// propely encoded and decode bools. If will encode a struct, but if you want
+// to properly handle structures you should use JsonEncoder.
+type DefaultEncoder struct {
+ // Empty
+}
+
+var trueB = []byte("true")
+var falseB = []byte("false")
+var nilB = []byte("")
+
+// Encode
+func (je *DefaultEncoder) Encode(subject string, v interface{}) ([]byte, error) {
+ switch arg := v.(type) {
+ case string:
+ bytes := *(*[]byte)(unsafe.Pointer(&arg))
+ return bytes, nil
+ case []byte:
+ return arg, nil
+ case bool:
+ if arg {
+ return trueB, nil
+ } else {
+ return falseB, nil
+ }
+ case nil:
+ return nilB, nil
+ default:
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "%+v", arg)
+ return buf.Bytes(), nil
+ }
+}
+
+// Decode
+func (je *DefaultEncoder) Decode(subject string, data []byte, vPtr interface{}) error {
+ // Figure out what it's pointing to...
+ sData := *(*string)(unsafe.Pointer(&data))
+ switch arg := vPtr.(type) {
+ case *string:
+ *arg = sData
+ return nil
+ case *[]byte:
+ *arg = data
+ return nil
+ case *int:
+ n, err := strconv.ParseInt(sData, 10, 64)
+ if err != nil {
+ return err
+ }
+ *arg = int(n)
+ return nil
+ case *int32:
+ n, err := strconv.ParseInt(sData, 10, 64)
+ if err != nil {
+ return err
+ }
+ *arg = int32(n)
+ return nil
+ case *int64:
+ n, err := strconv.ParseInt(sData, 10, 64)
+ if err != nil {
+ return err
+ }
+ *arg = int64(n)
+ return nil
+ case *float32:
+ n, err := strconv.ParseFloat(sData, 32)
+ if err != nil {
+ return err
+ }
+ *arg = float32(n)
+ return nil
+ case *float64:
+ n, err := strconv.ParseFloat(sData, 64)
+ if err != nil {
+ return err
+ }
+ *arg = float64(n)
+ return nil
+ case *bool:
+ b, err := strconv.ParseBool(sData)
+ if err != nil {
+ return err
+ }
+ *arg = b
+ return nil
+ default:
+ vt := reflect.TypeOf(arg).Elem()
+ return fmt.Errorf("nats: Default Encoder can't decode to type %s", vt)
+ }
+}
diff --git a/vendor/github.com/nats-io/go-nats/encoders/builtin/gob_enc.go b/vendor/github.com/nats-io/go-nats/encoders/builtin/gob_enc.go
new file mode 100644
index 00000000..632bcbd3
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/encoders/builtin/gob_enc.go
@@ -0,0 +1,45 @@
+// Copyright 2013-2018 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package builtin
+
+import (
+ "bytes"
+ "encoding/gob"
+)
+
+// GobEncoder is a Go specific GOB Encoder implementation for EncodedConn.
+// This encoder will use the builtin encoding/gob to Marshal
+// and Unmarshal most types, including structs.
+type GobEncoder struct {
+ // Empty
+}
+
+// FIXME(dlc) - This could probably be more efficient.
+
+// Encode
+func (ge *GobEncoder) Encode(subject string, v interface{}) ([]byte, error) {
+ b := new(bytes.Buffer)
+ enc := gob.NewEncoder(b)
+ if err := enc.Encode(v); err != nil {
+ return nil, err
+ }
+ return b.Bytes(), nil
+}
+
+// Decode
+func (ge *GobEncoder) Decode(subject string, data []byte, vPtr interface{}) (err error) {
+ dec := gob.NewDecoder(bytes.NewBuffer(data))
+ err = dec.Decode(vPtr)
+ return
+}
diff --git a/vendor/github.com/nats-io/go-nats/encoders/builtin/json_enc.go b/vendor/github.com/nats-io/go-nats/encoders/builtin/json_enc.go
new file mode 100644
index 00000000..c9670f31
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/encoders/builtin/json_enc.go
@@ -0,0 +1,56 @@
+// Copyright 2012-2018 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package builtin
+
+import (
+ "encoding/json"
+ "strings"
+)
+
+// JsonEncoder is a JSON Encoder implementation for EncodedConn.
+// This encoder will use the builtin encoding/json to Marshal
+// and Unmarshal most types, including structs.
+type JsonEncoder struct {
+ // Empty
+}
+
+// Encode
+func (je *JsonEncoder) Encode(subject string, v interface{}) ([]byte, error) {
+ b, err := json.Marshal(v)
+ if err != nil {
+ return nil, err
+ }
+ return b, nil
+}
+
+// Decode
+func (je *JsonEncoder) Decode(subject string, data []byte, vPtr interface{}) (err error) {
+ switch arg := vPtr.(type) {
+ case *string:
+ // If they want a string and it is a JSON string, strip quotes
+ // This allows someone to send a struct but receive as a plain string
+ // This cast should be efficient for Go 1.3 and beyond.
+ str := string(data)
+ if strings.HasPrefix(str, `"`) && strings.HasSuffix(str, `"`) {
+ *arg = str[1 : len(str)-1]
+ } else {
+ *arg = str
+ }
+ case *[]byte:
+ *arg = data
+ default:
+ err = json.Unmarshal(data, arg)
+ }
+ return
+}
diff --git a/vendor/github.com/nats-io/go-nats/nats.go b/vendor/github.com/nats-io/go-nats/nats.go
new file mode 100644
index 00000000..0b33a7aa
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/nats.go
@@ -0,0 +1,3477 @@
+// Copyright 2012-2018 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// A Go client for the NATS messaging system (https://nats.io).
+package nats
+
+import (
+ "bufio"
+ "bytes"
+ "crypto/tls"
+ "crypto/x509"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "math/rand"
+ "net"
+ "net/url"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/nats-io/go-nats/util"
+ "github.com/nats-io/nuid"
+)
+
+// Default Constants
+const (
+ Version = "1.6.0"
+ DefaultURL = "nats://localhost:4222"
+ DefaultPort = 4222
+ DefaultMaxReconnect = 60
+ DefaultReconnectWait = 2 * time.Second
+ DefaultTimeout = 2 * time.Second
+ DefaultPingInterval = 2 * time.Minute
+ DefaultMaxPingOut = 2
+ DefaultMaxChanLen = 8192 // 8k
+ DefaultReconnectBufSize = 8 * 1024 * 1024 // 8MB
+ RequestChanLen = 8
+ DefaultDrainTimeout = 30 * time.Second
+ LangString = "go"
+)
+
+// STALE_CONNECTION is for detection and proper handling of stale connections.
+const STALE_CONNECTION = "stale connection"
+
+// PERMISSIONS_ERR is for when nats server subject authorization has failed.
+const PERMISSIONS_ERR = "permissions violation"
+
+// AUTHORIZATION_ERR is for when nats server user authorization has failed.
+const AUTHORIZATION_ERR = "authorization violation"
+
+// Errors
+var (
+ ErrConnectionClosed = errors.New("nats: connection closed")
+ ErrConnectionDraining = errors.New("nats: connection draining")
+ ErrDrainTimeout = errors.New("nats: draining connection timed out")
+ ErrConnectionReconnecting = errors.New("nats: connection reconnecting")
+ ErrSecureConnRequired = errors.New("nats: secure connection required")
+ ErrSecureConnWanted = errors.New("nats: secure connection not available")
+ ErrBadSubscription = errors.New("nats: invalid subscription")
+ ErrTypeSubscription = errors.New("nats: invalid subscription type")
+ ErrBadSubject = errors.New("nats: invalid subject")
+ ErrSlowConsumer = errors.New("nats: slow consumer, messages dropped")
+ ErrTimeout = errors.New("nats: timeout")
+ ErrBadTimeout = errors.New("nats: timeout invalid")
+ ErrAuthorization = errors.New("nats: authorization violation")
+ ErrNoServers = errors.New("nats: no servers available for connection")
+ ErrJsonParse = errors.New("nats: connect message, json parse error")
+ ErrChanArg = errors.New("nats: argument needs to be a channel type")
+ ErrMaxPayload = errors.New("nats: maximum payload exceeded")
+ ErrMaxMessages = errors.New("nats: maximum messages delivered")
+ ErrSyncSubRequired = errors.New("nats: illegal call on an async subscription")
+ ErrMultipleTLSConfigs = errors.New("nats: multiple tls.Configs not allowed")
+ ErrNoInfoReceived = errors.New("nats: protocol exception, INFO not received")
+ ErrReconnectBufExceeded = errors.New("nats: outbound buffer limit exceeded")
+ ErrInvalidConnection = errors.New("nats: invalid connection")
+ ErrInvalidMsg = errors.New("nats: invalid message or message nil")
+ ErrInvalidArg = errors.New("nats: invalid argument")
+ ErrInvalidContext = errors.New("nats: invalid context")
+ ErrNoEchoNotSupported = errors.New("nats: no echo option not supported by this server")
+ ErrStaleConnection = errors.New("nats: " + STALE_CONNECTION)
+)
+
+// GetDefaultOptions returns default configuration options for the client.
+func GetDefaultOptions() Options {
+ return Options{
+ AllowReconnect: true,
+ MaxReconnect: DefaultMaxReconnect,
+ ReconnectWait: DefaultReconnectWait,
+ Timeout: DefaultTimeout,
+ PingInterval: DefaultPingInterval,
+ MaxPingsOut: DefaultMaxPingOut,
+ SubChanLen: DefaultMaxChanLen,
+ ReconnectBufSize: DefaultReconnectBufSize,
+ DrainTimeout: DefaultDrainTimeout,
+ }
+}
+
+// DEPRECATED: Use GetDefaultOptions() instead.
+// DefaultOptions is not safe for use by multiple clients.
+// For details see #308.
+var DefaultOptions = GetDefaultOptions()
+
+// Status represents the state of the connection.
+type Status int
+
+const (
+ DISCONNECTED = Status(iota)
+ CONNECTED
+ CLOSED
+ RECONNECTING
+ CONNECTING
+ DRAINING_SUBS
+ DRAINING_PUBS
+)
+
+// ConnHandler is used for asynchronous events such as
+// disconnected and closed connections.
+type ConnHandler func(*Conn)
+
+// ErrHandler is used to process asynchronous errors encountered
+// while processing inbound messages.
+type ErrHandler func(*Conn, *Subscription, error)
+
+// asyncCB is used to preserve order for async callbacks.
+type asyncCB struct {
+ f func()
+ next *asyncCB
+}
+
+type asyncCallbacksHandler struct {
+ mu sync.Mutex
+ cond *sync.Cond
+ head *asyncCB
+ tail *asyncCB
+}
+
+// Option is a function on the options for a connection.
+type Option func(*Options) error
+
+// CustomDialer can be used to specify any dialer, not necessarily
+// a *net.Dialer.
+type CustomDialer interface {
+ Dial(network, address string) (net.Conn, error)
+}
+
+// Options can be used to create a customized connection.
+type Options struct {
+
+ // Url represents a single NATS server url to which the client
+ // will be connecting. If the Servers option is also set, it
+ // then becomes the first server in the Servers array.
+ Url string
+
+ // Servers is a configured set of servers which this client
+ // will use when attempting to connect.
+ Servers []string
+
+ // NoRandomize configures whether we will randomize the
+ // server pool.
+ NoRandomize bool
+
+ // NoEcho configures whether the server will echo back messages
+ // that are sent on this connection if we also have matching subscriptions.
+ // Note this is supported on servers >= version 1.2. Proto 1 or greater.
+ NoEcho bool
+
+ // Name is an optional name label which will be sent to the server
+ // on CONNECT to identify the client.
+ Name string
+
+ // Verbose signals the server to send an OK ack for commands
+ // successfully processed by the server.
+ Verbose bool
+
+ // Pedantic signals the server whether it should be doing further
+ // validation of subjects.
+ Pedantic bool
+
+ // Secure enables TLS secure connections that skip server
+ // verification by default. NOT RECOMMENDED.
+ Secure bool
+
+ // TLSConfig is a custom TLS configuration to use for secure
+ // transports.
+ TLSConfig *tls.Config
+
+ // AllowReconnect enables reconnection logic to be used when we
+ // encounter a disconnect from the current server.
+ AllowReconnect bool
+
+ // MaxReconnect sets the number of reconnect attempts that will be
+ // tried before giving up. If negative, then it will never give up
+ // trying to reconnect.
+ MaxReconnect int
+
+ // ReconnectWait sets the time to backoff after attempting a reconnect
+ // to a server that we were already connected to previously.
+ ReconnectWait time.Duration
+
+ // Timeout sets the timeout for a Dial operation on a connection.
+ Timeout time.Duration
+
+ // DrainTimeout sets the timeout for a Drain Operation to complete.
+ DrainTimeout time.Duration
+
+ // FlusherTimeout is the maximum time to wait for the flusher loop
+ // to be able to finish writing to the underlying connection.
+ FlusherTimeout time.Duration
+
+ // PingInterval is the period at which the client will be sending ping
+ // commands to the server, disabled if 0 or negative.
+ PingInterval time.Duration
+
+ // MaxPingsOut is the maximum number of pending ping commands that can
+ // be awaiting a response before raising an ErrStaleConnection error.
+ MaxPingsOut int
+
+ // ClosedCB sets the closed handler that is called when a client will
+ // no longer be connected.
+ ClosedCB ConnHandler
+
+ // DisconnectedCB sets the disconnected handler that is called
+ // whenever the connection is disconnected.
+ DisconnectedCB ConnHandler
+
+ // ReconnectedCB sets the reconnected handler called whenever
+ // the connection is successfully reconnected.
+ ReconnectedCB ConnHandler
+
+ // DiscoveredServersCB sets the callback that is invoked whenever a new
+ // server has joined the cluster.
+ DiscoveredServersCB ConnHandler
+
+ // AsyncErrorCB sets the async error handler (e.g. slow consumer errors)
+ AsyncErrorCB ErrHandler
+
+ // ReconnectBufSize is the size of the backing bufio during reconnect.
+ // Once this has been exhausted publish operations will return an error.
+ ReconnectBufSize int
+
+ // SubChanLen is the size of the buffered channel used between the socket
+ // Go routine and the message delivery for SyncSubscriptions.
+ // NOTE: This does not affect AsyncSubscriptions which are
+ // dictated by PendingLimits()
+ SubChanLen int
+
+ // User sets the username to be used when connecting to the server.
+ User string
+
+ // Password sets the password to be used when connecting to a server.
+ Password string
+
+ // Token sets the token to be used when connecting to a server.
+ Token string
+
+ // Dialer allows a custom net.Dialer when forming connections.
+ // DEPRECATED: should use CustomDialer instead.
+ Dialer *net.Dialer
+
+ // CustomDialer allows to specify a custom dialer (not necessarily
+ // a *net.Dialer).
+ CustomDialer CustomDialer
+
+ // UseOldRequestStyle forces the old method of Requests that utilize
+ // a new Inbox and a new Subscription for each request.
+ UseOldRequestStyle bool
+}
+
+const (
+ // Scratch storage for assembling protocol headers
+ scratchSize = 512
+
+ // The size of the bufio reader/writer on top of the socket.
+ defaultBufSize = 32768
+
+ // The buffered size of the flush "kick" channel
+ flushChanSize = 1024
+
+ // Default server pool size
+ srvPoolSize = 4
+
+ // NUID size
+ nuidSize = 22
+
+ // Default port used if none is specified in given URL(s)
+ defaultPortString = "4222"
+)
+
+// A Conn represents a bare connection to a nats-server.
+// It can send and receive []byte payloads.
+type Conn struct {
+ // Keep all members for which we use atomic at the beginning of the
+ // struct and make sure they are all 64bits (or use padding if necessary).
+ // atomic.* functions crash on 32bit machines if operand is not aligned
+ // at 64bit. See https://github.com/golang/go/issues/599
+ Statistics
+ mu sync.Mutex
+ // Opts holds the configuration of the Conn.
+ // Modifying the configuration of a running Conn is a race.
+ Opts Options
+ wg sync.WaitGroup
+ url *url.URL
+ conn net.Conn
+ srvPool []*srv
+ urls map[string]struct{} // Keep track of all known URLs (used by processInfo)
+ bw *bufio.Writer
+ pending *bytes.Buffer
+ fch chan struct{}
+ info serverInfo
+ ssid int64
+ subsMu sync.RWMutex
+ subs map[int64]*Subscription
+ ach *asyncCallbacksHandler
+ pongs []chan struct{}
+ scratch [scratchSize]byte
+ status Status
+ initc bool // true if the connection is performing the initial connect
+ err error
+ ps *parseState
+ ptmr *time.Timer
+ pout int
+
+ // New style response handler
+ respSub string // The wildcard subject
+ respMux *Subscription // A single response subscription
+ respMap map[string]chan *Msg // Request map for the response msg channels
+ respSetup sync.Once // Ensures response subscription occurs once
+}
+
+// A Subscription represents interest in a given subject.
+type Subscription struct {
+ mu sync.Mutex
+ sid int64
+
+ // Subject that represents this subscription. This can be different
+ // than the received subject inside a Msg if this is a wildcard.
+ Subject string
+
+ // Optional queue group name. If present, all subscriptions with the
+ // same name will form a distributed queue, and each message will
+ // only be processed by one member of the group.
+ Queue string
+
+ delivered uint64
+ max uint64
+ conn *Conn
+ mcb MsgHandler
+ mch chan *Msg
+ closed bool
+ sc bool
+ connClosed bool
+
+ // Type of Subscription
+ typ SubscriptionType
+
+ // Async linked list
+ pHead *Msg
+ pTail *Msg
+ pCond *sync.Cond
+
+ // Pending stats, async subscriptions, high-speed etc.
+ pMsgs int
+ pBytes int
+ pMsgsMax int
+ pBytesMax int
+ pMsgsLimit int
+ pBytesLimit int
+ dropped int
+}
+
+// Msg is a structure used by Subscribers and PublishMsg().
+type Msg struct {
+ Subject string
+ Reply string
+ Data []byte
+ Sub *Subscription
+ next *Msg
+ barrier *barrierInfo
+}
+
+type barrierInfo struct {
+ refs int64
+ f func()
+}
+
+// Tracks various stats received and sent on this connection,
+// including counts for messages and bytes.
+type Statistics struct {
+ InMsgs uint64
+ OutMsgs uint64
+ InBytes uint64
+ OutBytes uint64
+ Reconnects uint64
+}
+
+// Tracks individual backend servers.
+type srv struct {
+ url *url.URL
+ didConnect bool
+ reconnects int
+ lastAttempt time.Time
+ isImplicit bool
+}
+
+type serverInfo struct {
+ Id string `json:"server_id"`
+ Host string `json:"host"`
+ Port uint `json:"port"`
+ Version string `json:"version"`
+ AuthRequired bool `json:"auth_required"`
+ TLSRequired bool `json:"tls_required"`
+ MaxPayload int64 `json:"max_payload"`
+ ConnectURLs []string `json:"connect_urls,omitempty"`
+ Proto int `json:"proto,omitempty"`
+}
+
+const (
+ // clientProtoZero is the original client protocol from 2009.
+ // http://nats.io/documentation/internals/nats-protocol/
+ /* clientProtoZero */ _ = iota
+ // clientProtoInfo signals a client can receive more then the original INFO block.
+ // This can be used to update clients on other cluster members, etc.
+ clientProtoInfo
+)
+
+type connectInfo struct {
+ Verbose bool `json:"verbose"`
+ Pedantic bool `json:"pedantic"`
+ User string `json:"user,omitempty"`
+ Pass string `json:"pass,omitempty"`
+ Token string `json:"auth_token,omitempty"`
+ TLS bool `json:"tls_required"`
+ Name string `json:"name"`
+ Lang string `json:"lang"`
+ Version string `json:"version"`
+ Protocol int `json:"protocol"`
+ Echo bool `json:"echo"`
+}
+
+// MsgHandler is a callback function that processes messages delivered to
+// asynchronous subscribers.
+type MsgHandler func(msg *Msg)
+
+// Connect will attempt to connect to the NATS system.
+// The url can contain username/password semantics. e.g. nats://derek:pass@localhost:4222
+// Comma separated arrays are also supported, e.g. urlA, urlB.
+// Options start with the defaults but can be overridden.
+func Connect(url string, options ...Option) (*Conn, error) {
+ opts := GetDefaultOptions()
+ opts.Servers = processUrlString(url)
+ for _, opt := range options {
+ if opt != nil {
+ if err := opt(&opts); err != nil {
+ return nil, err
+ }
+ }
+ }
+ return opts.Connect()
+}
+
+// Options that can be passed to Connect.
+
+// Name is an Option to set the client name.
+func Name(name string) Option {
+ return func(o *Options) error {
+ o.Name = name
+ return nil
+ }
+}
+
+// Secure is an Option to enable TLS secure connections that skip server verification by default.
+// Pass a TLS Configuration for proper TLS.
+func Secure(tls ...*tls.Config) Option {
+ return func(o *Options) error {
+ o.Secure = true
+ // Use of variadic just simplifies testing scenarios. We only take the first one.
+ // fixme(DLC) - Could panic if more than one. Could also do TLS option.
+ if len(tls) > 1 {
+ return ErrMultipleTLSConfigs
+ }
+ if len(tls) == 1 {
+ o.TLSConfig = tls[0]
+ }
+ return nil
+ }
+}
+
+// RootCAs is a helper option to provide the RootCAs pool from a list of filenames. If Secure is
+// not already set this will set it as well.
+func RootCAs(file ...string) Option {
+ return func(o *Options) error {
+ pool := x509.NewCertPool()
+ for _, f := range file {
+ rootPEM, err := ioutil.ReadFile(f)
+ if err != nil || rootPEM == nil {
+ return fmt.Errorf("nats: error loading or parsing rootCA file: %v", err)
+ }
+ ok := pool.AppendCertsFromPEM(rootPEM)
+ if !ok {
+ return fmt.Errorf("nats: failed to parse root certificate from %q", f)
+ }
+ }
+ if o.TLSConfig == nil {
+ o.TLSConfig = &tls.Config{MinVersion: tls.VersionTLS12}
+ }
+ o.TLSConfig.RootCAs = pool
+ o.Secure = true
+ return nil
+ }
+}
+
+// ClientCert is a helper option to provide the client certificate from a file. If Secure is
+// not already set this will set it as well
+func ClientCert(certFile, keyFile string) Option {
+ return func(o *Options) error {
+ cert, err := tls.LoadX509KeyPair(certFile, keyFile)
+ if err != nil {
+ return fmt.Errorf("nats: error loading client certificate: %v", err)
+ }
+ cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
+ if err != nil {
+ return fmt.Errorf("nats: error parsing client certificate: %v", err)
+ }
+ if o.TLSConfig == nil {
+ o.TLSConfig = &tls.Config{MinVersion: tls.VersionTLS12}
+ }
+ o.TLSConfig.Certificates = []tls.Certificate{cert}
+ o.Secure = true
+ return nil
+ }
+}
+
+// NoReconnect is an Option to turn off reconnect behavior.
+func NoReconnect() Option {
+ return func(o *Options) error {
+ o.AllowReconnect = false
+ return nil
+ }
+}
+
+// DontRandomize is an Option to turn off randomizing the server pool.
+func DontRandomize() Option {
+ return func(o *Options) error {
+ o.NoRandomize = true
+ return nil
+ }
+}
+
+// NoEcho is an Option to turn off messages echoing back from a server.
+// Note this is supported on servers >= version 1.2. Proto 1 or greater.
+func NoEcho() Option {
+ return func(o *Options) error {
+ o.NoEcho = true
+ return nil
+ }
+}
+
+// ReconnectWait is an Option to set the wait time between reconnect attempts.
+func ReconnectWait(t time.Duration) Option {
+ return func(o *Options) error {
+ o.ReconnectWait = t
+ return nil
+ }
+}
+
+// MaxReconnects is an Option to set the maximum number of reconnect attempts.
+func MaxReconnects(max int) Option {
+ return func(o *Options) error {
+ o.MaxReconnect = max
+ return nil
+ }
+}
+
+// PingInterval is an Option to set the period for client ping commands
+func PingInterval(t time.Duration) Option {
+ return func(o *Options) error {
+ o.PingInterval = t
+ return nil
+ }
+}
+
+// ReconnectBufSize sets the buffer size of messages kept while busy reconnecting
+func ReconnectBufSize(size int) Option {
+ return func(o *Options) error {
+ o.ReconnectBufSize = size
+ return nil
+ }
+}
+
+// Timeout is an Option to set the timeout for Dial on a connection.
+func Timeout(t time.Duration) Option {
+ return func(o *Options) error {
+ o.Timeout = t
+ return nil
+ }
+}
+
+// DrainTimeout is an Option to set the timeout for draining a connection.
+func DrainTimeout(t time.Duration) Option {
+ return func(o *Options) error {
+ o.DrainTimeout = t
+ return nil
+ }
+}
+
+// DisconnectHandler is an Option to set the disconnected handler.
+func DisconnectHandler(cb ConnHandler) Option {
+ return func(o *Options) error {
+ o.DisconnectedCB = cb
+ return nil
+ }
+}
+
+// ReconnectHandler is an Option to set the reconnected handler.
+func ReconnectHandler(cb ConnHandler) Option {
+ return func(o *Options) error {
+ o.ReconnectedCB = cb
+ return nil
+ }
+}
+
+// ClosedHandler is an Option to set the closed handler.
+func ClosedHandler(cb ConnHandler) Option {
+ return func(o *Options) error {
+ o.ClosedCB = cb
+ return nil
+ }
+}
+
+// DiscoveredServersHandler is an Option to set the new servers handler.
+func DiscoveredServersHandler(cb ConnHandler) Option {
+ return func(o *Options) error {
+ o.DiscoveredServersCB = cb
+ return nil
+ }
+}
+
+// ErrorHandler is an Option to set the async error handler.
+func ErrorHandler(cb ErrHandler) Option {
+ return func(o *Options) error {
+ o.AsyncErrorCB = cb
+ return nil
+ }
+}
+
+// UserInfo is an Option to set the username and password to
+// use when not included directly in the URLs.
+func UserInfo(user, password string) Option {
+ return func(o *Options) error {
+ o.User = user
+ o.Password = password
+ return nil
+ }
+}
+
+// Token is an Option to set the token to use when not included
+// directly in the URLs.
+func Token(token string) Option {
+ return func(o *Options) error {
+ o.Token = token
+ return nil
+ }
+}
+
+// Dialer is an Option to set the dialer which will be used when
+// attempting to establish a connection.
+// DEPRECATED: Should use CustomDialer instead.
+func Dialer(dialer *net.Dialer) Option {
+ return func(o *Options) error {
+ o.Dialer = dialer
+ return nil
+ }
+}
+
+// SetCustomDialer is an Option to set a custom dialer which will be
+// used when attempting to establish a connection. If both Dialer
+// and CustomDialer are specified, CustomDialer takes precedence.
+func SetCustomDialer(dialer CustomDialer) Option {
+ return func(o *Options) error {
+ o.CustomDialer = dialer
+ return nil
+ }
+}
+
+// UseOldRequestStyle is an Option to force usage of the old Request style.
+func UseOldRequestStyle() Option {
+ return func(o *Options) error {
+ o.UseOldRequestStyle = true
+ return nil
+ }
+}
+
+// Handler processing
+
+// SetDisconnectHandler will set the disconnect event handler.
+func (nc *Conn) SetDisconnectHandler(dcb ConnHandler) {
+ if nc == nil {
+ return
+ }
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ nc.Opts.DisconnectedCB = dcb
+}
+
+// SetReconnectHandler will set the reconnect event handler.
+func (nc *Conn) SetReconnectHandler(rcb ConnHandler) {
+ if nc == nil {
+ return
+ }
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ nc.Opts.ReconnectedCB = rcb
+}
+
+// SetDiscoveredServersHandler will set the discovered servers handler.
+func (nc *Conn) SetDiscoveredServersHandler(dscb ConnHandler) {
+ if nc == nil {
+ return
+ }
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ nc.Opts.DiscoveredServersCB = dscb
+}
+
+// SetClosedHandler will set the reconnect event handler.
+func (nc *Conn) SetClosedHandler(cb ConnHandler) {
+ if nc == nil {
+ return
+ }
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ nc.Opts.ClosedCB = cb
+}
+
+// SetErrorHandler will set the async error handler.
+func (nc *Conn) SetErrorHandler(cb ErrHandler) {
+ if nc == nil {
+ return
+ }
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ nc.Opts.AsyncErrorCB = cb
+}
+
+// Process the url string argument to Connect. Return an array of
+// urls, even if only one.
+func processUrlString(url string) []string {
+ urls := strings.Split(url, ",")
+ for i, s := range urls {
+ urls[i] = strings.TrimSpace(s)
+ }
+ return urls
+}
+
+// Connect will attempt to connect to a NATS server with multiple options.
+func (o Options) Connect() (*Conn, error) {
+ nc := &Conn{Opts: o}
+
+ // Some default options processing.
+ if nc.Opts.MaxPingsOut == 0 {
+ nc.Opts.MaxPingsOut = DefaultMaxPingOut
+ }
+ // Allow old default for channel length to work correctly.
+ if nc.Opts.SubChanLen == 0 {
+ nc.Opts.SubChanLen = DefaultMaxChanLen
+ }
+ // Default ReconnectBufSize
+ if nc.Opts.ReconnectBufSize == 0 {
+ nc.Opts.ReconnectBufSize = DefaultReconnectBufSize
+ }
+ // Ensure that Timeout is not 0
+ if nc.Opts.Timeout == 0 {
+ nc.Opts.Timeout = DefaultTimeout
+ }
+
+ // Allow custom Dialer for connecting using DialTimeout by default
+ if nc.Opts.Dialer == nil {
+ nc.Opts.Dialer = &net.Dialer{
+ Timeout: nc.Opts.Timeout,
+ }
+ }
+
+ if err := nc.setupServerPool(); err != nil {
+ return nil, err
+ }
+
+ // Create the async callback handler.
+ nc.ach = &asyncCallbacksHandler{}
+ nc.ach.cond = sync.NewCond(&nc.ach.mu)
+
+ if err := nc.connect(); err != nil {
+ return nil, err
+ }
+
+ // Spin up the async cb dispatcher on success
+ go nc.ach.asyncCBDispatcher()
+
+ return nc, nil
+}
+
+const (
+ _CRLF_ = "\r\n"
+ _EMPTY_ = ""
+ _SPC_ = " "
+ _PUB_P_ = "PUB "
+)
+
+const (
+ _OK_OP_ = "+OK"
+ _ERR_OP_ = "-ERR"
+ _PONG_OP_ = "PONG"
+ _INFO_OP_ = "INFO"
+)
+
+const (
+ conProto = "CONNECT %s" + _CRLF_
+ pingProto = "PING" + _CRLF_
+ pongProto = "PONG" + _CRLF_
+ subProto = "SUB %s %s %d" + _CRLF_
+ unsubProto = "UNSUB %d %s" + _CRLF_
+ okProto = _OK_OP_ + _CRLF_
+)
+
+// Return the currently selected server
+func (nc *Conn) currentServer() (int, *srv) {
+ for i, s := range nc.srvPool {
+ if s == nil {
+ continue
+ }
+ if s.url == nc.url {
+ return i, s
+ }
+ }
+ return -1, nil
+}
+
+// Pop the current server and put onto the end of the list. Select head of list as long
+// as number of reconnect attempts under MaxReconnect.
+func (nc *Conn) selectNextServer() (*srv, error) {
+ i, s := nc.currentServer()
+ if i < 0 {
+ return nil, ErrNoServers
+ }
+ sp := nc.srvPool
+ num := len(sp)
+ copy(sp[i:num-1], sp[i+1:num])
+ maxReconnect := nc.Opts.MaxReconnect
+ if maxReconnect < 0 || s.reconnects < maxReconnect {
+ nc.srvPool[num-1] = s
+ } else {
+ nc.srvPool = sp[0 : num-1]
+ }
+ if len(nc.srvPool) <= 0 {
+ nc.url = nil
+ return nil, ErrNoServers
+ }
+ nc.url = nc.srvPool[0].url
+ return nc.srvPool[0], nil
+}
+
+// Will assign the correct server to the nc.Url
+func (nc *Conn) pickServer() error {
+ nc.url = nil
+ if len(nc.srvPool) <= 0 {
+ return ErrNoServers
+ }
+ for _, s := range nc.srvPool {
+ if s != nil {
+ nc.url = s.url
+ return nil
+ }
+ }
+ return ErrNoServers
+}
+
+const tlsScheme = "tls"
+
+// Create the server pool using the options given.
+// We will place a Url option first, followed by any
+// Server Options. We will randomize the server pool unless
+// the NoRandomize flag is set.
+func (nc *Conn) setupServerPool() error {
+ nc.srvPool = make([]*srv, 0, srvPoolSize)
+ nc.urls = make(map[string]struct{}, srvPoolSize)
+
+ // Create srv objects from each url string in nc.Opts.Servers
+ // and add them to the pool
+ for _, urlString := range nc.Opts.Servers {
+ if err := nc.addURLToPool(urlString, false); err != nil {
+ return err
+ }
+ }
+
+ // Randomize if allowed to
+ if !nc.Opts.NoRandomize {
+ nc.shufflePool()
+ }
+
+ // Normally, if this one is set, Options.Servers should not be,
+ // but we always allowed that, so continue to do so.
+ if nc.Opts.Url != _EMPTY_ {
+ // Add to the end of the array
+ if err := nc.addURLToPool(nc.Opts.Url, false); err != nil {
+ return err
+ }
+ // Then swap it with first to guarantee that Options.Url is tried first.
+ last := len(nc.srvPool) - 1
+ if last > 0 {
+ nc.srvPool[0], nc.srvPool[last] = nc.srvPool[last], nc.srvPool[0]
+ }
+ } else if len(nc.srvPool) <= 0 {
+ // Place default URL if pool is empty.
+ if err := nc.addURLToPool(DefaultURL, false); err != nil {
+ return err
+ }
+ }
+
+ // Check for Scheme hint to move to TLS mode.
+ for _, srv := range nc.srvPool {
+ if srv.url.Scheme == tlsScheme {
+ // FIXME(dlc), this is for all in the pool, should be case by case.
+ nc.Opts.Secure = true
+ if nc.Opts.TLSConfig == nil {
+ nc.Opts.TLSConfig = &tls.Config{MinVersion: tls.VersionTLS12}
+ }
+ }
+ }
+
+ return nc.pickServer()
+}
+
+// addURLToPool adds an entry to the server pool
+func (nc *Conn) addURLToPool(sURL string, implicit bool) error {
+ if !strings.Contains(sURL, "://") {
+ sURL = "nats://" + sURL
+ }
+ var (
+ u *url.URL
+ err error
+ )
+ for i := 0; i < 2; i++ {
+ u, err = url.Parse(sURL)
+ if err != nil {
+ return err
+ }
+ if u.Port() != "" {
+ break
+ }
+ // In case given URL is of the form "localhost:", just add
+ // the port number at the end, otherwise, add ":4222".
+ if sURL[len(sURL)-1] != ':' {
+ sURL += ":"
+ }
+ sURL += defaultPortString
+ }
+ s := &srv{url: u, isImplicit: implicit}
+ nc.srvPool = append(nc.srvPool, s)
+ nc.urls[u.Host] = struct{}{}
+ return nil
+}
+
+// shufflePool swaps randomly elements in the server pool
+func (nc *Conn) shufflePool() {
+ if len(nc.srvPool) <= 1 {
+ return
+ }
+ source := rand.NewSource(time.Now().UnixNano())
+ r := rand.New(source)
+ for i := range nc.srvPool {
+ j := r.Intn(i + 1)
+ nc.srvPool[i], nc.srvPool[j] = nc.srvPool[j], nc.srvPool[i]
+ }
+}
+
+// createConn will connect to the server and wrap the appropriate
+// bufio structures. It will do the right thing when an existing
+// connection is in place.
+func (nc *Conn) createConn() (err error) {
+ if nc.Opts.Timeout < 0 {
+ return ErrBadTimeout
+ }
+ if _, cur := nc.currentServer(); cur == nil {
+ return ErrNoServers
+ } else {
+ cur.lastAttempt = time.Now()
+ }
+
+ // CustomDialer takes precedence. If not set, use Opts.Dialer which
+ // is set to a default *net.Dialer (in Connect()) if not explicitly
+ // set by the user.
+ dialer := nc.Opts.CustomDialer
+ if dialer == nil {
+ dialer = nc.Opts.Dialer
+ }
+ nc.conn, err = dialer.Dial("tcp", nc.url.Host)
+ if err != nil {
+ return err
+ }
+
+ // No clue why, but this stalls and kills performance on Mac (Mavericks).
+ // https://code.google.com/p/go/issues/detail?id=6930
+ //if ip, ok := nc.conn.(*net.TCPConn); ok {
+ // ip.SetReadBuffer(defaultBufSize)
+ //}
+
+ if nc.pending != nil && nc.bw != nil {
+ // Move to pending buffer.
+ nc.bw.Flush()
+ }
+ nc.bw = bufio.NewWriterSize(nc.conn, defaultBufSize)
+ return nil
+}
+
+// makeTLSConn will wrap an existing Conn using TLS
+func (nc *Conn) makeTLSConn() {
+ // Allow the user to configure their own tls.Config structure, otherwise
+ // default to InsecureSkipVerify.
+ // TODO(dlc) - We should make the more secure version the default.
+ if nc.Opts.TLSConfig != nil {
+ tlsCopy := util.CloneTLSConfig(nc.Opts.TLSConfig)
+ // If its blank we will override it with the current host
+ if tlsCopy.ServerName == _EMPTY_ {
+ h, _, _ := net.SplitHostPort(nc.url.Host)
+ tlsCopy.ServerName = h
+ }
+ nc.conn = tls.Client(nc.conn, tlsCopy)
+ } else {
+ nc.conn = tls.Client(nc.conn, &tls.Config{InsecureSkipVerify: true})
+ }
+ conn := nc.conn.(*tls.Conn)
+ conn.Handshake()
+ nc.bw = bufio.NewWriterSize(nc.conn, defaultBufSize)
+}
+
+// waitForExits will wait for all socket watcher Go routines to
+// be shutdown before proceeding.
+func (nc *Conn) waitForExits() {
+ // Kick old flusher forcefully.
+ select {
+ case nc.fch <- struct{}{}:
+ default:
+ }
+
+ // Wait for any previous go routines.
+ nc.wg.Wait()
+}
+
+// Report the connected server's Url
+func (nc *Conn) ConnectedUrl() string {
+ if nc == nil {
+ return _EMPTY_
+ }
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ if nc.status != CONNECTED {
+ return _EMPTY_
+ }
+ return nc.url.String()
+}
+
+// Report the connected server's Id
+func (nc *Conn) ConnectedServerId() string {
+ if nc == nil {
+ return _EMPTY_
+ }
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ if nc.status != CONNECTED {
+ return _EMPTY_
+ }
+ return nc.info.Id
+}
+
+// Low level setup for structs, etc
+func (nc *Conn) setup() {
+ nc.subs = make(map[int64]*Subscription)
+ nc.pongs = make([]chan struct{}, 0, 8)
+
+ nc.fch = make(chan struct{}, flushChanSize)
+
+ // Setup scratch outbound buffer for PUB
+ pub := nc.scratch[:len(_PUB_P_)]
+ copy(pub, _PUB_P_)
+}
+
+// Process a connected connection and initialize properly.
+func (nc *Conn) processConnectInit() error {
+
+ // Set our deadline for the whole connect process
+ nc.conn.SetDeadline(time.Now().Add(nc.Opts.Timeout))
+ defer nc.conn.SetDeadline(time.Time{})
+
+ // Set our status to connecting.
+ nc.status = CONNECTING
+
+ // Process the INFO protocol received from the server
+ err := nc.processExpectedInfo()
+ if err != nil {
+ return err
+ }
+
+ // Send the CONNECT protocol along with the initial PING protocol.
+ // Wait for the PONG response (or any error that we get from the server).
+ err = nc.sendConnect()
+ if err != nil {
+ return err
+ }
+
+ // Reset the number of PING sent out
+ nc.pout = 0
+
+ // Start or reset Timer
+ if nc.Opts.PingInterval > 0 {
+ if nc.ptmr == nil {
+ nc.ptmr = time.AfterFunc(nc.Opts.PingInterval, nc.processPingTimer)
+ } else {
+ nc.ptmr.Reset(nc.Opts.PingInterval)
+ }
+ }
+
+ // Start the readLoop and flusher go routines, we will wait on both on a reconnect event.
+ nc.wg.Add(2)
+ go nc.readLoop()
+ go nc.flusher()
+
+ return nil
+}
+
+// Main connect function. Will connect to the nats-server
+func (nc *Conn) connect() error {
+ var returnedErr error
+
+ // Create actual socket connection
+ // For first connect we walk all servers in the pool and try
+ // to connect immediately.
+ nc.mu.Lock()
+ nc.initc = true
+ // The pool may change inside the loop iteration due to INFO protocol.
+ for i := 0; i < len(nc.srvPool); i++ {
+ nc.url = nc.srvPool[i].url
+
+ if err := nc.createConn(); err == nil {
+ // This was moved out of processConnectInit() because
+ // that function is now invoked from doReconnect() too.
+ nc.setup()
+
+ err = nc.processConnectInit()
+
+ if err == nil {
+ nc.srvPool[i].didConnect = true
+ nc.srvPool[i].reconnects = 0
+ returnedErr = nil
+ break
+ } else {
+ returnedErr = err
+ nc.mu.Unlock()
+ nc.close(DISCONNECTED, false)
+ nc.mu.Lock()
+ nc.url = nil
+ }
+ } else {
+ // Cancel out default connection refused, will trigger the
+ // No servers error conditional
+ if strings.Contains(err.Error(), "connection refused") {
+ returnedErr = nil
+ }
+ }
+ }
+ nc.initc = false
+ defer nc.mu.Unlock()
+
+ if returnedErr == nil && nc.status != CONNECTED {
+ returnedErr = ErrNoServers
+ }
+ return returnedErr
+}
+
+// This will check to see if the connection should be
+// secure. This can be dictated from either end and should
+// only be called after the INIT protocol has been received.
+func (nc *Conn) checkForSecure() error {
+ // Check to see if we need to engage TLS
+ o := nc.Opts
+
+ // Check for mismatch in setups
+ if o.Secure && !nc.info.TLSRequired {
+ return ErrSecureConnWanted
+ } else if nc.info.TLSRequired && !o.Secure {
+ // Switch to Secure since server needs TLS.
+ o.Secure = true
+ }
+
+ // Need to rewrap with bufio
+ if o.Secure {
+ nc.makeTLSConn()
+ }
+ return nil
+}
+
+// processExpectedInfo will look for the expected first INFO message
+// sent when a connection is established. The lock should be held entering.
+func (nc *Conn) processExpectedInfo() error {
+
+ c := &control{}
+
+ // Read the protocol
+ err := nc.readOp(c)
+ if err != nil {
+ return err
+ }
+
+ // The nats protocol should send INFO first always.
+ if c.op != _INFO_OP_ {
+ return ErrNoInfoReceived
+ }
+
+ // Parse the protocol
+ if err := nc.processInfo(c.args); err != nil {
+ return err
+ }
+
+ return nc.checkForSecure()
+}
+
+// Sends a protocol control message by queuing into the bufio writer
+// and kicking the flush Go routine. These writes are protected.
+func (nc *Conn) sendProto(proto string) {
+ nc.mu.Lock()
+ nc.bw.WriteString(proto)
+ nc.kickFlusher()
+ nc.mu.Unlock()
+}
+
+// Generate a connect protocol message, issuing user/password if
+// applicable. The lock is assumed to be held upon entering.
+func (nc *Conn) connectProto() (string, error) {
+ o := nc.Opts
+ var user, pass, token string
+ u := nc.url.User
+ if u != nil {
+ // if no password, assume username is authToken
+ if _, ok := u.Password(); !ok {
+ token = u.Username()
+ } else {
+ user = u.Username()
+ pass, _ = u.Password()
+ }
+ } else {
+ // Take from options (possibly all empty strings)
+ user = nc.Opts.User
+ pass = nc.Opts.Password
+ token = nc.Opts.Token
+ }
+
+ cinfo := connectInfo{o.Verbose, o.Pedantic, user, pass, token,
+ o.Secure, o.Name, LangString, Version, clientProtoInfo, !o.NoEcho}
+
+ b, err := json.Marshal(cinfo)
+ if err != nil {
+ return _EMPTY_, ErrJsonParse
+ }
+
+ // Check if NoEcho is set and we have a server that supports it.
+ if o.NoEcho && nc.info.Proto < 1 {
+ return _EMPTY_, ErrNoEchoNotSupported
+ }
+
+ return fmt.Sprintf(conProto, b), nil
+}
+
+// normalizeErr removes the prefix -ERR, trim spaces and remove the quotes.
+func normalizeErr(line string) string {
+ s := strings.ToLower(strings.TrimSpace(strings.TrimPrefix(line, _ERR_OP_)))
+ s = strings.TrimLeft(strings.TrimRight(s, "'"), "'")
+ return s
+}
+
+// Send a connect protocol message to the server, issue user/password if
+// applicable. Will wait for a flush to return from the server for error
+// processing.
+func (nc *Conn) sendConnect() error {
+
+ // Construct the CONNECT protocol string
+ cProto, err := nc.connectProto()
+ if err != nil {
+ return err
+ }
+
+ // Write the protocol into the buffer
+ _, err = nc.bw.WriteString(cProto)
+ if err != nil {
+ return err
+ }
+
+ // Add to the buffer the PING protocol
+ _, err = nc.bw.WriteString(pingProto)
+ if err != nil {
+ return err
+ }
+
+ // Flush the buffer
+ err = nc.bw.Flush()
+ if err != nil {
+ return err
+ }
+
+ // We don't want to read more than we need here, otherwise
+ // we would need to transfer the excess read data to the readLoop.
+ // Since in normal situations we just are looking for a PONG\r\n,
+ // reading byte-by-byte here is ok.
+ proto, err := nc.readProto()
+ if err != nil {
+ return err
+ }
+
+ // If opts.Verbose is set, handle +OK
+ if nc.Opts.Verbose && proto == okProto {
+ // Read the rest now...
+ proto, err = nc.readProto()
+ if err != nil {
+ return err
+ }
+ }
+
+ // We expect a PONG
+ if proto != pongProto {
+ // But it could be something else, like -ERR
+
+ // Since we no longer use ReadLine(), trim the trailing "\r\n"
+ proto = strings.TrimRight(proto, "\r\n")
+
+ // If it's a server error...
+ if strings.HasPrefix(proto, _ERR_OP_) {
+ // Remove -ERR, trim spaces and quotes, and convert to lower case.
+ proto = normalizeErr(proto)
+ return errors.New("nats: " + proto)
+ }
+
+ // Notify that we got an unexpected protocol.
+ return fmt.Errorf("nats: expected '%s', got '%s'", _PONG_OP_, proto)
+ }
+
+ // This is where we are truly connected.
+ nc.status = CONNECTED
+
+ return nil
+}
+
+// reads a protocol one byte at a time.
+func (nc *Conn) readProto() (string, error) {
+ var (
+ _buf = [10]byte{}
+ buf = _buf[:0]
+ b = [1]byte{}
+ protoEnd = byte('\n')
+ )
+ for {
+ if _, err := nc.conn.Read(b[:1]); err != nil {
+ // Do not report EOF error
+ if err == io.EOF {
+ return string(buf), nil
+ }
+ return "", err
+ }
+ buf = append(buf, b[0])
+ if b[0] == protoEnd {
+ return string(buf), nil
+ }
+ }
+}
+
+// A control protocol line.
+type control struct {
+ op, args string
+}
+
+// Read a control line and process the intended op.
+func (nc *Conn) readOp(c *control) error {
+ br := bufio.NewReaderSize(nc.conn, defaultBufSize)
+ line, err := br.ReadString('\n')
+ if err != nil {
+ return err
+ }
+ parseControl(line, c)
+ return nil
+}
+
+// Parse a control line from the server.
+func parseControl(line string, c *control) {
+ toks := strings.SplitN(line, _SPC_, 2)
+ if len(toks) == 1 {
+ c.op = strings.TrimSpace(toks[0])
+ c.args = _EMPTY_
+ } else if len(toks) == 2 {
+ c.op, c.args = strings.TrimSpace(toks[0]), strings.TrimSpace(toks[1])
+ } else {
+ c.op = _EMPTY_
+ }
+}
+
+// flushReconnectPending will push the pending items that were
+// gathered while we were in a RECONNECTING state to the socket.
+func (nc *Conn) flushReconnectPendingItems() {
+ if nc.pending == nil {
+ return
+ }
+ if nc.pending.Len() > 0 {
+ nc.bw.Write(nc.pending.Bytes())
+ }
+}
+
+// Stops the ping timer if set.
+// Connection lock is held on entry.
+func (nc *Conn) stopPingTimer() {
+ if nc.ptmr != nil {
+ nc.ptmr.Stop()
+ }
+}
+
+// Try to reconnect using the option parameters.
+// This function assumes we are allowed to reconnect.
+func (nc *Conn) doReconnect() {
+ // We want to make sure we have the other watchers shutdown properly
+ // here before we proceed past this point.
+ nc.waitForExits()
+
+ // FIXME(dlc) - We have an issue here if we have
+ // outstanding flush points (pongs) and they were not
+ // sent out, but are still in the pipe.
+
+ // Hold the lock manually and release where needed below,
+ // can't do defer here.
+ nc.mu.Lock()
+
+ // Clear any queued pongs, e.g. pending flush calls.
+ nc.clearPendingFlushCalls()
+
+ // Clear any errors.
+ nc.err = nil
+ // Perform appropriate callback if needed for a disconnect.
+ if nc.Opts.DisconnectedCB != nil {
+ nc.ach.push(func() { nc.Opts.DisconnectedCB(nc) })
+ }
+
+ // This is used to wait on go routines exit if we start them in the loop
+ // but an error occurs after that.
+ waitForGoRoutines := false
+
+ for len(nc.srvPool) > 0 {
+ cur, err := nc.selectNextServer()
+ if err != nil {
+ nc.err = err
+ break
+ }
+
+ sleepTime := int64(0)
+
+ // Sleep appropriate amount of time before the
+ // connection attempt if connecting to same server
+ // we just got disconnected from..
+ if time.Since(cur.lastAttempt) < nc.Opts.ReconnectWait {
+ sleepTime = int64(nc.Opts.ReconnectWait - time.Since(cur.lastAttempt))
+ }
+
+ // On Windows, createConn() will take more than a second when no
+ // server is running at that address. So it could be that the
+ // time elapsed between reconnect attempts is always > than
+ // the set option. Release the lock to give a chance to a parallel
+ // nc.Close() to break the loop.
+ nc.mu.Unlock()
+ if sleepTime <= 0 {
+ runtime.Gosched()
+ } else {
+ time.Sleep(time.Duration(sleepTime))
+ }
+ // If the readLoop, etc.. go routines were started, wait for them to complete.
+ if waitForGoRoutines {
+ nc.waitForExits()
+ waitForGoRoutines = false
+ }
+ nc.mu.Lock()
+
+ // Check if we have been closed first.
+ if nc.isClosed() {
+ break
+ }
+
+ // Mark that we tried a reconnect
+ cur.reconnects++
+
+ // Try to create a new connection
+ err = nc.createConn()
+
+ // Not yet connected, retry...
+ // Continue to hold the lock
+ if err != nil {
+ nc.err = nil
+ continue
+ }
+
+ // We are reconnected
+ nc.Reconnects++
+
+ // Process connect logic
+ if nc.err = nc.processConnectInit(); nc.err != nil {
+ nc.status = RECONNECTING
+ // Reset the buffered writer to the pending buffer
+ // (was set to a buffered writer on nc.conn in createConn)
+ nc.bw.Reset(nc.pending)
+ continue
+ }
+
+ // Clear out server stats for the server we connected to..
+ cur.didConnect = true
+ cur.reconnects = 0
+
+ // Send existing subscription state
+ nc.resendSubscriptions()
+
+ // Now send off and clear pending buffer
+ nc.flushReconnectPendingItems()
+
+ // Flush the buffer
+ nc.err = nc.bw.Flush()
+ if nc.err != nil {
+ nc.status = RECONNECTING
+ // Reset the buffered writer to the pending buffer (bytes.Buffer).
+ nc.bw.Reset(nc.pending)
+ // Stop the ping timer (if set)
+ nc.stopPingTimer()
+ // Since processConnectInit() returned without error, the
+ // go routines were started, so wait for them to return
+ // on the next iteration (after releasing the lock).
+ waitForGoRoutines = true
+ continue
+ }
+
+ // Done with the pending buffer
+ nc.pending = nil
+
+ // This is where we are truly connected.
+ nc.status = CONNECTED
+
+ // Queue up the reconnect callback.
+ if nc.Opts.ReconnectedCB != nil {
+ nc.ach.push(func() { nc.Opts.ReconnectedCB(nc) })
+ }
+ // Release lock here, we will return below.
+ nc.mu.Unlock()
+
+ // Make sure to flush everything
+ nc.Flush()
+
+ return
+ }
+
+ // Call into close.. We have no servers left..
+ if nc.err == nil {
+ nc.err = ErrNoServers
+ }
+ nc.mu.Unlock()
+ nc.Close()
+}
+
+// processOpErr handles errors from reading or parsing the protocol.
+// The lock should not be held entering this function.
+func (nc *Conn) processOpErr(err error) {
+ nc.mu.Lock()
+ if nc.isConnecting() || nc.isClosed() || nc.isReconnecting() {
+ nc.mu.Unlock()
+ return
+ }
+
+ if nc.Opts.AllowReconnect && nc.status == CONNECTED {
+ // Set our new status
+ nc.status = RECONNECTING
+ // Stop ping timer if set
+ nc.stopPingTimer()
+ if nc.conn != nil {
+ nc.bw.Flush()
+ nc.conn.Close()
+ nc.conn = nil
+ }
+
+ // Create pending buffer before reconnecting.
+ nc.pending = new(bytes.Buffer)
+ nc.bw.Reset(nc.pending)
+
+ go nc.doReconnect()
+ nc.mu.Unlock()
+ return
+ }
+
+ nc.status = DISCONNECTED
+ nc.err = err
+ nc.mu.Unlock()
+ nc.Close()
+}
+
+// dispatch is responsible for calling any async callbacks
+func (ac *asyncCallbacksHandler) asyncCBDispatcher() {
+ for {
+ ac.mu.Lock()
+ // Protect for spurious wakeups. We should get out of the
+ // wait only if there is an element to pop from the list.
+ for ac.head == nil {
+ ac.cond.Wait()
+ }
+ cur := ac.head
+ ac.head = cur.next
+ if cur == ac.tail {
+ ac.tail = nil
+ }
+ ac.mu.Unlock()
+
+ // This signals that the dispatcher has been closed and all
+ // previous callbacks have been dispatched.
+ if cur.f == nil {
+ return
+ }
+ // Invoke callback outside of handler's lock
+ cur.f()
+ }
+}
+
+// Add the given function to the tail of the list and
+// signals the dispatcher.
+func (ac *asyncCallbacksHandler) push(f func()) {
+ ac.pushOrClose(f, false)
+}
+
+// Signals that we are closing...
+func (ac *asyncCallbacksHandler) close() {
+ ac.pushOrClose(nil, true)
+}
+
+// Add the given function to the tail of the list and
+// signals the dispatcher.
+func (ac *asyncCallbacksHandler) pushOrClose(f func(), close bool) {
+ ac.mu.Lock()
+ defer ac.mu.Unlock()
+ // Make sure that library is not calling push with nil function,
+ // since this is used to notify the dispatcher that it should stop.
+ if !close && f == nil {
+ panic("pushing a nil callback")
+ }
+ cb := &asyncCB{f: f}
+ if ac.tail != nil {
+ ac.tail.next = cb
+ } else {
+ ac.head = cb
+ }
+ ac.tail = cb
+ if close {
+ ac.cond.Broadcast()
+ } else {
+ ac.cond.Signal()
+ }
+}
+
+// readLoop() will sit on the socket reading and processing the
+// protocol from the server. It will dispatch appropriately based
+// on the op type.
+func (nc *Conn) readLoop() {
+ // Release the wait group on exit
+ defer nc.wg.Done()
+
+ // Create a parseState if needed.
+ nc.mu.Lock()
+ if nc.ps == nil {
+ nc.ps = &parseState{}
+ }
+ nc.mu.Unlock()
+
+ // Stack based buffer.
+ b := make([]byte, defaultBufSize)
+
+ for {
+ // FIXME(dlc): RWLock here?
+ nc.mu.Lock()
+ sb := nc.isClosed() || nc.isReconnecting()
+ if sb {
+ nc.ps = &parseState{}
+ }
+ conn := nc.conn
+ nc.mu.Unlock()
+
+ if sb || conn == nil {
+ break
+ }
+
+ n, err := conn.Read(b)
+ if err != nil {
+ nc.processOpErr(err)
+ break
+ }
+
+ if err := nc.parse(b[:n]); err != nil {
+ nc.processOpErr(err)
+ break
+ }
+ }
+ // Clear the parseState here..
+ nc.mu.Lock()
+ nc.ps = nil
+ nc.mu.Unlock()
+}
+
+// waitForMsgs waits on the conditional shared with readLoop and processMsg.
+// It is used to deliver messages to asynchronous subscribers.
+func (nc *Conn) waitForMsgs(s *Subscription) {
+ var closed bool
+ var delivered, max uint64
+
+ // Used to account for adjustments to sub.pBytes when we wrap back around.
+ msgLen := -1
+
+ for {
+ s.mu.Lock()
+ // Do accounting for last msg delivered here so we only lock once
+ // and drain state trips after callback has returned.
+ if msgLen >= 0 {
+ s.pMsgs--
+ s.pBytes -= msgLen
+ msgLen = -1
+ }
+
+ if s.pHead == nil && !s.closed {
+ s.pCond.Wait()
+ }
+ // Pop the msg off the list
+ m := s.pHead
+ if m != nil {
+ s.pHead = m.next
+ if s.pHead == nil {
+ s.pTail = nil
+ }
+ if m.barrier != nil {
+ s.mu.Unlock()
+ if atomic.AddInt64(&m.barrier.refs, -1) == 0 {
+ m.barrier.f()
+ }
+ continue
+ }
+ msgLen = len(m.Data)
+ }
+ mcb := s.mcb
+ max = s.max
+ closed = s.closed
+ if !s.closed {
+ s.delivered++
+ delivered = s.delivered
+ }
+ s.mu.Unlock()
+
+ if closed {
+ break
+ }
+
+ // Deliver the message.
+ if m != nil && (max == 0 || delivered <= max) {
+ mcb(m)
+ }
+ // If we have hit the max for delivered msgs, remove sub.
+ if max > 0 && delivered >= max {
+ nc.mu.Lock()
+ nc.removeSub(s)
+ nc.mu.Unlock()
+ break
+ }
+ }
+ // Check for barrier messages
+ s.mu.Lock()
+ for m := s.pHead; m != nil; m = s.pHead {
+ if m.barrier != nil {
+ s.mu.Unlock()
+ if atomic.AddInt64(&m.barrier.refs, -1) == 0 {
+ m.barrier.f()
+ }
+ s.mu.Lock()
+ }
+ s.pHead = m.next
+ }
+ s.mu.Unlock()
+}
+
+// processMsg is called by parse and will place the msg on the
+// appropriate channel/pending queue for processing. If the channel is full,
+// or the pending queue is over the pending limits, the connection is
+// considered a slow consumer.
+func (nc *Conn) processMsg(data []byte) {
+ // Don't lock the connection to avoid server cutting us off if the
+ // flusher is holding the connection lock, trying to send to the server
+ // that is itself trying to send data to us.
+ nc.subsMu.RLock()
+
+ // Stats
+ nc.InMsgs++
+ nc.InBytes += uint64(len(data))
+
+ sub := nc.subs[nc.ps.ma.sid]
+ if sub == nil {
+ nc.subsMu.RUnlock()
+ return
+ }
+
+ // Copy them into string
+ subj := string(nc.ps.ma.subject)
+ reply := string(nc.ps.ma.reply)
+
+ // Doing message create outside of the sub's lock to reduce contention.
+ // It's possible that we end-up not using the message, but that's ok.
+
+ // FIXME(dlc): Need to copy, should/can do COW?
+ msgPayload := make([]byte, len(data))
+ copy(msgPayload, data)
+
+ // FIXME(dlc): Should we recycle these containers?
+ m := &Msg{Data: msgPayload, Subject: subj, Reply: reply, Sub: sub}
+
+ sub.mu.Lock()
+
+ // Subscription internal stats (applicable only for non ChanSubscription's)
+ if sub.typ != ChanSubscription {
+ sub.pMsgs++
+ if sub.pMsgs > sub.pMsgsMax {
+ sub.pMsgsMax = sub.pMsgs
+ }
+ sub.pBytes += len(m.Data)
+ if sub.pBytes > sub.pBytesMax {
+ sub.pBytesMax = sub.pBytes
+ }
+
+ // Check for a Slow Consumer
+ if (sub.pMsgsLimit > 0 && sub.pMsgs > sub.pMsgsLimit) ||
+ (sub.pBytesLimit > 0 && sub.pBytes > sub.pBytesLimit) {
+ goto slowConsumer
+ }
+ }
+
+ // We have two modes of delivery. One is the channel, used by channel
+ // subscribers and syncSubscribers, the other is a linked list for async.
+ if sub.mch != nil {
+ select {
+ case sub.mch <- m:
+ default:
+ goto slowConsumer
+ }
+ } else {
+ // Push onto the async pList
+ if sub.pHead == nil {
+ sub.pHead = m
+ sub.pTail = m
+ sub.pCond.Signal()
+ } else {
+ sub.pTail.next = m
+ sub.pTail = m
+ }
+ }
+
+ // Clear SlowConsumer status.
+ sub.sc = false
+
+ sub.mu.Unlock()
+ nc.subsMu.RUnlock()
+ return
+
+slowConsumer:
+ sub.dropped++
+ sc := !sub.sc
+ sub.sc = true
+ // Undo stats from above
+ if sub.typ != ChanSubscription {
+ sub.pMsgs--
+ sub.pBytes -= len(m.Data)
+ }
+ sub.mu.Unlock()
+ nc.subsMu.RUnlock()
+ if sc {
+ // Now we need connection's lock and we may end-up in the situation
+ // that we were trying to avoid, except that in this case, the client
+ // is already experiencing client-side slow consumer situation.
+ nc.mu.Lock()
+ nc.err = ErrSlowConsumer
+ if nc.Opts.AsyncErrorCB != nil {
+ nc.ach.push(func() { nc.Opts.AsyncErrorCB(nc, sub, ErrSlowConsumer) })
+ }
+ nc.mu.Unlock()
+ }
+}
+
+// processPermissionsViolation is called when the server signals a subject
+// permissions violation on either publish or subscribe.
+func (nc *Conn) processPermissionsViolation(err string) {
+ nc.mu.Lock()
+ // create error here so we can pass it as a closure to the async cb dispatcher.
+ e := errors.New("nats: " + err)
+ nc.err = e
+ if nc.Opts.AsyncErrorCB != nil {
+ nc.ach.push(func() { nc.Opts.AsyncErrorCB(nc, nil, e) })
+ }
+ nc.mu.Unlock()
+}
+
+// processAuthorizationViolation is called when the server signals a user
+// authorization violation.
+func (nc *Conn) processAuthorizationViolation(err string) {
+ nc.mu.Lock()
+ nc.err = ErrAuthorization
+ if nc.Opts.AsyncErrorCB != nil {
+ nc.ach.push(func() { nc.Opts.AsyncErrorCB(nc, nil, ErrAuthorization) })
+ }
+ nc.mu.Unlock()
+}
+
+// flusher is a separate Go routine that will process flush requests for the write
+// bufio. This allows coalescing of writes to the underlying socket.
+func (nc *Conn) flusher() {
+ // Release the wait group
+ defer nc.wg.Done()
+
+ // snapshot the bw and conn since they can change from underneath of us.
+ nc.mu.Lock()
+ bw := nc.bw
+ conn := nc.conn
+ fch := nc.fch
+ flusherTimeout := nc.Opts.FlusherTimeout
+ nc.mu.Unlock()
+
+ if conn == nil || bw == nil {
+ return
+ }
+
+ for {
+ if _, ok := <-fch; !ok {
+ return
+ }
+ nc.mu.Lock()
+
+ // Check to see if we should bail out.
+ if !nc.isConnected() || nc.isConnecting() || bw != nc.bw || conn != nc.conn {
+ nc.mu.Unlock()
+ return
+ }
+ if bw.Buffered() > 0 {
+ // Allow customizing how long we should wait for a flush to be done
+ // to prevent unhealthy connections blocking the client for too long.
+ if flusherTimeout > 0 {
+ conn.SetWriteDeadline(time.Now().Add(flusherTimeout))
+ }
+
+ if err := bw.Flush(); err != nil {
+ if nc.err == nil {
+ nc.err = err
+ }
+ }
+ conn.SetWriteDeadline(time.Time{})
+ }
+ nc.mu.Unlock()
+ }
+}
+
+// processPing will send an immediate pong protocol response to the
+// server. The server uses this mechanism to detect dead clients.
+func (nc *Conn) processPing() {
+ nc.sendProto(pongProto)
+}
+
+// processPong is used to process responses to the client's ping
+// messages. We use pings for the flush mechanism as well.
+func (nc *Conn) processPong() {
+ var ch chan struct{}
+
+ nc.mu.Lock()
+ if len(nc.pongs) > 0 {
+ ch = nc.pongs[0]
+ nc.pongs = nc.pongs[1:]
+ }
+ nc.pout = 0
+ nc.mu.Unlock()
+ if ch != nil {
+ ch <- struct{}{}
+ }
+}
+
+// processOK is a placeholder for processing OK messages.
+func (nc *Conn) processOK() {
+ // do nothing
+}
+
+// processInfo is used to parse the info messages sent
+// from the server.
+// This function may update the server pool.
+func (nc *Conn) processInfo(info string) error {
+ if info == _EMPTY_ {
+ return nil
+ }
+ ncInfo := serverInfo{}
+ if err := json.Unmarshal([]byte(info), &ncInfo); err != nil {
+ return err
+ }
+ // Copy content into connection's info structure.
+ nc.info = ncInfo
+ // The array could be empty/not present on initial connect,
+ // if advertise is disabled on that server, or servers that
+ // did not include themselves in the async INFO protocol.
+ // If empty, do not remove the implicit servers from the pool.
+ if len(ncInfo.ConnectURLs) == 0 {
+ return nil
+ }
+ // Note about pool randomization: when the pool was first created,
+ // it was randomized (if allowed). We keep the order the same (removing
+ // implicit servers that are no longer sent to us). New URLs are sent
+ // to us in no specific order so don't need extra randomization.
+ hasNew := false
+ // This is what we got from the server we are connected to.
+ urls := nc.info.ConnectURLs
+ // Transform that to a map for easy lookups
+ tmp := make(map[string]struct{}, len(urls))
+ for _, curl := range urls {
+ tmp[curl] = struct{}{}
+ }
+ // Walk the pool and removed the implicit servers that are no longer in the
+ // given array/map
+ sp := nc.srvPool
+ for i := 0; i < len(sp); i++ {
+ srv := sp[i]
+ curl := srv.url.Host
+ // Check if this URL is in the INFO protocol
+ _, inInfo := tmp[curl]
+ // Remove from the temp map so that at the end we are left with only
+ // new (or restarted) servers that need to be added to the pool.
+ delete(tmp, curl)
+ // Keep servers that were set through Options, but also the one that
+ // we are currently connected to (even if it is a discovered server).
+ if !srv.isImplicit || srv.url == nc.url {
+ continue
+ }
+ if !inInfo {
+ // Remove from server pool. Keep current order.
+ copy(sp[i:], sp[i+1:])
+ nc.srvPool = sp[:len(sp)-1]
+ sp = nc.srvPool
+ i--
+ }
+ }
+ // If there are any left in the tmp map, these are new (or restarted) servers
+ // and need to be added to the pool.
+ for curl := range tmp {
+ // Before adding, check if this is a new (as in never seen) URL.
+ // This is used to figure out if we invoke the DiscoveredServersCB
+ if _, present := nc.urls[curl]; !present {
+ hasNew = true
+ }
+ nc.addURLToPool(fmt.Sprintf("nats://%s", curl), true)
+ }
+ if hasNew && !nc.initc && nc.Opts.DiscoveredServersCB != nil {
+ nc.ach.push(func() { nc.Opts.DiscoveredServersCB(nc) })
+ }
+ return nil
+}
+
+// processAsyncInfo does the same than processInfo, but is called
+// from the parser. Calls processInfo under connection's lock
+// protection.
+func (nc *Conn) processAsyncInfo(info []byte) {
+ nc.mu.Lock()
+ // Ignore errors, we will simply not update the server pool...
+ nc.processInfo(string(info))
+ nc.mu.Unlock()
+}
+
+// LastError reports the last error encountered via the connection.
+// It can be used reliably within ClosedCB in order to find out reason
+// why connection was closed for example.
+func (nc *Conn) LastError() error {
+ if nc == nil {
+ return ErrInvalidConnection
+ }
+ nc.mu.Lock()
+ err := nc.err
+ nc.mu.Unlock()
+ return err
+}
+
+// processErr processes any error messages from the server and
+// sets the connection's lastError.
+func (nc *Conn) processErr(e string) {
+ // Trim, remove quotes, convert to lower case.
+ e = normalizeErr(e)
+
+ // FIXME(dlc) - process Slow Consumer signals special.
+ if e == STALE_CONNECTION {
+ nc.processOpErr(ErrStaleConnection)
+ } else if strings.HasPrefix(e, PERMISSIONS_ERR) {
+ nc.processPermissionsViolation(e)
+ } else if strings.HasPrefix(e, AUTHORIZATION_ERR) {
+ nc.processAuthorizationViolation(e)
+ } else {
+ nc.mu.Lock()
+ nc.err = errors.New("nats: " + e)
+ nc.mu.Unlock()
+ nc.Close()
+ }
+}
+
+// kickFlusher will send a bool on a channel to kick the
+// flush Go routine to flush data to the server.
+func (nc *Conn) kickFlusher() {
+ if nc.bw != nil {
+ select {
+ case nc.fch <- struct{}{}:
+ default:
+ }
+ }
+}
+
+// Publish publishes the data argument to the given subject. The data
+// argument is left untouched and needs to be correctly interpreted on
+// the receiver.
+func (nc *Conn) Publish(subj string, data []byte) error {
+ return nc.publish(subj, _EMPTY_, data)
+}
+
+// PublishMsg publishes the Msg structure, which includes the
+// Subject, an optional Reply and an optional Data field.
+func (nc *Conn) PublishMsg(m *Msg) error {
+ if m == nil {
+ return ErrInvalidMsg
+ }
+ return nc.publish(m.Subject, m.Reply, m.Data)
+}
+
+// PublishRequest will perform a Publish() excpecting a response on the
+// reply subject. Use Request() for automatically waiting for a response
+// inline.
+func (nc *Conn) PublishRequest(subj, reply string, data []byte) error {
+ return nc.publish(subj, reply, data)
+}
+
+// Used for handrolled itoa
+const digits = "0123456789"
+
+// publish is the internal function to publish messages to a nats-server.
+// Sends a protocol data message by queuing into the bufio writer
+// and kicking the flush go routine. These writes should be protected.
+func (nc *Conn) publish(subj, reply string, data []byte) error {
+ if nc == nil {
+ return ErrInvalidConnection
+ }
+ if subj == "" {
+ return ErrBadSubject
+ }
+ nc.mu.Lock()
+
+ if nc.isClosed() {
+ nc.mu.Unlock()
+ return ErrConnectionClosed
+ }
+
+ if nc.isDrainingPubs() {
+ nc.mu.Unlock()
+ return ErrConnectionDraining
+ }
+
+ // Proactively reject payloads over the threshold set by server.
+ msgSize := int64(len(data))
+ if msgSize > nc.info.MaxPayload {
+ nc.mu.Unlock()
+ return ErrMaxPayload
+ }
+
+ // Check if we are reconnecting, and if so check if
+ // we have exceeded our reconnect outbound buffer limits.
+ if nc.isReconnecting() {
+ // Flush to underlying buffer.
+ nc.bw.Flush()
+ // Check if we are over
+ if nc.pending.Len() >= nc.Opts.ReconnectBufSize {
+ nc.mu.Unlock()
+ return ErrReconnectBufExceeded
+ }
+ }
+
+ msgh := nc.scratch[:len(_PUB_P_)]
+ msgh = append(msgh, subj...)
+ msgh = append(msgh, ' ')
+ if reply != "" {
+ msgh = append(msgh, reply...)
+ msgh = append(msgh, ' ')
+ }
+
+ // We could be smarter here, but simple loop is ok,
+ // just avoid strconv in fast path
+ // FIXME(dlc) - Find a better way here.
+ // msgh = strconv.AppendInt(msgh, int64(len(data)), 10)
+
+ var b [12]byte
+ var i = len(b)
+ if len(data) > 0 {
+ for l := len(data); l > 0; l /= 10 {
+ i -= 1
+ b[i] = digits[l%10]
+ }
+ } else {
+ i -= 1
+ b[i] = digits[0]
+ }
+
+ msgh = append(msgh, b[i:]...)
+ msgh = append(msgh, _CRLF_...)
+
+ _, err := nc.bw.Write(msgh)
+ if err == nil {
+ _, err = nc.bw.Write(data)
+ }
+ if err == nil {
+ _, err = nc.bw.WriteString(_CRLF_)
+ }
+ if err != nil {
+ nc.mu.Unlock()
+ return err
+ }
+
+ nc.OutMsgs++
+ nc.OutBytes += uint64(len(data))
+
+ if len(nc.fch) == 0 {
+ nc.kickFlusher()
+ }
+ nc.mu.Unlock()
+ return nil
+}
+
+// respHandler is the global response handler. It will look up
+// the appropriate channel based on the last token and place
+// the message on the channel if possible.
+func (nc *Conn) respHandler(m *Msg) {
+ rt := respToken(m.Subject)
+
+ nc.mu.Lock()
+ // Just return if closed.
+ if nc.isClosed() {
+ nc.mu.Unlock()
+ return
+ }
+
+ // Grab mch
+ mch := nc.respMap[rt]
+ // Delete the key regardless, one response only.
+ // FIXME(dlc) - should we track responses past 1
+ // just statistics wise?
+ delete(nc.respMap, rt)
+ nc.mu.Unlock()
+
+ // Don't block, let Request timeout instead, mch is
+ // buffered and we should delete the key before a
+ // second response is processed.
+ select {
+ case mch <- m:
+ default:
+ return
+ }
+}
+
+// Create the response subscription we will use for all
+// new style responses. This will be on an _INBOX with an
+// additional terminal token. The subscription will be on
+// a wildcard. Caller is responsible for ensuring this is
+// only called once.
+func (nc *Conn) createRespMux(respSub string) error {
+ s, err := nc.Subscribe(respSub, nc.respHandler)
+ if err != nil {
+ return err
+ }
+ nc.mu.Lock()
+ nc.respMux = s
+ nc.mu.Unlock()
+ return nil
+}
+
+// Request will send a request payload and deliver the response message,
+// or an error, including a timeout if no message was received properly.
+func (nc *Conn) Request(subj string, data []byte, timeout time.Duration) (*Msg, error) {
+ if nc == nil {
+ return nil, ErrInvalidConnection
+ }
+
+ nc.mu.Lock()
+ // If user wants the old style.
+ if nc.Opts.UseOldRequestStyle {
+ nc.mu.Unlock()
+ return nc.oldRequest(subj, data, timeout)
+ }
+
+ // Do setup for the new style.
+ if nc.respMap == nil {
+ // _INBOX wildcard
+ nc.respSub = fmt.Sprintf("%s.*", NewInbox())
+ nc.respMap = make(map[string]chan *Msg)
+ }
+ // Create literal Inbox and map to a chan msg.
+ mch := make(chan *Msg, RequestChanLen)
+ respInbox := nc.newRespInbox()
+ token := respToken(respInbox)
+ nc.respMap[token] = mch
+ createSub := nc.respMux == nil
+ ginbox := nc.respSub
+ nc.mu.Unlock()
+
+ if createSub {
+ // Make sure scoped subscription is setup only once.
+ var err error
+ nc.respSetup.Do(func() { err = nc.createRespMux(ginbox) })
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if err := nc.PublishRequest(subj, respInbox, data); err != nil {
+ return nil, err
+ }
+
+ t := globalTimerPool.Get(timeout)
+ defer globalTimerPool.Put(t)
+
+ var ok bool
+ var msg *Msg
+
+ select {
+ case msg, ok = <-mch:
+ if !ok {
+ return nil, ErrConnectionClosed
+ }
+ case <-t.C:
+ nc.mu.Lock()
+ delete(nc.respMap, token)
+ nc.mu.Unlock()
+ return nil, ErrTimeout
+ }
+
+ return msg, nil
+}
+
+// oldRequest will create an Inbox and perform a Request() call
+// with the Inbox reply and return the first reply received.
+// This is optimized for the case of multiple responses.
+func (nc *Conn) oldRequest(subj string, data []byte, timeout time.Duration) (*Msg, error) {
+ inbox := NewInbox()
+ ch := make(chan *Msg, RequestChanLen)
+
+ s, err := nc.subscribe(inbox, _EMPTY_, nil, ch)
+ if err != nil {
+ return nil, err
+ }
+ s.AutoUnsubscribe(1)
+ defer s.Unsubscribe()
+
+ err = nc.PublishRequest(subj, inbox, data)
+ if err != nil {
+ return nil, err
+ }
+ return s.NextMsg(timeout)
+}
+
+// InboxPrefix is the prefix for all inbox subjects.
+const InboxPrefix = "_INBOX."
+const inboxPrefixLen = len(InboxPrefix)
+const respInboxPrefixLen = inboxPrefixLen + nuidSize + 1
+
+// NewInbox will return an inbox string which can be used for directed replies from
+// subscribers. These are guaranteed to be unique, but can be shared and subscribed
+// to by others.
+func NewInbox() string {
+ var b [inboxPrefixLen + nuidSize]byte
+ pres := b[:inboxPrefixLen]
+ copy(pres, InboxPrefix)
+ ns := b[inboxPrefixLen:]
+ copy(ns, nuid.Next())
+ return string(b[:])
+}
+
+// Creates a new literal response subject that will trigger
+// the global subscription handler.
+func (nc *Conn) newRespInbox() string {
+ var b [inboxPrefixLen + (2 * nuidSize) + 1]byte
+ pres := b[:respInboxPrefixLen]
+ copy(pres, nc.respSub)
+ ns := b[respInboxPrefixLen:]
+ copy(ns, nuid.Next())
+ return string(b[:])
+}
+
+// respToken will return the last token of a literal response inbox
+// which we use for the message channel lookup.
+func respToken(respInbox string) string {
+ return respInbox[respInboxPrefixLen:]
+}
+
+// Subscribe will express interest in the given subject. The subject
+// can have wildcards (partial:*, full:>). Messages will be delivered
+// to the associated MsgHandler.
+func (nc *Conn) Subscribe(subj string, cb MsgHandler) (*Subscription, error) {
+ return nc.subscribe(subj, _EMPTY_, cb, nil)
+}
+
+// ChanSubscribe will express interest in the given subject and place
+// all messages received on the channel.
+// You should not close the channel until sub.Unsubscribe() has been called.
+func (nc *Conn) ChanSubscribe(subj string, ch chan *Msg) (*Subscription, error) {
+ return nc.subscribe(subj, _EMPTY_, nil, ch)
+}
+
+// ChanQueueSubscribe will express interest in the given subject.
+// All subscribers with the same queue name will form the queue group
+// and only one member of the group will be selected to receive any given message,
+// which will be placed on the channel.
+// You should not close the channel until sub.Unsubscribe() has been called.
+// Note: This is the same than QueueSubscribeSyncWithChan.
+func (nc *Conn) ChanQueueSubscribe(subj, group string, ch chan *Msg) (*Subscription, error) {
+ return nc.subscribe(subj, group, nil, ch)
+}
+
+// SubscribeSync will express interest on the given subject. Messages will
+// be received synchronously using Subscription.NextMsg().
+func (nc *Conn) SubscribeSync(subj string) (*Subscription, error) {
+ if nc == nil {
+ return nil, ErrInvalidConnection
+ }
+ mch := make(chan *Msg, nc.Opts.SubChanLen)
+ s, e := nc.subscribe(subj, _EMPTY_, nil, mch)
+ if s != nil {
+ s.typ = SyncSubscription
+ }
+ return s, e
+}
+
+// QueueSubscribe creates an asynchronous queue subscriber on the given subject.
+// All subscribers with the same queue name will form the queue group and
+// only one member of the group will be selected to receive any given
+// message asynchronously.
+func (nc *Conn) QueueSubscribe(subj, queue string, cb MsgHandler) (*Subscription, error) {
+ return nc.subscribe(subj, queue, cb, nil)
+}
+
+// QueueSubscribeSync creates a synchronous queue subscriber on the given
+// subject. All subscribers with the same queue name will form the queue
+// group and only one member of the group will be selected to receive any
+// given message synchronously using Subscription.NextMsg().
+func (nc *Conn) QueueSubscribeSync(subj, queue string) (*Subscription, error) {
+ mch := make(chan *Msg, nc.Opts.SubChanLen)
+ s, e := nc.subscribe(subj, queue, nil, mch)
+ if s != nil {
+ s.typ = SyncSubscription
+ }
+ return s, e
+}
+
+// QueueSubscribeSyncWithChan will express interest in the given subject.
+// All subscribers with the same queue name will form the queue group
+// and only one member of the group will be selected to receive any given message,
+// which will be placed on the channel.
+// You should not close the channel until sub.Unsubscribe() has been called.
+// Note: This is the same than ChanQueueSubscribe.
+func (nc *Conn) QueueSubscribeSyncWithChan(subj, queue string, ch chan *Msg) (*Subscription, error) {
+ return nc.subscribe(subj, queue, nil, ch)
+}
+
+// subscribe is the internal subscribe function that indicates interest in a subject.
+func (nc *Conn) subscribe(subj, queue string, cb MsgHandler, ch chan *Msg) (*Subscription, error) {
+ if nc == nil {
+ return nil, ErrInvalidConnection
+ }
+ nc.mu.Lock()
+ // ok here, but defer is generally expensive
+ defer nc.mu.Unlock()
+ defer nc.kickFlusher()
+
+ // Check for some error conditions.
+ if nc.isClosed() {
+ return nil, ErrConnectionClosed
+ }
+ if nc.isDraining() {
+ return nil, ErrConnectionDraining
+ }
+
+ if cb == nil && ch == nil {
+ return nil, ErrBadSubscription
+ }
+
+ sub := &Subscription{Subject: subj, Queue: queue, mcb: cb, conn: nc}
+ // Set pending limits.
+ sub.pMsgsLimit = DefaultSubPendingMsgsLimit
+ sub.pBytesLimit = DefaultSubPendingBytesLimit
+
+ // If we have an async callback, start up a sub specific
+ // Go routine to deliver the messages.
+ if cb != nil {
+ sub.typ = AsyncSubscription
+ sub.pCond = sync.NewCond(&sub.mu)
+ go nc.waitForMsgs(sub)
+ } else {
+ sub.typ = ChanSubscription
+ sub.mch = ch
+ }
+
+ nc.subsMu.Lock()
+ nc.ssid++
+ sub.sid = nc.ssid
+ nc.subs[sub.sid] = sub
+ nc.subsMu.Unlock()
+
+ // We will send these for all subs when we reconnect
+ // so that we can suppress here.
+ if !nc.isReconnecting() {
+ fmt.Fprintf(nc.bw, subProto, subj, queue, sub.sid)
+ }
+ return sub, nil
+}
+
+// NumSubscriptions returns active number of subscriptions.
+func (nc *Conn) NumSubscriptions() int {
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ return len(nc.subs)
+}
+
+// Lock for nc should be held here upon entry
+func (nc *Conn) removeSub(s *Subscription) {
+ nc.subsMu.Lock()
+ delete(nc.subs, s.sid)
+ nc.subsMu.Unlock()
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ // Release callers on NextMsg for SyncSubscription only
+ if s.mch != nil && s.typ == SyncSubscription {
+ close(s.mch)
+ }
+ s.mch = nil
+
+ // Mark as invalid
+ s.conn = nil
+ s.closed = true
+ if s.pCond != nil {
+ s.pCond.Broadcast()
+ }
+}
+
+// SubscriptionType is the type of the Subscription.
+type SubscriptionType int
+
+// The different types of subscription types.
+const (
+ AsyncSubscription = SubscriptionType(iota)
+ SyncSubscription
+ ChanSubscription
+ NilSubscription
+)
+
+// Type returns the type of Subscription.
+func (s *Subscription) Type() SubscriptionType {
+ if s == nil {
+ return NilSubscription
+ }
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.typ
+}
+
+// IsValid returns a boolean indicating whether the subscription
+// is still active. This will return false if the subscription has
+// already been closed.
+func (s *Subscription) IsValid() bool {
+ if s == nil {
+ return false
+ }
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.conn != nil
+}
+
+// Drain will remove interest but continue callbacks until all messages
+// have been processed.
+func (s *Subscription) Drain() error {
+ if s == nil {
+ return ErrBadSubscription
+ }
+ s.mu.Lock()
+ conn := s.conn
+ s.mu.Unlock()
+ if conn == nil {
+ return ErrBadSubscription
+ }
+ return conn.unsubscribe(s, 0, true)
+}
+
+// Unsubscribe will remove interest in the given subject.
+func (s *Subscription) Unsubscribe() error {
+ if s == nil {
+ return ErrBadSubscription
+ }
+ s.mu.Lock()
+ conn := s.conn
+ s.mu.Unlock()
+ if conn == nil {
+ return ErrBadSubscription
+ }
+ if conn.IsDraining() {
+ return ErrConnectionDraining
+ }
+ return conn.unsubscribe(s, 0, false)
+}
+
+// checkDrained will watch for a subscription to be fully drained
+// and then remove it.
+func (nc *Conn) checkDrained(sub *Subscription) {
+ if nc == nil || sub == nil {
+ return
+ }
+
+ // This allows us to know that whatever we have in the client pending
+ // is correct and the server will not send additional information.
+ nc.Flush()
+
+ // Once we are here we just wait for Pending to reach 0 or
+ // any other state to exit this go routine.
+ for {
+ // check connection is still valid.
+ if nc.IsClosed() {
+ return
+ }
+
+ // Check subscription state
+ sub.mu.Lock()
+ conn := sub.conn
+ closed := sub.closed
+ pMsgs := sub.pMsgs
+ sub.mu.Unlock()
+
+ if conn == nil || closed || pMsgs == 0 {
+ nc.mu.Lock()
+ nc.removeSub(sub)
+ nc.mu.Unlock()
+ return
+ }
+
+ time.Sleep(100 * time.Millisecond)
+ }
+}
+
+// AutoUnsubscribe will issue an automatic Unsubscribe that is
+// processed by the server when max messages have been received.
+// This can be useful when sending a request to an unknown number
+// of subscribers.
+func (s *Subscription) AutoUnsubscribe(max int) error {
+ if s == nil {
+ return ErrBadSubscription
+ }
+ s.mu.Lock()
+ conn := s.conn
+ s.mu.Unlock()
+ if conn == nil {
+ return ErrBadSubscription
+ }
+ return conn.unsubscribe(s, max, false)
+}
+
+// unsubscribe performs the low level unsubscribe to the server.
+// Use Subscription.Unsubscribe()
+func (nc *Conn) unsubscribe(sub *Subscription, max int, drainMode bool) error {
+ nc.mu.Lock()
+ // ok here, but defer is expensive
+ defer nc.mu.Unlock()
+ defer nc.kickFlusher()
+
+ if nc.isClosed() {
+ return ErrConnectionClosed
+ }
+
+ nc.subsMu.RLock()
+ s := nc.subs[sub.sid]
+ nc.subsMu.RUnlock()
+ // Already unsubscribed
+ if s == nil {
+ return nil
+ }
+
+ maxStr := _EMPTY_
+ if max > 0 {
+ s.max = uint64(max)
+ maxStr = strconv.Itoa(max)
+ } else if !drainMode {
+ nc.removeSub(s)
+ }
+
+ if drainMode {
+ go nc.checkDrained(sub)
+ }
+
+ // We will send these for all subs when we reconnect
+ // so that we can suppress here.
+ if !nc.isReconnecting() {
+ fmt.Fprintf(nc.bw, unsubProto, s.sid, maxStr)
+ }
+ return nil
+}
+
+// NextMsg will return the next message available to a synchronous subscriber
+// or block until one is available. A timeout can be used to return when no
+// message has been delivered.
+func (s *Subscription) NextMsg(timeout time.Duration) (*Msg, error) {
+ if s == nil {
+ return nil, ErrBadSubscription
+ }
+
+ s.mu.Lock()
+ err := s.validateNextMsgState()
+ if err != nil {
+ s.mu.Unlock()
+ return nil, err
+ }
+
+ // snapshot
+ mch := s.mch
+ s.mu.Unlock()
+
+ var ok bool
+ var msg *Msg
+
+ t := globalTimerPool.Get(timeout)
+ defer globalTimerPool.Put(t)
+
+ select {
+ case msg, ok = <-mch:
+ if !ok {
+ return nil, ErrConnectionClosed
+ }
+ err := s.processNextMsgDelivered(msg)
+ if err != nil {
+ return nil, err
+ }
+ case <-t.C:
+ return nil, ErrTimeout
+ }
+
+ return msg, nil
+}
+
+// validateNextMsgState checks whether the subscription is in a valid
+// state to call NextMsg and be delivered another message synchronously.
+// This should be called while holding the lock.
+func (s *Subscription) validateNextMsgState() error {
+ if s.connClosed {
+ return ErrConnectionClosed
+ }
+ if s.mch == nil {
+ if s.max > 0 && s.delivered >= s.max {
+ return ErrMaxMessages
+ } else if s.closed {
+ return ErrBadSubscription
+ }
+ }
+ if s.mcb != nil {
+ return ErrSyncSubRequired
+ }
+ if s.sc {
+ s.sc = false
+ return ErrSlowConsumer
+ }
+
+ return nil
+}
+
+// processNextMsgDelivered takes a message and applies the needed
+// accounting to the stats from the subscription, returning an
+// error in case we have the maximum number of messages have been
+// delivered already. It should not be called while holding the lock.
+func (s *Subscription) processNextMsgDelivered(msg *Msg) error {
+ s.mu.Lock()
+ nc := s.conn
+ max := s.max
+
+ // Update some stats.
+ s.delivered++
+ delivered := s.delivered
+ if s.typ == SyncSubscription {
+ s.pMsgs--
+ s.pBytes -= len(msg.Data)
+ }
+ s.mu.Unlock()
+
+ if max > 0 {
+ if delivered > max {
+ return ErrMaxMessages
+ }
+ // Remove subscription if we have reached max.
+ if delivered == max {
+ nc.mu.Lock()
+ nc.removeSub(s)
+ nc.mu.Unlock()
+ }
+ }
+
+ return nil
+}
+
+// Queued returns the number of queued messages in the client for this subscription.
+// DEPRECATED: Use Pending()
+func (s *Subscription) QueuedMsgs() (int, error) {
+ m, _, err := s.Pending()
+ return int(m), err
+}
+
+// Pending returns the number of queued messages and queued bytes in the client for this subscription.
+func (s *Subscription) Pending() (int, int, error) {
+ if s == nil {
+ return -1, -1, ErrBadSubscription
+ }
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.conn == nil {
+ return -1, -1, ErrBadSubscription
+ }
+ if s.typ == ChanSubscription {
+ return -1, -1, ErrTypeSubscription
+ }
+ return s.pMsgs, s.pBytes, nil
+}
+
+// MaxPending returns the maximum number of queued messages and queued bytes seen so far.
+func (s *Subscription) MaxPending() (int, int, error) {
+ if s == nil {
+ return -1, -1, ErrBadSubscription
+ }
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.conn == nil {
+ return -1, -1, ErrBadSubscription
+ }
+ if s.typ == ChanSubscription {
+ return -1, -1, ErrTypeSubscription
+ }
+ return s.pMsgsMax, s.pBytesMax, nil
+}
+
+// ClearMaxPending resets the maximums seen so far.
+func (s *Subscription) ClearMaxPending() error {
+ if s == nil {
+ return ErrBadSubscription
+ }
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.conn == nil {
+ return ErrBadSubscription
+ }
+ if s.typ == ChanSubscription {
+ return ErrTypeSubscription
+ }
+ s.pMsgsMax, s.pBytesMax = 0, 0
+ return nil
+}
+
+// Pending Limits
+const (
+ DefaultSubPendingMsgsLimit = 65536
+ DefaultSubPendingBytesLimit = 65536 * 1024
+)
+
+// PendingLimits returns the current limits for this subscription.
+// If no error is returned, a negative value indicates that the
+// given metric is not limited.
+func (s *Subscription) PendingLimits() (int, int, error) {
+ if s == nil {
+ return -1, -1, ErrBadSubscription
+ }
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.conn == nil {
+ return -1, -1, ErrBadSubscription
+ }
+ if s.typ == ChanSubscription {
+ return -1, -1, ErrTypeSubscription
+ }
+ return s.pMsgsLimit, s.pBytesLimit, nil
+}
+
+// SetPendingLimits sets the limits for pending msgs and bytes for this subscription.
+// Zero is not allowed. Any negative value means that the given metric is not limited.
+func (s *Subscription) SetPendingLimits(msgLimit, bytesLimit int) error {
+ if s == nil {
+ return ErrBadSubscription
+ }
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.conn == nil {
+ return ErrBadSubscription
+ }
+ if s.typ == ChanSubscription {
+ return ErrTypeSubscription
+ }
+ if msgLimit == 0 || bytesLimit == 0 {
+ return ErrInvalidArg
+ }
+ s.pMsgsLimit, s.pBytesLimit = msgLimit, bytesLimit
+ return nil
+}
+
+// Delivered returns the number of delivered messages for this subscription.
+func (s *Subscription) Delivered() (int64, error) {
+ if s == nil {
+ return -1, ErrBadSubscription
+ }
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.conn == nil {
+ return -1, ErrBadSubscription
+ }
+ return int64(s.delivered), nil
+}
+
+// Dropped returns the number of known dropped messages for this subscription.
+// This will correspond to messages dropped by violations of PendingLimits. If
+// the server declares the connection a SlowConsumer, this number may not be
+// valid.
+func (s *Subscription) Dropped() (int, error) {
+ if s == nil {
+ return -1, ErrBadSubscription
+ }
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.conn == nil {
+ return -1, ErrBadSubscription
+ }
+ return s.dropped, nil
+}
+
+// FIXME: This is a hack
+// removeFlushEntry is needed when we need to discard queued up responses
+// for our pings as part of a flush call. This happens when we have a flush
+// call outstanding and we call close.
+func (nc *Conn) removeFlushEntry(ch chan struct{}) bool {
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ if nc.pongs == nil {
+ return false
+ }
+ for i, c := range nc.pongs {
+ if c == ch {
+ nc.pongs[i] = nil
+ return true
+ }
+ }
+ return false
+}
+
+// The lock must be held entering this function.
+func (nc *Conn) sendPing(ch chan struct{}) {
+ nc.pongs = append(nc.pongs, ch)
+ nc.bw.WriteString(pingProto)
+ // Flush in place.
+ nc.bw.Flush()
+}
+
+// This will fire periodically and send a client origin
+// ping to the server. Will also check that we have received
+// responses from the server.
+func (nc *Conn) processPingTimer() {
+ nc.mu.Lock()
+
+ if nc.status != CONNECTED {
+ nc.mu.Unlock()
+ return
+ }
+
+ // Check for violation
+ nc.pout++
+ if nc.pout > nc.Opts.MaxPingsOut {
+ nc.mu.Unlock()
+ nc.processOpErr(ErrStaleConnection)
+ return
+ }
+
+ nc.sendPing(nil)
+ nc.ptmr.Reset(nc.Opts.PingInterval)
+ nc.mu.Unlock()
+}
+
+// FlushTimeout allows a Flush operation to have an associated timeout.
+func (nc *Conn) FlushTimeout(timeout time.Duration) (err error) {
+ if nc == nil {
+ return ErrInvalidConnection
+ }
+ if timeout <= 0 {
+ return ErrBadTimeout
+ }
+
+ nc.mu.Lock()
+ if nc.isClosed() {
+ nc.mu.Unlock()
+ return ErrConnectionClosed
+ }
+ t := globalTimerPool.Get(timeout)
+ defer globalTimerPool.Put(t)
+
+ // Create a buffered channel to prevent chan send to block
+ // in processPong() if this code here times out just when
+ // PONG was received.
+ ch := make(chan struct{}, 1)
+ nc.sendPing(ch)
+ nc.mu.Unlock()
+
+ select {
+ case _, ok := <-ch:
+ if !ok {
+ err = ErrConnectionClosed
+ } else {
+ close(ch)
+ }
+ case <-t.C:
+ err = ErrTimeout
+ }
+
+ if err != nil {
+ nc.removeFlushEntry(ch)
+ }
+ return
+}
+
+// Flush will perform a round trip to the server and return when it
+// receives the internal reply.
+func (nc *Conn) Flush() error {
+ return nc.FlushTimeout(60 * time.Second)
+}
+
+// Buffered will return the number of bytes buffered to be sent to the server.
+// FIXME(dlc) take into account disconnected state.
+func (nc *Conn) Buffered() (int, error) {
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ if nc.isClosed() || nc.bw == nil {
+ return -1, ErrConnectionClosed
+ }
+ return nc.bw.Buffered(), nil
+}
+
+// resendSubscriptions will send our subscription state back to the
+// server. Used in reconnects
+func (nc *Conn) resendSubscriptions() {
+ // Since we are going to send protocols to the server, we don't want to
+ // be holding the subsMu lock (which is used in processMsg). So copy
+ // the subscriptions in a temporary array.
+ nc.subsMu.RLock()
+ subs := make([]*Subscription, 0, len(nc.subs))
+ for _, s := range nc.subs {
+ subs = append(subs, s)
+ }
+ nc.subsMu.RUnlock()
+ for _, s := range subs {
+ adjustedMax := uint64(0)
+ s.mu.Lock()
+ if s.max > 0 {
+ if s.delivered < s.max {
+ adjustedMax = s.max - s.delivered
+ }
+
+ // adjustedMax could be 0 here if the number of delivered msgs
+ // reached the max, if so unsubscribe.
+ if adjustedMax == 0 {
+ s.mu.Unlock()
+ fmt.Fprintf(nc.bw, unsubProto, s.sid, _EMPTY_)
+ continue
+ }
+ }
+ s.mu.Unlock()
+
+ fmt.Fprintf(nc.bw, subProto, s.Subject, s.Queue, s.sid)
+ if adjustedMax > 0 {
+ maxStr := strconv.Itoa(int(adjustedMax))
+ fmt.Fprintf(nc.bw, unsubProto, s.sid, maxStr)
+ }
+ }
+}
+
+// This will clear any pending flush calls and release pending calls.
+// Lock is assumed to be held by the caller.
+func (nc *Conn) clearPendingFlushCalls() {
+ // Clear any queued pongs, e.g. pending flush calls.
+ for _, ch := range nc.pongs {
+ if ch != nil {
+ close(ch)
+ }
+ }
+ nc.pongs = nil
+}
+
+// This will clear any pending Request calls.
+// Lock is assumed to be held by the caller.
+func (nc *Conn) clearPendingRequestCalls() {
+ if nc.respMap == nil {
+ return
+ }
+ for key, ch := range nc.respMap {
+ if ch != nil {
+ close(ch)
+ delete(nc.respMap, key)
+ }
+ }
+}
+
+// Low level close call that will do correct cleanup and set
+// desired status. Also controls whether user defined callbacks
+// will be triggered. The lock should not be held entering this
+// function. This function will handle the locking manually.
+func (nc *Conn) close(status Status, doCBs bool) {
+ nc.mu.Lock()
+ if nc.isClosed() {
+ nc.status = status
+ nc.mu.Unlock()
+ return
+ }
+ nc.status = CLOSED
+
+ // Kick the Go routines so they fall out.
+ nc.kickFlusher()
+ nc.mu.Unlock()
+
+ nc.mu.Lock()
+
+ // Clear any queued pongs, e.g. pending flush calls.
+ nc.clearPendingFlushCalls()
+
+ // Clear any queued and blocking Requests.
+ nc.clearPendingRequestCalls()
+
+ // Stop ping timer if set.
+ nc.stopPingTimer()
+ nc.ptmr = nil
+
+ // Go ahead and make sure we have flushed the outbound
+ if nc.conn != nil {
+ nc.bw.Flush()
+ defer nc.conn.Close()
+ }
+
+ // Close sync subscriber channels and release any
+ // pending NextMsg() calls.
+ nc.subsMu.Lock()
+ for _, s := range nc.subs {
+ s.mu.Lock()
+
+ // Release callers on NextMsg for SyncSubscription only
+ if s.mch != nil && s.typ == SyncSubscription {
+ close(s.mch)
+ }
+ s.mch = nil
+ // Mark as invalid, for signaling to deliverMsgs
+ s.closed = true
+ // Mark connection closed in subscription
+ s.connClosed = true
+ // If we have an async subscription, signals it to exit
+ if s.typ == AsyncSubscription && s.pCond != nil {
+ s.pCond.Signal()
+ }
+
+ s.mu.Unlock()
+ }
+ nc.subs = nil
+ nc.subsMu.Unlock()
+
+ nc.status = status
+
+ // Perform appropriate callback if needed for a disconnect.
+ if doCBs {
+ if nc.Opts.DisconnectedCB != nil && nc.conn != nil {
+ nc.ach.push(func() { nc.Opts.DisconnectedCB(nc) })
+ }
+ if nc.Opts.ClosedCB != nil {
+ nc.ach.push(func() { nc.Opts.ClosedCB(nc) })
+ }
+ nc.ach.close()
+ }
+ nc.mu.Unlock()
+}
+
+// Close will close the connection to the server. This call will release
+// all blocking calls, such as Flush() and NextMsg()
+func (nc *Conn) Close() {
+ nc.close(CLOSED, true)
+}
+
+// IsClosed tests if a Conn has been closed.
+func (nc *Conn) IsClosed() bool {
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ return nc.isClosed()
+}
+
+// IsReconnecting tests if a Conn is reconnecting.
+func (nc *Conn) IsReconnecting() bool {
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ return nc.isReconnecting()
+}
+
+// IsConnected tests if a Conn is connected.
+func (nc *Conn) IsConnected() bool {
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ return nc.isConnected()
+}
+
+// drainConnection will run in a separate Go routine and will
+// flush all publishes and drain all active subscriptions.
+func (nc *Conn) drainConnection() {
+ // Snapshot subs list.
+ nc.mu.Lock()
+ subs := make([]*Subscription, 0, len(nc.subs))
+ for _, s := range nc.subs {
+ subs = append(subs, s)
+ }
+ errCB := nc.Opts.AsyncErrorCB
+ drainWait := nc.Opts.DrainTimeout
+ nc.mu.Unlock()
+
+ // for pushing errors with context.
+ pushErr := func(err error) {
+ nc.mu.Lock()
+ if errCB != nil {
+ nc.ach.push(func() { errCB(nc, nil, err) })
+ }
+ nc.mu.Unlock()
+ }
+
+ // Do subs first
+ for _, s := range subs {
+ if err := s.Drain(); err != nil {
+ // We will notify about these but continue.
+ pushErr(err)
+ }
+ }
+
+ // Wait for the subscriptions to drop to zero.
+ timeout := time.Now().Add(drainWait)
+ for time.Now().Before(timeout) {
+ if nc.NumSubscriptions() == 0 {
+ break
+ }
+ time.Sleep(10 * time.Millisecond)
+ }
+
+ // Check if we timed out.
+ if nc.NumSubscriptions() != 0 {
+ pushErr(ErrDrainTimeout)
+ }
+
+ // Flip State
+ nc.mu.Lock()
+ nc.status = DRAINING_PUBS
+ nc.mu.Unlock()
+
+ // Do publish drain via Flush() call.
+ err := nc.Flush()
+ if err != nil {
+ pushErr(err)
+ nc.Close()
+ return
+ }
+
+ // Move to closed state.
+ nc.Close()
+}
+
+// Drain will put a connection into a drain state. All subscriptions will
+// immediately be put into a drain state. Upon completion, the publishers
+// will be drained and can not publish any additional messages. Upon draining
+// of the publishers, the connection will be closed. Use the ClosedCB()
+// option to know when the connection has moved from draining to closed.
+func (nc *Conn) Drain() error {
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+
+ if nc.isClosed() {
+ return ErrConnectionClosed
+ }
+ if nc.isConnecting() || nc.isReconnecting() {
+ return ErrConnectionReconnecting
+ }
+ if nc.isDraining() {
+ return nil
+ }
+
+ nc.status = DRAINING_SUBS
+ go nc.drainConnection()
+ return nil
+}
+
+// IsDraining tests if a Conn is in the draining state.
+func (nc *Conn) IsDraining() bool {
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ return nc.isDraining()
+}
+
+// caller must lock
+func (nc *Conn) getServers(implicitOnly bool) []string {
+ poolSize := len(nc.srvPool)
+ var servers = make([]string, 0)
+ for i := 0; i < poolSize; i++ {
+ if implicitOnly && !nc.srvPool[i].isImplicit {
+ continue
+ }
+ url := nc.srvPool[i].url
+ servers = append(servers, fmt.Sprintf("%s://%s", url.Scheme, url.Host))
+ }
+ return servers
+}
+
+// Servers returns the list of known server urls, including additional
+// servers discovered after a connection has been established. If
+// authentication is enabled, use UserInfo or Token when connecting with
+// these urls.
+func (nc *Conn) Servers() []string {
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ return nc.getServers(false)
+}
+
+// DiscoveredServers returns only the server urls that have been discovered
+// after a connection has been established. If authentication is enabled,
+// use UserInfo or Token when connecting with these urls.
+func (nc *Conn) DiscoveredServers() []string {
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ return nc.getServers(true)
+}
+
+// Status returns the current state of the connection.
+func (nc *Conn) Status() Status {
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ return nc.status
+}
+
+// Test if Conn has been closed Lock is assumed held.
+func (nc *Conn) isClosed() bool {
+ return nc.status == CLOSED
+}
+
+// Test if Conn is in the process of connecting
+func (nc *Conn) isConnecting() bool {
+ return nc.status == CONNECTING
+}
+
+// Test if Conn is being reconnected.
+func (nc *Conn) isReconnecting() bool {
+ return nc.status == RECONNECTING
+}
+
+// Test if Conn is connected or connecting.
+func (nc *Conn) isConnected() bool {
+ return nc.status == CONNECTED || nc.isDraining()
+}
+
+// Test if Conn is in the draining state.
+func (nc *Conn) isDraining() bool {
+ return nc.status == DRAINING_SUBS || nc.status == DRAINING_PUBS
+}
+
+// Test if Conn is in the draining state for pubs.
+func (nc *Conn) isDrainingPubs() bool {
+ return nc.status == DRAINING_PUBS
+}
+
+// Stats will return a race safe copy of the Statistics section for the connection.
+func (nc *Conn) Stats() Statistics {
+ // Stats are updated either under connection's mu or subsMu mutexes.
+ // Lock both to safely get them.
+ nc.mu.Lock()
+ nc.subsMu.RLock()
+ stats := Statistics{
+ InMsgs: nc.InMsgs,
+ InBytes: nc.InBytes,
+ OutMsgs: nc.OutMsgs,
+ OutBytes: nc.OutBytes,
+ Reconnects: nc.Reconnects,
+ }
+ nc.subsMu.RUnlock()
+ nc.mu.Unlock()
+ return stats
+}
+
+// MaxPayload returns the size limit that a message payload can have.
+// This is set by the server configuration and delivered to the client
+// upon connect.
+func (nc *Conn) MaxPayload() int64 {
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ return nc.info.MaxPayload
+}
+
+// AuthRequired will return if the connected server requires authorization.
+func (nc *Conn) AuthRequired() bool {
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ return nc.info.AuthRequired
+}
+
+// TLSRequired will return if the connected server requires TLS connections.
+func (nc *Conn) TLSRequired() bool {
+ nc.mu.Lock()
+ defer nc.mu.Unlock()
+ return nc.info.TLSRequired
+}
+
+// Barrier schedules the given function `f` to all registered asynchronous
+// subscriptions.
+// Only the last subscription to see this barrier will invoke the function.
+// If no subscription is registered at the time of this call, `f()` is invoked
+// right away.
+// ErrConnectionClosed is returned if the connection is closed prior to
+// the call.
+func (nc *Conn) Barrier(f func()) error {
+ nc.mu.Lock()
+ if nc.isClosed() {
+ nc.mu.Unlock()
+ return ErrConnectionClosed
+ }
+ nc.subsMu.Lock()
+ // Need to figure out how many non chan subscriptions there are
+ numSubs := 0
+ for _, sub := range nc.subs {
+ if sub.typ == AsyncSubscription {
+ numSubs++
+ }
+ }
+ if numSubs == 0 {
+ nc.subsMu.Unlock()
+ nc.mu.Unlock()
+ f()
+ return nil
+ }
+ barrier := &barrierInfo{refs: int64(numSubs), f: f}
+ for _, sub := range nc.subs {
+ sub.mu.Lock()
+ if sub.mch == nil {
+ msg := &Msg{barrier: barrier}
+ // Push onto the async pList
+ if sub.pTail != nil {
+ sub.pTail.next = msg
+ } else {
+ sub.pHead = msg
+ sub.pCond.Signal()
+ }
+ sub.pTail = msg
+ }
+ sub.mu.Unlock()
+ }
+ nc.subsMu.Unlock()
+ nc.mu.Unlock()
+ return nil
+}
diff --git a/vendor/github.com/nats-io/go-nats/netchan.go b/vendor/github.com/nats-io/go-nats/netchan.go
new file mode 100644
index 00000000..add3cba5
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/netchan.go
@@ -0,0 +1,111 @@
+// Copyright 2013-2018 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package nats
+
+import (
+ "errors"
+ "reflect"
+)
+
+// This allows the functionality for network channels by binding send and receive Go chans
+// to subjects and optionally queue groups.
+// Data will be encoded and decoded via the EncodedConn and its associated encoders.
+
+// BindSendChan binds a channel for send operations to NATS.
+func (c *EncodedConn) BindSendChan(subject string, channel interface{}) error {
+ chVal := reflect.ValueOf(channel)
+ if chVal.Kind() != reflect.Chan {
+ return ErrChanArg
+ }
+ go chPublish(c, chVal, subject)
+ return nil
+}
+
+// Publish all values that arrive on the channel until it is closed or we
+// encounter an error.
+func chPublish(c *EncodedConn, chVal reflect.Value, subject string) {
+ for {
+ val, ok := chVal.Recv()
+ if !ok {
+ // Channel has most likely been closed.
+ return
+ }
+ if e := c.Publish(subject, val.Interface()); e != nil {
+ // Do this under lock.
+ c.Conn.mu.Lock()
+ defer c.Conn.mu.Unlock()
+
+ if c.Conn.Opts.AsyncErrorCB != nil {
+ // FIXME(dlc) - Not sure this is the right thing to do.
+ // FIXME(ivan) - If the connection is not yet closed, try to schedule the callback
+ if c.Conn.isClosed() {
+ go c.Conn.Opts.AsyncErrorCB(c.Conn, nil, e)
+ } else {
+ c.Conn.ach.push(func() { c.Conn.Opts.AsyncErrorCB(c.Conn, nil, e) })
+ }
+ }
+ return
+ }
+ }
+}
+
+// BindRecvChan binds a channel for receive operations from NATS.
+func (c *EncodedConn) BindRecvChan(subject string, channel interface{}) (*Subscription, error) {
+ return c.bindRecvChan(subject, _EMPTY_, channel)
+}
+
+// BindRecvQueueChan binds a channel for queue-based receive operations from NATS.
+func (c *EncodedConn) BindRecvQueueChan(subject, queue string, channel interface{}) (*Subscription, error) {
+ return c.bindRecvChan(subject, queue, channel)
+}
+
+// Internal function to bind receive operations for a channel.
+func (c *EncodedConn) bindRecvChan(subject, queue string, channel interface{}) (*Subscription, error) {
+ chVal := reflect.ValueOf(channel)
+ if chVal.Kind() != reflect.Chan {
+ return nil, ErrChanArg
+ }
+ argType := chVal.Type().Elem()
+
+ cb := func(m *Msg) {
+ var oPtr reflect.Value
+ if argType.Kind() != reflect.Ptr {
+ oPtr = reflect.New(argType)
+ } else {
+ oPtr = reflect.New(argType.Elem())
+ }
+ if err := c.Enc.Decode(m.Subject, m.Data, oPtr.Interface()); err != nil {
+ c.Conn.err = errors.New("nats: Got an error trying to unmarshal: " + err.Error())
+ if c.Conn.Opts.AsyncErrorCB != nil {
+ c.Conn.ach.push(func() { c.Conn.Opts.AsyncErrorCB(c.Conn, m.Sub, c.Conn.err) })
+ }
+ return
+ }
+ if argType.Kind() != reflect.Ptr {
+ oPtr = reflect.Indirect(oPtr)
+ }
+ // This is a bit hacky, but in this instance we may be trying to send to a closed channel.
+ // and the user does not know when it is safe to close the channel.
+ defer func() {
+ // If we have panicked, recover and close the subscription.
+ if r := recover(); r != nil {
+ m.Sub.Unsubscribe()
+ }
+ }()
+ // Actually do the send to the channel.
+ chVal.Send(oPtr)
+ }
+
+ return c.Conn.subscribe(subject, queue, cb, nil)
+}
diff --git a/vendor/github.com/nats-io/go-nats/parser.go b/vendor/github.com/nats-io/go-nats/parser.go
new file mode 100644
index 00000000..a4b3ea0e
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/parser.go
@@ -0,0 +1,481 @@
+// Copyright 2012-2018 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package nats
+
+import (
+ "fmt"
+)
+
+type msgArg struct {
+ subject []byte
+ reply []byte
+ sid int64
+ size int
+}
+
+const MAX_CONTROL_LINE_SIZE = 1024
+
+type parseState struct {
+ state int
+ as int
+ drop int
+ ma msgArg
+ argBuf []byte
+ msgBuf []byte
+ scratch [MAX_CONTROL_LINE_SIZE]byte
+}
+
+const (
+ OP_START = iota
+ OP_PLUS
+ OP_PLUS_O
+ OP_PLUS_OK
+ OP_MINUS
+ OP_MINUS_E
+ OP_MINUS_ER
+ OP_MINUS_ERR
+ OP_MINUS_ERR_SPC
+ MINUS_ERR_ARG
+ OP_M
+ OP_MS
+ OP_MSG
+ OP_MSG_SPC
+ MSG_ARG
+ MSG_PAYLOAD
+ MSG_END
+ OP_P
+ OP_PI
+ OP_PIN
+ OP_PING
+ OP_PO
+ OP_PON
+ OP_PONG
+ OP_I
+ OP_IN
+ OP_INF
+ OP_INFO
+ OP_INFO_SPC
+ INFO_ARG
+)
+
+// parse is the fast protocol parser engine.
+func (nc *Conn) parse(buf []byte) error {
+ var i int
+ var b byte
+
+ // Move to loop instead of range syntax to allow jumping of i
+ for i = 0; i < len(buf); i++ {
+ b = buf[i]
+
+ switch nc.ps.state {
+ case OP_START:
+ switch b {
+ case 'M', 'm':
+ nc.ps.state = OP_M
+ case 'P', 'p':
+ nc.ps.state = OP_P
+ case '+':
+ nc.ps.state = OP_PLUS
+ case '-':
+ nc.ps.state = OP_MINUS
+ case 'I', 'i':
+ nc.ps.state = OP_I
+ default:
+ goto parseErr
+ }
+ case OP_M:
+ switch b {
+ case 'S', 's':
+ nc.ps.state = OP_MS
+ default:
+ goto parseErr
+ }
+ case OP_MS:
+ switch b {
+ case 'G', 'g':
+ nc.ps.state = OP_MSG
+ default:
+ goto parseErr
+ }
+ case OP_MSG:
+ switch b {
+ case ' ', '\t':
+ nc.ps.state = OP_MSG_SPC
+ default:
+ goto parseErr
+ }
+ case OP_MSG_SPC:
+ switch b {
+ case ' ', '\t':
+ continue
+ default:
+ nc.ps.state = MSG_ARG
+ nc.ps.as = i
+ }
+ case MSG_ARG:
+ switch b {
+ case '\r':
+ nc.ps.drop = 1
+ case '\n':
+ var arg []byte
+ if nc.ps.argBuf != nil {
+ arg = nc.ps.argBuf
+ } else {
+ arg = buf[nc.ps.as : i-nc.ps.drop]
+ }
+ if err := nc.processMsgArgs(arg); err != nil {
+ return err
+ }
+ nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, MSG_PAYLOAD
+
+ // jump ahead with the index. If this overruns
+ // what is left we fall out and process split
+ // buffer.
+ i = nc.ps.as + nc.ps.ma.size - 1
+ default:
+ if nc.ps.argBuf != nil {
+ nc.ps.argBuf = append(nc.ps.argBuf, b)
+ }
+ }
+ case MSG_PAYLOAD:
+ if nc.ps.msgBuf != nil {
+ if len(nc.ps.msgBuf) >= nc.ps.ma.size {
+ nc.processMsg(nc.ps.msgBuf)
+ nc.ps.argBuf, nc.ps.msgBuf, nc.ps.state = nil, nil, MSG_END
+ } else {
+ // copy as much as we can to the buffer and skip ahead.
+ toCopy := nc.ps.ma.size - len(nc.ps.msgBuf)
+ avail := len(buf) - i
+
+ if avail < toCopy {
+ toCopy = avail
+ }
+
+ if toCopy > 0 {
+ start := len(nc.ps.msgBuf)
+ // This is needed for copy to work.
+ nc.ps.msgBuf = nc.ps.msgBuf[:start+toCopy]
+ copy(nc.ps.msgBuf[start:], buf[i:i+toCopy])
+ // Update our index
+ i = (i + toCopy) - 1
+ } else {
+ nc.ps.msgBuf = append(nc.ps.msgBuf, b)
+ }
+ }
+ } else if i-nc.ps.as >= nc.ps.ma.size {
+ nc.processMsg(buf[nc.ps.as:i])
+ nc.ps.argBuf, nc.ps.msgBuf, nc.ps.state = nil, nil, MSG_END
+ }
+ case MSG_END:
+ switch b {
+ case '\n':
+ nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, OP_START
+ default:
+ continue
+ }
+ case OP_PLUS:
+ switch b {
+ case 'O', 'o':
+ nc.ps.state = OP_PLUS_O
+ default:
+ goto parseErr
+ }
+ case OP_PLUS_O:
+ switch b {
+ case 'K', 'k':
+ nc.ps.state = OP_PLUS_OK
+ default:
+ goto parseErr
+ }
+ case OP_PLUS_OK:
+ switch b {
+ case '\n':
+ nc.processOK()
+ nc.ps.drop, nc.ps.state = 0, OP_START
+ }
+ case OP_MINUS:
+ switch b {
+ case 'E', 'e':
+ nc.ps.state = OP_MINUS_E
+ default:
+ goto parseErr
+ }
+ case OP_MINUS_E:
+ switch b {
+ case 'R', 'r':
+ nc.ps.state = OP_MINUS_ER
+ default:
+ goto parseErr
+ }
+ case OP_MINUS_ER:
+ switch b {
+ case 'R', 'r':
+ nc.ps.state = OP_MINUS_ERR
+ default:
+ goto parseErr
+ }
+ case OP_MINUS_ERR:
+ switch b {
+ case ' ', '\t':
+ nc.ps.state = OP_MINUS_ERR_SPC
+ default:
+ goto parseErr
+ }
+ case OP_MINUS_ERR_SPC:
+ switch b {
+ case ' ', '\t':
+ continue
+ default:
+ nc.ps.state = MINUS_ERR_ARG
+ nc.ps.as = i
+ }
+ case MINUS_ERR_ARG:
+ switch b {
+ case '\r':
+ nc.ps.drop = 1
+ case '\n':
+ var arg []byte
+ if nc.ps.argBuf != nil {
+ arg = nc.ps.argBuf
+ nc.ps.argBuf = nil
+ } else {
+ arg = buf[nc.ps.as : i-nc.ps.drop]
+ }
+ nc.processErr(string(arg))
+ nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, OP_START
+ default:
+ if nc.ps.argBuf != nil {
+ nc.ps.argBuf = append(nc.ps.argBuf, b)
+ }
+ }
+ case OP_P:
+ switch b {
+ case 'I', 'i':
+ nc.ps.state = OP_PI
+ case 'O', 'o':
+ nc.ps.state = OP_PO
+ default:
+ goto parseErr
+ }
+ case OP_PO:
+ switch b {
+ case 'N', 'n':
+ nc.ps.state = OP_PON
+ default:
+ goto parseErr
+ }
+ case OP_PON:
+ switch b {
+ case 'G', 'g':
+ nc.ps.state = OP_PONG
+ default:
+ goto parseErr
+ }
+ case OP_PONG:
+ switch b {
+ case '\n':
+ nc.processPong()
+ nc.ps.drop, nc.ps.state = 0, OP_START
+ }
+ case OP_PI:
+ switch b {
+ case 'N', 'n':
+ nc.ps.state = OP_PIN
+ default:
+ goto parseErr
+ }
+ case OP_PIN:
+ switch b {
+ case 'G', 'g':
+ nc.ps.state = OP_PING
+ default:
+ goto parseErr
+ }
+ case OP_PING:
+ switch b {
+ case '\n':
+ nc.processPing()
+ nc.ps.drop, nc.ps.state = 0, OP_START
+ }
+ case OP_I:
+ switch b {
+ case 'N', 'n':
+ nc.ps.state = OP_IN
+ default:
+ goto parseErr
+ }
+ case OP_IN:
+ switch b {
+ case 'F', 'f':
+ nc.ps.state = OP_INF
+ default:
+ goto parseErr
+ }
+ case OP_INF:
+ switch b {
+ case 'O', 'o':
+ nc.ps.state = OP_INFO
+ default:
+ goto parseErr
+ }
+ case OP_INFO:
+ switch b {
+ case ' ', '\t':
+ nc.ps.state = OP_INFO_SPC
+ default:
+ goto parseErr
+ }
+ case OP_INFO_SPC:
+ switch b {
+ case ' ', '\t':
+ continue
+ default:
+ nc.ps.state = INFO_ARG
+ nc.ps.as = i
+ }
+ case INFO_ARG:
+ switch b {
+ case '\r':
+ nc.ps.drop = 1
+ case '\n':
+ var arg []byte
+ if nc.ps.argBuf != nil {
+ arg = nc.ps.argBuf
+ nc.ps.argBuf = nil
+ } else {
+ arg = buf[nc.ps.as : i-nc.ps.drop]
+ }
+ nc.processAsyncInfo(arg)
+ nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, OP_START
+ default:
+ if nc.ps.argBuf != nil {
+ nc.ps.argBuf = append(nc.ps.argBuf, b)
+ }
+ }
+ default:
+ goto parseErr
+ }
+ }
+ // Check for split buffer scenarios
+ if (nc.ps.state == MSG_ARG || nc.ps.state == MINUS_ERR_ARG || nc.ps.state == INFO_ARG) && nc.ps.argBuf == nil {
+ nc.ps.argBuf = nc.ps.scratch[:0]
+ nc.ps.argBuf = append(nc.ps.argBuf, buf[nc.ps.as:i-nc.ps.drop]...)
+ // FIXME, check max len
+ }
+ // Check for split msg
+ if nc.ps.state == MSG_PAYLOAD && nc.ps.msgBuf == nil {
+ // We need to clone the msgArg if it is still referencing the
+ // read buffer and we are not able to process the msg.
+ if nc.ps.argBuf == nil {
+ nc.cloneMsgArg()
+ }
+
+ // If we will overflow the scratch buffer, just create a
+ // new buffer to hold the split message.
+ if nc.ps.ma.size > cap(nc.ps.scratch)-len(nc.ps.argBuf) {
+ lrem := len(buf[nc.ps.as:])
+
+ nc.ps.msgBuf = make([]byte, lrem, nc.ps.ma.size)
+ copy(nc.ps.msgBuf, buf[nc.ps.as:])
+ } else {
+ nc.ps.msgBuf = nc.ps.scratch[len(nc.ps.argBuf):len(nc.ps.argBuf)]
+ nc.ps.msgBuf = append(nc.ps.msgBuf, (buf[nc.ps.as:])...)
+ }
+ }
+
+ return nil
+
+parseErr:
+ return fmt.Errorf("nats: Parse Error [%d]: '%s'", nc.ps.state, buf[i:])
+}
+
+// cloneMsgArg is used when the split buffer scenario has the pubArg in the existing read buffer, but
+// we need to hold onto it into the next read.
+func (nc *Conn) cloneMsgArg() {
+ nc.ps.argBuf = nc.ps.scratch[:0]
+ nc.ps.argBuf = append(nc.ps.argBuf, nc.ps.ma.subject...)
+ nc.ps.argBuf = append(nc.ps.argBuf, nc.ps.ma.reply...)
+ nc.ps.ma.subject = nc.ps.argBuf[:len(nc.ps.ma.subject)]
+ if nc.ps.ma.reply != nil {
+ nc.ps.ma.reply = nc.ps.argBuf[len(nc.ps.ma.subject):]
+ }
+}
+
+const argsLenMax = 4
+
+func (nc *Conn) processMsgArgs(arg []byte) error {
+ // Unroll splitArgs to avoid runtime/heap issues
+ a := [argsLenMax][]byte{}
+ args := a[:0]
+ start := -1
+ for i, b := range arg {
+ switch b {
+ case ' ', '\t', '\r', '\n':
+ if start >= 0 {
+ args = append(args, arg[start:i])
+ start = -1
+ }
+ default:
+ if start < 0 {
+ start = i
+ }
+ }
+ }
+ if start >= 0 {
+ args = append(args, arg[start:])
+ }
+
+ switch len(args) {
+ case 3:
+ nc.ps.ma.subject = args[0]
+ nc.ps.ma.sid = parseInt64(args[1])
+ nc.ps.ma.reply = nil
+ nc.ps.ma.size = int(parseInt64(args[2]))
+ case 4:
+ nc.ps.ma.subject = args[0]
+ nc.ps.ma.sid = parseInt64(args[1])
+ nc.ps.ma.reply = args[2]
+ nc.ps.ma.size = int(parseInt64(args[3]))
+ default:
+ return fmt.Errorf("nats: processMsgArgs Parse Error: '%s'", arg)
+ }
+ if nc.ps.ma.sid < 0 {
+ return fmt.Errorf("nats: processMsgArgs Bad or Missing Sid: '%s'", arg)
+ }
+ if nc.ps.ma.size < 0 {
+ return fmt.Errorf("nats: processMsgArgs Bad or Missing Size: '%s'", arg)
+ }
+ return nil
+}
+
+// Ascii numbers 0-9
+const (
+ ascii_0 = 48
+ ascii_9 = 57
+)
+
+// parseInt64 expects decimal positive numbers. We
+// return -1 to signal error
+func parseInt64(d []byte) (n int64) {
+ if len(d) == 0 {
+ return -1
+ }
+ for _, dec := range d {
+ if dec < ascii_0 || dec > ascii_9 {
+ return -1
+ }
+ n = n*10 + (int64(dec) - ascii_0)
+ }
+ return n
+}
diff --git a/vendor/github.com/nats-io/go-nats/staticcheck.ignore b/vendor/github.com/nats-io/go-nats/staticcheck.ignore
new file mode 100644
index 00000000..25bbf020
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/staticcheck.ignore
@@ -0,0 +1,4 @@
+github.com/nats-io/go-nats/*_test.go:SA2002
+github.com/nats-io/go-nats/*/*_test.go:SA2002
+github.com/nats-io/go-nats/test/context_test.go:SA1012
+github.com/nats-io/go-nats/nats.go:SA6000
diff --git a/vendor/github.com/nats-io/go-nats/timer.go b/vendor/github.com/nats-io/go-nats/timer.go
new file mode 100644
index 00000000..1216762d
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/timer.go
@@ -0,0 +1,56 @@
+// Copyright 2017-2018 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package nats
+
+import (
+ "sync"
+ "time"
+)
+
+// global pool of *time.Timer's. can be used by multiple goroutines concurrently.
+var globalTimerPool timerPool
+
+// timerPool provides GC-able pooling of *time.Timer's.
+// can be used by multiple goroutines concurrently.
+type timerPool struct {
+ p sync.Pool
+}
+
+// Get returns a timer that completes after the given duration.
+func (tp *timerPool) Get(d time.Duration) *time.Timer {
+ if t, _ := tp.p.Get().(*time.Timer); t != nil {
+ t.Reset(d)
+ return t
+ }
+
+ return time.NewTimer(d)
+}
+
+// Put pools the given timer.
+//
+// There is no need to call t.Stop() before calling Put.
+//
+// Put will try to stop the timer before pooling. If the
+// given timer already expired, Put will read the unreceived
+// value if there is one.
+func (tp *timerPool) Put(t *time.Timer) {
+ if !t.Stop() {
+ select {
+ case <-t.C:
+ default:
+ }
+ }
+
+ tp.p.Put(t)
+}
diff --git a/vendor/github.com/nats-io/go-nats/util/tls.go b/vendor/github.com/nats-io/go-nats/util/tls.go
new file mode 100644
index 00000000..53ff9aa2
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/util/tls.go
@@ -0,0 +1,27 @@
+// Copyright 2017-2018 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build go1.8
+
+package util
+
+import "crypto/tls"
+
+// CloneTLSConfig returns a copy of c.
+func CloneTLSConfig(c *tls.Config) *tls.Config {
+ if c == nil {
+ return &tls.Config{}
+ }
+
+ return c.Clone()
+}
diff --git a/vendor/github.com/nats-io/go-nats/util/tls_go17.go b/vendor/github.com/nats-io/go-nats/util/tls_go17.go
new file mode 100644
index 00000000..fd646d31
--- /dev/null
+++ b/vendor/github.com/nats-io/go-nats/util/tls_go17.go
@@ -0,0 +1,49 @@
+// Copyright 2016-2018 The NATS Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build go1.7,!go1.8
+
+package util
+
+import (
+ "crypto/tls"
+)
+
+// CloneTLSConfig returns a copy of c. Only the exported fields are copied.
+// This is temporary, until this is provided by the language.
+// https://go-review.googlesource.com/#/c/28075/
+func CloneTLSConfig(c *tls.Config) *tls.Config {
+ return &tls.Config{
+ Rand: c.Rand,
+ Time: c.Time,
+ Certificates: c.Certificates,
+ NameToCertificate: c.NameToCertificate,
+ GetCertificate: c.GetCertificate,
+ RootCAs: c.RootCAs,
+ NextProtos: c.NextProtos,
+ ServerName: c.ServerName,
+ ClientAuth: c.ClientAuth,
+ ClientCAs: c.ClientCAs,
+ InsecureSkipVerify: c.InsecureSkipVerify,
+ CipherSuites: c.CipherSuites,
+ PreferServerCipherSuites: c.PreferServerCipherSuites,
+ SessionTicketsDisabled: c.SessionTicketsDisabled,
+ SessionTicketKey: c.SessionTicketKey,
+ ClientSessionCache: c.ClientSessionCache,
+ MinVersion: c.MinVersion,
+ MaxVersion: c.MaxVersion,
+ CurvePreferences: c.CurvePreferences,
+ DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
+ Renegotiation: c.Renegotiation,
+ }
+}
diff --git a/vendor/github.com/nats-io/nuid/.gitignore b/vendor/github.com/nats-io/nuid/.gitignore
new file mode 100644
index 00000000..daf913b1
--- /dev/null
+++ b/vendor/github.com/nats-io/nuid/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/nats-io/nuid/.travis.yml b/vendor/github.com/nats-io/nuid/.travis.yml
new file mode 100644
index 00000000..d1024beb
--- /dev/null
+++ b/vendor/github.com/nats-io/nuid/.travis.yml
@@ -0,0 +1,16 @@
+language: go
+sudo: false
+go:
+- 1.5
+
+install:
+- go get -t ./...
+- go get github.com/mattn/goveralls
+
+script:
+- go fmt ./...
+- go vet ./...
+- go test -v
+- go test -v --race
+- go test -v -covermode=count -coverprofile=coverage.out
+- $HOME/gopath/bin/goveralls -coverprofile coverage.out -service travis-ci
diff --git a/vendor/github.com/nats-io/nuid/LICENSE b/vendor/github.com/nats-io/nuid/LICENSE
new file mode 100644
index 00000000..cadc3a49
--- /dev/null
+++ b/vendor/github.com/nats-io/nuid/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2012-2016 Apcera Inc.
+
+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.
diff --git a/vendor/github.com/nats-io/nuid/README.md b/vendor/github.com/nats-io/nuid/README.md
new file mode 100644
index 00000000..73d42e14
--- /dev/null
+++ b/vendor/github.com/nats-io/nuid/README.md
@@ -0,0 +1,66 @@
+# NUID
+
+[](http://opensource.org/licenses/MIT)
+[](http://goreportcard.com/report/nats-io/nuid)
+[](http://travis-ci.org/nats-io/nuid)
+[](https://github.com/nats-io/nuid/releases/tag/v1.0.0)
+[](http://godoc.org/github.com/nats-io/nuid)
+[](https://coveralls.io/github/nats-io/nuid?branch=master)
+
+A highly performant unique identifier generator.
+
+## Installation
+
+Use the `go` command:
+
+ $ go get github.com/nats-io/nuid
+
+## Basic Usage
+```go
+
+// Utilize the global locked instance
+nuid := nuid.Next()
+
+// Create an instance, these are not locked.
+n := nuid.New()
+nuid = n.Next()
+
+// Generate a new crypto/rand seeded prefix.
+// Generally not needed, happens automatically.
+n.RandomizePrefix()
+```
+
+## Performance
+NUID needs to be very fast to generate and be truly unique, all while being entropy pool friendly.
+NUID uses 12 bytes of crypto generated data (entropy draining), and 10 bytes of pseudo-random
+sequential data that increments with a pseudo-random increment.
+
+Total length of a NUID string is 22 bytes of base 36 ascii text, so 36^22 or
+17324272922341479351919144385642496 possibilities.
+
+NUID can generate identifiers as fast as 60ns, or ~16 million per second. There is an associated
+benchmark you can use to test performance on your own hardware.
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2016 Apcera Inc.
+
+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.
diff --git a/vendor/github.com/nats-io/nuid/nuid.go b/vendor/github.com/nats-io/nuid/nuid.go
new file mode 100644
index 00000000..1fda3770
--- /dev/null
+++ b/vendor/github.com/nats-io/nuid/nuid.go
@@ -0,0 +1,124 @@
+// Copyright 2016 Apcera Inc. All rights reserved.
+
+// A unique identifier generator that is high performance, very fast, and tries to be entropy pool friendly.
+package nuid
+
+import (
+ "crypto/rand"
+ "fmt"
+ "math"
+ "math/big"
+ "sync"
+ "time"
+
+ prand "math/rand"
+)
+
+// NUID needs to be very fast to generate and truly unique, all while being entropy pool friendly.
+// We will use 12 bytes of crypto generated data (entropy draining), and 10 bytes of sequential data
+// that is started at a pseudo random number and increments with a pseudo-random increment.
+// Total is 22 bytes of base 62 ascii text :)
+
+// Version of the library
+const Version = "1.0.0"
+
+const (
+ digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ base = 62
+ preLen = 12
+ seqLen = 10
+ maxSeq = int64(839299365868340224) // base^seqLen == 62^10
+ minInc = int64(33)
+ maxInc = int64(333)
+ totalLen = preLen + seqLen
+)
+
+type NUID struct {
+ pre []byte
+ seq int64
+ inc int64
+}
+
+type lockedNUID struct {
+ sync.Mutex
+ *NUID
+}
+
+// Global NUID
+var globalNUID *lockedNUID
+
+// Seed sequential random with crypto or math/random and current time
+// and generate crypto prefix.
+func init() {
+ r, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
+ if err != nil {
+ prand.Seed(time.Now().UnixNano())
+ } else {
+ prand.Seed(r.Int64())
+ }
+ globalNUID = &lockedNUID{NUID: New()}
+ globalNUID.RandomizePrefix()
+}
+
+// New will generate a new NUID and properly initialize the prefix, sequential start, and sequential increment.
+func New() *NUID {
+ n := &NUID{
+ seq: prand.Int63n(maxSeq),
+ inc: minInc + prand.Int63n(maxInc-minInc),
+ pre: make([]byte, preLen),
+ }
+ n.RandomizePrefix()
+ return n
+}
+
+// Generate the next NUID string from the global locked NUID instance.
+func Next() string {
+ globalNUID.Lock()
+ nuid := globalNUID.Next()
+ globalNUID.Unlock()
+ return nuid
+}
+
+// Generate the next NUID string.
+func (n *NUID) Next() string {
+ // Increment and capture.
+ n.seq += n.inc
+ if n.seq >= maxSeq {
+ n.RandomizePrefix()
+ n.resetSequential()
+ }
+ seq := n.seq
+
+ // Copy prefix
+ var b [totalLen]byte
+ bs := b[:preLen]
+ copy(bs, n.pre)
+
+ // copy in the seq in base36.
+ for i, l := len(b), seq; i > preLen; l /= base {
+ i -= 1
+ b[i] = digits[l%base]
+ }
+ return string(b[:])
+}
+
+// Resets the sequential portion of the NUID.
+func (n *NUID) resetSequential() {
+ n.seq = prand.Int63n(maxSeq)
+ n.inc = minInc + prand.Int63n(maxInc-minInc)
+}
+
+// Generate a new prefix from crypto/rand.
+// This call *can* drain entropy and will be called automatically when we exhaust the sequential range.
+// Will panic if it gets an error from rand.Int()
+func (n *NUID) RandomizePrefix() {
+ var cb [preLen]byte
+ cbs := cb[:]
+ if nb, err := rand.Read(cbs); nb != preLen || err != nil {
+ panic(fmt.Sprintf("nuid: failed generating crypto random number: %v\n", err))
+ }
+
+ for i := 0; i < preLen; i++ {
+ n.pre[i] = digits[int(cbs[i])%base]
+ }
+}
diff --git a/vendor/github.com/peterh/liner/COPYING b/vendor/github.com/peterh/liner/COPYING
new file mode 100644
index 00000000..9e8c9f20
--- /dev/null
+++ b/vendor/github.com/peterh/liner/COPYING
@@ -0,0 +1,21 @@
+Copyright © 2012 Peter Harris
+
+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 (including the next
+paragraph) 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.
+
diff --git a/vendor/github.com/peterh/liner/README.md b/vendor/github.com/peterh/liner/README.md
new file mode 100644
index 00000000..9148b249
--- /dev/null
+++ b/vendor/github.com/peterh/liner/README.md
@@ -0,0 +1,100 @@
+Liner
+=====
+
+Liner is a command line editor with history. It was inspired by linenoise;
+everything Unix-like is a VT100 (or is trying very hard to be). If your
+terminal is not pretending to be a VT100, change it. Liner also support
+Windows.
+
+Liner is released under the X11 license (which is similar to the new BSD
+license).
+
+Line Editing
+------------
+
+The following line editing commands are supported on platforms and terminals
+that Liner supports:
+
+Keystroke | Action
+--------- | ------
+Ctrl-A, Home | Move cursor to beginning of line
+Ctrl-E, End | Move cursor to end of line
+Ctrl-B, Left | Move cursor one character left
+Ctrl-F, Right| Move cursor one character right
+Ctrl-Left, Alt-B | Move cursor to previous word
+Ctrl-Right, Alt-F | Move cursor to next word
+Ctrl-D, Del | (if line is *not* empty) Delete character under cursor
+Ctrl-D | (if line *is* empty) End of File - usually quits application
+Ctrl-C | Reset input (create new empty prompt)
+Ctrl-L | Clear screen (line is unmodified)
+Ctrl-T | Transpose previous character with current character
+Ctrl-H, BackSpace | Delete character before cursor
+Ctrl-W | Delete word leading up to cursor
+Ctrl-K | Delete from cursor to end of line
+Ctrl-U | Delete from start of line to cursor
+Ctrl-P, Up | Previous match from history
+Ctrl-N, Down | Next match from history
+Ctrl-R | Reverse Search history (Ctrl-S forward, Ctrl-G cancel)
+Ctrl-Y | Paste from Yank buffer (Alt-Y to paste next yank instead)
+Tab | Next completion
+Shift-Tab | (after Tab) Previous completion
+
+Getting started
+-----------------
+
+```go
+package main
+
+import (
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/peterh/liner"
+)
+
+var (
+ history_fn = filepath.Join(os.TempDir(), ".liner_example_history")
+ names = []string{"john", "james", "mary", "nancy"}
+)
+
+func main() {
+ line := liner.NewLiner()
+ defer line.Close()
+
+ line.SetCtrlCAborts(true)
+
+ line.SetCompleter(func(line string) (c []string) {
+ for _, n := range names {
+ if strings.HasPrefix(n, strings.ToLower(line)) {
+ c = append(c, n)
+ }
+ }
+ return
+ })
+
+ if f, err := os.Open(history_fn); err == nil {
+ line.ReadHistory(f)
+ f.Close()
+ }
+
+ if name, err := line.Prompt("What is your name? "); err == nil {
+ log.Print("Got: ", name)
+ line.AppendHistory(name)
+ } else if err == liner.ErrPromptAborted {
+ log.Print("Aborted")
+ } else {
+ log.Print("Error reading line: ", err)
+ }
+
+ if f, err := os.Create(history_fn); err != nil {
+ log.Print("Error writing history file: ", err)
+ } else {
+ line.WriteHistory(f)
+ f.Close()
+ }
+}
+```
+
+For documentation, see http://godoc.org/github.com/peterh/liner
diff --git a/vendor/github.com/peterh/liner/bsdinput.go b/vendor/github.com/peterh/liner/bsdinput.go
new file mode 100644
index 00000000..35933982
--- /dev/null
+++ b/vendor/github.com/peterh/liner/bsdinput.go
@@ -0,0 +1,41 @@
+// +build openbsd freebsd netbsd
+
+package liner
+
+import "syscall"
+
+const (
+ getTermios = syscall.TIOCGETA
+ setTermios = syscall.TIOCSETA
+)
+
+const (
+ // Input flags
+ inpck = 0x010
+ istrip = 0x020
+ icrnl = 0x100
+ ixon = 0x200
+
+ // Output flags
+ opost = 0x1
+
+ // Control flags
+ cs8 = 0x300
+
+ // Local flags
+ isig = 0x080
+ icanon = 0x100
+ iexten = 0x400
+)
+
+type termios struct {
+ Iflag uint32
+ Oflag uint32
+ Cflag uint32
+ Lflag uint32
+ Cc [20]byte
+ Ispeed int32
+ Ospeed int32
+}
+
+const cursorColumn = false
diff --git a/vendor/github.com/peterh/liner/common.go b/vendor/github.com/peterh/liner/common.go
new file mode 100644
index 00000000..e16ecbc0
--- /dev/null
+++ b/vendor/github.com/peterh/liner/common.go
@@ -0,0 +1,255 @@
+/*
+Package liner implements a simple command line editor, inspired by linenoise
+(https://github.com/antirez/linenoise/). This package supports WIN32 in
+addition to the xterm codes supported by everything else.
+*/
+package liner
+
+import (
+ "bufio"
+ "container/ring"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+ "sync"
+ "unicode/utf8"
+)
+
+type commonState struct {
+ terminalSupported bool
+ outputRedirected bool
+ inputRedirected bool
+ history []string
+ historyMutex sync.RWMutex
+ completer WordCompleter
+ columns int
+ killRing *ring.Ring
+ ctrlCAborts bool
+ r *bufio.Reader
+ tabStyle TabStyle
+ multiLineMode bool
+ cursorRows int
+ maxRows int
+ shouldRestart ShouldRestart
+ needRefresh bool
+}
+
+// TabStyle is used to select how tab completions are displayed.
+type TabStyle int
+
+// Two tab styles are currently available:
+//
+// TabCircular cycles through each completion item and displays it directly on
+// the prompt
+//
+// TabPrints prints the list of completion items to the screen after a second
+// tab key is pressed. This behaves similar to GNU readline and BASH (which
+// uses readline)
+const (
+ TabCircular TabStyle = iota
+ TabPrints
+)
+
+// ErrPromptAborted is returned from Prompt or PasswordPrompt when the user presses Ctrl-C
+// if SetCtrlCAborts(true) has been called on the State
+var ErrPromptAborted = errors.New("prompt aborted")
+
+// ErrNotTerminalOutput is returned from Prompt or PasswordPrompt if the
+// platform is normally supported, but stdout has been redirected
+var ErrNotTerminalOutput = errors.New("standard output is not a terminal")
+
+// ErrInvalidPrompt is returned from Prompt or PasswordPrompt if the
+// prompt contains any unprintable runes (including substrings that could
+// be colour codes on some platforms).
+var ErrInvalidPrompt = errors.New("invalid prompt")
+
+// ErrInternal is returned when liner experiences an error that it cannot
+// handle. For example, if the number of colums becomes zero during an
+// active call to Prompt
+var ErrInternal = errors.New("liner: internal error")
+
+// KillRingMax is the max number of elements to save on the killring.
+const KillRingMax = 60
+
+// HistoryLimit is the maximum number of entries saved in the scrollback history.
+const HistoryLimit = 1000
+
+// ReadHistory reads scrollback history from r. Returns the number of lines
+// read, and any read error (except io.EOF).
+func (s *State) ReadHistory(r io.Reader) (num int, err error) {
+ s.historyMutex.Lock()
+ defer s.historyMutex.Unlock()
+
+ in := bufio.NewReader(r)
+ num = 0
+ for {
+ line, part, err := in.ReadLine()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return num, err
+ }
+ if part {
+ return num, fmt.Errorf("line %d is too long", num+1)
+ }
+ if !utf8.Valid(line) {
+ return num, fmt.Errorf("invalid string at line %d", num+1)
+ }
+ num++
+ s.history = append(s.history, string(line))
+ if len(s.history) > HistoryLimit {
+ s.history = s.history[1:]
+ }
+ }
+ return num, nil
+}
+
+// WriteHistory writes scrollback history to w. Returns the number of lines
+// successfully written, and any write error.
+//
+// Unlike the rest of liner's API, WriteHistory is safe to call
+// from another goroutine while Prompt is in progress.
+// This exception is to facilitate the saving of the history buffer
+// during an unexpected exit (for example, due to Ctrl-C being invoked)
+func (s *State) WriteHistory(w io.Writer) (num int, err error) {
+ s.historyMutex.RLock()
+ defer s.historyMutex.RUnlock()
+
+ for _, item := range s.history {
+ _, err := fmt.Fprintln(w, item)
+ if err != nil {
+ return num, err
+ }
+ num++
+ }
+ return num, nil
+}
+
+// AppendHistory appends an entry to the scrollback history. AppendHistory
+// should be called iff Prompt returns a valid command.
+func (s *State) AppendHistory(item string) {
+ s.historyMutex.Lock()
+ defer s.historyMutex.Unlock()
+
+ if len(s.history) > 0 {
+ if item == s.history[len(s.history)-1] {
+ return
+ }
+ }
+ s.history = append(s.history, item)
+ if len(s.history) > HistoryLimit {
+ s.history = s.history[1:]
+ }
+}
+
+// ClearHistory clears the scroollback history.
+func (s *State) ClearHistory() {
+ s.historyMutex.Lock()
+ defer s.historyMutex.Unlock()
+ s.history = nil
+}
+
+// Returns the history lines starting with prefix
+func (s *State) getHistoryByPrefix(prefix string) (ph []string) {
+ for _, h := range s.history {
+ if strings.HasPrefix(h, prefix) {
+ ph = append(ph, h)
+ }
+ }
+ return
+}
+
+// Returns the history lines matching the intelligent search
+func (s *State) getHistoryByPattern(pattern string) (ph []string, pos []int) {
+ if pattern == "" {
+ return
+ }
+ for _, h := range s.history {
+ if i := strings.Index(h, pattern); i >= 0 {
+ ph = append(ph, h)
+ pos = append(pos, i)
+ }
+ }
+ return
+}
+
+// Completer takes the currently edited line content at the left of the cursor
+// and returns a list of completion candidates.
+// If the line is "Hello, wo!!!" and the cursor is before the first '!', "Hello, wo" is passed
+// to the completer which may return {"Hello, world", "Hello, Word"} to have "Hello, world!!!".
+type Completer func(line string) []string
+
+// WordCompleter takes the currently edited line with the cursor position and
+// returns the completion candidates for the partial word to be completed.
+// If the line is "Hello, wo!!!" and the cursor is before the first '!', ("Hello, wo!!!", 9) is passed
+// to the completer which may returns ("Hello, ", {"world", "Word"}, "!!!") to have "Hello, world!!!".
+type WordCompleter func(line string, pos int) (head string, completions []string, tail string)
+
+// SetCompleter sets the completion function that Liner will call to
+// fetch completion candidates when the user presses tab.
+func (s *State) SetCompleter(f Completer) {
+ if f == nil {
+ s.completer = nil
+ return
+ }
+ s.completer = func(line string, pos int) (string, []string, string) {
+ return "", f(string([]rune(line)[:pos])), string([]rune(line)[pos:])
+ }
+}
+
+// SetWordCompleter sets the completion function that Liner will call to
+// fetch completion candidates when the user presses tab.
+func (s *State) SetWordCompleter(f WordCompleter) {
+ s.completer = f
+}
+
+// SetTabCompletionStyle sets the behvavior when the Tab key is pressed
+// for auto-completion. TabCircular is the default behavior and cycles
+// through the list of candidates at the prompt. TabPrints will print
+// the available completion candidates to the screen similar to BASH
+// and GNU Readline
+func (s *State) SetTabCompletionStyle(tabStyle TabStyle) {
+ s.tabStyle = tabStyle
+}
+
+// ModeApplier is the interface that wraps a representation of the terminal
+// mode. ApplyMode sets the terminal to this mode.
+type ModeApplier interface {
+ ApplyMode() error
+}
+
+// SetCtrlCAborts sets whether Prompt on a supported terminal will return an
+// ErrPromptAborted when Ctrl-C is pressed. The default is false (will not
+// return when Ctrl-C is pressed). Unsupported terminals typically raise SIGINT
+// (and Prompt does not return) regardless of the value passed to SetCtrlCAborts.
+func (s *State) SetCtrlCAborts(aborts bool) {
+ s.ctrlCAborts = aborts
+}
+
+// SetMultiLineMode sets whether line is auto-wrapped. The default is false (single line).
+func (s *State) SetMultiLineMode(mlmode bool) {
+ s.multiLineMode = mlmode
+}
+
+// ShouldRestart is passed the error generated by readNext and returns true if
+// the the read should be restarted or false if the error should be returned.
+type ShouldRestart func(err error) bool
+
+// SetShouldRestart sets the restart function that Liner will call to determine
+// whether to retry the call to, or return the error returned by, readNext.
+func (s *State) SetShouldRestart(f ShouldRestart) {
+ s.shouldRestart = f
+}
+
+func (s *State) promptUnsupported(p string) (string, error) {
+ if !s.inputRedirected || !s.terminalSupported {
+ fmt.Print(p)
+ }
+ linebuf, _, err := s.r.ReadLine()
+ if err != nil {
+ return "", err
+ }
+ return string(linebuf), nil
+}
diff --git a/vendor/github.com/peterh/liner/fallbackinput.go b/vendor/github.com/peterh/liner/fallbackinput.go
new file mode 100644
index 00000000..d9eb79d9
--- /dev/null
+++ b/vendor/github.com/peterh/liner/fallbackinput.go
@@ -0,0 +1,57 @@
+// +build !windows,!linux,!darwin,!openbsd,!freebsd,!netbsd
+
+package liner
+
+import (
+ "bufio"
+ "errors"
+ "os"
+)
+
+// State represents an open terminal
+type State struct {
+ commonState
+}
+
+// Prompt displays p, and then waits for user input. Prompt does not support
+// line editing on this operating system.
+func (s *State) Prompt(p string) (string, error) {
+ return s.promptUnsupported(p)
+}
+
+// PasswordPrompt is not supported in this OS.
+func (s *State) PasswordPrompt(p string) (string, error) {
+ return "", errors.New("liner: function not supported in this terminal")
+}
+
+// NewLiner initializes a new *State
+//
+// Note that this operating system uses a fallback mode without line
+// editing. Patches welcome.
+func NewLiner() *State {
+ var s State
+ s.r = bufio.NewReader(os.Stdin)
+ return &s
+}
+
+// Close returns the terminal to its previous mode
+func (s *State) Close() error {
+ return nil
+}
+
+// TerminalSupported returns false because line editing is not
+// supported on this platform.
+func TerminalSupported() bool {
+ return false
+}
+
+type noopMode struct{}
+
+func (n noopMode) ApplyMode() error {
+ return nil
+}
+
+// TerminalMode returns a noop InputModeSetter on this platform.
+func TerminalMode() (ModeApplier, error) {
+ return noopMode{}, nil
+}
diff --git a/vendor/github.com/peterh/liner/input.go b/vendor/github.com/peterh/liner/input.go
new file mode 100644
index 00000000..95dd5d14
--- /dev/null
+++ b/vendor/github.com/peterh/liner/input.go
@@ -0,0 +1,364 @@
+// +build linux darwin openbsd freebsd netbsd
+
+package liner
+
+import (
+ "bufio"
+ "errors"
+ "os"
+ "os/signal"
+ "strconv"
+ "strings"
+ "syscall"
+ "time"
+)
+
+type nexter struct {
+ r rune
+ err error
+}
+
+// State represents an open terminal
+type State struct {
+ commonState
+ origMode termios
+ defaultMode termios
+ next <-chan nexter
+ winch chan os.Signal
+ pending []rune
+ useCHA bool
+}
+
+// NewLiner initializes a new *State, and sets the terminal into raw mode. To
+// restore the terminal to its previous state, call State.Close().
+func NewLiner() *State {
+ var s State
+ s.r = bufio.NewReader(os.Stdin)
+
+ s.terminalSupported = TerminalSupported()
+ if m, err := TerminalMode(); err == nil {
+ s.origMode = *m.(*termios)
+ } else {
+ s.inputRedirected = true
+ }
+ if _, err := getMode(syscall.Stdout); err != 0 {
+ s.outputRedirected = true
+ }
+ if s.inputRedirected && s.outputRedirected {
+ s.terminalSupported = false
+ }
+ if s.terminalSupported && !s.inputRedirected && !s.outputRedirected {
+ mode := s.origMode
+ mode.Iflag &^= icrnl | inpck | istrip | ixon
+ mode.Cflag |= cs8
+ mode.Lflag &^= syscall.ECHO | icanon | iexten
+ mode.ApplyMode()
+
+ winch := make(chan os.Signal, 1)
+ signal.Notify(winch, syscall.SIGWINCH)
+ s.winch = winch
+
+ s.checkOutput()
+ }
+
+ if !s.outputRedirected {
+ s.outputRedirected = !s.getColumns()
+ }
+
+ return &s
+}
+
+var errTimedOut = errors.New("timeout")
+
+func (s *State) startPrompt() {
+ if s.terminalSupported {
+ if m, err := TerminalMode(); err == nil {
+ s.defaultMode = *m.(*termios)
+ mode := s.defaultMode
+ mode.Lflag &^= isig
+ mode.ApplyMode()
+ }
+ }
+ s.restartPrompt()
+}
+
+func (s *State) inputWaiting() bool {
+ return len(s.next) > 0
+}
+
+func (s *State) restartPrompt() {
+ next := make(chan nexter, 200)
+ go func() {
+ for {
+ var n nexter
+ n.r, _, n.err = s.r.ReadRune()
+ next <- n
+ // Shut down nexter loop when an end condition has been reached
+ if n.err != nil || n.r == '\n' || n.r == '\r' || n.r == ctrlC || n.r == ctrlD {
+ close(next)
+ return
+ }
+ }
+ }()
+ s.next = next
+}
+
+func (s *State) stopPrompt() {
+ if s.terminalSupported {
+ s.defaultMode.ApplyMode()
+ }
+}
+
+func (s *State) nextPending(timeout <-chan time.Time) (rune, error) {
+ select {
+ case thing, ok := <-s.next:
+ if !ok {
+ return 0, ErrInternal
+ }
+ if thing.err != nil {
+ return 0, thing.err
+ }
+ s.pending = append(s.pending, thing.r)
+ return thing.r, nil
+ case <-timeout:
+ rv := s.pending[0]
+ s.pending = s.pending[1:]
+ return rv, errTimedOut
+ }
+}
+
+func (s *State) readNext() (interface{}, error) {
+ if len(s.pending) > 0 {
+ rv := s.pending[0]
+ s.pending = s.pending[1:]
+ return rv, nil
+ }
+ var r rune
+ select {
+ case thing, ok := <-s.next:
+ if !ok {
+ return 0, ErrInternal
+ }
+ if thing.err != nil {
+ return nil, thing.err
+ }
+ r = thing.r
+ case <-s.winch:
+ s.getColumns()
+ return winch, nil
+ }
+ if r != esc {
+ return r, nil
+ }
+ s.pending = append(s.pending, r)
+
+ // Wait at most 50 ms for the rest of the escape sequence
+ // If nothing else arrives, it was an actual press of the esc key
+ timeout := time.After(50 * time.Millisecond)
+ flag, err := s.nextPending(timeout)
+ if err != nil {
+ if err == errTimedOut {
+ return flag, nil
+ }
+ return unknown, err
+ }
+
+ switch flag {
+ case '[':
+ code, err := s.nextPending(timeout)
+ if err != nil {
+ if err == errTimedOut {
+ return code, nil
+ }
+ return unknown, err
+ }
+ switch code {
+ case 'A':
+ s.pending = s.pending[:0] // escape code complete
+ return up, nil
+ case 'B':
+ s.pending = s.pending[:0] // escape code complete
+ return down, nil
+ case 'C':
+ s.pending = s.pending[:0] // escape code complete
+ return right, nil
+ case 'D':
+ s.pending = s.pending[:0] // escape code complete
+ return left, nil
+ case 'F':
+ s.pending = s.pending[:0] // escape code complete
+ return end, nil
+ case 'H':
+ s.pending = s.pending[:0] // escape code complete
+ return home, nil
+ case 'Z':
+ s.pending = s.pending[:0] // escape code complete
+ return shiftTab, nil
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ num := []rune{code}
+ for {
+ code, err := s.nextPending(timeout)
+ if err != nil {
+ if err == errTimedOut {
+ return code, nil
+ }
+ return nil, err
+ }
+ switch code {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ num = append(num, code)
+ case ';':
+ // Modifier code to follow
+ // This only supports Ctrl-left and Ctrl-right for now
+ x, _ := strconv.ParseInt(string(num), 10, 32)
+ if x != 1 {
+ // Can't be left or right
+ rv := s.pending[0]
+ s.pending = s.pending[1:]
+ return rv, nil
+ }
+ num = num[:0]
+ for {
+ code, err = s.nextPending(timeout)
+ if err != nil {
+ if err == errTimedOut {
+ rv := s.pending[0]
+ s.pending = s.pending[1:]
+ return rv, nil
+ }
+ return nil, err
+ }
+ switch code {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ num = append(num, code)
+ case 'C', 'D':
+ // right, left
+ mod, _ := strconv.ParseInt(string(num), 10, 32)
+ if mod != 5 {
+ // Not bare Ctrl
+ rv := s.pending[0]
+ s.pending = s.pending[1:]
+ return rv, nil
+ }
+ s.pending = s.pending[:0] // escape code complete
+ if code == 'C' {
+ return wordRight, nil
+ }
+ return wordLeft, nil
+ default:
+ // Not left or right
+ rv := s.pending[0]
+ s.pending = s.pending[1:]
+ return rv, nil
+ }
+ }
+ case '~':
+ s.pending = s.pending[:0] // escape code complete
+ x, _ := strconv.ParseInt(string(num), 10, 32)
+ switch x {
+ case 2:
+ return insert, nil
+ case 3:
+ return del, nil
+ case 5:
+ return pageUp, nil
+ case 6:
+ return pageDown, nil
+ case 7:
+ return home, nil
+ case 8:
+ return end, nil
+ case 15:
+ return f5, nil
+ case 17:
+ return f6, nil
+ case 18:
+ return f7, nil
+ case 19:
+ return f8, nil
+ case 20:
+ return f9, nil
+ case 21:
+ return f10, nil
+ case 23:
+ return f11, nil
+ case 24:
+ return f12, nil
+ default:
+ return unknown, nil
+ }
+ default:
+ // unrecognized escape code
+ rv := s.pending[0]
+ s.pending = s.pending[1:]
+ return rv, nil
+ }
+ }
+ }
+
+ case 'O':
+ code, err := s.nextPending(timeout)
+ if err != nil {
+ if err == errTimedOut {
+ return code, nil
+ }
+ return nil, err
+ }
+ s.pending = s.pending[:0] // escape code complete
+ switch code {
+ case 'c':
+ return wordRight, nil
+ case 'd':
+ return wordLeft, nil
+ case 'H':
+ return home, nil
+ case 'F':
+ return end, nil
+ case 'P':
+ return f1, nil
+ case 'Q':
+ return f2, nil
+ case 'R':
+ return f3, nil
+ case 'S':
+ return f4, nil
+ default:
+ return unknown, nil
+ }
+ case 'b':
+ s.pending = s.pending[:0] // escape code complete
+ return altB, nil
+ case 'f':
+ s.pending = s.pending[:0] // escape code complete
+ return altF, nil
+ case 'y':
+ s.pending = s.pending[:0] // escape code complete
+ return altY, nil
+ default:
+ rv := s.pending[0]
+ s.pending = s.pending[1:]
+ return rv, nil
+ }
+
+ // not reached
+ return r, nil
+}
+
+// Close returns the terminal to its previous mode
+func (s *State) Close() error {
+ signal.Stop(s.winch)
+ if !s.inputRedirected {
+ s.origMode.ApplyMode()
+ }
+ return nil
+}
+
+// TerminalSupported returns true if the current terminal supports
+// line editing features, and false if liner will use the 'dumb'
+// fallback for input.
+// Note that TerminalSupported does not check all factors that may
+// cause liner to not fully support the terminal (such as stdin redirection)
+func TerminalSupported() bool {
+ bad := map[string]bool{"": true, "dumb": true, "cons25": true}
+ return !bad[strings.ToLower(os.Getenv("TERM"))]
+}
diff --git a/vendor/github.com/peterh/liner/input_darwin.go b/vendor/github.com/peterh/liner/input_darwin.go
new file mode 100644
index 00000000..e98ab4a4
--- /dev/null
+++ b/vendor/github.com/peterh/liner/input_darwin.go
@@ -0,0 +1,43 @@
+// +build darwin
+
+package liner
+
+import "syscall"
+
+const (
+ getTermios = syscall.TIOCGETA
+ setTermios = syscall.TIOCSETA
+)
+
+const (
+ // Input flags
+ inpck = 0x010
+ istrip = 0x020
+ icrnl = 0x100
+ ixon = 0x200
+
+ // Output flags
+ opost = 0x1
+
+ // Control flags
+ cs8 = 0x300
+
+ // Local flags
+ isig = 0x080
+ icanon = 0x100
+ iexten = 0x400
+)
+
+type termios struct {
+ Iflag uintptr
+ Oflag uintptr
+ Cflag uintptr
+ Lflag uintptr
+ Cc [20]byte
+ Ispeed uintptr
+ Ospeed uintptr
+}
+
+// Terminal.app needs a column for the cursor when the input line is at the
+// bottom of the window.
+const cursorColumn = true
diff --git a/vendor/github.com/peterh/liner/input_linux.go b/vendor/github.com/peterh/liner/input_linux.go
new file mode 100644
index 00000000..56ed185f
--- /dev/null
+++ b/vendor/github.com/peterh/liner/input_linux.go
@@ -0,0 +1,28 @@
+// +build linux
+
+package liner
+
+import "syscall"
+
+const (
+ getTermios = syscall.TCGETS
+ setTermios = syscall.TCSETS
+)
+
+const (
+ icrnl = syscall.ICRNL
+ inpck = syscall.INPCK
+ istrip = syscall.ISTRIP
+ ixon = syscall.IXON
+ opost = syscall.OPOST
+ cs8 = syscall.CS8
+ isig = syscall.ISIG
+ icanon = syscall.ICANON
+ iexten = syscall.IEXTEN
+)
+
+type termios struct {
+ syscall.Termios
+}
+
+const cursorColumn = false
diff --git a/vendor/github.com/peterh/liner/input_windows.go b/vendor/github.com/peterh/liner/input_windows.go
new file mode 100644
index 00000000..a48eb0f1
--- /dev/null
+++ b/vendor/github.com/peterh/liner/input_windows.go
@@ -0,0 +1,339 @@
+package liner
+
+import (
+ "bufio"
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+var (
+ kernel32 = syscall.NewLazyDLL("kernel32.dll")
+
+ procGetStdHandle = kernel32.NewProc("GetStdHandle")
+ procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW")
+ procGetNumberOfConsoleInputEvents = kernel32.NewProc("GetNumberOfConsoleInputEvents")
+ procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
+ procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
+ procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
+ procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
+ procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
+)
+
+// These names are from the Win32 api, so they use underscores (contrary to
+// what golint suggests)
+const (
+ std_input_handle = uint32(-10 & 0xFFFFFFFF)
+ std_output_handle = uint32(-11 & 0xFFFFFFFF)
+ std_error_handle = uint32(-12 & 0xFFFFFFFF)
+ invalid_handle_value = ^uintptr(0)
+)
+
+type inputMode uint32
+
+// State represents an open terminal
+type State struct {
+ commonState
+ handle syscall.Handle
+ hOut syscall.Handle
+ origMode inputMode
+ defaultMode inputMode
+ key interface{}
+ repeat uint16
+}
+
+const (
+ enableEchoInput = 0x4
+ enableInsertMode = 0x20
+ enableLineInput = 0x2
+ enableMouseInput = 0x10
+ enableProcessedInput = 0x1
+ enableQuickEditMode = 0x40
+ enableWindowInput = 0x8
+)
+
+// NewLiner initializes a new *State, and sets the terminal into raw mode. To
+// restore the terminal to its previous state, call State.Close().
+func NewLiner() *State {
+ var s State
+ hIn, _, _ := procGetStdHandle.Call(uintptr(std_input_handle))
+ s.handle = syscall.Handle(hIn)
+ hOut, _, _ := procGetStdHandle.Call(uintptr(std_output_handle))
+ s.hOut = syscall.Handle(hOut)
+
+ s.terminalSupported = true
+ if m, err := TerminalMode(); err == nil {
+ s.origMode = m.(inputMode)
+ mode := s.origMode
+ mode &^= enableEchoInput
+ mode &^= enableInsertMode
+ mode &^= enableLineInput
+ mode &^= enableMouseInput
+ mode |= enableWindowInput
+ mode.ApplyMode()
+ } else {
+ s.inputRedirected = true
+ s.r = bufio.NewReader(os.Stdin)
+ }
+
+ s.getColumns()
+ s.outputRedirected = s.columns <= 0
+
+ return &s
+}
+
+// These names are from the Win32 api, so they use underscores (contrary to
+// what golint suggests)
+const (
+ focus_event = 0x0010
+ key_event = 0x0001
+ menu_event = 0x0008
+ mouse_event = 0x0002
+ window_buffer_size_event = 0x0004
+)
+
+type input_record struct {
+ eventType uint16
+ pad uint16
+ blob [16]byte
+}
+
+type key_event_record struct {
+ KeyDown int32
+ RepeatCount uint16
+ VirtualKeyCode uint16
+ VirtualScanCode uint16
+ Char int16
+ ControlKeyState uint32
+}
+
+// These names are from the Win32 api, so they use underscores (contrary to
+// what golint suggests)
+const (
+ vk_tab = 0x09
+ vk_prior = 0x21
+ vk_next = 0x22
+ vk_end = 0x23
+ vk_home = 0x24
+ vk_left = 0x25
+ vk_up = 0x26
+ vk_right = 0x27
+ vk_down = 0x28
+ vk_insert = 0x2d
+ vk_delete = 0x2e
+ vk_f1 = 0x70
+ vk_f2 = 0x71
+ vk_f3 = 0x72
+ vk_f4 = 0x73
+ vk_f5 = 0x74
+ vk_f6 = 0x75
+ vk_f7 = 0x76
+ vk_f8 = 0x77
+ vk_f9 = 0x78
+ vk_f10 = 0x79
+ vk_f11 = 0x7a
+ vk_f12 = 0x7b
+ bKey = 0x42
+ fKey = 0x46
+ yKey = 0x59
+)
+
+const (
+ shiftPressed = 0x0010
+ leftAltPressed = 0x0002
+ leftCtrlPressed = 0x0008
+ rightAltPressed = 0x0001
+ rightCtrlPressed = 0x0004
+
+ modKeys = shiftPressed | leftAltPressed | rightAltPressed | leftCtrlPressed | rightCtrlPressed
+)
+
+// inputWaiting only returns true if the next call to readNext will return immediately.
+func (s *State) inputWaiting() bool {
+ var num uint32
+ ok, _, _ := procGetNumberOfConsoleInputEvents.Call(uintptr(s.handle), uintptr(unsafe.Pointer(&num)))
+ if ok == 0 {
+ // call failed, so we cannot guarantee a non-blocking readNext
+ return false
+ }
+
+ // during a "paste" input events are always an odd number, and
+ // the last one results in a blocking readNext, so return false
+ // when num is 1 or 0.
+ return num > 1
+}
+
+func (s *State) readNext() (interface{}, error) {
+ if s.repeat > 0 {
+ s.repeat--
+ return s.key, nil
+ }
+
+ var input input_record
+ pbuf := uintptr(unsafe.Pointer(&input))
+ var rv uint32
+ prv := uintptr(unsafe.Pointer(&rv))
+
+ for {
+ ok, _, err := procReadConsoleInput.Call(uintptr(s.handle), pbuf, 1, prv)
+
+ if ok == 0 {
+ return nil, err
+ }
+
+ if input.eventType == window_buffer_size_event {
+ xy := (*coord)(unsafe.Pointer(&input.blob[0]))
+ s.columns = int(xy.x)
+ if s.columns > 1 {
+ s.columns--
+ }
+ return winch, nil
+ }
+ if input.eventType != key_event {
+ continue
+ }
+ ke := (*key_event_record)(unsafe.Pointer(&input.blob[0]))
+ if ke.KeyDown == 0 {
+ continue
+ }
+
+ if ke.VirtualKeyCode == vk_tab && ke.ControlKeyState&modKeys == shiftPressed {
+ s.key = shiftTab
+ } else if ke.VirtualKeyCode == bKey && (ke.ControlKeyState&modKeys == leftAltPressed ||
+ ke.ControlKeyState&modKeys == rightAltPressed) {
+ s.key = altB
+ } else if ke.VirtualKeyCode == fKey && (ke.ControlKeyState&modKeys == leftAltPressed ||
+ ke.ControlKeyState&modKeys == rightAltPressed) {
+ s.key = altF
+ } else if ke.VirtualKeyCode == yKey && (ke.ControlKeyState&modKeys == leftAltPressed ||
+ ke.ControlKeyState&modKeys == rightAltPressed) {
+ s.key = altY
+ } else if ke.Char > 0 {
+ s.key = rune(ke.Char)
+ } else {
+ switch ke.VirtualKeyCode {
+ case vk_prior:
+ s.key = pageUp
+ case vk_next:
+ s.key = pageDown
+ case vk_end:
+ s.key = end
+ case vk_home:
+ s.key = home
+ case vk_left:
+ s.key = left
+ if ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) != 0 {
+ if ke.ControlKeyState&modKeys == ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) {
+ s.key = wordLeft
+ }
+ }
+ case vk_right:
+ s.key = right
+ if ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) != 0 {
+ if ke.ControlKeyState&modKeys == ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) {
+ s.key = wordRight
+ }
+ }
+ case vk_up:
+ s.key = up
+ case vk_down:
+ s.key = down
+ case vk_insert:
+ s.key = insert
+ case vk_delete:
+ s.key = del
+ case vk_f1:
+ s.key = f1
+ case vk_f2:
+ s.key = f2
+ case vk_f3:
+ s.key = f3
+ case vk_f4:
+ s.key = f4
+ case vk_f5:
+ s.key = f5
+ case vk_f6:
+ s.key = f6
+ case vk_f7:
+ s.key = f7
+ case vk_f8:
+ s.key = f8
+ case vk_f9:
+ s.key = f9
+ case vk_f10:
+ s.key = f10
+ case vk_f11:
+ s.key = f11
+ case vk_f12:
+ s.key = f12
+ default:
+ // Eat modifier keys
+ // TODO: return Action(Unknown) if the key isn't a
+ // modifier.
+ continue
+ }
+ }
+
+ if ke.RepeatCount > 1 {
+ s.repeat = ke.RepeatCount - 1
+ }
+ return s.key, nil
+ }
+}
+
+// Close returns the terminal to its previous mode
+func (s *State) Close() error {
+ s.origMode.ApplyMode()
+ return nil
+}
+
+func (s *State) startPrompt() {
+ if m, err := TerminalMode(); err == nil {
+ s.defaultMode = m.(inputMode)
+ mode := s.defaultMode
+ mode &^= enableProcessedInput
+ mode.ApplyMode()
+ }
+}
+
+func (s *State) restartPrompt() {
+}
+
+func (s *State) stopPrompt() {
+ s.defaultMode.ApplyMode()
+}
+
+// TerminalSupported returns true because line editing is always
+// supported on Windows.
+func TerminalSupported() bool {
+ return true
+}
+
+func (mode inputMode) ApplyMode() error {
+ hIn, _, err := procGetStdHandle.Call(uintptr(std_input_handle))
+ if hIn == invalid_handle_value || hIn == 0 {
+ return err
+ }
+ ok, _, err := procSetConsoleMode.Call(hIn, uintptr(mode))
+ if ok != 0 {
+ err = nil
+ }
+ return err
+}
+
+// TerminalMode returns the current terminal input mode as an InputModeSetter.
+//
+// This function is provided for convenience, and should
+// not be necessary for most users of liner.
+func TerminalMode() (ModeApplier, error) {
+ var mode inputMode
+ hIn, _, err := procGetStdHandle.Call(uintptr(std_input_handle))
+ if hIn == invalid_handle_value || hIn == 0 {
+ return nil, err
+ }
+ ok, _, err := procGetConsoleMode.Call(hIn, uintptr(unsafe.Pointer(&mode)))
+ if ok != 0 {
+ err = nil
+ }
+ return mode, err
+}
diff --git a/vendor/github.com/peterh/liner/line.go b/vendor/github.com/peterh/liner/line.go
new file mode 100644
index 00000000..d61f0696
--- /dev/null
+++ b/vendor/github.com/peterh/liner/line.go
@@ -0,0 +1,1129 @@
+// +build windows linux darwin openbsd freebsd netbsd
+
+package liner
+
+import (
+ "bufio"
+ "container/ring"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+type action int
+
+const (
+ left action = iota
+ right
+ up
+ down
+ home
+ end
+ insert
+ del
+ pageUp
+ pageDown
+ f1
+ f2
+ f3
+ f4
+ f5
+ f6
+ f7
+ f8
+ f9
+ f10
+ f11
+ f12
+ altB
+ altF
+ altY
+ shiftTab
+ wordLeft
+ wordRight
+ winch
+ unknown
+)
+
+const (
+ ctrlA = 1
+ ctrlB = 2
+ ctrlC = 3
+ ctrlD = 4
+ ctrlE = 5
+ ctrlF = 6
+ ctrlG = 7
+ ctrlH = 8
+ tab = 9
+ lf = 10
+ ctrlK = 11
+ ctrlL = 12
+ cr = 13
+ ctrlN = 14
+ ctrlO = 15
+ ctrlP = 16
+ ctrlQ = 17
+ ctrlR = 18
+ ctrlS = 19
+ ctrlT = 20
+ ctrlU = 21
+ ctrlV = 22
+ ctrlW = 23
+ ctrlX = 24
+ ctrlY = 25
+ ctrlZ = 26
+ esc = 27
+ bs = 127
+)
+
+const (
+ beep = "\a"
+)
+
+type tabDirection int
+
+const (
+ tabForward tabDirection = iota
+ tabReverse
+)
+
+func (s *State) refresh(prompt []rune, buf []rune, pos int) error {
+ if s.columns == 0 {
+ return ErrInternal
+ }
+
+ s.needRefresh = false
+ if s.multiLineMode {
+ return s.refreshMultiLine(prompt, buf, pos)
+ }
+ return s.refreshSingleLine(prompt, buf, pos)
+}
+
+func (s *State) refreshSingleLine(prompt []rune, buf []rune, pos int) error {
+ s.cursorPos(0)
+ _, err := fmt.Print(string(prompt))
+ if err != nil {
+ return err
+ }
+
+ pLen := countGlyphs(prompt)
+ bLen := countGlyphs(buf)
+ pos = countGlyphs(buf[:pos])
+ if pLen+bLen < s.columns {
+ _, err = fmt.Print(string(buf))
+ s.eraseLine()
+ s.cursorPos(pLen + pos)
+ } else {
+ // Find space available
+ space := s.columns - pLen
+ space-- // space for cursor
+ start := pos - space/2
+ end := start + space
+ if end > bLen {
+ end = bLen
+ start = end - space
+ }
+ if start < 0 {
+ start = 0
+ end = space
+ }
+ pos -= start
+
+ // Leave space for markers
+ if start > 0 {
+ start++
+ }
+ if end < bLen {
+ end--
+ }
+ startRune := len(getPrefixGlyphs(buf, start))
+ line := getPrefixGlyphs(buf[startRune:], end-start)
+
+ // Output
+ if start > 0 {
+ fmt.Print("{")
+ }
+ fmt.Print(string(line))
+ if end < bLen {
+ fmt.Print("}")
+ }
+
+ // Set cursor position
+ s.eraseLine()
+ s.cursorPos(pLen + pos)
+ }
+ return err
+}
+
+func (s *State) refreshMultiLine(prompt []rune, buf []rune, pos int) error {
+ promptColumns := countMultiLineGlyphs(prompt, s.columns, 0)
+ totalColumns := countMultiLineGlyphs(buf, s.columns, promptColumns)
+ totalRows := (totalColumns + s.columns - 1) / s.columns
+ maxRows := s.maxRows
+ if totalRows > s.maxRows {
+ s.maxRows = totalRows
+ }
+ cursorRows := s.cursorRows
+ if cursorRows == 0 {
+ cursorRows = 1
+ }
+
+ /* First step: clear all the lines used before. To do so start by
+ * going to the last row. */
+ if maxRows-cursorRows > 0 {
+ s.moveDown(maxRows - cursorRows)
+ }
+
+ /* Now for every row clear it, go up. */
+ for i := 0; i < maxRows-1; i++ {
+ s.cursorPos(0)
+ s.eraseLine()
+ s.moveUp(1)
+ }
+
+ /* Clean the top line. */
+ s.cursorPos(0)
+ s.eraseLine()
+
+ /* Write the prompt and the current buffer content */
+ if _, err := fmt.Print(string(prompt)); err != nil {
+ return err
+ }
+ if _, err := fmt.Print(string(buf)); err != nil {
+ return err
+ }
+
+ /* If we are at the very end of the screen with our prompt, we need to
+ * emit a newline and move the prompt to the first column. */
+ cursorColumns := countMultiLineGlyphs(buf[:pos], s.columns, promptColumns)
+ if cursorColumns == totalColumns && totalColumns%s.columns == 0 {
+ s.emitNewLine()
+ s.cursorPos(0)
+ totalRows++
+ if totalRows > s.maxRows {
+ s.maxRows = totalRows
+ }
+ }
+
+ /* Move cursor to right position. */
+ cursorRows = (cursorColumns + s.columns) / s.columns
+ if s.cursorRows > 0 && totalRows-cursorRows > 0 {
+ s.moveUp(totalRows - cursorRows)
+ }
+ /* Set column. */
+ s.cursorPos(cursorColumns % s.columns)
+
+ s.cursorRows = cursorRows
+ return nil
+}
+
+func (s *State) resetMultiLine(prompt []rune, buf []rune, pos int) {
+ columns := countMultiLineGlyphs(prompt, s.columns, 0)
+ columns = countMultiLineGlyphs(buf[:pos], s.columns, columns)
+ columns += 2 // ^C
+ cursorRows := (columns + s.columns) / s.columns
+ if s.maxRows-cursorRows > 0 {
+ for i := 0; i < s.maxRows-cursorRows; i++ {
+ fmt.Println() // always moves the cursor down or scrolls the window up as needed
+ }
+ }
+ s.maxRows = 1
+ s.cursorRows = 0
+}
+
+func longestCommonPrefix(strs []string) string {
+ if len(strs) == 0 {
+ return ""
+ }
+ longest := strs[0]
+
+ for _, str := range strs[1:] {
+ for !strings.HasPrefix(str, longest) {
+ longest = longest[:len(longest)-1]
+ }
+ }
+ // Remove trailing partial runes
+ longest = strings.TrimRight(longest, "\uFFFD")
+ return longest
+}
+
+func (s *State) circularTabs(items []string) func(tabDirection) (string, error) {
+ item := -1
+ return func(direction tabDirection) (string, error) {
+ if direction == tabForward {
+ if item < len(items)-1 {
+ item++
+ } else {
+ item = 0
+ }
+ } else if direction == tabReverse {
+ if item > 0 {
+ item--
+ } else {
+ item = len(items) - 1
+ }
+ }
+ return items[item], nil
+ }
+}
+
+func calculateColumns(screenWidth int, items []string) (numColumns, numRows, maxWidth int) {
+ for _, item := range items {
+ if len(item) >= screenWidth {
+ return 1, len(items), screenWidth - 1
+ }
+ if len(item) >= maxWidth {
+ maxWidth = len(item) + 1
+ }
+ }
+
+ numColumns = screenWidth / maxWidth
+ numRows = len(items) / numColumns
+ if len(items)%numColumns > 0 {
+ numRows++
+ }
+
+ if len(items) <= numColumns {
+ maxWidth = 0
+ }
+
+ return
+}
+
+func (s *State) printedTabs(items []string) func(tabDirection) (string, error) {
+ numTabs := 1
+ prefix := longestCommonPrefix(items)
+ return func(direction tabDirection) (string, error) {
+ if len(items) == 1 {
+ return items[0], nil
+ }
+
+ if numTabs == 2 {
+ if len(items) > 100 {
+ fmt.Printf("\nDisplay all %d possibilities? (y or n) ", len(items))
+ prompt:
+ for {
+ next, err := s.readNext()
+ if err != nil {
+ return prefix, err
+ }
+
+ if key, ok := next.(rune); ok {
+ switch key {
+ case 'n', 'N':
+ return prefix, nil
+ case 'y', 'Y':
+ break prompt
+ case ctrlC, ctrlD, cr, lf:
+ s.restartPrompt()
+ }
+ }
+ }
+ }
+ fmt.Println("")
+
+ numColumns, numRows, maxWidth := calculateColumns(s.columns, items)
+
+ for i := 0; i < numRows; i++ {
+ for j := 0; j < numColumns*numRows; j += numRows {
+ if i+j < len(items) {
+ if maxWidth > 0 {
+ fmt.Printf("%-*.[1]*s", maxWidth, items[i+j])
+ } else {
+ fmt.Printf("%v ", items[i+j])
+ }
+ }
+ }
+ fmt.Println("")
+ }
+ } else {
+ numTabs++
+ }
+ return prefix, nil
+ }
+}
+
+func (s *State) tabComplete(p []rune, line []rune, pos int) ([]rune, int, interface{}, error) {
+ if s.completer == nil {
+ return line, pos, rune(esc), nil
+ }
+ head, list, tail := s.completer(string(line), pos)
+ if len(list) <= 0 {
+ return line, pos, rune(esc), nil
+ }
+ hl := utf8.RuneCountInString(head)
+ if len(list) == 1 {
+ err := s.refresh(p, []rune(head+list[0]+tail), hl+utf8.RuneCountInString(list[0]))
+ return []rune(head + list[0] + tail), hl + utf8.RuneCountInString(list[0]), rune(esc), err
+ }
+
+ direction := tabForward
+ tabPrinter := s.circularTabs(list)
+ if s.tabStyle == TabPrints {
+ tabPrinter = s.printedTabs(list)
+ }
+
+ for {
+ pick, err := tabPrinter(direction)
+ if err != nil {
+ return line, pos, rune(esc), err
+ }
+ err = s.refresh(p, []rune(head+pick+tail), hl+utf8.RuneCountInString(pick))
+ if err != nil {
+ return line, pos, rune(esc), err
+ }
+
+ next, err := s.readNext()
+ if err != nil {
+ return line, pos, rune(esc), err
+ }
+ if key, ok := next.(rune); ok {
+ if key == tab {
+ direction = tabForward
+ continue
+ }
+ if key == esc {
+ return line, pos, rune(esc), nil
+ }
+ }
+ if a, ok := next.(action); ok && a == shiftTab {
+ direction = tabReverse
+ continue
+ }
+ return []rune(head + pick + tail), hl + utf8.RuneCountInString(pick), next, nil
+ }
+}
+
+// reverse intelligent search, implements a bash-like history search.
+func (s *State) reverseISearch(origLine []rune, origPos int) ([]rune, int, interface{}, error) {
+ p := "(reverse-i-search)`': "
+ err := s.refresh([]rune(p), origLine, origPos)
+ if err != nil {
+ return origLine, origPos, rune(esc), err
+ }
+
+ line := []rune{}
+ pos := 0
+ foundLine := string(origLine)
+ foundPos := origPos
+
+ getLine := func() ([]rune, []rune, int) {
+ search := string(line)
+ prompt := "(reverse-i-search)`%s': "
+ return []rune(fmt.Sprintf(prompt, search)), []rune(foundLine), foundPos
+ }
+
+ history, positions := s.getHistoryByPattern(string(line))
+ historyPos := len(history) - 1
+
+ for {
+ next, err := s.readNext()
+ if err != nil {
+ return []rune(foundLine), foundPos, rune(esc), err
+ }
+
+ switch v := next.(type) {
+ case rune:
+ switch v {
+ case ctrlR: // Search backwards
+ if historyPos > 0 && historyPos < len(history) {
+ historyPos--
+ foundLine = history[historyPos]
+ foundPos = positions[historyPos]
+ } else {
+ fmt.Print(beep)
+ }
+ case ctrlS: // Search forward
+ if historyPos < len(history)-1 && historyPos >= 0 {
+ historyPos++
+ foundLine = history[historyPos]
+ foundPos = positions[historyPos]
+ } else {
+ fmt.Print(beep)
+ }
+ case ctrlH, bs: // Backspace
+ if pos <= 0 {
+ fmt.Print(beep)
+ } else {
+ n := len(getSuffixGlyphs(line[:pos], 1))
+ line = append(line[:pos-n], line[pos:]...)
+ pos -= n
+
+ // For each char deleted, display the last matching line of history
+ history, positions := s.getHistoryByPattern(string(line))
+ historyPos = len(history) - 1
+ if len(history) > 0 {
+ foundLine = history[historyPos]
+ foundPos = positions[historyPos]
+ } else {
+ foundLine = ""
+ foundPos = 0
+ }
+ }
+ case ctrlG: // Cancel
+ return origLine, origPos, rune(esc), err
+
+ case tab, cr, lf, ctrlA, ctrlB, ctrlD, ctrlE, ctrlF, ctrlK,
+ ctrlL, ctrlN, ctrlO, ctrlP, ctrlQ, ctrlT, ctrlU, ctrlV, ctrlW, ctrlX, ctrlY, ctrlZ:
+ fallthrough
+ case 0, ctrlC, esc, 28, 29, 30, 31:
+ return []rune(foundLine), foundPos, next, err
+ default:
+ line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
+ pos++
+
+ // For each keystroke typed, display the last matching line of history
+ history, positions = s.getHistoryByPattern(string(line))
+ historyPos = len(history) - 1
+ if len(history) > 0 {
+ foundLine = history[historyPos]
+ foundPos = positions[historyPos]
+ } else {
+ foundLine = ""
+ foundPos = 0
+ }
+ }
+ case action:
+ return []rune(foundLine), foundPos, next, err
+ }
+ err = s.refresh(getLine())
+ if err != nil {
+ return []rune(foundLine), foundPos, rune(esc), err
+ }
+ }
+}
+
+// addToKillRing adds some text to the kill ring. If mode is 0 it adds it to a
+// new node in the end of the kill ring, and move the current pointer to the new
+// node. If mode is 1 or 2 it appends or prepends the text to the current entry
+// of the killRing.
+func (s *State) addToKillRing(text []rune, mode int) {
+ // Don't use the same underlying array as text
+ killLine := make([]rune, len(text))
+ copy(killLine, text)
+
+ // Point killRing to a newNode, procedure depends on the killring state and
+ // append mode.
+ if mode == 0 { // Add new node to killRing
+ if s.killRing == nil { // if killring is empty, create a new one
+ s.killRing = ring.New(1)
+ } else if s.killRing.Len() >= KillRingMax { // if killring is "full"
+ s.killRing = s.killRing.Next()
+ } else { // Normal case
+ s.killRing.Link(ring.New(1))
+ s.killRing = s.killRing.Next()
+ }
+ } else {
+ if s.killRing == nil { // if killring is empty, create a new one
+ s.killRing = ring.New(1)
+ s.killRing.Value = []rune{}
+ }
+ if mode == 1 { // Append to last entry
+ killLine = append(s.killRing.Value.([]rune), killLine...)
+ } else if mode == 2 { // Prepend to last entry
+ killLine = append(killLine, s.killRing.Value.([]rune)...)
+ }
+ }
+
+ // Save text in the current killring node
+ s.killRing.Value = killLine
+}
+
+func (s *State) yank(p []rune, text []rune, pos int) ([]rune, int, interface{}, error) {
+ if s.killRing == nil {
+ return text, pos, rune(esc), nil
+ }
+
+ lineStart := text[:pos]
+ lineEnd := text[pos:]
+ var line []rune
+
+ for {
+ value := s.killRing.Value.([]rune)
+ line = make([]rune, 0)
+ line = append(line, lineStart...)
+ line = append(line, value...)
+ line = append(line, lineEnd...)
+
+ pos = len(lineStart) + len(value)
+ err := s.refresh(p, line, pos)
+ if err != nil {
+ return line, pos, 0, err
+ }
+
+ next, err := s.readNext()
+ if err != nil {
+ return line, pos, next, err
+ }
+
+ switch v := next.(type) {
+ case rune:
+ return line, pos, next, nil
+ case action:
+ switch v {
+ case altY:
+ s.killRing = s.killRing.Prev()
+ default:
+ return line, pos, next, nil
+ }
+ }
+ }
+}
+
+// Prompt displays p and returns a line of user input, not including a trailing
+// newline character. An io.EOF error is returned if the user signals end-of-file
+// by pressing Ctrl-D. Prompt allows line editing if the terminal supports it.
+func (s *State) Prompt(prompt string) (string, error) {
+ return s.PromptWithSuggestion(prompt, "", 0)
+}
+
+// PromptWithSuggestion displays prompt and an editable text with cursor at
+// given position. The cursor will be set to the end of the line if given position
+// is negative or greater than length of text. Returns a line of user input, not
+// including a trailing newline character. An io.EOF error is returned if the user
+// signals end-of-file by pressing Ctrl-D.
+func (s *State) PromptWithSuggestion(prompt string, text string, pos int) (string, error) {
+ for _, r := range prompt {
+ if unicode.Is(unicode.C, r) {
+ return "", ErrInvalidPrompt
+ }
+ }
+ if s.inputRedirected || !s.terminalSupported {
+ return s.promptUnsupported(prompt)
+ }
+ p := []rune(prompt)
+ const minWorkingSpace = 10
+ if s.columns < countGlyphs(p)+minWorkingSpace {
+ return s.tooNarrow(prompt)
+ }
+ if s.outputRedirected {
+ return "", ErrNotTerminalOutput
+ }
+
+ s.historyMutex.RLock()
+ defer s.historyMutex.RUnlock()
+
+ fmt.Print(prompt)
+ var line = []rune(text)
+ historyEnd := ""
+ var historyPrefix []string
+ historyPos := 0
+ historyStale := true
+ historyAction := false // used to mark history related actions
+ killAction := 0 // used to mark kill related actions
+
+ defer s.stopPrompt()
+
+ if pos < 0 || len(text) < pos {
+ pos = len(text)
+ }
+ if len(line) > 0 {
+ err := s.refresh(p, line, pos)
+ if err != nil {
+ return "", err
+ }
+ }
+
+restart:
+ s.startPrompt()
+ s.getColumns()
+
+mainLoop:
+ for {
+ next, err := s.readNext()
+ haveNext:
+ if err != nil {
+ if s.shouldRestart != nil && s.shouldRestart(err) {
+ goto restart
+ }
+ return "", err
+ }
+
+ historyAction = false
+ switch v := next.(type) {
+ case rune:
+ switch v {
+ case cr, lf:
+ if s.needRefresh {
+ err := s.refresh(p, line, pos)
+ if err != nil {
+ return "", err
+ }
+ }
+ if s.multiLineMode {
+ s.resetMultiLine(p, line, pos)
+ }
+ fmt.Println()
+ break mainLoop
+ case ctrlA: // Start of line
+ pos = 0
+ s.needRefresh = true
+ case ctrlE: // End of line
+ pos = len(line)
+ s.needRefresh = true
+ case ctrlB: // left
+ if pos > 0 {
+ pos -= len(getSuffixGlyphs(line[:pos], 1))
+ s.needRefresh = true
+ } else {
+ fmt.Print(beep)
+ }
+ case ctrlF: // right
+ if pos < len(line) {
+ pos += len(getPrefixGlyphs(line[pos:], 1))
+ s.needRefresh = true
+ } else {
+ fmt.Print(beep)
+ }
+ case ctrlD: // del
+ if pos == 0 && len(line) == 0 {
+ // exit
+ return "", io.EOF
+ }
+
+ // ctrlD is a potential EOF, so the rune reader shuts down.
+ // Therefore, if it isn't actually an EOF, we must re-startPrompt.
+ s.restartPrompt()
+
+ if pos >= len(line) {
+ fmt.Print(beep)
+ } else {
+ n := len(getPrefixGlyphs(line[pos:], 1))
+ line = append(line[:pos], line[pos+n:]...)
+ s.needRefresh = true
+ }
+ case ctrlK: // delete remainder of line
+ if pos >= len(line) {
+ fmt.Print(beep)
+ } else {
+ if killAction > 0 {
+ s.addToKillRing(line[pos:], 1) // Add in apend mode
+ } else {
+ s.addToKillRing(line[pos:], 0) // Add in normal mode
+ }
+
+ killAction = 2 // Mark that there was a kill action
+ line = line[:pos]
+ s.needRefresh = true
+ }
+ case ctrlP: // up
+ historyAction = true
+ if historyStale {
+ historyPrefix = s.getHistoryByPrefix(string(line))
+ historyPos = len(historyPrefix)
+ historyStale = false
+ }
+ if historyPos > 0 {
+ if historyPos == len(historyPrefix) {
+ historyEnd = string(line)
+ }
+ historyPos--
+ line = []rune(historyPrefix[historyPos])
+ pos = len(line)
+ s.needRefresh = true
+ } else {
+ fmt.Print(beep)
+ }
+ case ctrlN: // down
+ historyAction = true
+ if historyStale {
+ historyPrefix = s.getHistoryByPrefix(string(line))
+ historyPos = len(historyPrefix)
+ historyStale = false
+ }
+ if historyPos < len(historyPrefix) {
+ historyPos++
+ if historyPos == len(historyPrefix) {
+ line = []rune(historyEnd)
+ } else {
+ line = []rune(historyPrefix[historyPos])
+ }
+ pos = len(line)
+ s.needRefresh = true
+ } else {
+ fmt.Print(beep)
+ }
+ case ctrlT: // transpose prev glyph with glyph under cursor
+ if len(line) < 2 || pos < 1 {
+ fmt.Print(beep)
+ } else {
+ if pos == len(line) {
+ pos -= len(getSuffixGlyphs(line, 1))
+ }
+ prev := getSuffixGlyphs(line[:pos], 1)
+ next := getPrefixGlyphs(line[pos:], 1)
+ scratch := make([]rune, len(prev))
+ copy(scratch, prev)
+ copy(line[pos-len(prev):], next)
+ copy(line[pos-len(prev)+len(next):], scratch)
+ pos += len(next)
+ s.needRefresh = true
+ }
+ case ctrlL: // clear screen
+ s.eraseScreen()
+ s.needRefresh = true
+ case ctrlC: // reset
+ fmt.Println("^C")
+ if s.multiLineMode {
+ s.resetMultiLine(p, line, pos)
+ }
+ if s.ctrlCAborts {
+ return "", ErrPromptAborted
+ }
+ line = line[:0]
+ pos = 0
+ fmt.Print(prompt)
+ s.restartPrompt()
+ case ctrlH, bs: // Backspace
+ if pos <= 0 {
+ fmt.Print(beep)
+ } else {
+ n := len(getSuffixGlyphs(line[:pos], 1))
+ line = append(line[:pos-n], line[pos:]...)
+ pos -= n
+ s.needRefresh = true
+ }
+ case ctrlU: // Erase line before cursor
+ if killAction > 0 {
+ s.addToKillRing(line[:pos], 2) // Add in prepend mode
+ } else {
+ s.addToKillRing(line[:pos], 0) // Add in normal mode
+ }
+
+ killAction = 2 // Mark that there was some killing
+ line = line[pos:]
+ pos = 0
+ s.needRefresh = true
+ case ctrlW: // Erase word
+ if pos == 0 {
+ fmt.Print(beep)
+ break
+ }
+ // Remove whitespace to the left
+ var buf []rune // Store the deleted chars in a buffer
+ for {
+ if pos == 0 || !unicode.IsSpace(line[pos-1]) {
+ break
+ }
+ buf = append(buf, line[pos-1])
+ line = append(line[:pos-1], line[pos:]...)
+ pos--
+ }
+ // Remove non-whitespace to the left
+ for {
+ if pos == 0 || unicode.IsSpace(line[pos-1]) {
+ break
+ }
+ buf = append(buf, line[pos-1])
+ line = append(line[:pos-1], line[pos:]...)
+ pos--
+ }
+ // Invert the buffer and save the result on the killRing
+ var newBuf []rune
+ for i := len(buf) - 1; i >= 0; i-- {
+ newBuf = append(newBuf, buf[i])
+ }
+ if killAction > 0 {
+ s.addToKillRing(newBuf, 2) // Add in prepend mode
+ } else {
+ s.addToKillRing(newBuf, 0) // Add in normal mode
+ }
+ killAction = 2 // Mark that there was some killing
+
+ s.needRefresh = true
+ case ctrlY: // Paste from Yank buffer
+ line, pos, next, err = s.yank(p, line, pos)
+ goto haveNext
+ case ctrlR: // Reverse Search
+ line, pos, next, err = s.reverseISearch(line, pos)
+ s.needRefresh = true
+ goto haveNext
+ case tab: // Tab completion
+ line, pos, next, err = s.tabComplete(p, line, pos)
+ goto haveNext
+ // Catch keys that do nothing, but you don't want them to beep
+ case esc:
+ // DO NOTHING
+ // Unused keys
+ case ctrlG, ctrlO, ctrlQ, ctrlS, ctrlV, ctrlX, ctrlZ:
+ fallthrough
+ // Catch unhandled control codes (anything <= 31)
+ case 0, 28, 29, 30, 31:
+ fmt.Print(beep)
+ default:
+ if pos == len(line) && !s.multiLineMode &&
+ len(p)+len(line) < s.columns*4 && // Avoid countGlyphs on large lines
+ countGlyphs(p)+countGlyphs(line) < s.columns-1 {
+ line = append(line, v)
+ fmt.Printf("%c", v)
+ pos++
+ } else {
+ line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
+ pos++
+ s.needRefresh = true
+ }
+ }
+ case action:
+ switch v {
+ case del:
+ if pos >= len(line) {
+ fmt.Print(beep)
+ } else {
+ n := len(getPrefixGlyphs(line[pos:], 1))
+ line = append(line[:pos], line[pos+n:]...)
+ }
+ case left:
+ if pos > 0 {
+ pos -= len(getSuffixGlyphs(line[:pos], 1))
+ } else {
+ fmt.Print(beep)
+ }
+ case wordLeft, altB:
+ if pos > 0 {
+ var spaceHere, spaceLeft, leftKnown bool
+ for {
+ pos--
+ if pos == 0 {
+ break
+ }
+ if leftKnown {
+ spaceHere = spaceLeft
+ } else {
+ spaceHere = unicode.IsSpace(line[pos])
+ }
+ spaceLeft, leftKnown = unicode.IsSpace(line[pos-1]), true
+ if !spaceHere && spaceLeft {
+ break
+ }
+ }
+ } else {
+ fmt.Print(beep)
+ }
+ case right:
+ if pos < len(line) {
+ pos += len(getPrefixGlyphs(line[pos:], 1))
+ } else {
+ fmt.Print(beep)
+ }
+ case wordRight, altF:
+ if pos < len(line) {
+ var spaceHere, spaceLeft, hereKnown bool
+ for {
+ pos++
+ if pos == len(line) {
+ break
+ }
+ if hereKnown {
+ spaceLeft = spaceHere
+ } else {
+ spaceLeft = unicode.IsSpace(line[pos-1])
+ }
+ spaceHere, hereKnown = unicode.IsSpace(line[pos]), true
+ if spaceHere && !spaceLeft {
+ break
+ }
+ }
+ } else {
+ fmt.Print(beep)
+ }
+ case up:
+ historyAction = true
+ if historyStale {
+ historyPrefix = s.getHistoryByPrefix(string(line))
+ historyPos = len(historyPrefix)
+ historyStale = false
+ }
+ if historyPos > 0 {
+ if historyPos == len(historyPrefix) {
+ historyEnd = string(line)
+ }
+ historyPos--
+ line = []rune(historyPrefix[historyPos])
+ pos = len(line)
+ } else {
+ fmt.Print(beep)
+ }
+ case down:
+ historyAction = true
+ if historyStale {
+ historyPrefix = s.getHistoryByPrefix(string(line))
+ historyPos = len(historyPrefix)
+ historyStale = false
+ }
+ if historyPos < len(historyPrefix) {
+ historyPos++
+ if historyPos == len(historyPrefix) {
+ line = []rune(historyEnd)
+ } else {
+ line = []rune(historyPrefix[historyPos])
+ }
+ pos = len(line)
+ } else {
+ fmt.Print(beep)
+ }
+ case home: // Start of line
+ pos = 0
+ case end: // End of line
+ pos = len(line)
+ case winch: // Window change
+ if s.multiLineMode {
+ if s.maxRows-s.cursorRows > 0 {
+ s.moveDown(s.maxRows - s.cursorRows)
+ }
+ for i := 0; i < s.maxRows-1; i++ {
+ s.cursorPos(0)
+ s.eraseLine()
+ s.moveUp(1)
+ }
+ s.maxRows = 1
+ s.cursorRows = 1
+ }
+ }
+ s.needRefresh = true
+ }
+ if s.needRefresh && !s.inputWaiting() {
+ err := s.refresh(p, line, pos)
+ if err != nil {
+ return "", err
+ }
+ }
+ if !historyAction {
+ historyStale = true
+ }
+ if killAction > 0 {
+ killAction--
+ }
+ }
+ return string(line), nil
+}
+
+// PasswordPrompt displays p, and then waits for user input. The input typed by
+// the user is not displayed in the terminal.
+func (s *State) PasswordPrompt(prompt string) (string, error) {
+ for _, r := range prompt {
+ if unicode.Is(unicode.C, r) {
+ return "", ErrInvalidPrompt
+ }
+ }
+ if !s.terminalSupported || s.columns == 0 {
+ return "", errors.New("liner: function not supported in this terminal")
+ }
+ if s.inputRedirected {
+ return s.promptUnsupported(prompt)
+ }
+ if s.outputRedirected {
+ return "", ErrNotTerminalOutput
+ }
+
+ p := []rune(prompt)
+ const minWorkingSpace = 1
+ if s.columns < countGlyphs(p)+minWorkingSpace {
+ return s.tooNarrow(prompt)
+ }
+
+ defer s.stopPrompt()
+
+restart:
+ s.startPrompt()
+ s.getColumns()
+
+ fmt.Print(prompt)
+ var line []rune
+ pos := 0
+
+mainLoop:
+ for {
+ next, err := s.readNext()
+ if err != nil {
+ if s.shouldRestart != nil && s.shouldRestart(err) {
+ goto restart
+ }
+ return "", err
+ }
+
+ switch v := next.(type) {
+ case rune:
+ switch v {
+ case cr, lf:
+ if s.needRefresh {
+ err := s.refresh(p, line, pos)
+ if err != nil {
+ return "", err
+ }
+ }
+ if s.multiLineMode {
+ s.resetMultiLine(p, line, pos)
+ }
+ fmt.Println()
+ break mainLoop
+ case ctrlD: // del
+ if pos == 0 && len(line) == 0 {
+ // exit
+ return "", io.EOF
+ }
+
+ // ctrlD is a potential EOF, so the rune reader shuts down.
+ // Therefore, if it isn't actually an EOF, we must re-startPrompt.
+ s.restartPrompt()
+ case ctrlL: // clear screen
+ s.eraseScreen()
+ err := s.refresh(p, []rune{}, 0)
+ if err != nil {
+ return "", err
+ }
+ case ctrlH, bs: // Backspace
+ if pos <= 0 {
+ fmt.Print(beep)
+ } else {
+ n := len(getSuffixGlyphs(line[:pos], 1))
+ line = append(line[:pos-n], line[pos:]...)
+ pos -= n
+ }
+ case ctrlC:
+ fmt.Println("^C")
+ if s.multiLineMode {
+ s.resetMultiLine(p, line, pos)
+ }
+ if s.ctrlCAborts {
+ return "", ErrPromptAborted
+ }
+ line = line[:0]
+ pos = 0
+ fmt.Print(prompt)
+ s.restartPrompt()
+ // Unused keys
+ case esc, tab, ctrlA, ctrlB, ctrlE, ctrlF, ctrlG, ctrlK, ctrlN, ctrlO, ctrlP, ctrlQ, ctrlR, ctrlS,
+ ctrlT, ctrlU, ctrlV, ctrlW, ctrlX, ctrlY, ctrlZ:
+ fallthrough
+ // Catch unhandled control codes (anything <= 31)
+ case 0, 28, 29, 30, 31:
+ fmt.Print(beep)
+ default:
+ line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
+ pos++
+ }
+ }
+ }
+ return string(line), nil
+}
+
+func (s *State) tooNarrow(prompt string) (string, error) {
+ // Docker and OpenWRT and etc sometimes return 0 column width
+ // Reset mode temporarily. Restore baked mode in case the terminal
+ // is wide enough for the next Prompt attempt.
+ m, merr := TerminalMode()
+ s.origMode.ApplyMode()
+ if merr == nil {
+ defer m.ApplyMode()
+ }
+ if s.r == nil {
+ // Windows does not always set s.r
+ s.r = bufio.NewReader(os.Stdin)
+ defer func() { s.r = nil }()
+ }
+ return s.promptUnsupported(prompt)
+}
diff --git a/vendor/github.com/peterh/liner/output.go b/vendor/github.com/peterh/liner/output.go
new file mode 100644
index 00000000..6d83d4eb
--- /dev/null
+++ b/vendor/github.com/peterh/liner/output.go
@@ -0,0 +1,79 @@
+// +build linux darwin openbsd freebsd netbsd
+
+package liner
+
+import (
+ "fmt"
+ "os"
+ "strings"
+ "syscall"
+ "unsafe"
+)
+
+func (s *State) cursorPos(x int) {
+ if s.useCHA {
+ // 'G' is "Cursor Character Absolute (CHA)"
+ fmt.Printf("\x1b[%dG", x+1)
+ } else {
+ // 'C' is "Cursor Forward (CUF)"
+ fmt.Print("\r")
+ if x > 0 {
+ fmt.Printf("\x1b[%dC", x)
+ }
+ }
+}
+
+func (s *State) eraseLine() {
+ fmt.Print("\x1b[0K")
+}
+
+func (s *State) eraseScreen() {
+ fmt.Print("\x1b[H\x1b[2J")
+}
+
+func (s *State) moveUp(lines int) {
+ fmt.Printf("\x1b[%dA", lines)
+}
+
+func (s *State) moveDown(lines int) {
+ fmt.Printf("\x1b[%dB", lines)
+}
+
+func (s *State) emitNewLine() {
+ fmt.Print("\n")
+}
+
+type winSize struct {
+ row, col uint16
+ xpixel, ypixel uint16
+}
+
+func (s *State) getColumns() bool {
+ var ws winSize
+ ok, _, _ := syscall.Syscall(syscall.SYS_IOCTL, uintptr(syscall.Stdout),
+ syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&ws)))
+ if int(ok) < 0 {
+ return false
+ }
+ s.columns = int(ws.col)
+ if cursorColumn && s.columns > 1 {
+ s.columns--
+ }
+ return true
+}
+
+func (s *State) checkOutput() {
+ // xterm is known to support CHA
+ if strings.Contains(strings.ToLower(os.Getenv("TERM")), "xterm") {
+ s.useCHA = true
+ return
+ }
+
+ // The test for functional ANSI CHA is unreliable (eg the Windows
+ // telnet command does not support reading the cursor position with
+ // an ANSI DSR request, despite setting TERM=ansi)
+
+ // Assume CHA isn't supported (which should be safe, although it
+ // does result in occasional visible cursor jitter)
+ s.useCHA = false
+}
diff --git a/vendor/github.com/peterh/liner/output_windows.go b/vendor/github.com/peterh/liner/output_windows.go
new file mode 100644
index 00000000..63c9c5d7
--- /dev/null
+++ b/vendor/github.com/peterh/liner/output_windows.go
@@ -0,0 +1,76 @@
+package liner
+
+import (
+ "unsafe"
+)
+
+type coord struct {
+ x, y int16
+}
+type smallRect struct {
+ left, top, right, bottom int16
+}
+
+type consoleScreenBufferInfo struct {
+ dwSize coord
+ dwCursorPosition coord
+ wAttributes int16
+ srWindow smallRect
+ dwMaximumWindowSize coord
+}
+
+func (s *State) cursorPos(x int) {
+ var sbi consoleScreenBufferInfo
+ procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi)))
+ procSetConsoleCursorPosition.Call(uintptr(s.hOut),
+ uintptr(int(x)&0xFFFF|int(sbi.dwCursorPosition.y)<<16))
+}
+
+func (s *State) eraseLine() {
+ var sbi consoleScreenBufferInfo
+ procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi)))
+ var numWritten uint32
+ procFillConsoleOutputCharacter.Call(uintptr(s.hOut), uintptr(' '),
+ uintptr(sbi.dwSize.x-sbi.dwCursorPosition.x),
+ uintptr(int(sbi.dwCursorPosition.x)&0xFFFF|int(sbi.dwCursorPosition.y)<<16),
+ uintptr(unsafe.Pointer(&numWritten)))
+}
+
+func (s *State) eraseScreen() {
+ var sbi consoleScreenBufferInfo
+ procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi)))
+ var numWritten uint32
+ procFillConsoleOutputCharacter.Call(uintptr(s.hOut), uintptr(' '),
+ uintptr(sbi.dwSize.x)*uintptr(sbi.dwSize.y),
+ 0,
+ uintptr(unsafe.Pointer(&numWritten)))
+ procSetConsoleCursorPosition.Call(uintptr(s.hOut), 0)
+}
+
+func (s *State) moveUp(lines int) {
+ var sbi consoleScreenBufferInfo
+ procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi)))
+ procSetConsoleCursorPosition.Call(uintptr(s.hOut),
+ uintptr(int(sbi.dwCursorPosition.x)&0xFFFF|(int(sbi.dwCursorPosition.y)-lines)<<16))
+}
+
+func (s *State) moveDown(lines int) {
+ var sbi consoleScreenBufferInfo
+ procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi)))
+ procSetConsoleCursorPosition.Call(uintptr(s.hOut),
+ uintptr(int(sbi.dwCursorPosition.x)&0xFFFF|(int(sbi.dwCursorPosition.y)+lines)<<16))
+}
+
+func (s *State) emitNewLine() {
+ // windows doesn't need to omit a new line
+}
+
+func (s *State) getColumns() {
+ var sbi consoleScreenBufferInfo
+ procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi)))
+ s.columns = int(sbi.dwSize.x)
+ if s.columns > 1 {
+ // Windows 10 needs a spare column for the cursor
+ s.columns--
+ }
+}
diff --git a/vendor/github.com/peterh/liner/unixmode.go b/vendor/github.com/peterh/liner/unixmode.go
new file mode 100644
index 00000000..9838923f
--- /dev/null
+++ b/vendor/github.com/peterh/liner/unixmode.go
@@ -0,0 +1,37 @@
+// +build linux darwin freebsd openbsd netbsd
+
+package liner
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func (mode *termios) ApplyMode() error {
+ _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(syscall.Stdin), setTermios, uintptr(unsafe.Pointer(mode)))
+
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
+
+// TerminalMode returns the current terminal input mode as an InputModeSetter.
+//
+// This function is provided for convenience, and should
+// not be necessary for most users of liner.
+func TerminalMode() (ModeApplier, error) {
+ mode, errno := getMode(syscall.Stdin)
+
+ if errno != 0 {
+ return nil, errno
+ }
+ return mode, nil
+}
+
+func getMode(handle int) (*termios, syscall.Errno) {
+ var mode termios
+ _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(handle), getTermios, uintptr(unsafe.Pointer(&mode)))
+
+ return &mode, errno
+}
diff --git a/vendor/github.com/peterh/liner/width.go b/vendor/github.com/peterh/liner/width.go
new file mode 100644
index 00000000..42e89998
--- /dev/null
+++ b/vendor/github.com/peterh/liner/width.go
@@ -0,0 +1,99 @@
+package liner
+
+import "unicode"
+
+// These character classes are mostly zero width (when combined).
+// A few might not be, depending on the user's font. Fixing this
+// is non-trivial, given that some terminals don't support
+// ANSI DSR/CPR
+var zeroWidth = []*unicode.RangeTable{
+ unicode.Mn,
+ unicode.Me,
+ unicode.Cc,
+ unicode.Cf,
+}
+
+var doubleWidth = []*unicode.RangeTable{
+ unicode.Han,
+ unicode.Hangul,
+ unicode.Hiragana,
+ unicode.Katakana,
+}
+
+// countGlyphs considers zero-width characters to be zero glyphs wide,
+// and members of Chinese, Japanese, and Korean scripts to be 2 glyphs wide.
+func countGlyphs(s []rune) int {
+ n := 0
+ for _, r := range s {
+ // speed up the common case
+ if r < 127 {
+ n++
+ continue
+ }
+
+ switch {
+ case unicode.IsOneOf(zeroWidth, r):
+ case unicode.IsOneOf(doubleWidth, r):
+ n += 2
+ default:
+ n++
+ }
+ }
+ return n
+}
+
+func countMultiLineGlyphs(s []rune, columns int, start int) int {
+ n := start
+ for _, r := range s {
+ if r < 127 {
+ n++
+ continue
+ }
+ switch {
+ case unicode.IsOneOf(zeroWidth, r):
+ case unicode.IsOneOf(doubleWidth, r):
+ n += 2
+ // no room for a 2-glyphs-wide char in the ending
+ // so skip a column and display it at the beginning
+ if n%columns == 1 {
+ n++
+ }
+ default:
+ n++
+ }
+ }
+ return n
+}
+
+func getPrefixGlyphs(s []rune, num int) []rune {
+ p := 0
+ for n := 0; n < num && p < len(s); p++ {
+ // speed up the common case
+ if s[p] < 127 {
+ n++
+ continue
+ }
+ if !unicode.IsOneOf(zeroWidth, s[p]) {
+ n++
+ }
+ }
+ for p < len(s) && unicode.IsOneOf(zeroWidth, s[p]) {
+ p++
+ }
+ return s[:p]
+}
+
+func getSuffixGlyphs(s []rune, num int) []rune {
+ p := len(s)
+ for n := 0; n < num && p > 0; p-- {
+ // speed up the common case
+ if s[p-1] < 127 {
+ n++
+ continue
+ }
+ if !unicode.IsOneOf(zeroWidth, s[p-1]) {
+ n++
+ }
+ }
+ return s[p:]
+}
diff --git a/vendor/github.com/pierrec/lz4/.gitignore b/vendor/github.com/pierrec/lz4/.gitignore
new file mode 100644
index 00000000..c2bb6e4a
--- /dev/null
+++ b/vendor/github.com/pierrec/lz4/.gitignore
@@ -0,0 +1,31 @@
+# Created by https://www.gitignore.io/api/macos
+
+### macOS ###
+*.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# End of https://www.gitignore.io/api/macos
diff --git a/vendor/github.com/pierrec/lz4/.travis.yml b/vendor/github.com/pierrec/lz4/.travis.yml
new file mode 100644
index 00000000..78be21cc
--- /dev/null
+++ b/vendor/github.com/pierrec/lz4/.travis.yml
@@ -0,0 +1,8 @@
+language: go
+
+go:
+ - 1.x
+
+script:
+ - go test -v -cpu=2
+ - go test -v -cpu=2 -race
\ No newline at end of file
diff --git a/vendor/github.com/pierrec/lz4/LICENSE b/vendor/github.com/pierrec/lz4/LICENSE
new file mode 100644
index 00000000..bd899d83
--- /dev/null
+++ b/vendor/github.com/pierrec/lz4/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2015, Pierre Curto
+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 xxHash 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+
diff --git a/vendor/github.com/pierrec/lz4/README.md b/vendor/github.com/pierrec/lz4/README.md
new file mode 100644
index 00000000..dd3c9d47
--- /dev/null
+++ b/vendor/github.com/pierrec/lz4/README.md
@@ -0,0 +1,31 @@
+[](https://godoc.org/github.com/pierrec/lz4)
+[](https://travis-ci.org/pierrec/lz4)
+
+# lz4
+LZ4 compression and decompression in pure Go
+
+## Usage
+
+```go
+import "github.com/pierrec/lz4"
+```
+
+## Description
+
+Package lz4 implements reading and writing lz4 compressed data (a frame),
+as specified in http://fastcompression.blogspot.fr/2013/04/lz4-streaming-format-final.html,
+using an io.Reader (decompression) and io.Writer (compression).
+It is designed to minimize memory usage while maximizing throughput by being able to
+[de]compress data concurrently.
+
+The Reader and the Writer support concurrent processing provided the supplied buffers are
+large enough (in multiples of BlockMaxSize) and there is no block dependency.
+Reader.WriteTo and Writer.ReadFrom do leverage the concurrency transparently.
+The runtime.GOMAXPROCS() value is used to apply concurrency or not.
+
+Although the block level compression and decompression functions are exposed and are fully compatible
+with the lz4 block format definition, they are low level and should not be used directly.
+For a complete description of an lz4 compressed block, see:
+http://fastcompression.blogspot.fr/2011/05/lz4-explained.html
+
+See https://github.com/Cyan4973/lz4 for the reference C implementation.
diff --git a/vendor/github.com/pierrec/lz4/block.go b/vendor/github.com/pierrec/lz4/block.go
new file mode 100644
index 00000000..145eec27
--- /dev/null
+++ b/vendor/github.com/pierrec/lz4/block.go
@@ -0,0 +1,445 @@
+package lz4
+
+import (
+ "encoding/binary"
+ "errors"
+)
+
+// block represents a frame data block.
+// Used when compressing or decompressing frame blocks concurrently.
+type block struct {
+ compressed bool
+ zdata []byte // compressed data
+ data []byte // decompressed data
+ offset int // offset within the data as with block dependency the 64Kb window is prepended to it
+ checksum uint32 // compressed data checksum
+ err error // error while [de]compressing
+}
+
+var (
+ // ErrInvalidSource is returned by UncompressBlock when a compressed block is corrupted.
+ ErrInvalidSource = errors.New("lz4: invalid source")
+ // ErrShortBuffer is returned by UncompressBlock, CompressBlock or CompressBlockHC when
+ // the supplied buffer for [de]compression is too small.
+ ErrShortBuffer = errors.New("lz4: short buffer")
+)
+
+// CompressBlockBound returns the maximum size of a given buffer of size n, when not compressible.
+func CompressBlockBound(n int) int {
+ return n + n/255 + 16
+}
+
+// UncompressBlock decompresses the source buffer into the destination one,
+// starting at the di index and returning the decompressed size.
+//
+// The destination buffer must be sized appropriately.
+//
+// An error is returned if the source data is invalid or the destination buffer is too small.
+func UncompressBlock(src, dst []byte, di int) (int, error) {
+ si, sn, di0 := 0, len(src), di
+ if sn == 0 {
+ return 0, nil
+ }
+
+ for {
+ // literals and match lengths (token)
+ lLen := int(src[si] >> 4)
+ mLen := int(src[si] & 0xF)
+ if si++; si == sn {
+ return di, ErrInvalidSource
+ }
+
+ // literals
+ if lLen > 0 {
+ if lLen == 0xF {
+ for src[si] == 0xFF {
+ lLen += 0xFF
+ if si++; si == sn {
+ return di - di0, ErrInvalidSource
+ }
+ }
+ lLen += int(src[si])
+ if si++; si == sn {
+ return di - di0, ErrInvalidSource
+ }
+ }
+ if len(dst)-di < lLen || si+lLen > sn {
+ return di - di0, ErrShortBuffer
+ }
+ di += copy(dst[di:], src[si:si+lLen])
+
+ if si += lLen; si >= sn {
+ return di - di0, nil
+ }
+ }
+
+ if si += 2; si >= sn {
+ return di, ErrInvalidSource
+ }
+ offset := int(src[si-2]) | int(src[si-1])<<8
+ if di-offset < 0 || offset == 0 {
+ return di - di0, ErrInvalidSource
+ }
+
+ // match
+ if mLen == 0xF {
+ for src[si] == 0xFF {
+ mLen += 0xFF
+ if si++; si == sn {
+ return di - di0, ErrInvalidSource
+ }
+ }
+ mLen += int(src[si])
+ if si++; si == sn {
+ return di - di0, ErrInvalidSource
+ }
+ }
+ // minimum match length is 4
+ mLen += 4
+ if len(dst)-di <= mLen {
+ return di - di0, ErrShortBuffer
+ }
+
+ // copy the match (NB. match is at least 4 bytes long)
+ // NB. past di, copy() would write old bytes instead of
+ // the ones we just copied, so split the work into the largest chunk.
+ for ; mLen >= offset; mLen -= offset {
+ di += copy(dst[di:], dst[di-offset:di])
+ }
+ di += copy(dst[di:], dst[di-offset:di-offset+mLen])
+ }
+}
+
+// CompressBlock compresses the source buffer starting at soffet into the destination one.
+// This is the fast version of LZ4 compression and also the default one.
+//
+// The size of the compressed data is returned. If it is 0 and no error, then the data is incompressible.
+//
+// An error is returned if the destination buffer is too small.
+func CompressBlock(src, dst []byte, soffset int) (int, error) {
+ sn, dn := len(src)-mfLimit, len(dst)
+ if sn <= 0 || dn == 0 || soffset >= sn {
+ return 0, nil
+ }
+ var si, di int
+
+ // fast scan strategy:
+ // we only need a hash table to store the last sequences (4 bytes)
+ var hashTable [1 << hashLog]int
+ var hashShift = uint((minMatch * 8) - hashLog)
+
+ // Initialise the hash table with the first 64Kb of the input buffer
+ // (used when compressing dependent blocks)
+ for si < soffset {
+ h := binary.LittleEndian.Uint32(src[si:]) * hasher >> hashShift
+ si++
+ hashTable[h] = si
+ }
+
+ anchor := si
+ fma := 1 << skipStrength
+ for si < sn-minMatch {
+ // hash the next 4 bytes (sequence)...
+ h := binary.LittleEndian.Uint32(src[si:]) * hasher >> hashShift
+ // -1 to separate existing entries from new ones
+ ref := hashTable[h] - 1
+ // ...and store the position of the hash in the hash table (+1 to compensate the -1 upon saving)
+ hashTable[h] = si + 1
+ // no need to check the last 3 bytes in the first literal 4 bytes as
+ // this guarantees that the next match, if any, is compressed with
+ // a lower size, since to have some compression we must have:
+ // ll+ml-overlap > 1 + (ll-15)/255 + (ml-4-15)/255 + 2 (uncompressed size>compressed size)
+ // => ll+ml>3+2*overlap => ll+ml>= 4+2*overlap
+ // and by definition we do have:
+ // ll >= 1, ml >= 4
+ // => ll+ml >= 5
+ // => so overlap must be 0
+
+ // the sequence is new, out of bound (64kb) or not valid: try next sequence
+ if ref < 0 || fma&(1<>winSizeLog > 0 ||
+ src[ref] != src[si] ||
+ src[ref+1] != src[si+1] ||
+ src[ref+2] != src[si+2] ||
+ src[ref+3] != src[si+3] {
+ // variable step: improves performance on non-compressible data
+ si += fma >> skipStrength
+ fma++
+ continue
+ }
+ // match found
+ fma = 1 << skipStrength
+ lLen := si - anchor
+ offset := si - ref
+
+ // encode match length part 1
+ si += minMatch
+ mLen := si // match length has minMatch already
+ for si <= sn && src[si] == src[si-offset] {
+ si++
+ }
+ mLen = si - mLen
+ if mLen < 0xF {
+ dst[di] = byte(mLen)
+ } else {
+ dst[di] = 0xF
+ }
+
+ // encode literals length
+ if lLen < 0xF {
+ dst[di] |= byte(lLen << 4)
+ } else {
+ dst[di] |= 0xF0
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+ l := lLen - 0xF
+ for ; l >= 0xFF; l -= 0xFF {
+ dst[di] = 0xFF
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+ }
+ dst[di] = byte(l)
+ }
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+
+ // literals
+ if di+lLen >= dn {
+ return di, ErrShortBuffer
+ }
+ di += copy(dst[di:], src[anchor:anchor+lLen])
+ anchor = si
+
+ // encode offset
+ if di += 2; di >= dn {
+ return di, ErrShortBuffer
+ }
+ dst[di-2], dst[di-1] = byte(offset), byte(offset>>8)
+
+ // encode match length part 2
+ if mLen >= 0xF {
+ for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF {
+ dst[di] = 0xFF
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+ }
+ dst[di] = byte(mLen)
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+ }
+ }
+
+ if anchor == 0 {
+ // incompressible
+ return 0, nil
+ }
+
+ // last literals
+ lLen := len(src) - anchor
+ if lLen < 0xF {
+ dst[di] = byte(lLen << 4)
+ } else {
+ dst[di] = 0xF0
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+ lLen -= 0xF
+ for ; lLen >= 0xFF; lLen -= 0xFF {
+ dst[di] = 0xFF
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+ }
+ dst[di] = byte(lLen)
+ }
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+
+ // write literals
+ src = src[anchor:]
+ switch n := di + len(src); {
+ case n > dn:
+ return di, ErrShortBuffer
+ case n >= sn:
+ // incompressible
+ return 0, nil
+ }
+ di += copy(dst[di:], src)
+ return di, nil
+}
+
+// CompressBlockHC compresses the source buffer starting at soffet into the destination one.
+// CompressBlockHC compression ratio is better than CompressBlock but it is also slower.
+//
+// The size of the compressed data is returned. If it is 0 and no error, then the data is not compressible.
+//
+// An error is returned if the destination buffer is too small.
+func CompressBlockHC(src, dst []byte, soffset int) (int, error) {
+ sn, dn := len(src)-mfLimit, len(dst)
+ if sn <= 0 || dn == 0 || soffset >= sn {
+ return 0, nil
+ }
+ var si, di int
+
+ // Hash Chain strategy:
+ // we need a hash table and a chain table
+ // the chain table cannot contain more entries than the window size (64Kb entries)
+ var hashTable [1 << hashLog]int
+ var chainTable [winSize]int
+ var hashShift = uint((minMatch * 8) - hashLog)
+
+ // Initialise the hash table with the first 64Kb of the input buffer
+ // (used when compressing dependent blocks)
+ for si < soffset {
+ h := binary.LittleEndian.Uint32(src[si:]) * hasher >> hashShift
+ chainTable[si&winMask] = hashTable[h]
+ si++
+ hashTable[h] = si
+ }
+
+ anchor := si
+ for si < sn-minMatch {
+ // hash the next 4 bytes (sequence)...
+ h := binary.LittleEndian.Uint32(src[si:]) * hasher >> hashShift
+
+ // follow the chain until out of window and give the longest match
+ mLen := 0
+ offset := 0
+ for next := hashTable[h] - 1; next > 0 && next > si-winSize; next = chainTable[next&winMask] - 1 {
+ // the first (mLen==0) or next byte (mLen>=minMatch) at current match length must match to improve on the match length
+ if src[next+mLen] == src[si+mLen] {
+ for ml := 0; ; ml++ {
+ if src[next+ml] != src[si+ml] || si+ml > sn {
+ // found a longer match, keep its position and length
+ if mLen < ml && ml >= minMatch {
+ mLen = ml
+ offset = si - next
+ }
+ break
+ }
+ }
+ }
+ }
+ chainTable[si&winMask] = hashTable[h]
+ hashTable[h] = si + 1
+
+ // no match found
+ if mLen == 0 {
+ si++
+ continue
+ }
+
+ // match found
+ // update hash/chain tables with overlaping bytes:
+ // si already hashed, add everything from si+1 up to the match length
+ for si, ml := si+1, si+mLen; si < ml; {
+ h := binary.LittleEndian.Uint32(src[si:]) * hasher >> hashShift
+ chainTable[si&winMask] = hashTable[h]
+ si++
+ hashTable[h] = si
+ }
+
+ lLen := si - anchor
+ si += mLen
+ mLen -= minMatch // match length does not include minMatch
+
+ if mLen < 0xF {
+ dst[di] = byte(mLen)
+ } else {
+ dst[di] = 0xF
+ }
+
+ // encode literals length
+ if lLen < 0xF {
+ dst[di] |= byte(lLen << 4)
+ } else {
+ dst[di] |= 0xF0
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+ l := lLen - 0xF
+ for ; l >= 0xFF; l -= 0xFF {
+ dst[di] = 0xFF
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+ }
+ dst[di] = byte(l)
+ }
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+
+ // literals
+ if di+lLen >= dn {
+ return di, ErrShortBuffer
+ }
+ di += copy(dst[di:], src[anchor:anchor+lLen])
+ anchor = si
+
+ // encode offset
+ if di += 2; di >= dn {
+ return di, ErrShortBuffer
+ }
+ dst[di-2], dst[di-1] = byte(offset), byte(offset>>8)
+
+ // encode match length part 2
+ if mLen >= 0xF {
+ for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF {
+ dst[di] = 0xFF
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+ }
+ dst[di] = byte(mLen)
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+ }
+ }
+
+ if anchor == 0 {
+ // incompressible
+ return 0, nil
+ }
+
+ // last literals
+ lLen := len(src) - anchor
+ if lLen < 0xF {
+ dst[di] = byte(lLen << 4)
+ } else {
+ dst[di] = 0xF0
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+ lLen -= 0xF
+ for ; lLen >= 0xFF; lLen -= 0xFF {
+ dst[di] = 0xFF
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+ }
+ dst[di] = byte(lLen)
+ }
+ if di++; di == dn {
+ return di, ErrShortBuffer
+ }
+
+ // write literals
+ src = src[anchor:]
+ switch n := di + len(src); {
+ case n > dn:
+ return di, ErrShortBuffer
+ case n >= sn:
+ // incompressible
+ return 0, nil
+ }
+ di += copy(dst[di:], src)
+ return di, nil
+}
diff --git a/vendor/github.com/pierrec/lz4/lz4.go b/vendor/github.com/pierrec/lz4/lz4.go
new file mode 100644
index 00000000..ddb82f66
--- /dev/null
+++ b/vendor/github.com/pierrec/lz4/lz4.go
@@ -0,0 +1,105 @@
+// Package lz4 implements reading and writing lz4 compressed data (a frame),
+// as specified in http://fastcompression.blogspot.fr/2013/04/lz4-streaming-format-final.html,
+// using an io.Reader (decompression) and io.Writer (compression).
+// It is designed to minimize memory usage while maximizing throughput by being able to
+// [de]compress data concurrently.
+//
+// The Reader and the Writer support concurrent processing provided the supplied buffers are
+// large enough (in multiples of BlockMaxSize) and there is no block dependency.
+// Reader.WriteTo and Writer.ReadFrom do leverage the concurrency transparently.
+// The runtime.GOMAXPROCS() value is used to apply concurrency or not.
+//
+// Although the block level compression and decompression functions are exposed and are fully compatible
+// with the lz4 block format definition, they are low level and should not be used directly.
+// For a complete description of an lz4 compressed block, see:
+// http://fastcompression.blogspot.fr/2011/05/lz4-explained.html
+//
+// See https://github.com/Cyan4973/lz4 for the reference C implementation.
+package lz4
+
+import (
+ "hash"
+ "sync"
+
+ "github.com/pierrec/xxHash/xxHash32"
+)
+
+const (
+ // Extension is the LZ4 frame file name extension
+ Extension = ".lz4"
+ // Version is the LZ4 frame format version
+ Version = 1
+
+ frameMagic = uint32(0x184D2204)
+ frameSkipMagic = uint32(0x184D2A50)
+
+ // The following constants are used to setup the compression algorithm.
+ minMatch = 4 // the minimum size of the match sequence size (4 bytes)
+ winSizeLog = 16 // LZ4 64Kb window size limit
+ winSize = 1 << winSizeLog
+ winMask = winSize - 1 // 64Kb window of previous data for dependent blocks
+
+ // hashLog determines the size of the hash table used to quickly find a previous match position.
+ // Its value influences the compression speed and memory usage, the lower the faster,
+ // but at the expense of the compression ratio.
+ // 16 seems to be the best compromise.
+ hashLog = 16
+ hashTableSize = 1 << hashLog
+ hashShift = uint((minMatch * 8) - hashLog)
+
+ mfLimit = 8 + minMatch // The last match cannot start within the last 12 bytes.
+ skipStrength = 6 // variable step for fast scan
+
+ hasher = uint32(2654435761) // prime number used to hash minMatch
+)
+
+// map the block max size id with its value in bytes: 64Kb, 256Kb, 1Mb and 4Mb.
+var bsMapID = map[byte]int{4: 64 << 10, 5: 256 << 10, 6: 1 << 20, 7: 4 << 20}
+var bsMapValue = map[int]byte{}
+
+// Reversed.
+func init() {
+ for i, v := range bsMapID {
+ bsMapValue[v] = i
+ }
+}
+
+// Header describes the various flags that can be set on a Writer or obtained from a Reader.
+// The default values match those of the LZ4 frame format definition (http://fastcompression.blogspot.com/2013/04/lz4-streaming-format-final.html).
+//
+// NB. in a Reader, in case of concatenated frames, the Header values may change between Read() calls.
+// It is the caller responsibility to check them if necessary (typically when using the Reader concurrency).
+type Header struct {
+ BlockDependency bool // compressed blocks are dependent (one block depends on the last 64Kb of the previous one)
+ BlockChecksum bool // compressed blocks are checksumed
+ NoChecksum bool // frame checksum
+ BlockMaxSize int // the size of the decompressed data block (one of [64KB, 256KB, 1MB, 4MB]). Default=4MB.
+ Size uint64 // the frame total size. It is _not_ computed by the Writer.
+ HighCompression bool // use high compression (only for the Writer)
+ done bool // whether the descriptor was processed (Read or Write and checked)
+ // Removed as not supported
+ // Dict bool // a dictionary id is to be used
+ // DictID uint32 // the dictionary id read from the frame, if any.
+}
+
+// xxhPool wraps the standard pool for xxHash items.
+// Putting items back in the pool automatically resets them.
+type xxhPool struct {
+ sync.Pool
+}
+
+func (p *xxhPool) Get() hash.Hash32 {
+ return p.Pool.Get().(hash.Hash32)
+}
+
+func (p *xxhPool) Put(h hash.Hash32) {
+ h.Reset()
+ p.Pool.Put(h)
+}
+
+// hashPool is used by readers and writers and contains xxHash items.
+var hashPool = xxhPool{
+ Pool: sync.Pool{
+ New: func() interface{} { return xxHash32.New(0) },
+ },
+}
diff --git a/vendor/github.com/pierrec/lz4/reader.go b/vendor/github.com/pierrec/lz4/reader.go
new file mode 100644
index 00000000..9f7fd604
--- /dev/null
+++ b/vendor/github.com/pierrec/lz4/reader.go
@@ -0,0 +1,364 @@
+package lz4
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "io/ioutil"
+ "runtime"
+ "sync"
+ "sync/atomic"
+)
+
+// ErrInvalid is returned when the data being read is not an LZ4 archive
+// (LZ4 magic number detection failed).
+var ErrInvalid = errors.New("invalid lz4 data")
+
+// errEndOfBlock is returned by readBlock when it has reached the last block of the frame.
+// It is not an error.
+var errEndOfBlock = errors.New("end of block")
+
+// Reader implements the LZ4 frame decoder.
+// The Header is set after the first call to Read().
+// The Header may change between Read() calls in case of concatenated frames.
+type Reader struct {
+ Pos int64 // position within the source
+ Header
+ src io.Reader
+ checksum hash.Hash32 // frame hash
+ wg sync.WaitGroup // decompressing go routine wait group
+ data []byte // buffered decompressed data
+ window []byte // 64Kb decompressed data window
+}
+
+// NewReader returns a new LZ4 frame decoder.
+// No access to the underlying io.Reader is performed.
+func NewReader(src io.Reader) *Reader {
+ return &Reader{
+ src: src,
+ checksum: hashPool.Get(),
+ }
+}
+
+// readHeader checks the frame magic number and parses the frame descriptoz.
+// Skippable frames are supported even as a first frame although the LZ4
+// specifications recommends skippable frames not to be used as first frames.
+func (z *Reader) readHeader(first bool) error {
+ defer z.checksum.Reset()
+
+ for {
+ var magic uint32
+ if err := binary.Read(z.src, binary.LittleEndian, &magic); err != nil {
+ if !first && err == io.ErrUnexpectedEOF {
+ return io.EOF
+ }
+ return err
+ }
+ z.Pos += 4
+ if magic>>8 == frameSkipMagic>>8 {
+ var skipSize uint32
+ if err := binary.Read(z.src, binary.LittleEndian, &skipSize); err != nil {
+ return err
+ }
+ z.Pos += 4
+ m, err := io.CopyN(ioutil.Discard, z.src, int64(skipSize))
+ z.Pos += m
+ if err != nil {
+ return err
+ }
+ continue
+ }
+ if magic != frameMagic {
+ return ErrInvalid
+ }
+ break
+ }
+
+ // header
+ var buf [8]byte
+ if _, err := io.ReadFull(z.src, buf[:2]); err != nil {
+ return err
+ }
+ z.Pos += 2
+
+ b := buf[0]
+ if b>>6 != Version {
+ return fmt.Errorf("lz4.Read: invalid version: got %d expected %d", b>>6, Version)
+ }
+ z.BlockDependency = b>>5&1 == 0
+ z.BlockChecksum = b>>4&1 > 0
+ frameSize := b>>3&1 > 0
+ z.NoChecksum = b>>2&1 == 0
+ // z.Dict = b&1 > 0
+
+ bmsID := buf[1] >> 4 & 0x7
+ bSize, ok := bsMapID[bmsID]
+ if !ok {
+ return fmt.Errorf("lz4.Read: invalid block max size: %d", bmsID)
+ }
+ z.BlockMaxSize = bSize
+
+ z.checksum.Write(buf[0:2])
+
+ if frameSize {
+ if err := binary.Read(z.src, binary.LittleEndian, &z.Size); err != nil {
+ return err
+ }
+ z.Pos += 8
+ binary.LittleEndian.PutUint64(buf[:], z.Size)
+ z.checksum.Write(buf[0:8])
+ }
+
+ // if z.Dict {
+ // if err := binary.Read(z.src, binary.LittleEndian, &z.DictID); err != nil {
+ // return err
+ // }
+ // z.Pos += 4
+ // binary.LittleEndian.PutUint32(buf[:], z.DictID)
+ // z.checksum.Write(buf[0:4])
+ // }
+
+ // header checksum
+ if _, err := io.ReadFull(z.src, buf[:1]); err != nil {
+ return err
+ }
+ z.Pos++
+ if h := byte(z.checksum.Sum32() >> 8 & 0xFF); h != buf[0] {
+ return fmt.Errorf("lz4.Read: invalid header checksum: got %v expected %v", buf[0], h)
+ }
+
+ z.Header.done = true
+
+ return nil
+}
+
+// Read decompresses data from the underlying source into the supplied buffer.
+//
+// Since there can be multiple streams concatenated, Header values may
+// change between calls to Read(). If that is the case, no data is actually read from
+// the underlying io.Reader, to allow for potential input buffer resizing.
+//
+// Data is buffered if the input buffer is too small, and exhausted upon successive calls.
+//
+// If the buffer is large enough (typically in multiples of BlockMaxSize) and there is
+// no block dependency, then the data will be decompressed concurrently based on the GOMAXPROCS value.
+func (z *Reader) Read(buf []byte) (n int, err error) {
+ if !z.Header.done {
+ if err = z.readHeader(true); err != nil {
+ return
+ }
+ }
+
+ if len(buf) == 0 {
+ return
+ }
+
+ // exhaust remaining data from previous Read()
+ if len(z.data) > 0 {
+ n = copy(buf, z.data)
+ z.data = z.data[n:]
+ if len(z.data) == 0 {
+ z.data = nil
+ }
+ return
+ }
+
+ // Break up the input buffer into BlockMaxSize blocks with at least one block.
+ // Then decompress into each of them concurrently if possible (no dependency).
+ // In case of dependency, the first block will be missing the window (except on the
+ // very first call), the rest will have it already since it comes from the previous block.
+ wbuf := buf
+ zn := (len(wbuf) + z.BlockMaxSize - 1) / z.BlockMaxSize
+ zblocks := make([]block, zn)
+ for zi, abort := 0, uint32(0); zi < zn && atomic.LoadUint32(&abort) == 0; zi++ {
+ zb := &zblocks[zi]
+ // last block may be too small
+ if len(wbuf) < z.BlockMaxSize+len(z.window) {
+ wbuf = make([]byte, z.BlockMaxSize+len(z.window))
+ }
+ copy(wbuf, z.window)
+ if zb.err = z.readBlock(wbuf, zb); zb.err != nil {
+ break
+ }
+ wbuf = wbuf[z.BlockMaxSize:]
+ if !z.BlockDependency {
+ z.wg.Add(1)
+ go z.decompressBlock(zb, &abort)
+ continue
+ }
+ // cannot decompress concurrently when dealing with block dependency
+ z.decompressBlock(zb, nil)
+ // the last block may not contain enough data
+ if len(z.window) == 0 {
+ z.window = make([]byte, winSize)
+ }
+ if len(zb.data) >= winSize {
+ copy(z.window, zb.data[len(zb.data)-winSize:])
+ } else {
+ copy(z.window, z.window[len(zb.data):])
+ copy(z.window[len(zb.data)+1:], zb.data)
+ }
+ }
+ z.wg.Wait()
+
+ // since a block size may be less then BlockMaxSize, trim the decompressed buffers
+ for _, zb := range zblocks {
+ if zb.err != nil {
+ if zb.err == errEndOfBlock {
+ return n, z.close()
+ }
+ return n, zb.err
+ }
+ bLen := len(zb.data)
+ if !z.NoChecksum {
+ z.checksum.Write(zb.data)
+ }
+ m := copy(buf[n:], zb.data)
+ // buffer the remaining data (this is necessarily the last block)
+ if m < bLen {
+ z.data = zb.data[m:]
+ }
+ n += m
+ }
+
+ return
+}
+
+// readBlock reads an entire frame block from the frame.
+// The input buffer is the one that will receive the decompressed data.
+// If the end of the frame is detected, it returns the errEndOfBlock error.
+func (z *Reader) readBlock(buf []byte, b *block) error {
+ var bLen uint32
+ if err := binary.Read(z.src, binary.LittleEndian, &bLen); err != nil {
+ return err
+ }
+ atomic.AddInt64(&z.Pos, 4)
+
+ switch {
+ case bLen == 0:
+ return errEndOfBlock
+ case bLen&(1<<31) == 0:
+ b.compressed = true
+ b.data = buf
+ b.zdata = make([]byte, bLen)
+ default:
+ bLen = bLen & (1<<31 - 1)
+ if int(bLen) > len(buf) {
+ return fmt.Errorf("lz4.Read: invalid block size: %d", bLen)
+ }
+ b.data = buf[:bLen]
+ b.zdata = buf[:bLen]
+ }
+ if _, err := io.ReadFull(z.src, b.zdata); err != nil {
+ return err
+ }
+
+ if z.BlockChecksum {
+ if err := binary.Read(z.src, binary.LittleEndian, &b.checksum); err != nil {
+ return err
+ }
+ xxh := hashPool.Get()
+ defer hashPool.Put(xxh)
+ xxh.Write(b.zdata)
+ if h := xxh.Sum32(); h != b.checksum {
+ return fmt.Errorf("lz4.Read: invalid block checksum: got %x expected %x", h, b.checksum)
+ }
+ }
+
+ return nil
+}
+
+// decompressBlock decompresses a frame block.
+// In case of an error, the block err is set with it and abort is set to 1.
+func (z *Reader) decompressBlock(b *block, abort *uint32) {
+ if abort != nil {
+ defer z.wg.Done()
+ }
+ if b.compressed {
+ n := len(z.window)
+ m, err := UncompressBlock(b.zdata, b.data, n)
+ if err != nil {
+ if abort != nil {
+ atomic.StoreUint32(abort, 1)
+ }
+ b.err = err
+ return
+ }
+ b.data = b.data[n : n+m]
+ }
+ atomic.AddInt64(&z.Pos, int64(len(b.data)))
+}
+
+// close validates the frame checksum (if any) and checks the next frame (if any).
+func (z *Reader) close() error {
+ if !z.NoChecksum {
+ var checksum uint32
+ if err := binary.Read(z.src, binary.LittleEndian, &checksum); err != nil {
+ return err
+ }
+ if checksum != z.checksum.Sum32() {
+ return fmt.Errorf("lz4.Read: invalid frame checksum: got %x expected %x", z.checksum.Sum32(), checksum)
+ }
+ }
+
+ // get ready for the next concatenated frame, but do not change the position
+ pos := z.Pos
+ z.Reset(z.src)
+ z.Pos = pos
+
+ // since multiple frames can be concatenated, check for another one
+ return z.readHeader(false)
+}
+
+// Reset discards the Reader's state and makes it equivalent to the
+// result of its original state from NewReader, but reading from r instead.
+// This permits reusing a Reader rather than allocating a new one.
+func (z *Reader) Reset(r io.Reader) {
+ z.Header = Header{}
+ z.Pos = 0
+ z.src = r
+ z.checksum.Reset()
+ z.data = nil
+ z.window = nil
+}
+
+// WriteTo decompresses the data from the underlying io.Reader and writes it to the io.Writer.
+// Returns the number of bytes written.
+func (z *Reader) WriteTo(w io.Writer) (n int64, err error) {
+ cpus := runtime.GOMAXPROCS(0)
+ var buf []byte
+
+ // The initial buffer being nil, the first Read will be only read the compressed frame options.
+ // The buffer can then be sized appropriately to support maximum concurrency decompression.
+ // If multiple frames are concatenated, Read() will return with no data decompressed but with
+ // potentially changed options. The buffer will be resized accordingly, always trying to
+ // maximize concurrency.
+ for {
+ nsize := 0
+ // the block max size can change if multiple streams are concatenated.
+ // Check it after every Read().
+ if z.BlockDependency {
+ // in case of dependency, we cannot decompress concurrently,
+ // so allocate the minimum buffer + window size
+ nsize = len(z.window) + z.BlockMaxSize
+ } else {
+ // if no dependency, allocate a buffer large enough for concurrent decompression
+ nsize = cpus * z.BlockMaxSize
+ }
+ if nsize != len(buf) {
+ buf = make([]byte, nsize)
+ }
+
+ m, er := z.Read(buf)
+ if er != nil && er != io.EOF {
+ return n, er
+ }
+ m, err = w.Write(buf[:m])
+ n += int64(m)
+ if err != nil || er == io.EOF {
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/pierrec/lz4/writer.go b/vendor/github.com/pierrec/lz4/writer.go
new file mode 100644
index 00000000..b1b712fe
--- /dev/null
+++ b/vendor/github.com/pierrec/lz4/writer.go
@@ -0,0 +1,377 @@
+package lz4
+
+import (
+ "encoding/binary"
+ "fmt"
+ "hash"
+ "io"
+ "runtime"
+)
+
+// Writer implements the LZ4 frame encoder.
+type Writer struct {
+ Header
+ dst io.Writer
+ checksum hash.Hash32 // frame checksum
+ data []byte // data to be compressed, only used when dealing with block dependency as we need 64Kb to work with
+ window []byte // last 64KB of decompressed data (block dependency) + blockMaxSize buffer
+
+ zbCompressBuf []byte // buffer for compressing lz4 blocks
+ writeSizeBuf []byte // four-byte slice for writing checksums and sizes in writeblock
+}
+
+// NewWriter returns a new LZ4 frame encoder.
+// No access to the underlying io.Writer is performed.
+// The supplied Header is checked at the first Write.
+// It is ok to change it before the first Write but then not until a Reset() is performed.
+func NewWriter(dst io.Writer) *Writer {
+ return &Writer{
+ dst: dst,
+ checksum: hashPool.Get(),
+ Header: Header{
+ BlockMaxSize: 4 << 20,
+ },
+ writeSizeBuf: make([]byte, 4),
+ }
+}
+
+// writeHeader builds and writes the header (magic+header) to the underlying io.Writer.
+func (z *Writer) writeHeader() error {
+ // Default to 4Mb if BlockMaxSize is not set
+ if z.Header.BlockMaxSize == 0 {
+ z.Header.BlockMaxSize = 4 << 20
+ }
+ // the only option that need to be validated
+ bSize, ok := bsMapValue[z.Header.BlockMaxSize]
+ if !ok {
+ return fmt.Errorf("lz4: invalid block max size: %d", z.Header.BlockMaxSize)
+ }
+
+ // magic number(4) + header(flags(2)+[Size(8)+DictID(4)]+checksum(1)) does not exceed 19 bytes
+ // Size and DictID are optional
+ var buf [19]byte
+
+ // set the fixed size data: magic number, block max size and flags
+ binary.LittleEndian.PutUint32(buf[0:], frameMagic)
+ flg := byte(Version << 6)
+ if !z.Header.BlockDependency {
+ flg |= 1 << 5
+ }
+ if z.Header.BlockChecksum {
+ flg |= 1 << 4
+ }
+ if z.Header.Size > 0 {
+ flg |= 1 << 3
+ }
+ if !z.Header.NoChecksum {
+ flg |= 1 << 2
+ }
+ // if z.Header.Dict {
+ // flg |= 1
+ // }
+ buf[4] = flg
+ buf[5] = bSize << 4
+
+ // current buffer size: magic(4) + flags(1) + block max size (1)
+ n := 6
+ // optional items
+ if z.Header.Size > 0 {
+ binary.LittleEndian.PutUint64(buf[n:], z.Header.Size)
+ n += 8
+ }
+ // if z.Header.Dict {
+ // binary.LittleEndian.PutUint32(buf[n:], z.Header.DictID)
+ // n += 4
+ // }
+
+ // header checksum includes the flags, block max size and optional Size and DictID
+ z.checksum.Write(buf[4:n])
+ buf[n] = byte(z.checksum.Sum32() >> 8 & 0xFF)
+ z.checksum.Reset()
+
+ // header ready, write it out
+ if _, err := z.dst.Write(buf[0 : n+1]); err != nil {
+ return err
+ }
+ z.Header.done = true
+
+ // initialize buffers dependent on header info
+ z.zbCompressBuf = make([]byte, winSize+z.BlockMaxSize)
+
+ return nil
+}
+
+// Write compresses data from the supplied buffer into the underlying io.Writer.
+// Write does not return until the data has been written.
+//
+// If the input buffer is large enough (typically in multiples of BlockMaxSize)
+// the data will be compressed concurrently.
+//
+// Write never buffers any data unless in BlockDependency mode where it may
+// do so until it has 64Kb of data, after which it never buffers any.
+func (z *Writer) Write(buf []byte) (n int, err error) {
+ if !z.Header.done {
+ if err = z.writeHeader(); err != nil {
+ return
+ }
+ }
+
+ if len(buf) == 0 {
+ return
+ }
+
+ if !z.NoChecksum {
+ z.checksum.Write(buf)
+ }
+
+ // with block dependency, require at least 64Kb of data to work with
+ // not having 64Kb only matters initially to setup the first window
+ bl := 0
+ if z.BlockDependency && len(z.window) == 0 {
+ bl = len(z.data)
+ z.data = append(z.data, buf...)
+ if len(z.data) < winSize {
+ return len(buf), nil
+ }
+ buf = z.data
+ z.data = nil
+ }
+
+ // Break up the input buffer into BlockMaxSize blocks, provisioning the left over block.
+ // Then compress into each of them concurrently if possible (no dependency).
+ var (
+ zb block
+ wbuf = buf
+ zn = len(wbuf) / z.BlockMaxSize
+ zi = 0
+ leftover = len(buf) % z.BlockMaxSize
+ )
+
+loop:
+ for zi < zn {
+ if z.BlockDependency {
+ if zi == 0 {
+ // first block does not have the window
+ zb.data = append(z.window, wbuf[:z.BlockMaxSize]...)
+ zb.offset = len(z.window)
+ wbuf = wbuf[z.BlockMaxSize-winSize:]
+ } else {
+ // set the uncompressed data including the window from previous block
+ zb.data = wbuf[:z.BlockMaxSize+winSize]
+ zb.offset = winSize
+ wbuf = wbuf[z.BlockMaxSize:]
+ }
+ } else {
+ zb.data = wbuf[:z.BlockMaxSize]
+ wbuf = wbuf[z.BlockMaxSize:]
+ }
+
+ goto write
+ }
+
+ // left over
+ if leftover > 0 {
+ zb = block{data: wbuf}
+ if z.BlockDependency {
+ if zn == 0 {
+ zb.data = append(z.window, zb.data...)
+ zb.offset = len(z.window)
+ } else {
+ zb.offset = winSize
+ }
+ }
+
+ leftover = 0
+ goto write
+ }
+
+ if z.BlockDependency {
+ if len(z.window) == 0 {
+ z.window = make([]byte, winSize)
+ }
+ // last buffer may be shorter than the window
+ if len(buf) >= winSize {
+ copy(z.window, buf[len(buf)-winSize:])
+ } else {
+ copy(z.window, z.window[len(buf):])
+ copy(z.window[len(buf)+1:], buf)
+ }
+ }
+
+ return
+
+write:
+ zb = z.compressBlock(zb)
+ _, err = z.writeBlock(zb)
+
+ written := len(zb.data)
+ if bl > 0 {
+ if written >= bl {
+ written -= bl
+ bl = 0
+ } else {
+ bl -= written
+ written = 0
+ }
+ }
+
+ n += written
+ // remove the window in zb.data
+ if z.BlockDependency {
+ if zi == 0 {
+ n -= len(z.window)
+ } else {
+ n -= winSize
+ }
+ }
+ if err != nil {
+ return
+ }
+ zi++
+ goto loop
+}
+
+// compressBlock compresses a block.
+func (z *Writer) compressBlock(zb block) block {
+ // compressed block size cannot exceed the input's
+ var (
+ n int
+ err error
+ zbuf = z.zbCompressBuf
+ )
+ if z.HighCompression {
+ n, err = CompressBlockHC(zb.data, zbuf, zb.offset)
+ } else {
+ n, err = CompressBlock(zb.data, zbuf, zb.offset)
+ }
+
+ // compressible and compressed size smaller than decompressed: ok!
+ if err == nil && n > 0 && len(zb.zdata) < len(zb.data) {
+ zb.compressed = true
+ zb.zdata = zbuf[:n]
+ } else {
+ zb.compressed = false
+ zb.zdata = zb.data[zb.offset:]
+ }
+
+ if z.BlockChecksum {
+ xxh := hashPool.Get()
+ xxh.Write(zb.zdata)
+ zb.checksum = xxh.Sum32()
+ hashPool.Put(xxh)
+ }
+
+ return zb
+}
+
+// writeBlock writes a frame block to the underlying io.Writer (size, data).
+func (z *Writer) writeBlock(zb block) (int, error) {
+ bLen := uint32(len(zb.zdata))
+ if !zb.compressed {
+ bLen |= 1 << 31
+ }
+
+ n := 0
+
+ binary.LittleEndian.PutUint32(z.writeSizeBuf, bLen)
+ n, err := z.dst.Write(z.writeSizeBuf)
+ if err != nil {
+ return n, err
+ }
+
+ m, err := z.dst.Write(zb.zdata)
+ n += m
+ if err != nil {
+ return n, err
+ }
+
+ if z.BlockChecksum {
+ binary.LittleEndian.PutUint32(z.writeSizeBuf, zb.checksum)
+ m, err := z.dst.Write(z.writeSizeBuf)
+ n += m
+
+ if err != nil {
+ return n, err
+ }
+ }
+
+ return n, nil
+}
+
+// Flush flushes any pending compressed data to the underlying writer.
+// Flush does not return until the data has been written.
+// If the underlying writer returns an error, Flush returns that error.
+//
+// Flush is only required when in BlockDependency mode and the total of
+// data written is less than 64Kb.
+func (z *Writer) Flush() error {
+ if len(z.data) == 0 {
+ return nil
+ }
+
+ zb := z.compressBlock(block{data: z.data})
+ if _, err := z.writeBlock(zb); err != nil {
+ return err
+ }
+ return nil
+}
+
+// Close closes the Writer, flushing any unwritten data to the underlying io.Writer, but does not close the underlying io.Writer.
+func (z *Writer) Close() error {
+ if !z.Header.done {
+ if err := z.writeHeader(); err != nil {
+ return err
+ }
+ }
+
+ // buffered data for the block dependency window
+ if z.BlockDependency && len(z.data) > 0 {
+ zb := block{data: z.data}
+ if _, err := z.writeBlock(z.compressBlock(zb)); err != nil {
+ return err
+ }
+ }
+
+ if err := binary.Write(z.dst, binary.LittleEndian, uint32(0)); err != nil {
+ return err
+ }
+ if !z.NoChecksum {
+ if err := binary.Write(z.dst, binary.LittleEndian, z.checksum.Sum32()); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Reset clears the state of the Writer z such that it is equivalent to its
+// initial state from NewWriter, but instead writing to w.
+// No access to the underlying io.Writer is performed.
+func (z *Writer) Reset(w io.Writer) {
+ z.Header = Header{}
+ z.dst = w
+ z.checksum.Reset()
+ z.data = nil
+ z.window = nil
+}
+
+// ReadFrom compresses the data read from the io.Reader and writes it to the underlying io.Writer.
+// Returns the number of bytes read.
+// It does not close the Writer.
+func (z *Writer) ReadFrom(r io.Reader) (n int64, err error) {
+ cpus := runtime.GOMAXPROCS(0)
+ buf := make([]byte, cpus*z.BlockMaxSize)
+ for {
+ m, er := io.ReadFull(r, buf)
+ n += int64(m)
+ if er == nil || er == io.ErrUnexpectedEOF || er == io.EOF {
+ if _, err = z.Write(buf[:m]); err != nil {
+ return
+ }
+ if er == nil {
+ continue
+ }
+ return
+ }
+ return n, er
+ }
+}
diff --git a/vendor/github.com/pierrec/xxHash/LICENSE b/vendor/github.com/pierrec/xxHash/LICENSE
new file mode 100644
index 00000000..c1418f3f
--- /dev/null
+++ b/vendor/github.com/pierrec/xxHash/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2014, Pierre Curto
+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 xxHash 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+
diff --git a/vendor/github.com/pierrec/xxHash/xxHash32/xxHash32.go b/vendor/github.com/pierrec/xxHash/xxHash32/xxHash32.go
new file mode 100644
index 00000000..411504e4
--- /dev/null
+++ b/vendor/github.com/pierrec/xxHash/xxHash32/xxHash32.go
@@ -0,0 +1,205 @@
+// Package xxHash32 implements the very fast xxHash hashing algorithm (32 bits version).
+// (https://github.com/Cyan4973/xxHash/)
+package xxHash32
+
+import "hash"
+
+const (
+ prime32_1 = 2654435761
+ prime32_2 = 2246822519
+ prime32_3 = 3266489917
+ prime32_4 = 668265263
+ prime32_5 = 374761393
+)
+
+type xxHash struct {
+ seed uint32
+ v1 uint32
+ v2 uint32
+ v3 uint32
+ v4 uint32
+ totalLen uint64
+ buf [16]byte
+ bufused int
+}
+
+// New returns a new Hash32 instance.
+func New(seed uint32) hash.Hash32 {
+ xxh := &xxHash{seed: seed}
+ xxh.Reset()
+ return xxh
+}
+
+// Sum appends the current hash to b and returns the resulting slice.
+// It does not change the underlying hash state.
+func (xxh xxHash) Sum(b []byte) []byte {
+ h32 := xxh.Sum32()
+ return append(b, byte(h32), byte(h32>>8), byte(h32>>16), byte(h32>>24))
+}
+
+// Reset resets the Hash to its initial state.
+func (xxh *xxHash) Reset() {
+ xxh.v1 = xxh.seed + prime32_1 + prime32_2
+ xxh.v2 = xxh.seed + prime32_2
+ xxh.v3 = xxh.seed
+ xxh.v4 = xxh.seed - prime32_1
+ xxh.totalLen = 0
+ xxh.bufused = 0
+}
+
+// Size returns the number of bytes returned by Sum().
+func (xxh *xxHash) Size() int {
+ return 4
+}
+
+// BlockSize gives the minimum number of bytes accepted by Write().
+func (xxh *xxHash) BlockSize() int {
+ return 1
+}
+
+// Write adds input bytes to the Hash.
+// It never returns an error.
+func (xxh *xxHash) Write(input []byte) (int, error) {
+ n := len(input)
+ m := xxh.bufused
+
+ xxh.totalLen += uint64(n)
+
+ r := len(xxh.buf) - m
+ if n < r {
+ copy(xxh.buf[m:], input)
+ xxh.bufused += len(input)
+ return n, nil
+ }
+
+ p := 0
+ if m > 0 {
+ // some data left from previous update
+ copy(xxh.buf[xxh.bufused:], input[:r])
+ xxh.bufused += len(input) - r
+
+ // fast rotl(13)
+ p32 := xxh.v1 + (uint32(xxh.buf[p+3])<<24|uint32(xxh.buf[p+2])<<16|uint32(xxh.buf[p+1])<<8|uint32(xxh.buf[p]))*prime32_2
+ xxh.v1 = (p32<<13 | p32>>19) * prime32_1
+ p += 4
+ p32 = xxh.v2 + (uint32(xxh.buf[p+3])<<24|uint32(xxh.buf[p+2])<<16|uint32(xxh.buf[p+1])<<8|uint32(xxh.buf[p]))*prime32_2
+ xxh.v2 = (p32<<13 | p32>>19) * prime32_1
+ p += 4
+ p32 = xxh.v3 + (uint32(xxh.buf[p+3])<<24|uint32(xxh.buf[p+2])<<16|uint32(xxh.buf[p+1])<<8|uint32(xxh.buf[p]))*prime32_2
+ xxh.v3 = (p32<<13 | p32>>19) * prime32_1
+ p += 4
+ p32 = xxh.v4 + (uint32(xxh.buf[p+3])<<24|uint32(xxh.buf[p+2])<<16|uint32(xxh.buf[p+1])<<8|uint32(xxh.buf[p]))*prime32_2
+ xxh.v4 = (p32<<13 | p32>>19) * prime32_1
+
+ p = r
+ xxh.bufused = 0
+ }
+
+ for n := n - 16; p <= n; {
+ p32 := xxh.v1 + (uint32(input[p+3])<<24|uint32(input[p+2])<<16|uint32(input[p+1])<<8|uint32(input[p]))*prime32_2
+ xxh.v1 = (p32<<13 | p32>>19) * prime32_1
+ p += 4
+ p32 = xxh.v2 + (uint32(input[p+3])<<24|uint32(input[p+2])<<16|uint32(input[p+1])<<8|uint32(input[p]))*prime32_2
+ xxh.v2 = (p32<<13 | p32>>19) * prime32_1
+ p += 4
+ p32 = xxh.v3 + (uint32(input[p+3])<<24|uint32(input[p+2])<<16|uint32(input[p+1])<<8|uint32(input[p]))*prime32_2
+ xxh.v3 = (p32<<13 | p32>>19) * prime32_1
+ p += 4
+ p32 = xxh.v4 + (uint32(input[p+3])<<24|uint32(input[p+2])<<16|uint32(input[p+1])<<8|uint32(input[p]))*prime32_2
+ xxh.v4 = (p32<<13 | p32>>19) * prime32_1
+ p += 4
+ }
+
+ copy(xxh.buf[xxh.bufused:], input[p:])
+ xxh.bufused += len(input) - p
+
+ return n, nil
+}
+
+// Sum32 returns the 32 bits Hash value.
+func (xxh *xxHash) Sum32() uint32 {
+ h32 := uint32(xxh.totalLen)
+ if xxh.totalLen >= 16 {
+ h32 += ((xxh.v1 << 1) | (xxh.v1 >> 31)) +
+ ((xxh.v2 << 7) | (xxh.v2 >> 25)) +
+ ((xxh.v3 << 12) | (xxh.v3 >> 20)) +
+ ((xxh.v4 << 18) | (xxh.v4 >> 14))
+ } else {
+ h32 += xxh.seed + prime32_5
+ }
+
+ p := 0
+ n := xxh.bufused
+ for n := n - 4; p <= n; p += 4 {
+ h32 += (uint32(xxh.buf[p+3])<<24 | uint32(xxh.buf[p+2])<<16 | uint32(xxh.buf[p+1])<<8 | uint32(xxh.buf[p])) * prime32_3
+ h32 = ((h32 << 17) | (h32 >> 15)) * prime32_4
+ }
+ for ; p < n; p++ {
+ h32 += uint32(xxh.buf[p]) * prime32_5
+ h32 = ((h32 << 11) | (h32 >> 21)) * prime32_1
+ }
+
+ h32 ^= h32 >> 15
+ h32 *= prime32_2
+ h32 ^= h32 >> 13
+ h32 *= prime32_3
+ h32 ^= h32 >> 16
+
+ return h32
+}
+
+// Checksum returns the 32bits Hash value.
+func Checksum(input []byte, seed uint32) uint32 {
+ n := len(input)
+ h32 := uint32(n)
+
+ if n < 16 {
+ h32 += seed + prime32_5
+ } else {
+ v1 := seed + prime32_1 + prime32_2
+ v2 := seed + prime32_2
+ v3 := seed
+ v4 := seed - prime32_1
+ p := 0
+ for p <= n-16 {
+ v1 += (uint32(input[p+3])<<24 | uint32(input[p+2])<<16 | uint32(input[p+1])<<8 | uint32(input[p])) * prime32_2
+ v1 = (v1<<13 | v1>>19) * prime32_1
+ p += 4
+ v2 += (uint32(input[p+3])<<24 | uint32(input[p+2])<<16 | uint32(input[p+1])<<8 | uint32(input[p])) * prime32_2
+ v2 = (v2<<13 | v2>>19) * prime32_1
+ p += 4
+ v3 += (uint32(input[p+3])<<24 | uint32(input[p+2])<<16 | uint32(input[p+1])<<8 | uint32(input[p])) * prime32_2
+ v3 = (v3<<13 | v3>>19) * prime32_1
+ p += 4
+ v4 += (uint32(input[p+3])<<24 | uint32(input[p+2])<<16 | uint32(input[p+1])<<8 | uint32(input[p])) * prime32_2
+ v4 = (v4<<13 | v4>>19) * prime32_1
+ p += 4
+ }
+ input = input[p:]
+ n -= p
+ h32 += ((v1 << 1) | (v1 >> 31)) +
+ ((v2 << 7) | (v2 >> 25)) +
+ ((v3 << 12) | (v3 >> 20)) +
+ ((v4 << 18) | (v4 >> 14))
+ }
+
+ p := 0
+ for p <= n-4 {
+ h32 += (uint32(input[p+3])<<24 | uint32(input[p+2])<<16 | uint32(input[p+1])<<8 | uint32(input[p])) * prime32_3
+ h32 = ((h32 << 17) | (h32 >> 15)) * prime32_4
+ p += 4
+ }
+ for p < n {
+ h32 += uint32(input[p]) * prime32_5
+ h32 = ((h32 << 11) | (h32 >> 21)) * prime32_1
+ p++
+ }
+
+ h32 ^= h32 >> 15
+ h32 *= prime32_2
+ h32 ^= h32 >> 13
+ h32 *= prime32_3
+ h32 ^= h32 >> 16
+
+ return h32
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/.gitignore b/vendor/github.com/rcrowley/go-metrics/.gitignore
new file mode 100644
index 00000000..83c8f823
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/.gitignore
@@ -0,0 +1,9 @@
+*.[68]
+*.a
+*.out
+*.swp
+_obj
+_testmain.go
+cmd/metrics-bench/metrics-bench
+cmd/metrics-example/metrics-example
+cmd/never-read/never-read
diff --git a/vendor/github.com/rcrowley/go-metrics/.travis.yml b/vendor/github.com/rcrowley/go-metrics/.travis.yml
new file mode 100644
index 00000000..20aa5d04
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/.travis.yml
@@ -0,0 +1,14 @@
+language: go
+
+go:
+ - 1.2
+ - 1.3
+ - 1.4
+ - 1.5
+
+script:
+ - ./validate.sh
+
+# this should give us faster builds according to
+# http://docs.travis-ci.com/user/migrating-from-legacy/
+sudo: false
diff --git a/vendor/github.com/rcrowley/go-metrics/LICENSE b/vendor/github.com/rcrowley/go-metrics/LICENSE
new file mode 100644
index 00000000..363fa9ee
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/LICENSE
@@ -0,0 +1,29 @@
+Copyright 2012 Richard Crowley. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+THIS SOFTWARE IS PROVIDED BY RICHARD CROWLEY ``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 RICHARD CROWLEY OR CONTRIBUTORS 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.
+
+The views and conclusions contained in the software and documentation
+are those of the authors and should not be interpreted as representing
+official policies, either expressed or implied, of Richard Crowley.
diff --git a/vendor/github.com/rcrowley/go-metrics/README.md b/vendor/github.com/rcrowley/go-metrics/README.md
new file mode 100644
index 00000000..2d1a6dcf
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/README.md
@@ -0,0 +1,153 @@
+go-metrics
+==========
+
+
+
+Go port of Coda Hale's Metrics library: .
+
+Documentation: .
+
+Usage
+-----
+
+Create and update metrics:
+
+```go
+c := metrics.NewCounter()
+metrics.Register("foo", c)
+c.Inc(47)
+
+g := metrics.NewGauge()
+metrics.Register("bar", g)
+g.Update(47)
+
+r := NewRegistry()
+g := metrics.NewRegisteredFunctionalGauge("cache-evictions", r, func() int64 { return cache.getEvictionsCount() })
+
+s := metrics.NewExpDecaySample(1028, 0.015) // or metrics.NewUniformSample(1028)
+h := metrics.NewHistogram(s)
+metrics.Register("baz", h)
+h.Update(47)
+
+m := metrics.NewMeter()
+metrics.Register("quux", m)
+m.Mark(47)
+
+t := metrics.NewTimer()
+metrics.Register("bang", t)
+t.Time(func() {})
+t.Update(47)
+```
+
+Register() is not threadsafe. For threadsafe metric registration use
+GetOrRegister:
+
+```
+t := metrics.GetOrRegisterTimer("account.create.latency", nil)
+t.Time(func() {})
+t.Update(47)
+```
+
+Periodically log every metric in human-readable form to standard error:
+
+```go
+go metrics.Log(metrics.DefaultRegistry, 5 * time.Second, log.New(os.Stderr, "metrics: ", log.Lmicroseconds))
+```
+
+Periodically log every metric in slightly-more-parseable form to syslog:
+
+```go
+w, _ := syslog.Dial("unixgram", "/dev/log", syslog.LOG_INFO, "metrics")
+go metrics.Syslog(metrics.DefaultRegistry, 60e9, w)
+```
+
+Periodically emit every metric to Graphite using the [Graphite client](https://github.com/cyberdelia/go-metrics-graphite):
+
+```go
+
+import "github.com/cyberdelia/go-metrics-graphite"
+
+addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:2003")
+go graphite.Graphite(metrics.DefaultRegistry, 10e9, "metrics", addr)
+```
+
+Periodically emit every metric into InfluxDB:
+
+**NOTE:** this has been pulled out of the library due to constant fluctuations
+in the InfluxDB API. In fact, all client libraries are on their way out. see
+issues [#121](https://github.com/rcrowley/go-metrics/issues/121) and
+[#124](https://github.com/rcrowley/go-metrics/issues/124) for progress and details.
+
+```go
+import "github.com/vrischmann/go-metrics-influxdb"
+
+go influxdb.Influxdb(metrics.DefaultRegistry, 10e9, &influxdb.Config{
+ Host: "127.0.0.1:8086",
+ Database: "metrics",
+ Username: "test",
+ Password: "test",
+})
+```
+
+Periodically upload every metric to Librato using the [Librato client](https://github.com/mihasya/go-metrics-librato):
+
+**Note**: the client included with this repository under the `librato` package
+has been deprecated and moved to the repository linked above.
+
+```go
+import "github.com/mihasya/go-metrics-librato"
+
+go librato.Librato(metrics.DefaultRegistry,
+ 10e9, // interval
+ "example@example.com", // account owner email address
+ "token", // Librato API token
+ "hostname", // source
+ []float64{0.95}, // percentiles to send
+ time.Millisecond, // time unit
+)
+```
+
+Periodically emit every metric to StatHat:
+
+```go
+import "github.com/rcrowley/go-metrics/stathat"
+
+go stathat.Stathat(metrics.DefaultRegistry, 10e9, "example@example.com")
+```
+
+Maintain all metrics along with expvars at `/debug/metrics`:
+
+This uses the same mechanism as [the official expvar](http://golang.org/pkg/expvar/)
+but exposed under `/debug/metrics`, which shows a json representation of all your usual expvars
+as well as all your go-metrics.
+
+
+```go
+import "github.com/rcrowley/go-metrics/exp"
+
+exp.Exp(metrics.DefaultRegistry)
+```
+
+Installation
+------------
+
+```sh
+go get github.com/rcrowley/go-metrics
+```
+
+StatHat support additionally requires their Go client:
+
+```sh
+go get github.com/stathat/go
+```
+
+Publishing Metrics
+------------------
+
+Clients are available for the following destinations:
+
+* Librato - [https://github.com/mihasya/go-metrics-librato](https://github.com/mihasya/go-metrics-librato)
+* Graphite - [https://github.com/cyberdelia/go-metrics-graphite](https://github.com/cyberdelia/go-metrics-graphite)
+* InfluxDB - [https://github.com/vrischmann/go-metrics-influxdb](https://github.com/vrischmann/go-metrics-influxdb)
+* Ganglia - [https://github.com/appscode/metlia](https://github.com/appscode/metlia)
+* Prometheus - [https://github.com/deathowl/go-metrics-prometheus](https://github.com/deathowl/go-metrics-prometheus)
diff --git a/vendor/github.com/rcrowley/go-metrics/counter.go b/vendor/github.com/rcrowley/go-metrics/counter.go
new file mode 100644
index 00000000..bb7b039c
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/counter.go
@@ -0,0 +1,112 @@
+package metrics
+
+import "sync/atomic"
+
+// Counters hold an int64 value that can be incremented and decremented.
+type Counter interface {
+ Clear()
+ Count() int64
+ Dec(int64)
+ Inc(int64)
+ Snapshot() Counter
+}
+
+// GetOrRegisterCounter returns an existing Counter or constructs and registers
+// a new StandardCounter.
+func GetOrRegisterCounter(name string, r Registry) Counter {
+ if nil == r {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, NewCounter).(Counter)
+}
+
+// NewCounter constructs a new StandardCounter.
+func NewCounter() Counter {
+ if UseNilMetrics {
+ return NilCounter{}
+ }
+ return &StandardCounter{0}
+}
+
+// NewRegisteredCounter constructs and registers a new StandardCounter.
+func NewRegisteredCounter(name string, r Registry) Counter {
+ c := NewCounter()
+ if nil == r {
+ r = DefaultRegistry
+ }
+ r.Register(name, c)
+ return c
+}
+
+// CounterSnapshot is a read-only copy of another Counter.
+type CounterSnapshot int64
+
+// Clear panics.
+func (CounterSnapshot) Clear() {
+ panic("Clear called on a CounterSnapshot")
+}
+
+// Count returns the count at the time the snapshot was taken.
+func (c CounterSnapshot) Count() int64 { return int64(c) }
+
+// Dec panics.
+func (CounterSnapshot) Dec(int64) {
+ panic("Dec called on a CounterSnapshot")
+}
+
+// Inc panics.
+func (CounterSnapshot) Inc(int64) {
+ panic("Inc called on a CounterSnapshot")
+}
+
+// Snapshot returns the snapshot.
+func (c CounterSnapshot) Snapshot() Counter { return c }
+
+// NilCounter is a no-op Counter.
+type NilCounter struct{}
+
+// Clear is a no-op.
+func (NilCounter) Clear() {}
+
+// Count is a no-op.
+func (NilCounter) Count() int64 { return 0 }
+
+// Dec is a no-op.
+func (NilCounter) Dec(i int64) {}
+
+// Inc is a no-op.
+func (NilCounter) Inc(i int64) {}
+
+// Snapshot is a no-op.
+func (NilCounter) Snapshot() Counter { return NilCounter{} }
+
+// StandardCounter is the standard implementation of a Counter and uses the
+// sync/atomic package to manage a single int64 value.
+type StandardCounter struct {
+ count int64
+}
+
+// Clear sets the counter to zero.
+func (c *StandardCounter) Clear() {
+ atomic.StoreInt64(&c.count, 0)
+}
+
+// Count returns the current count.
+func (c *StandardCounter) Count() int64 {
+ return atomic.LoadInt64(&c.count)
+}
+
+// Dec decrements the counter by the given amount.
+func (c *StandardCounter) Dec(i int64) {
+ atomic.AddInt64(&c.count, -i)
+}
+
+// Inc increments the counter by the given amount.
+func (c *StandardCounter) Inc(i int64) {
+ atomic.AddInt64(&c.count, i)
+}
+
+// Snapshot returns a read-only copy of the counter.
+func (c *StandardCounter) Snapshot() Counter {
+ return CounterSnapshot(c.Count())
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/debug.go b/vendor/github.com/rcrowley/go-metrics/debug.go
new file mode 100644
index 00000000..043ccefa
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/debug.go
@@ -0,0 +1,76 @@
+package metrics
+
+import (
+ "runtime/debug"
+ "time"
+)
+
+var (
+ debugMetrics struct {
+ GCStats struct {
+ LastGC Gauge
+ NumGC Gauge
+ Pause Histogram
+ //PauseQuantiles Histogram
+ PauseTotal Gauge
+ }
+ ReadGCStats Timer
+ }
+ gcStats debug.GCStats
+)
+
+// Capture new values for the Go garbage collector statistics exported in
+// debug.GCStats. This is designed to be called as a goroutine.
+func CaptureDebugGCStats(r Registry, d time.Duration) {
+ for _ = range time.Tick(d) {
+ CaptureDebugGCStatsOnce(r)
+ }
+}
+
+// Capture new values for the Go garbage collector statistics exported in
+// debug.GCStats. This is designed to be called in a background goroutine.
+// Giving a registry which has not been given to RegisterDebugGCStats will
+// panic.
+//
+// Be careful (but much less so) with this because debug.ReadGCStats calls
+// the C function runtime·lock(runtime·mheap) which, while not a stop-the-world
+// operation, isn't something you want to be doing all the time.
+func CaptureDebugGCStatsOnce(r Registry) {
+ lastGC := gcStats.LastGC
+ t := time.Now()
+ debug.ReadGCStats(&gcStats)
+ debugMetrics.ReadGCStats.UpdateSince(t)
+
+ debugMetrics.GCStats.LastGC.Update(int64(gcStats.LastGC.UnixNano()))
+ debugMetrics.GCStats.NumGC.Update(int64(gcStats.NumGC))
+ if lastGC != gcStats.LastGC && 0 < len(gcStats.Pause) {
+ debugMetrics.GCStats.Pause.Update(int64(gcStats.Pause[0]))
+ }
+ //debugMetrics.GCStats.PauseQuantiles.Update(gcStats.PauseQuantiles)
+ debugMetrics.GCStats.PauseTotal.Update(int64(gcStats.PauseTotal))
+}
+
+// Register metrics for the Go garbage collector statistics exported in
+// debug.GCStats. The metrics are named by their fully-qualified Go symbols,
+// i.e. debug.GCStats.PauseTotal.
+func RegisterDebugGCStats(r Registry) {
+ debugMetrics.GCStats.LastGC = NewGauge()
+ debugMetrics.GCStats.NumGC = NewGauge()
+ debugMetrics.GCStats.Pause = NewHistogram(NewExpDecaySample(1028, 0.015))
+ //debugMetrics.GCStats.PauseQuantiles = NewHistogram(NewExpDecaySample(1028, 0.015))
+ debugMetrics.GCStats.PauseTotal = NewGauge()
+ debugMetrics.ReadGCStats = NewTimer()
+
+ r.Register("debug.GCStats.LastGC", debugMetrics.GCStats.LastGC)
+ r.Register("debug.GCStats.NumGC", debugMetrics.GCStats.NumGC)
+ r.Register("debug.GCStats.Pause", debugMetrics.GCStats.Pause)
+ //r.Register("debug.GCStats.PauseQuantiles", debugMetrics.GCStats.PauseQuantiles)
+ r.Register("debug.GCStats.PauseTotal", debugMetrics.GCStats.PauseTotal)
+ r.Register("debug.ReadGCStats", debugMetrics.ReadGCStats)
+}
+
+// Allocate an initial slice for gcStats.Pause to avoid allocations during
+// normal operation.
+func init() {
+ gcStats.Pause = make([]time.Duration, 11)
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/ewma.go b/vendor/github.com/rcrowley/go-metrics/ewma.go
new file mode 100644
index 00000000..694a1d03
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/ewma.go
@@ -0,0 +1,118 @@
+package metrics
+
+import (
+ "math"
+ "sync"
+ "sync/atomic"
+)
+
+// EWMAs continuously calculate an exponentially-weighted moving average
+// based on an outside source of clock ticks.
+type EWMA interface {
+ Rate() float64
+ Snapshot() EWMA
+ Tick()
+ Update(int64)
+}
+
+// NewEWMA constructs a new EWMA with the given alpha.
+func NewEWMA(alpha float64) EWMA {
+ if UseNilMetrics {
+ return NilEWMA{}
+ }
+ return &StandardEWMA{alpha: alpha}
+}
+
+// NewEWMA1 constructs a new EWMA for a one-minute moving average.
+func NewEWMA1() EWMA {
+ return NewEWMA(1 - math.Exp(-5.0/60.0/1))
+}
+
+// NewEWMA5 constructs a new EWMA for a five-minute moving average.
+func NewEWMA5() EWMA {
+ return NewEWMA(1 - math.Exp(-5.0/60.0/5))
+}
+
+// NewEWMA15 constructs a new EWMA for a fifteen-minute moving average.
+func NewEWMA15() EWMA {
+ return NewEWMA(1 - math.Exp(-5.0/60.0/15))
+}
+
+// EWMASnapshot is a read-only copy of another EWMA.
+type EWMASnapshot float64
+
+// Rate returns the rate of events per second at the time the snapshot was
+// taken.
+func (a EWMASnapshot) Rate() float64 { return float64(a) }
+
+// Snapshot returns the snapshot.
+func (a EWMASnapshot) Snapshot() EWMA { return a }
+
+// Tick panics.
+func (EWMASnapshot) Tick() {
+ panic("Tick called on an EWMASnapshot")
+}
+
+// Update panics.
+func (EWMASnapshot) Update(int64) {
+ panic("Update called on an EWMASnapshot")
+}
+
+// NilEWMA is a no-op EWMA.
+type NilEWMA struct{}
+
+// Rate is a no-op.
+func (NilEWMA) Rate() float64 { return 0.0 }
+
+// Snapshot is a no-op.
+func (NilEWMA) Snapshot() EWMA { return NilEWMA{} }
+
+// Tick is a no-op.
+func (NilEWMA) Tick() {}
+
+// Update is a no-op.
+func (NilEWMA) Update(n int64) {}
+
+// StandardEWMA is the standard implementation of an EWMA and tracks the number
+// of uncounted events and processes them on each tick. It uses the
+// sync/atomic package to manage uncounted events.
+type StandardEWMA struct {
+ uncounted int64 // /!\ this should be the first member to ensure 64-bit alignment
+ alpha float64
+ rate float64
+ init bool
+ mutex sync.Mutex
+}
+
+// Rate returns the moving average rate of events per second.
+func (a *StandardEWMA) Rate() float64 {
+ a.mutex.Lock()
+ defer a.mutex.Unlock()
+ return a.rate * float64(1e9)
+}
+
+// Snapshot returns a read-only copy of the EWMA.
+func (a *StandardEWMA) Snapshot() EWMA {
+ return EWMASnapshot(a.Rate())
+}
+
+// Tick ticks the clock to update the moving average. It assumes it is called
+// every five seconds.
+func (a *StandardEWMA) Tick() {
+ count := atomic.LoadInt64(&a.uncounted)
+ atomic.AddInt64(&a.uncounted, -count)
+ instantRate := float64(count) / float64(5e9)
+ a.mutex.Lock()
+ defer a.mutex.Unlock()
+ if a.init {
+ a.rate += a.alpha * (instantRate - a.rate)
+ } else {
+ a.init = true
+ a.rate = instantRate
+ }
+}
+
+// Update adds n uncounted events.
+func (a *StandardEWMA) Update(n int64) {
+ atomic.AddInt64(&a.uncounted, n)
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/gauge.go b/vendor/github.com/rcrowley/go-metrics/gauge.go
new file mode 100644
index 00000000..cb57a938
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/gauge.go
@@ -0,0 +1,120 @@
+package metrics
+
+import "sync/atomic"
+
+// Gauges hold an int64 value that can be set arbitrarily.
+type Gauge interface {
+ Snapshot() Gauge
+ Update(int64)
+ Value() int64
+}
+
+// GetOrRegisterGauge returns an existing Gauge or constructs and registers a
+// new StandardGauge.
+func GetOrRegisterGauge(name string, r Registry) Gauge {
+ if nil == r {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, NewGauge).(Gauge)
+}
+
+// NewGauge constructs a new StandardGauge.
+func NewGauge() Gauge {
+ if UseNilMetrics {
+ return NilGauge{}
+ }
+ return &StandardGauge{0}
+}
+
+// NewRegisteredGauge constructs and registers a new StandardGauge.
+func NewRegisteredGauge(name string, r Registry) Gauge {
+ c := NewGauge()
+ if nil == r {
+ r = DefaultRegistry
+ }
+ r.Register(name, c)
+ return c
+}
+
+// NewFunctionalGauge constructs a new FunctionalGauge.
+func NewFunctionalGauge(f func() int64) Gauge {
+ if UseNilMetrics {
+ return NilGauge{}
+ }
+ return &FunctionalGauge{value: f}
+}
+
+// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge.
+func NewRegisteredFunctionalGauge(name string, r Registry, f func() int64) Gauge {
+ c := NewFunctionalGauge(f)
+ if nil == r {
+ r = DefaultRegistry
+ }
+ r.Register(name, c)
+ return c
+}
+
+// GaugeSnapshot is a read-only copy of another Gauge.
+type GaugeSnapshot int64
+
+// Snapshot returns the snapshot.
+func (g GaugeSnapshot) Snapshot() Gauge { return g }
+
+// Update panics.
+func (GaugeSnapshot) Update(int64) {
+ panic("Update called on a GaugeSnapshot")
+}
+
+// Value returns the value at the time the snapshot was taken.
+func (g GaugeSnapshot) Value() int64 { return int64(g) }
+
+// NilGauge is a no-op Gauge.
+type NilGauge struct{}
+
+// Snapshot is a no-op.
+func (NilGauge) Snapshot() Gauge { return NilGauge{} }
+
+// Update is a no-op.
+func (NilGauge) Update(v int64) {}
+
+// Value is a no-op.
+func (NilGauge) Value() int64 { return 0 }
+
+// StandardGauge is the standard implementation of a Gauge and uses the
+// sync/atomic package to manage a single int64 value.
+type StandardGauge struct {
+ value int64
+}
+
+// Snapshot returns a read-only copy of the gauge.
+func (g *StandardGauge) Snapshot() Gauge {
+ return GaugeSnapshot(g.Value())
+}
+
+// Update updates the gauge's value.
+func (g *StandardGauge) Update(v int64) {
+ atomic.StoreInt64(&g.value, v)
+}
+
+// Value returns the gauge's current value.
+func (g *StandardGauge) Value() int64 {
+ return atomic.LoadInt64(&g.value)
+}
+
+// FunctionalGauge returns value from given function
+type FunctionalGauge struct {
+ value func() int64
+}
+
+// Value returns the gauge's current value.
+func (g FunctionalGauge) Value() int64 {
+ return g.value()
+}
+
+// Snapshot returns the snapshot.
+func (g FunctionalGauge) Snapshot() Gauge { return GaugeSnapshot(g.Value()) }
+
+// Update panics.
+func (FunctionalGauge) Update(int64) {
+ panic("Update called on a FunctionalGauge")
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/gauge_float64.go b/vendor/github.com/rcrowley/go-metrics/gauge_float64.go
new file mode 100644
index 00000000..6f93920b
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/gauge_float64.go
@@ -0,0 +1,127 @@
+package metrics
+
+import "sync"
+
+// GaugeFloat64s hold a float64 value that can be set arbitrarily.
+type GaugeFloat64 interface {
+ Snapshot() GaugeFloat64
+ Update(float64)
+ Value() float64
+}
+
+// GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a
+// new StandardGaugeFloat64.
+func GetOrRegisterGaugeFloat64(name string, r Registry) GaugeFloat64 {
+ if nil == r {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, NewGaugeFloat64()).(GaugeFloat64)
+}
+
+// NewGaugeFloat64 constructs a new StandardGaugeFloat64.
+func NewGaugeFloat64() GaugeFloat64 {
+ if UseNilMetrics {
+ return NilGaugeFloat64{}
+ }
+ return &StandardGaugeFloat64{
+ value: 0.0,
+ }
+}
+
+// NewRegisteredGaugeFloat64 constructs and registers a new StandardGaugeFloat64.
+func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 {
+ c := NewGaugeFloat64()
+ if nil == r {
+ r = DefaultRegistry
+ }
+ r.Register(name, c)
+ return c
+}
+
+// NewFunctionalGauge constructs a new FunctionalGauge.
+func NewFunctionalGaugeFloat64(f func() float64) GaugeFloat64 {
+ if UseNilMetrics {
+ return NilGaugeFloat64{}
+ }
+ return &FunctionalGaugeFloat64{value: f}
+}
+
+// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge.
+func NewRegisteredFunctionalGaugeFloat64(name string, r Registry, f func() float64) GaugeFloat64 {
+ c := NewFunctionalGaugeFloat64(f)
+ if nil == r {
+ r = DefaultRegistry
+ }
+ r.Register(name, c)
+ return c
+}
+
+// GaugeFloat64Snapshot is a read-only copy of another GaugeFloat64.
+type GaugeFloat64Snapshot float64
+
+// Snapshot returns the snapshot.
+func (g GaugeFloat64Snapshot) Snapshot() GaugeFloat64 { return g }
+
+// Update panics.
+func (GaugeFloat64Snapshot) Update(float64) {
+ panic("Update called on a GaugeFloat64Snapshot")
+}
+
+// Value returns the value at the time the snapshot was taken.
+func (g GaugeFloat64Snapshot) Value() float64 { return float64(g) }
+
+// NilGauge is a no-op Gauge.
+type NilGaugeFloat64 struct{}
+
+// Snapshot is a no-op.
+func (NilGaugeFloat64) Snapshot() GaugeFloat64 { return NilGaugeFloat64{} }
+
+// Update is a no-op.
+func (NilGaugeFloat64) Update(v float64) {}
+
+// Value is a no-op.
+func (NilGaugeFloat64) Value() float64 { return 0.0 }
+
+// StandardGaugeFloat64 is the standard implementation of a GaugeFloat64 and uses
+// sync.Mutex to manage a single float64 value.
+type StandardGaugeFloat64 struct {
+ mutex sync.Mutex
+ value float64
+}
+
+// Snapshot returns a read-only copy of the gauge.
+func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64 {
+ return GaugeFloat64Snapshot(g.Value())
+}
+
+// Update updates the gauge's value.
+func (g *StandardGaugeFloat64) Update(v float64) {
+ g.mutex.Lock()
+ defer g.mutex.Unlock()
+ g.value = v
+}
+
+// Value returns the gauge's current value.
+func (g *StandardGaugeFloat64) Value() float64 {
+ g.mutex.Lock()
+ defer g.mutex.Unlock()
+ return g.value
+}
+
+// FunctionalGaugeFloat64 returns value from given function
+type FunctionalGaugeFloat64 struct {
+ value func() float64
+}
+
+// Value returns the gauge's current value.
+func (g FunctionalGaugeFloat64) Value() float64 {
+ return g.value()
+}
+
+// Snapshot returns the snapshot.
+func (g FunctionalGaugeFloat64) Snapshot() GaugeFloat64 { return GaugeFloat64Snapshot(g.Value()) }
+
+// Update panics.
+func (FunctionalGaugeFloat64) Update(float64) {
+ panic("Update called on a FunctionalGaugeFloat64")
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/graphite.go b/vendor/github.com/rcrowley/go-metrics/graphite.go
new file mode 100644
index 00000000..abd0a7d2
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/graphite.go
@@ -0,0 +1,113 @@
+package metrics
+
+import (
+ "bufio"
+ "fmt"
+ "log"
+ "net"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// GraphiteConfig provides a container with configuration parameters for
+// the Graphite exporter
+type GraphiteConfig struct {
+ Addr *net.TCPAddr // Network address to connect to
+ Registry Registry // Registry to be exported
+ FlushInterval time.Duration // Flush interval
+ DurationUnit time.Duration // Time conversion unit for durations
+ Prefix string // Prefix to be prepended to metric names
+ Percentiles []float64 // Percentiles to export from timers and histograms
+}
+
+// Graphite is a blocking exporter function which reports metrics in r
+// to a graphite server located at addr, flushing them every d duration
+// and prepending metric names with prefix.
+func Graphite(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) {
+ GraphiteWithConfig(GraphiteConfig{
+ Addr: addr,
+ Registry: r,
+ FlushInterval: d,
+ DurationUnit: time.Nanosecond,
+ Prefix: prefix,
+ Percentiles: []float64{0.5, 0.75, 0.95, 0.99, 0.999},
+ })
+}
+
+// GraphiteWithConfig is a blocking exporter function just like Graphite,
+// but it takes a GraphiteConfig instead.
+func GraphiteWithConfig(c GraphiteConfig) {
+ log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015")
+ for _ = range time.Tick(c.FlushInterval) {
+ if err := graphite(&c); nil != err {
+ log.Println(err)
+ }
+ }
+}
+
+// GraphiteOnce performs a single submission to Graphite, returning a
+// non-nil error on failed connections. This can be used in a loop
+// similar to GraphiteWithConfig for custom error handling.
+func GraphiteOnce(c GraphiteConfig) error {
+ log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015")
+ return graphite(&c)
+}
+
+func graphite(c *GraphiteConfig) error {
+ now := time.Now().Unix()
+ du := float64(c.DurationUnit)
+ conn, err := net.DialTCP("tcp", nil, c.Addr)
+ if nil != err {
+ return err
+ }
+ defer conn.Close()
+ w := bufio.NewWriter(conn)
+ c.Registry.Each(func(name string, i interface{}) {
+ switch metric := i.(type) {
+ case Counter:
+ fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Count(), now)
+ case Gauge:
+ fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now)
+ case GaugeFloat64:
+ fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Value(), now)
+ case Histogram:
+ h := metric.Snapshot()
+ ps := h.Percentiles(c.Percentiles)
+ fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, h.Count(), now)
+ fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, h.Min(), now)
+ fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, h.Max(), now)
+ fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, h.Mean(), now)
+ fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, h.StdDev(), now)
+ for psIdx, psKey := range c.Percentiles {
+ key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1)
+ fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now)
+ }
+ case Meter:
+ m := metric.Snapshot()
+ fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, m.Count(), now)
+ fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, m.Rate1(), now)
+ fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, m.Rate5(), now)
+ fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, m.Rate15(), now)
+ fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, m.RateMean(), now)
+ case Timer:
+ t := metric.Snapshot()
+ ps := t.Percentiles(c.Percentiles)
+ fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, t.Count(), now)
+ fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, t.Min()/int64(du), now)
+ fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, t.Max()/int64(du), now)
+ fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, t.Mean()/du, now)
+ fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, t.StdDev()/du, now)
+ for psIdx, psKey := range c.Percentiles {
+ key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1)
+ fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now)
+ }
+ fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, t.Rate1(), now)
+ fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, t.Rate5(), now)
+ fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, t.Rate15(), now)
+ fmt.Fprintf(w, "%s.%s.mean-rate %.2f %d\n", c.Prefix, name, t.RateMean(), now)
+ }
+ w.Flush()
+ })
+ return nil
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/healthcheck.go b/vendor/github.com/rcrowley/go-metrics/healthcheck.go
new file mode 100644
index 00000000..445131ca
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/healthcheck.go
@@ -0,0 +1,61 @@
+package metrics
+
+// Healthchecks hold an error value describing an arbitrary up/down status.
+type Healthcheck interface {
+ Check()
+ Error() error
+ Healthy()
+ Unhealthy(error)
+}
+
+// NewHealthcheck constructs a new Healthcheck which will use the given
+// function to update its status.
+func NewHealthcheck(f func(Healthcheck)) Healthcheck {
+ if UseNilMetrics {
+ return NilHealthcheck{}
+ }
+ return &StandardHealthcheck{nil, f}
+}
+
+// NilHealthcheck is a no-op.
+type NilHealthcheck struct{}
+
+// Check is a no-op.
+func (NilHealthcheck) Check() {}
+
+// Error is a no-op.
+func (NilHealthcheck) Error() error { return nil }
+
+// Healthy is a no-op.
+func (NilHealthcheck) Healthy() {}
+
+// Unhealthy is a no-op.
+func (NilHealthcheck) Unhealthy(error) {}
+
+// StandardHealthcheck is the standard implementation of a Healthcheck and
+// stores the status and a function to call to update the status.
+type StandardHealthcheck struct {
+ err error
+ f func(Healthcheck)
+}
+
+// Check runs the healthcheck function to update the healthcheck's status.
+func (h *StandardHealthcheck) Check() {
+ h.f(h)
+}
+
+// Error returns the healthcheck's status, which will be nil if it is healthy.
+func (h *StandardHealthcheck) Error() error {
+ return h.err
+}
+
+// Healthy marks the healthcheck as healthy.
+func (h *StandardHealthcheck) Healthy() {
+ h.err = nil
+}
+
+// Unhealthy marks the healthcheck as unhealthy. The error is stored and
+// may be retrieved by the Error method.
+func (h *StandardHealthcheck) Unhealthy(err error) {
+ h.err = err
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/histogram.go b/vendor/github.com/rcrowley/go-metrics/histogram.go
new file mode 100644
index 00000000..dbc837fe
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/histogram.go
@@ -0,0 +1,202 @@
+package metrics
+
+// Histograms calculate distribution statistics from a series of int64 values.
+type Histogram interface {
+ Clear()
+ Count() int64
+ Max() int64
+ Mean() float64
+ Min() int64
+ Percentile(float64) float64
+ Percentiles([]float64) []float64
+ Sample() Sample
+ Snapshot() Histogram
+ StdDev() float64
+ Sum() int64
+ Update(int64)
+ Variance() float64
+}
+
+// GetOrRegisterHistogram returns an existing Histogram or constructs and
+// registers a new StandardHistogram.
+func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram {
+ if nil == r {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, func() Histogram { return NewHistogram(s) }).(Histogram)
+}
+
+// NewHistogram constructs a new StandardHistogram from a Sample.
+func NewHistogram(s Sample) Histogram {
+ if UseNilMetrics {
+ return NilHistogram{}
+ }
+ return &StandardHistogram{sample: s}
+}
+
+// NewRegisteredHistogram constructs and registers a new StandardHistogram from
+// a Sample.
+func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram {
+ c := NewHistogram(s)
+ if nil == r {
+ r = DefaultRegistry
+ }
+ r.Register(name, c)
+ return c
+}
+
+// HistogramSnapshot is a read-only copy of another Histogram.
+type HistogramSnapshot struct {
+ sample *SampleSnapshot
+}
+
+// Clear panics.
+func (*HistogramSnapshot) Clear() {
+ panic("Clear called on a HistogramSnapshot")
+}
+
+// Count returns the number of samples recorded at the time the snapshot was
+// taken.
+func (h *HistogramSnapshot) Count() int64 { return h.sample.Count() }
+
+// Max returns the maximum value in the sample at the time the snapshot was
+// taken.
+func (h *HistogramSnapshot) Max() int64 { return h.sample.Max() }
+
+// Mean returns the mean of the values in the sample at the time the snapshot
+// was taken.
+func (h *HistogramSnapshot) Mean() float64 { return h.sample.Mean() }
+
+// Min returns the minimum value in the sample at the time the snapshot was
+// taken.
+func (h *HistogramSnapshot) Min() int64 { return h.sample.Min() }
+
+// Percentile returns an arbitrary percentile of values in the sample at the
+// time the snapshot was taken.
+func (h *HistogramSnapshot) Percentile(p float64) float64 {
+ return h.sample.Percentile(p)
+}
+
+// Percentiles returns a slice of arbitrary percentiles of values in the sample
+// at the time the snapshot was taken.
+func (h *HistogramSnapshot) Percentiles(ps []float64) []float64 {
+ return h.sample.Percentiles(ps)
+}
+
+// Sample returns the Sample underlying the histogram.
+func (h *HistogramSnapshot) Sample() Sample { return h.sample }
+
+// Snapshot returns the snapshot.
+func (h *HistogramSnapshot) Snapshot() Histogram { return h }
+
+// StdDev returns the standard deviation of the values in the sample at the
+// time the snapshot was taken.
+func (h *HistogramSnapshot) StdDev() float64 { return h.sample.StdDev() }
+
+// Sum returns the sum in the sample at the time the snapshot was taken.
+func (h *HistogramSnapshot) Sum() int64 { return h.sample.Sum() }
+
+// Update panics.
+func (*HistogramSnapshot) Update(int64) {
+ panic("Update called on a HistogramSnapshot")
+}
+
+// Variance returns the variance of inputs at the time the snapshot was taken.
+func (h *HistogramSnapshot) Variance() float64 { return h.sample.Variance() }
+
+// NilHistogram is a no-op Histogram.
+type NilHistogram struct{}
+
+// Clear is a no-op.
+func (NilHistogram) Clear() {}
+
+// Count is a no-op.
+func (NilHistogram) Count() int64 { return 0 }
+
+// Max is a no-op.
+func (NilHistogram) Max() int64 { return 0 }
+
+// Mean is a no-op.
+func (NilHistogram) Mean() float64 { return 0.0 }
+
+// Min is a no-op.
+func (NilHistogram) Min() int64 { return 0 }
+
+// Percentile is a no-op.
+func (NilHistogram) Percentile(p float64) float64 { return 0.0 }
+
+// Percentiles is a no-op.
+func (NilHistogram) Percentiles(ps []float64) []float64 {
+ return make([]float64, len(ps))
+}
+
+// Sample is a no-op.
+func (NilHistogram) Sample() Sample { return NilSample{} }
+
+// Snapshot is a no-op.
+func (NilHistogram) Snapshot() Histogram { return NilHistogram{} }
+
+// StdDev is a no-op.
+func (NilHistogram) StdDev() float64 { return 0.0 }
+
+// Sum is a no-op.
+func (NilHistogram) Sum() int64 { return 0 }
+
+// Update is a no-op.
+func (NilHistogram) Update(v int64) {}
+
+// Variance is a no-op.
+func (NilHistogram) Variance() float64 { return 0.0 }
+
+// StandardHistogram is the standard implementation of a Histogram and uses a
+// Sample to bound its memory use.
+type StandardHistogram struct {
+ sample Sample
+}
+
+// Clear clears the histogram and its sample.
+func (h *StandardHistogram) Clear() { h.sample.Clear() }
+
+// Count returns the number of samples recorded since the histogram was last
+// cleared.
+func (h *StandardHistogram) Count() int64 { return h.sample.Count() }
+
+// Max returns the maximum value in the sample.
+func (h *StandardHistogram) Max() int64 { return h.sample.Max() }
+
+// Mean returns the mean of the values in the sample.
+func (h *StandardHistogram) Mean() float64 { return h.sample.Mean() }
+
+// Min returns the minimum value in the sample.
+func (h *StandardHistogram) Min() int64 { return h.sample.Min() }
+
+// Percentile returns an arbitrary percentile of the values in the sample.
+func (h *StandardHistogram) Percentile(p float64) float64 {
+ return h.sample.Percentile(p)
+}
+
+// Percentiles returns a slice of arbitrary percentiles of the values in the
+// sample.
+func (h *StandardHistogram) Percentiles(ps []float64) []float64 {
+ return h.sample.Percentiles(ps)
+}
+
+// Sample returns the Sample underlying the histogram.
+func (h *StandardHistogram) Sample() Sample { return h.sample }
+
+// Snapshot returns a read-only copy of the histogram.
+func (h *StandardHistogram) Snapshot() Histogram {
+ return &HistogramSnapshot{sample: h.sample.Snapshot().(*SampleSnapshot)}
+}
+
+// StdDev returns the standard deviation of the values in the sample.
+func (h *StandardHistogram) StdDev() float64 { return h.sample.StdDev() }
+
+// Sum returns the sum in the sample.
+func (h *StandardHistogram) Sum() int64 { return h.sample.Sum() }
+
+// Update samples a new value.
+func (h *StandardHistogram) Update(v int64) { h.sample.Update(v) }
+
+// Variance returns the variance of the values in the sample.
+func (h *StandardHistogram) Variance() float64 { return h.sample.Variance() }
diff --git a/vendor/github.com/rcrowley/go-metrics/json.go b/vendor/github.com/rcrowley/go-metrics/json.go
new file mode 100644
index 00000000..2fdcbcfb
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/json.go
@@ -0,0 +1,87 @@
+package metrics
+
+import (
+ "encoding/json"
+ "io"
+ "time"
+)
+
+// MarshalJSON returns a byte slice containing a JSON representation of all
+// the metrics in the Registry.
+func (r *StandardRegistry) MarshalJSON() ([]byte, error) {
+ data := make(map[string]map[string]interface{})
+ r.Each(func(name string, i interface{}) {
+ values := make(map[string]interface{})
+ switch metric := i.(type) {
+ case Counter:
+ values["count"] = metric.Count()
+ case Gauge:
+ values["value"] = metric.Value()
+ case GaugeFloat64:
+ values["value"] = metric.Value()
+ case Healthcheck:
+ values["error"] = nil
+ metric.Check()
+ if err := metric.Error(); nil != err {
+ values["error"] = metric.Error().Error()
+ }
+ case Histogram:
+ h := metric.Snapshot()
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
+ values["count"] = h.Count()
+ values["min"] = h.Min()
+ values["max"] = h.Max()
+ values["mean"] = h.Mean()
+ values["stddev"] = h.StdDev()
+ values["median"] = ps[0]
+ values["75%"] = ps[1]
+ values["95%"] = ps[2]
+ values["99%"] = ps[3]
+ values["99.9%"] = ps[4]
+ case Meter:
+ m := metric.Snapshot()
+ values["count"] = m.Count()
+ values["1m.rate"] = m.Rate1()
+ values["5m.rate"] = m.Rate5()
+ values["15m.rate"] = m.Rate15()
+ values["mean.rate"] = m.RateMean()
+ case Timer:
+ t := metric.Snapshot()
+ ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
+ values["count"] = t.Count()
+ values["min"] = t.Min()
+ values["max"] = t.Max()
+ values["mean"] = t.Mean()
+ values["stddev"] = t.StdDev()
+ values["median"] = ps[0]
+ values["75%"] = ps[1]
+ values["95%"] = ps[2]
+ values["99%"] = ps[3]
+ values["99.9%"] = ps[4]
+ values["1m.rate"] = t.Rate1()
+ values["5m.rate"] = t.Rate5()
+ values["15m.rate"] = t.Rate15()
+ values["mean.rate"] = t.RateMean()
+ }
+ data[name] = values
+ })
+ return json.Marshal(data)
+}
+
+// WriteJSON writes metrics from the given registry periodically to the
+// specified io.Writer as JSON.
+func WriteJSON(r Registry, d time.Duration, w io.Writer) {
+ for _ = range time.Tick(d) {
+ WriteJSONOnce(r, w)
+ }
+}
+
+// WriteJSONOnce writes metrics from the given registry to the specified
+// io.Writer as JSON.
+func WriteJSONOnce(r Registry, w io.Writer) {
+ json.NewEncoder(w).Encode(r)
+}
+
+func (p *PrefixedRegistry) MarshalJSON() ([]byte, error) {
+ return json.Marshal(p.underlying)
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/log.go b/vendor/github.com/rcrowley/go-metrics/log.go
new file mode 100644
index 00000000..f8074c04
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/log.go
@@ -0,0 +1,80 @@
+package metrics
+
+import (
+ "time"
+)
+
+type Logger interface {
+ Printf(format string, v ...interface{})
+}
+
+func Log(r Registry, freq time.Duration, l Logger) {
+ LogScaled(r, freq, time.Nanosecond, l)
+}
+
+// Output each metric in the given registry periodically using the given
+// logger. Print timings in `scale` units (eg time.Millisecond) rather than nanos.
+func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) {
+ du := float64(scale)
+ duSuffix := scale.String()[1:]
+
+ for _ = range time.Tick(freq) {
+ r.Each(func(name string, i interface{}) {
+ switch metric := i.(type) {
+ case Counter:
+ l.Printf("counter %s\n", name)
+ l.Printf(" count: %9d\n", metric.Count())
+ case Gauge:
+ l.Printf("gauge %s\n", name)
+ l.Printf(" value: %9d\n", metric.Value())
+ case GaugeFloat64:
+ l.Printf("gauge %s\n", name)
+ l.Printf(" value: %f\n", metric.Value())
+ case Healthcheck:
+ metric.Check()
+ l.Printf("healthcheck %s\n", name)
+ l.Printf(" error: %v\n", metric.Error())
+ case Histogram:
+ h := metric.Snapshot()
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
+ l.Printf("histogram %s\n", name)
+ l.Printf(" count: %9d\n", h.Count())
+ l.Printf(" min: %9d\n", h.Min())
+ l.Printf(" max: %9d\n", h.Max())
+ l.Printf(" mean: %12.2f\n", h.Mean())
+ l.Printf(" stddev: %12.2f\n", h.StdDev())
+ l.Printf(" median: %12.2f\n", ps[0])
+ l.Printf(" 75%%: %12.2f\n", ps[1])
+ l.Printf(" 95%%: %12.2f\n", ps[2])
+ l.Printf(" 99%%: %12.2f\n", ps[3])
+ l.Printf(" 99.9%%: %12.2f\n", ps[4])
+ case Meter:
+ m := metric.Snapshot()
+ l.Printf("meter %s\n", name)
+ l.Printf(" count: %9d\n", m.Count())
+ l.Printf(" 1-min rate: %12.2f\n", m.Rate1())
+ l.Printf(" 5-min rate: %12.2f\n", m.Rate5())
+ l.Printf(" 15-min rate: %12.2f\n", m.Rate15())
+ l.Printf(" mean rate: %12.2f\n", m.RateMean())
+ case Timer:
+ t := metric.Snapshot()
+ ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
+ l.Printf("timer %s\n", name)
+ l.Printf(" count: %9d\n", t.Count())
+ l.Printf(" min: %12.2f%s\n", float64(t.Min())/du, duSuffix)
+ l.Printf(" max: %12.2f%s\n", float64(t.Max())/du, duSuffix)
+ l.Printf(" mean: %12.2f%s\n", t.Mean()/du, duSuffix)
+ l.Printf(" stddev: %12.2f%s\n", t.StdDev()/du, duSuffix)
+ l.Printf(" median: %12.2f%s\n", ps[0]/du, duSuffix)
+ l.Printf(" 75%%: %12.2f%s\n", ps[1]/du, duSuffix)
+ l.Printf(" 95%%: %12.2f%s\n", ps[2]/du, duSuffix)
+ l.Printf(" 99%%: %12.2f%s\n", ps[3]/du, duSuffix)
+ l.Printf(" 99.9%%: %12.2f%s\n", ps[4]/du, duSuffix)
+ l.Printf(" 1-min rate: %12.2f\n", t.Rate1())
+ l.Printf(" 5-min rate: %12.2f\n", t.Rate5())
+ l.Printf(" 15-min rate: %12.2f\n", t.Rate15())
+ l.Printf(" mean rate: %12.2f\n", t.RateMean())
+ }
+ })
+ }
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/memory.md b/vendor/github.com/rcrowley/go-metrics/memory.md
new file mode 100644
index 00000000..47454f54
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/memory.md
@@ -0,0 +1,285 @@
+Memory usage
+============
+
+(Highly unscientific.)
+
+Command used to gather static memory usage:
+
+```sh
+grep ^Vm "/proc/$(ps fax | grep [m]etrics-bench | awk '{print $1}')/status"
+```
+
+Program used to gather baseline memory usage:
+
+```go
+package main
+
+import "time"
+
+func main() {
+ time.Sleep(600e9)
+}
+```
+
+Baseline
+--------
+
+```
+VmPeak: 42604 kB
+VmSize: 42604 kB
+VmLck: 0 kB
+VmHWM: 1120 kB
+VmRSS: 1120 kB
+VmData: 35460 kB
+VmStk: 136 kB
+VmExe: 1020 kB
+VmLib: 1848 kB
+VmPTE: 36 kB
+VmSwap: 0 kB
+```
+
+Program used to gather metric memory usage (with other metrics being similar):
+
+```go
+package main
+
+import (
+ "fmt"
+ "metrics"
+ "time"
+)
+
+func main() {
+ fmt.Sprintf("foo")
+ metrics.NewRegistry()
+ time.Sleep(600e9)
+}
+```
+
+1000 counters registered
+------------------------
+
+```
+VmPeak: 44016 kB
+VmSize: 44016 kB
+VmLck: 0 kB
+VmHWM: 1928 kB
+VmRSS: 1928 kB
+VmData: 36868 kB
+VmStk: 136 kB
+VmExe: 1024 kB
+VmLib: 1848 kB
+VmPTE: 40 kB
+VmSwap: 0 kB
+```
+
+**1.412 kB virtual, TODO 0.808 kB resident per counter.**
+
+100000 counters registered
+--------------------------
+
+```
+VmPeak: 55024 kB
+VmSize: 55024 kB
+VmLck: 0 kB
+VmHWM: 12440 kB
+VmRSS: 12440 kB
+VmData: 47876 kB
+VmStk: 136 kB
+VmExe: 1024 kB
+VmLib: 1848 kB
+VmPTE: 64 kB
+VmSwap: 0 kB
+```
+
+**0.1242 kB virtual, 0.1132 kB resident per counter.**
+
+1000 gauges registered
+----------------------
+
+```
+VmPeak: 44012 kB
+VmSize: 44012 kB
+VmLck: 0 kB
+VmHWM: 1928 kB
+VmRSS: 1928 kB
+VmData: 36868 kB
+VmStk: 136 kB
+VmExe: 1020 kB
+VmLib: 1848 kB
+VmPTE: 40 kB
+VmSwap: 0 kB
+```
+
+**1.408 kB virtual, 0.808 kB resident per counter.**
+
+100000 gauges registered
+------------------------
+
+```
+VmPeak: 55020 kB
+VmSize: 55020 kB
+VmLck: 0 kB
+VmHWM: 12432 kB
+VmRSS: 12432 kB
+VmData: 47876 kB
+VmStk: 136 kB
+VmExe: 1020 kB
+VmLib: 1848 kB
+VmPTE: 60 kB
+VmSwap: 0 kB
+```
+
+**0.12416 kB virtual, 0.11312 resident per gauge.**
+
+1000 histograms with a uniform sample size of 1028
+--------------------------------------------------
+
+```
+VmPeak: 72272 kB
+VmSize: 72272 kB
+VmLck: 0 kB
+VmHWM: 16204 kB
+VmRSS: 16204 kB
+VmData: 65100 kB
+VmStk: 136 kB
+VmExe: 1048 kB
+VmLib: 1848 kB
+VmPTE: 80 kB
+VmSwap: 0 kB
+```
+
+**29.668 kB virtual, TODO 15.084 resident per histogram.**
+
+10000 histograms with a uniform sample size of 1028
+---------------------------------------------------
+
+```
+VmPeak: 256912 kB
+VmSize: 256912 kB
+VmLck: 0 kB
+VmHWM: 146204 kB
+VmRSS: 146204 kB
+VmData: 249740 kB
+VmStk: 136 kB
+VmExe: 1048 kB
+VmLib: 1848 kB
+VmPTE: 448 kB
+VmSwap: 0 kB
+```
+
+**21.4308 kB virtual, 14.5084 kB resident per histogram.**
+
+50000 histograms with a uniform sample size of 1028
+---------------------------------------------------
+
+```
+VmPeak: 908112 kB
+VmSize: 908112 kB
+VmLck: 0 kB
+VmHWM: 645832 kB
+VmRSS: 645588 kB
+VmData: 900940 kB
+VmStk: 136 kB
+VmExe: 1048 kB
+VmLib: 1848 kB
+VmPTE: 1716 kB
+VmSwap: 1544 kB
+```
+
+**17.31016 kB virtual, 12.88936 kB resident per histogram.**
+
+1000 histograms with an exponentially-decaying sample size of 1028 and alpha of 0.015
+-------------------------------------------------------------------------------------
+
+```
+VmPeak: 62480 kB
+VmSize: 62480 kB
+VmLck: 0 kB
+VmHWM: 11572 kB
+VmRSS: 11572 kB
+VmData: 55308 kB
+VmStk: 136 kB
+VmExe: 1048 kB
+VmLib: 1848 kB
+VmPTE: 64 kB
+VmSwap: 0 kB
+```
+
+**19.876 kB virtual, 10.452 kB resident per histogram.**
+
+10000 histograms with an exponentially-decaying sample size of 1028 and alpha of 0.015
+--------------------------------------------------------------------------------------
+
+```
+VmPeak: 153296 kB
+VmSize: 153296 kB
+VmLck: 0 kB
+VmHWM: 101176 kB
+VmRSS: 101176 kB
+VmData: 146124 kB
+VmStk: 136 kB
+VmExe: 1048 kB
+VmLib: 1848 kB
+VmPTE: 240 kB
+VmSwap: 0 kB
+```
+
+**11.0692 kB virtual, 10.0056 kB resident per histogram.**
+
+50000 histograms with an exponentially-decaying sample size of 1028 and alpha of 0.015
+--------------------------------------------------------------------------------------
+
+```
+VmPeak: 557264 kB
+VmSize: 557264 kB
+VmLck: 0 kB
+VmHWM: 501056 kB
+VmRSS: 501056 kB
+VmData: 550092 kB
+VmStk: 136 kB
+VmExe: 1048 kB
+VmLib: 1848 kB
+VmPTE: 1032 kB
+VmSwap: 0 kB
+```
+
+**10.2932 kB virtual, 9.99872 kB resident per histogram.**
+
+1000 meters
+-----------
+
+```
+VmPeak: 74504 kB
+VmSize: 74504 kB
+VmLck: 0 kB
+VmHWM: 24124 kB
+VmRSS: 24124 kB
+VmData: 67340 kB
+VmStk: 136 kB
+VmExe: 1040 kB
+VmLib: 1848 kB
+VmPTE: 92 kB
+VmSwap: 0 kB
+```
+
+**31.9 kB virtual, 23.004 kB resident per meter.**
+
+10000 meters
+------------
+
+```
+VmPeak: 278920 kB
+VmSize: 278920 kB
+VmLck: 0 kB
+VmHWM: 227300 kB
+VmRSS: 227300 kB
+VmData: 271756 kB
+VmStk: 136 kB
+VmExe: 1040 kB
+VmLib: 1848 kB
+VmPTE: 488 kB
+VmSwap: 0 kB
+```
+
+**23.6316 kB virtual, 22.618 kB resident per meter.**
diff --git a/vendor/github.com/rcrowley/go-metrics/meter.go b/vendor/github.com/rcrowley/go-metrics/meter.go
new file mode 100644
index 00000000..0389ab0b
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/meter.go
@@ -0,0 +1,233 @@
+package metrics
+
+import (
+ "sync"
+ "time"
+)
+
+// Meters count events to produce exponentially-weighted moving average rates
+// at one-, five-, and fifteen-minutes and a mean rate.
+type Meter interface {
+ Count() int64
+ Mark(int64)
+ Rate1() float64
+ Rate5() float64
+ Rate15() float64
+ RateMean() float64
+ Snapshot() Meter
+}
+
+// GetOrRegisterMeter returns an existing Meter or constructs and registers a
+// new StandardMeter.
+func GetOrRegisterMeter(name string, r Registry) Meter {
+ if nil == r {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, NewMeter).(Meter)
+}
+
+// NewMeter constructs a new StandardMeter and launches a goroutine.
+func NewMeter() Meter {
+ if UseNilMetrics {
+ return NilMeter{}
+ }
+ m := newStandardMeter()
+ arbiter.Lock()
+ defer arbiter.Unlock()
+ arbiter.meters = append(arbiter.meters, m)
+ if !arbiter.started {
+ arbiter.started = true
+ go arbiter.tick()
+ }
+ return m
+}
+
+// NewMeter constructs and registers a new StandardMeter and launches a
+// goroutine.
+func NewRegisteredMeter(name string, r Registry) Meter {
+ c := NewMeter()
+ if nil == r {
+ r = DefaultRegistry
+ }
+ r.Register(name, c)
+ return c
+}
+
+// MeterSnapshot is a read-only copy of another Meter.
+type MeterSnapshot struct {
+ count int64
+ rate1, rate5, rate15, rateMean float64
+}
+
+// Count returns the count of events at the time the snapshot was taken.
+func (m *MeterSnapshot) Count() int64 { return m.count }
+
+// Mark panics.
+func (*MeterSnapshot) Mark(n int64) {
+ panic("Mark called on a MeterSnapshot")
+}
+
+// Rate1 returns the one-minute moving average rate of events per second at the
+// time the snapshot was taken.
+func (m *MeterSnapshot) Rate1() float64 { return m.rate1 }
+
+// Rate5 returns the five-minute moving average rate of events per second at
+// the time the snapshot was taken.
+func (m *MeterSnapshot) Rate5() float64 { return m.rate5 }
+
+// Rate15 returns the fifteen-minute moving average rate of events per second
+// at the time the snapshot was taken.
+func (m *MeterSnapshot) Rate15() float64 { return m.rate15 }
+
+// RateMean returns the meter's mean rate of events per second at the time the
+// snapshot was taken.
+func (m *MeterSnapshot) RateMean() float64 { return m.rateMean }
+
+// Snapshot returns the snapshot.
+func (m *MeterSnapshot) Snapshot() Meter { return m }
+
+// NilMeter is a no-op Meter.
+type NilMeter struct{}
+
+// Count is a no-op.
+func (NilMeter) Count() int64 { return 0 }
+
+// Mark is a no-op.
+func (NilMeter) Mark(n int64) {}
+
+// Rate1 is a no-op.
+func (NilMeter) Rate1() float64 { return 0.0 }
+
+// Rate5 is a no-op.
+func (NilMeter) Rate5() float64 { return 0.0 }
+
+// Rate15is a no-op.
+func (NilMeter) Rate15() float64 { return 0.0 }
+
+// RateMean is a no-op.
+func (NilMeter) RateMean() float64 { return 0.0 }
+
+// Snapshot is a no-op.
+func (NilMeter) Snapshot() Meter { return NilMeter{} }
+
+// StandardMeter is the standard implementation of a Meter.
+type StandardMeter struct {
+ lock sync.RWMutex
+ snapshot *MeterSnapshot
+ a1, a5, a15 EWMA
+ startTime time.Time
+}
+
+func newStandardMeter() *StandardMeter {
+ return &StandardMeter{
+ snapshot: &MeterSnapshot{},
+ a1: NewEWMA1(),
+ a5: NewEWMA5(),
+ a15: NewEWMA15(),
+ startTime: time.Now(),
+ }
+}
+
+// Count returns the number of events recorded.
+func (m *StandardMeter) Count() int64 {
+ m.lock.RLock()
+ count := m.snapshot.count
+ m.lock.RUnlock()
+ return count
+}
+
+// Mark records the occurance of n events.
+func (m *StandardMeter) Mark(n int64) {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ m.snapshot.count += n
+ m.a1.Update(n)
+ m.a5.Update(n)
+ m.a15.Update(n)
+ m.updateSnapshot()
+}
+
+// Rate1 returns the one-minute moving average rate of events per second.
+func (m *StandardMeter) Rate1() float64 {
+ m.lock.RLock()
+ rate1 := m.snapshot.rate1
+ m.lock.RUnlock()
+ return rate1
+}
+
+// Rate5 returns the five-minute moving average rate of events per second.
+func (m *StandardMeter) Rate5() float64 {
+ m.lock.RLock()
+ rate5 := m.snapshot.rate5
+ m.lock.RUnlock()
+ return rate5
+}
+
+// Rate15 returns the fifteen-minute moving average rate of events per second.
+func (m *StandardMeter) Rate15() float64 {
+ m.lock.RLock()
+ rate15 := m.snapshot.rate15
+ m.lock.RUnlock()
+ return rate15
+}
+
+// RateMean returns the meter's mean rate of events per second.
+func (m *StandardMeter) RateMean() float64 {
+ m.lock.RLock()
+ rateMean := m.snapshot.rateMean
+ m.lock.RUnlock()
+ return rateMean
+}
+
+// Snapshot returns a read-only copy of the meter.
+func (m *StandardMeter) Snapshot() Meter {
+ m.lock.RLock()
+ snapshot := *m.snapshot
+ m.lock.RUnlock()
+ return &snapshot
+}
+
+func (m *StandardMeter) updateSnapshot() {
+ // should run with write lock held on m.lock
+ snapshot := m.snapshot
+ snapshot.rate1 = m.a1.Rate()
+ snapshot.rate5 = m.a5.Rate()
+ snapshot.rate15 = m.a15.Rate()
+ snapshot.rateMean = float64(snapshot.count) / time.Since(m.startTime).Seconds()
+}
+
+func (m *StandardMeter) tick() {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ m.a1.Tick()
+ m.a5.Tick()
+ m.a15.Tick()
+ m.updateSnapshot()
+}
+
+type meterArbiter struct {
+ sync.RWMutex
+ started bool
+ meters []*StandardMeter
+ ticker *time.Ticker
+}
+
+var arbiter = meterArbiter{ticker: time.NewTicker(5e9)}
+
+// Ticks meters on the scheduled interval
+func (ma *meterArbiter) tick() {
+ for {
+ select {
+ case <-ma.ticker.C:
+ ma.tickMeters()
+ }
+ }
+}
+
+func (ma *meterArbiter) tickMeters() {
+ ma.RLock()
+ defer ma.RUnlock()
+ for _, meter := range ma.meters {
+ meter.tick()
+ }
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/metrics.go b/vendor/github.com/rcrowley/go-metrics/metrics.go
new file mode 100644
index 00000000..b97a49ed
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/metrics.go
@@ -0,0 +1,13 @@
+// Go port of Coda Hale's Metrics library
+//
+//
+//
+// Coda Hale's original work:
+package metrics
+
+// UseNilMetrics is checked by the constructor functions for all of the
+// standard metrics. If it is true, the metric returned is a stub.
+//
+// This global kill-switch helps quantify the observer effect and makes
+// for less cluttered pprof profiles.
+var UseNilMetrics bool = false
diff --git a/vendor/github.com/rcrowley/go-metrics/opentsdb.go b/vendor/github.com/rcrowley/go-metrics/opentsdb.go
new file mode 100644
index 00000000..266b6c93
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/opentsdb.go
@@ -0,0 +1,119 @@
+package metrics
+
+import (
+ "bufio"
+ "fmt"
+ "log"
+ "net"
+ "os"
+ "strings"
+ "time"
+)
+
+var shortHostName string = ""
+
+// OpenTSDBConfig provides a container with configuration parameters for
+// the OpenTSDB exporter
+type OpenTSDBConfig struct {
+ Addr *net.TCPAddr // Network address to connect to
+ Registry Registry // Registry to be exported
+ FlushInterval time.Duration // Flush interval
+ DurationUnit time.Duration // Time conversion unit for durations
+ Prefix string // Prefix to be prepended to metric names
+}
+
+// OpenTSDB is a blocking exporter function which reports metrics in r
+// to a TSDB server located at addr, flushing them every d duration
+// and prepending metric names with prefix.
+func OpenTSDB(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) {
+ OpenTSDBWithConfig(OpenTSDBConfig{
+ Addr: addr,
+ Registry: r,
+ FlushInterval: d,
+ DurationUnit: time.Nanosecond,
+ Prefix: prefix,
+ })
+}
+
+// OpenTSDBWithConfig is a blocking exporter function just like OpenTSDB,
+// but it takes a OpenTSDBConfig instead.
+func OpenTSDBWithConfig(c OpenTSDBConfig) {
+ for _ = range time.Tick(c.FlushInterval) {
+ if err := openTSDB(&c); nil != err {
+ log.Println(err)
+ }
+ }
+}
+
+func getShortHostname() string {
+ if shortHostName == "" {
+ host, _ := os.Hostname()
+ if index := strings.Index(host, "."); index > 0 {
+ shortHostName = host[:index]
+ } else {
+ shortHostName = host
+ }
+ }
+ return shortHostName
+}
+
+func openTSDB(c *OpenTSDBConfig) error {
+ shortHostname := getShortHostname()
+ now := time.Now().Unix()
+ du := float64(c.DurationUnit)
+ conn, err := net.DialTCP("tcp", nil, c.Addr)
+ if nil != err {
+ return err
+ }
+ defer conn.Close()
+ w := bufio.NewWriter(conn)
+ c.Registry.Each(func(name string, i interface{}) {
+ switch metric := i.(type) {
+ case Counter:
+ fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, metric.Count(), shortHostname)
+ case Gauge:
+ fmt.Fprintf(w, "put %s.%s.value %d %d host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname)
+ case GaugeFloat64:
+ fmt.Fprintf(w, "put %s.%s.value %d %f host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname)
+ case Histogram:
+ h := metric.Snapshot()
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
+ fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, h.Count(), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.min %d %d host=%s\n", c.Prefix, name, now, h.Min(), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.max %d %d host=%s\n", c.Prefix, name, now, h.Max(), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, h.Mean(), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.std-dev %d %.2f host=%s\n", c.Prefix, name, now, h.StdDev(), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.50-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[0], shortHostname)
+ fmt.Fprintf(w, "put %s.%s.75-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[1], shortHostname)
+ fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2], shortHostname)
+ fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3], shortHostname)
+ fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4], shortHostname)
+ case Meter:
+ m := metric.Snapshot()
+ fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, m.Count(), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate1(), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate5(), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate15(), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, m.RateMean(), shortHostname)
+ case Timer:
+ t := metric.Snapshot()
+ ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
+ fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, t.Count(), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.min %d %d host=%s\n", c.Prefix, name, now, t.Min()/int64(du), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.max %d %d host=%s\n", c.Prefix, name, now, t.Max()/int64(du), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, t.Mean()/du, shortHostname)
+ fmt.Fprintf(w, "put %s.%s.std-dev %d %.2f host=%s\n", c.Prefix, name, now, t.StdDev()/du, shortHostname)
+ fmt.Fprintf(w, "put %s.%s.50-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[0]/du, shortHostname)
+ fmt.Fprintf(w, "put %s.%s.75-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[1]/du, shortHostname)
+ fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2]/du, shortHostname)
+ fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3]/du, shortHostname)
+ fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4]/du, shortHostname)
+ fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate1(), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate5(), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate15(), shortHostname)
+ fmt.Fprintf(w, "put %s.%s.mean-rate %d %.2f host=%s\n", c.Prefix, name, now, t.RateMean(), shortHostname)
+ }
+ w.Flush()
+ })
+ return nil
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/registry.go b/vendor/github.com/rcrowley/go-metrics/registry.go
new file mode 100644
index 00000000..2bb7a1e7
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/registry.go
@@ -0,0 +1,270 @@
+package metrics
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+ "sync"
+)
+
+// DuplicateMetric is the error returned by Registry.Register when a metric
+// already exists. If you mean to Register that metric you must first
+// Unregister the existing metric.
+type DuplicateMetric string
+
+func (err DuplicateMetric) Error() string {
+ return fmt.Sprintf("duplicate metric: %s", string(err))
+}
+
+// A Registry holds references to a set of metrics by name and can iterate
+// over them, calling callback functions provided by the user.
+//
+// This is an interface so as to encourage other structs to implement
+// the Registry API as appropriate.
+type Registry interface {
+
+ // Call the given function for each registered metric.
+ Each(func(string, interface{}))
+
+ // Get the metric by the given name or nil if none is registered.
+ Get(string) interface{}
+
+ // Gets an existing metric or registers the given one.
+ // The interface can be the metric to register if not found in registry,
+ // or a function returning the metric for lazy instantiation.
+ GetOrRegister(string, interface{}) interface{}
+
+ // Register the given metric under the given name.
+ Register(string, interface{}) error
+
+ // Run all registered healthchecks.
+ RunHealthchecks()
+
+ // Unregister the metric with the given name.
+ Unregister(string)
+
+ // Unregister all metrics. (Mostly for testing.)
+ UnregisterAll()
+}
+
+// The standard implementation of a Registry is a mutex-protected map
+// of names to metrics.
+type StandardRegistry struct {
+ metrics map[string]interface{}
+ mutex sync.Mutex
+}
+
+// Create a new registry.
+func NewRegistry() Registry {
+ return &StandardRegistry{metrics: make(map[string]interface{})}
+}
+
+// Call the given function for each registered metric.
+func (r *StandardRegistry) Each(f func(string, interface{})) {
+ for name, i := range r.registered() {
+ f(name, i)
+ }
+}
+
+// Get the metric by the given name or nil if none is registered.
+func (r *StandardRegistry) Get(name string) interface{} {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+ return r.metrics[name]
+}
+
+// Gets an existing metric or creates and registers a new one. Threadsafe
+// alternative to calling Get and Register on failure.
+// The interface can be the metric to register if not found in registry,
+// or a function returning the metric for lazy instantiation.
+func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+ if metric, ok := r.metrics[name]; ok {
+ return metric
+ }
+ if v := reflect.ValueOf(i); v.Kind() == reflect.Func {
+ i = v.Call(nil)[0].Interface()
+ }
+ r.register(name, i)
+ return i
+}
+
+// Register the given metric under the given name. Returns a DuplicateMetric
+// if a metric by the given name is already registered.
+func (r *StandardRegistry) Register(name string, i interface{}) error {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+ return r.register(name, i)
+}
+
+// Run all registered healthchecks.
+func (r *StandardRegistry) RunHealthchecks() {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+ for _, i := range r.metrics {
+ if h, ok := i.(Healthcheck); ok {
+ h.Check()
+ }
+ }
+}
+
+// Unregister the metric with the given name.
+func (r *StandardRegistry) Unregister(name string) {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+ delete(r.metrics, name)
+}
+
+// Unregister all metrics. (Mostly for testing.)
+func (r *StandardRegistry) UnregisterAll() {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+ for name, _ := range r.metrics {
+ delete(r.metrics, name)
+ }
+}
+
+func (r *StandardRegistry) register(name string, i interface{}) error {
+ if _, ok := r.metrics[name]; ok {
+ return DuplicateMetric(name)
+ }
+ switch i.(type) {
+ case Counter, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer:
+ r.metrics[name] = i
+ }
+ return nil
+}
+
+func (r *StandardRegistry) registered() map[string]interface{} {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+ metrics := make(map[string]interface{}, len(r.metrics))
+ for name, i := range r.metrics {
+ metrics[name] = i
+ }
+ return metrics
+}
+
+type PrefixedRegistry struct {
+ underlying Registry
+ prefix string
+}
+
+func NewPrefixedRegistry(prefix string) Registry {
+ return &PrefixedRegistry{
+ underlying: NewRegistry(),
+ prefix: prefix,
+ }
+}
+
+func NewPrefixedChildRegistry(parent Registry, prefix string) Registry {
+ return &PrefixedRegistry{
+ underlying: parent,
+ prefix: prefix,
+ }
+}
+
+// Call the given function for each registered metric.
+func (r *PrefixedRegistry) Each(fn func(string, interface{})) {
+ wrappedFn := func(prefix string) func(string, interface{}) {
+ return func(name string, iface interface{}) {
+ if strings.HasPrefix(name, prefix) {
+ fn(name, iface)
+ } else {
+ return
+ }
+ }
+ }
+
+ baseRegistry, prefix := findPrefix(r, "")
+ baseRegistry.Each(wrappedFn(prefix))
+}
+
+func findPrefix(registry Registry, prefix string) (Registry, string) {
+ switch r := registry.(type) {
+ case *PrefixedRegistry:
+ return findPrefix(r.underlying, r.prefix+prefix)
+ case *StandardRegistry:
+ return r, prefix
+ }
+ return nil, ""
+}
+
+// Get the metric by the given name or nil if none is registered.
+func (r *PrefixedRegistry) Get(name string) interface{} {
+ realName := r.prefix + name
+ return r.underlying.Get(realName)
+}
+
+// Gets an existing metric or registers the given one.
+// The interface can be the metric to register if not found in registry,
+// or a function returning the metric for lazy instantiation.
+func (r *PrefixedRegistry) GetOrRegister(name string, metric interface{}) interface{} {
+ realName := r.prefix + name
+ return r.underlying.GetOrRegister(realName, metric)
+}
+
+// Register the given metric under the given name. The name will be prefixed.
+func (r *PrefixedRegistry) Register(name string, metric interface{}) error {
+ realName := r.prefix + name
+ return r.underlying.Register(realName, metric)
+}
+
+// Run all registered healthchecks.
+func (r *PrefixedRegistry) RunHealthchecks() {
+ r.underlying.RunHealthchecks()
+}
+
+// Unregister the metric with the given name. The name will be prefixed.
+func (r *PrefixedRegistry) Unregister(name string) {
+ realName := r.prefix + name
+ r.underlying.Unregister(realName)
+}
+
+// Unregister all metrics. (Mostly for testing.)
+func (r *PrefixedRegistry) UnregisterAll() {
+ r.underlying.UnregisterAll()
+}
+
+var DefaultRegistry Registry = NewRegistry()
+
+// Call the given function for each registered metric.
+func Each(f func(string, interface{})) {
+ DefaultRegistry.Each(f)
+}
+
+// Get the metric by the given name or nil if none is registered.
+func Get(name string) interface{} {
+ return DefaultRegistry.Get(name)
+}
+
+// Gets an existing metric or creates and registers a new one. Threadsafe
+// alternative to calling Get and Register on failure.
+func GetOrRegister(name string, i interface{}) interface{} {
+ return DefaultRegistry.GetOrRegister(name, i)
+}
+
+// Register the given metric under the given name. Returns a DuplicateMetric
+// if a metric by the given name is already registered.
+func Register(name string, i interface{}) error {
+ return DefaultRegistry.Register(name, i)
+}
+
+// Register the given metric under the given name. Panics if a metric by the
+// given name is already registered.
+func MustRegister(name string, i interface{}) {
+ if err := Register(name, i); err != nil {
+ panic(err)
+ }
+}
+
+// Run all registered healthchecks.
+func RunHealthchecks() {
+ DefaultRegistry.RunHealthchecks()
+}
+
+// Unregister the metric with the given name.
+func Unregister(name string) {
+ DefaultRegistry.Unregister(name)
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/runtime.go b/vendor/github.com/rcrowley/go-metrics/runtime.go
new file mode 100644
index 00000000..11c6b785
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/runtime.go
@@ -0,0 +1,212 @@
+package metrics
+
+import (
+ "runtime"
+ "runtime/pprof"
+ "time"
+)
+
+var (
+ memStats runtime.MemStats
+ runtimeMetrics struct {
+ MemStats struct {
+ Alloc Gauge
+ BuckHashSys Gauge
+ DebugGC Gauge
+ EnableGC Gauge
+ Frees Gauge
+ HeapAlloc Gauge
+ HeapIdle Gauge
+ HeapInuse Gauge
+ HeapObjects Gauge
+ HeapReleased Gauge
+ HeapSys Gauge
+ LastGC Gauge
+ Lookups Gauge
+ Mallocs Gauge
+ MCacheInuse Gauge
+ MCacheSys Gauge
+ MSpanInuse Gauge
+ MSpanSys Gauge
+ NextGC Gauge
+ NumGC Gauge
+ GCCPUFraction GaugeFloat64
+ PauseNs Histogram
+ PauseTotalNs Gauge
+ StackInuse Gauge
+ StackSys Gauge
+ Sys Gauge
+ TotalAlloc Gauge
+ }
+ NumCgoCall Gauge
+ NumGoroutine Gauge
+ NumThread Gauge
+ ReadMemStats Timer
+ }
+ frees uint64
+ lookups uint64
+ mallocs uint64
+ numGC uint32
+ numCgoCalls int64
+
+ threadCreateProfile = pprof.Lookup("threadcreate")
+)
+
+// Capture new values for the Go runtime statistics exported in
+// runtime.MemStats. This is designed to be called as a goroutine.
+func CaptureRuntimeMemStats(r Registry, d time.Duration) {
+ for _ = range time.Tick(d) {
+ CaptureRuntimeMemStatsOnce(r)
+ }
+}
+
+// Capture new values for the Go runtime statistics exported in
+// runtime.MemStats. This is designed to be called in a background
+// goroutine. Giving a registry which has not been given to
+// RegisterRuntimeMemStats will panic.
+//
+// Be very careful with this because runtime.ReadMemStats calls the C
+// functions runtime·semacquire(&runtime·worldsema) and runtime·stoptheworld()
+// and that last one does what it says on the tin.
+func CaptureRuntimeMemStatsOnce(r Registry) {
+ t := time.Now()
+ runtime.ReadMemStats(&memStats) // This takes 50-200us.
+ runtimeMetrics.ReadMemStats.UpdateSince(t)
+
+ runtimeMetrics.MemStats.Alloc.Update(int64(memStats.Alloc))
+ runtimeMetrics.MemStats.BuckHashSys.Update(int64(memStats.BuckHashSys))
+ if memStats.DebugGC {
+ runtimeMetrics.MemStats.DebugGC.Update(1)
+ } else {
+ runtimeMetrics.MemStats.DebugGC.Update(0)
+ }
+ if memStats.EnableGC {
+ runtimeMetrics.MemStats.EnableGC.Update(1)
+ } else {
+ runtimeMetrics.MemStats.EnableGC.Update(0)
+ }
+
+ runtimeMetrics.MemStats.Frees.Update(int64(memStats.Frees - frees))
+ runtimeMetrics.MemStats.HeapAlloc.Update(int64(memStats.HeapAlloc))
+ runtimeMetrics.MemStats.HeapIdle.Update(int64(memStats.HeapIdle))
+ runtimeMetrics.MemStats.HeapInuse.Update(int64(memStats.HeapInuse))
+ runtimeMetrics.MemStats.HeapObjects.Update(int64(memStats.HeapObjects))
+ runtimeMetrics.MemStats.HeapReleased.Update(int64(memStats.HeapReleased))
+ runtimeMetrics.MemStats.HeapSys.Update(int64(memStats.HeapSys))
+ runtimeMetrics.MemStats.LastGC.Update(int64(memStats.LastGC))
+ runtimeMetrics.MemStats.Lookups.Update(int64(memStats.Lookups - lookups))
+ runtimeMetrics.MemStats.Mallocs.Update(int64(memStats.Mallocs - mallocs))
+ runtimeMetrics.MemStats.MCacheInuse.Update(int64(memStats.MCacheInuse))
+ runtimeMetrics.MemStats.MCacheSys.Update(int64(memStats.MCacheSys))
+ runtimeMetrics.MemStats.MSpanInuse.Update(int64(memStats.MSpanInuse))
+ runtimeMetrics.MemStats.MSpanSys.Update(int64(memStats.MSpanSys))
+ runtimeMetrics.MemStats.NextGC.Update(int64(memStats.NextGC))
+ runtimeMetrics.MemStats.NumGC.Update(int64(memStats.NumGC - numGC))
+ runtimeMetrics.MemStats.GCCPUFraction.Update(gcCPUFraction(&memStats))
+
+ //
+ i := numGC % uint32(len(memStats.PauseNs))
+ ii := memStats.NumGC % uint32(len(memStats.PauseNs))
+ if memStats.NumGC-numGC >= uint32(len(memStats.PauseNs)) {
+ for i = 0; i < uint32(len(memStats.PauseNs)); i++ {
+ runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i]))
+ }
+ } else {
+ if i > ii {
+ for ; i < uint32(len(memStats.PauseNs)); i++ {
+ runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i]))
+ }
+ i = 0
+ }
+ for ; i < ii; i++ {
+ runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i]))
+ }
+ }
+ frees = memStats.Frees
+ lookups = memStats.Lookups
+ mallocs = memStats.Mallocs
+ numGC = memStats.NumGC
+
+ runtimeMetrics.MemStats.PauseTotalNs.Update(int64(memStats.PauseTotalNs))
+ runtimeMetrics.MemStats.StackInuse.Update(int64(memStats.StackInuse))
+ runtimeMetrics.MemStats.StackSys.Update(int64(memStats.StackSys))
+ runtimeMetrics.MemStats.Sys.Update(int64(memStats.Sys))
+ runtimeMetrics.MemStats.TotalAlloc.Update(int64(memStats.TotalAlloc))
+
+ currentNumCgoCalls := numCgoCall()
+ runtimeMetrics.NumCgoCall.Update(currentNumCgoCalls - numCgoCalls)
+ numCgoCalls = currentNumCgoCalls
+
+ runtimeMetrics.NumGoroutine.Update(int64(runtime.NumGoroutine()))
+
+ runtimeMetrics.NumThread.Update(int64(threadCreateProfile.Count()))
+}
+
+// Register runtimeMetrics for the Go runtime statistics exported in runtime and
+// specifically runtime.MemStats. The runtimeMetrics are named by their
+// fully-qualified Go symbols, i.e. runtime.MemStats.Alloc.
+func RegisterRuntimeMemStats(r Registry) {
+ runtimeMetrics.MemStats.Alloc = NewGauge()
+ runtimeMetrics.MemStats.BuckHashSys = NewGauge()
+ runtimeMetrics.MemStats.DebugGC = NewGauge()
+ runtimeMetrics.MemStats.EnableGC = NewGauge()
+ runtimeMetrics.MemStats.Frees = NewGauge()
+ runtimeMetrics.MemStats.HeapAlloc = NewGauge()
+ runtimeMetrics.MemStats.HeapIdle = NewGauge()
+ runtimeMetrics.MemStats.HeapInuse = NewGauge()
+ runtimeMetrics.MemStats.HeapObjects = NewGauge()
+ runtimeMetrics.MemStats.HeapReleased = NewGauge()
+ runtimeMetrics.MemStats.HeapSys = NewGauge()
+ runtimeMetrics.MemStats.LastGC = NewGauge()
+ runtimeMetrics.MemStats.Lookups = NewGauge()
+ runtimeMetrics.MemStats.Mallocs = NewGauge()
+ runtimeMetrics.MemStats.MCacheInuse = NewGauge()
+ runtimeMetrics.MemStats.MCacheSys = NewGauge()
+ runtimeMetrics.MemStats.MSpanInuse = NewGauge()
+ runtimeMetrics.MemStats.MSpanSys = NewGauge()
+ runtimeMetrics.MemStats.NextGC = NewGauge()
+ runtimeMetrics.MemStats.NumGC = NewGauge()
+ runtimeMetrics.MemStats.GCCPUFraction = NewGaugeFloat64()
+ runtimeMetrics.MemStats.PauseNs = NewHistogram(NewExpDecaySample(1028, 0.015))
+ runtimeMetrics.MemStats.PauseTotalNs = NewGauge()
+ runtimeMetrics.MemStats.StackInuse = NewGauge()
+ runtimeMetrics.MemStats.StackSys = NewGauge()
+ runtimeMetrics.MemStats.Sys = NewGauge()
+ runtimeMetrics.MemStats.TotalAlloc = NewGauge()
+ runtimeMetrics.NumCgoCall = NewGauge()
+ runtimeMetrics.NumGoroutine = NewGauge()
+ runtimeMetrics.NumThread = NewGauge()
+ runtimeMetrics.ReadMemStats = NewTimer()
+
+ r.Register("runtime.MemStats.Alloc", runtimeMetrics.MemStats.Alloc)
+ r.Register("runtime.MemStats.BuckHashSys", runtimeMetrics.MemStats.BuckHashSys)
+ r.Register("runtime.MemStats.DebugGC", runtimeMetrics.MemStats.DebugGC)
+ r.Register("runtime.MemStats.EnableGC", runtimeMetrics.MemStats.EnableGC)
+ r.Register("runtime.MemStats.Frees", runtimeMetrics.MemStats.Frees)
+ r.Register("runtime.MemStats.HeapAlloc", runtimeMetrics.MemStats.HeapAlloc)
+ r.Register("runtime.MemStats.HeapIdle", runtimeMetrics.MemStats.HeapIdle)
+ r.Register("runtime.MemStats.HeapInuse", runtimeMetrics.MemStats.HeapInuse)
+ r.Register("runtime.MemStats.HeapObjects", runtimeMetrics.MemStats.HeapObjects)
+ r.Register("runtime.MemStats.HeapReleased", runtimeMetrics.MemStats.HeapReleased)
+ r.Register("runtime.MemStats.HeapSys", runtimeMetrics.MemStats.HeapSys)
+ r.Register("runtime.MemStats.LastGC", runtimeMetrics.MemStats.LastGC)
+ r.Register("runtime.MemStats.Lookups", runtimeMetrics.MemStats.Lookups)
+ r.Register("runtime.MemStats.Mallocs", runtimeMetrics.MemStats.Mallocs)
+ r.Register("runtime.MemStats.MCacheInuse", runtimeMetrics.MemStats.MCacheInuse)
+ r.Register("runtime.MemStats.MCacheSys", runtimeMetrics.MemStats.MCacheSys)
+ r.Register("runtime.MemStats.MSpanInuse", runtimeMetrics.MemStats.MSpanInuse)
+ r.Register("runtime.MemStats.MSpanSys", runtimeMetrics.MemStats.MSpanSys)
+ r.Register("runtime.MemStats.NextGC", runtimeMetrics.MemStats.NextGC)
+ r.Register("runtime.MemStats.NumGC", runtimeMetrics.MemStats.NumGC)
+ r.Register("runtime.MemStats.GCCPUFraction", runtimeMetrics.MemStats.GCCPUFraction)
+ r.Register("runtime.MemStats.PauseNs", runtimeMetrics.MemStats.PauseNs)
+ r.Register("runtime.MemStats.PauseTotalNs", runtimeMetrics.MemStats.PauseTotalNs)
+ r.Register("runtime.MemStats.StackInuse", runtimeMetrics.MemStats.StackInuse)
+ r.Register("runtime.MemStats.StackSys", runtimeMetrics.MemStats.StackSys)
+ r.Register("runtime.MemStats.Sys", runtimeMetrics.MemStats.Sys)
+ r.Register("runtime.MemStats.TotalAlloc", runtimeMetrics.MemStats.TotalAlloc)
+ r.Register("runtime.NumCgoCall", runtimeMetrics.NumCgoCall)
+ r.Register("runtime.NumGoroutine", runtimeMetrics.NumGoroutine)
+ r.Register("runtime.NumThread", runtimeMetrics.NumThread)
+ r.Register("runtime.ReadMemStats", runtimeMetrics.ReadMemStats)
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/runtime_cgo.go b/vendor/github.com/rcrowley/go-metrics/runtime_cgo.go
new file mode 100644
index 00000000..e3391f4e
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/runtime_cgo.go
@@ -0,0 +1,10 @@
+// +build cgo
+// +build !appengine
+
+package metrics
+
+import "runtime"
+
+func numCgoCall() int64 {
+ return runtime.NumCgoCall()
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go b/vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go
new file mode 100644
index 00000000..ca12c05b
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go
@@ -0,0 +1,9 @@
+// +build go1.5
+
+package metrics
+
+import "runtime"
+
+func gcCPUFraction(memStats *runtime.MemStats) float64 {
+ return memStats.GCCPUFraction
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go b/vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go
new file mode 100644
index 00000000..616a3b47
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go
@@ -0,0 +1,7 @@
+// +build !cgo appengine
+
+package metrics
+
+func numCgoCall() int64 {
+ return 0
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go b/vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go
new file mode 100644
index 00000000..be96aa6f
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go
@@ -0,0 +1,9 @@
+// +build !go1.5
+
+package metrics
+
+import "runtime"
+
+func gcCPUFraction(memStats *runtime.MemStats) float64 {
+ return 0
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/sample.go b/vendor/github.com/rcrowley/go-metrics/sample.go
new file mode 100644
index 00000000..fecee5ef
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/sample.go
@@ -0,0 +1,616 @@
+package metrics
+
+import (
+ "math"
+ "math/rand"
+ "sort"
+ "sync"
+ "time"
+)
+
+const rescaleThreshold = time.Hour
+
+// Samples maintain a statistically-significant selection of values from
+// a stream.
+type Sample interface {
+ Clear()
+ Count() int64
+ Max() int64
+ Mean() float64
+ Min() int64
+ Percentile(float64) float64
+ Percentiles([]float64) []float64
+ Size() int
+ Snapshot() Sample
+ StdDev() float64
+ Sum() int64
+ Update(int64)
+ Values() []int64
+ Variance() float64
+}
+
+// ExpDecaySample is an exponentially-decaying sample using a forward-decaying
+// priority reservoir. See Cormode et al's "Forward Decay: A Practical Time
+// Decay Model for Streaming Systems".
+//
+//
+type ExpDecaySample struct {
+ alpha float64
+ count int64
+ mutex sync.Mutex
+ reservoirSize int
+ t0, t1 time.Time
+ values *expDecaySampleHeap
+}
+
+// NewExpDecaySample constructs a new exponentially-decaying sample with the
+// given reservoir size and alpha.
+func NewExpDecaySample(reservoirSize int, alpha float64) Sample {
+ if UseNilMetrics {
+ return NilSample{}
+ }
+ s := &ExpDecaySample{
+ alpha: alpha,
+ reservoirSize: reservoirSize,
+ t0: time.Now(),
+ values: newExpDecaySampleHeap(reservoirSize),
+ }
+ s.t1 = s.t0.Add(rescaleThreshold)
+ return s
+}
+
+// Clear clears all samples.
+func (s *ExpDecaySample) Clear() {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ s.count = 0
+ s.t0 = time.Now()
+ s.t1 = s.t0.Add(rescaleThreshold)
+ s.values.Clear()
+}
+
+// Count returns the number of samples recorded, which may exceed the
+// reservoir size.
+func (s *ExpDecaySample) Count() int64 {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ return s.count
+}
+
+// Max returns the maximum value in the sample, which may not be the maximum
+// value ever to be part of the sample.
+func (s *ExpDecaySample) Max() int64 {
+ return SampleMax(s.Values())
+}
+
+// Mean returns the mean of the values in the sample.
+func (s *ExpDecaySample) Mean() float64 {
+ return SampleMean(s.Values())
+}
+
+// Min returns the minimum value in the sample, which may not be the minimum
+// value ever to be part of the sample.
+func (s *ExpDecaySample) Min() int64 {
+ return SampleMin(s.Values())
+}
+
+// Percentile returns an arbitrary percentile of values in the sample.
+func (s *ExpDecaySample) Percentile(p float64) float64 {
+ return SamplePercentile(s.Values(), p)
+}
+
+// Percentiles returns a slice of arbitrary percentiles of values in the
+// sample.
+func (s *ExpDecaySample) Percentiles(ps []float64) []float64 {
+ return SamplePercentiles(s.Values(), ps)
+}
+
+// Size returns the size of the sample, which is at most the reservoir size.
+func (s *ExpDecaySample) Size() int {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ return s.values.Size()
+}
+
+// Snapshot returns a read-only copy of the sample.
+func (s *ExpDecaySample) Snapshot() Sample {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ vals := s.values.Values()
+ values := make([]int64, len(vals))
+ for i, v := range vals {
+ values[i] = v.v
+ }
+ return &SampleSnapshot{
+ count: s.count,
+ values: values,
+ }
+}
+
+// StdDev returns the standard deviation of the values in the sample.
+func (s *ExpDecaySample) StdDev() float64 {
+ return SampleStdDev(s.Values())
+}
+
+// Sum returns the sum of the values in the sample.
+func (s *ExpDecaySample) Sum() int64 {
+ return SampleSum(s.Values())
+}
+
+// Update samples a new value.
+func (s *ExpDecaySample) Update(v int64) {
+ s.update(time.Now(), v)
+}
+
+// Values returns a copy of the values in the sample.
+func (s *ExpDecaySample) Values() []int64 {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ vals := s.values.Values()
+ values := make([]int64, len(vals))
+ for i, v := range vals {
+ values[i] = v.v
+ }
+ return values
+}
+
+// Variance returns the variance of the values in the sample.
+func (s *ExpDecaySample) Variance() float64 {
+ return SampleVariance(s.Values())
+}
+
+// update samples a new value at a particular timestamp. This is a method all
+// its own to facilitate testing.
+func (s *ExpDecaySample) update(t time.Time, v int64) {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ s.count++
+ if s.values.Size() == s.reservoirSize {
+ s.values.Pop()
+ }
+ s.values.Push(expDecaySample{
+ k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / rand.Float64(),
+ v: v,
+ })
+ if t.After(s.t1) {
+ values := s.values.Values()
+ t0 := s.t0
+ s.values.Clear()
+ s.t0 = t
+ s.t1 = s.t0.Add(rescaleThreshold)
+ for _, v := range values {
+ v.k = v.k * math.Exp(-s.alpha*s.t0.Sub(t0).Seconds())
+ s.values.Push(v)
+ }
+ }
+}
+
+// NilSample is a no-op Sample.
+type NilSample struct{}
+
+// Clear is a no-op.
+func (NilSample) Clear() {}
+
+// Count is a no-op.
+func (NilSample) Count() int64 { return 0 }
+
+// Max is a no-op.
+func (NilSample) Max() int64 { return 0 }
+
+// Mean is a no-op.
+func (NilSample) Mean() float64 { return 0.0 }
+
+// Min is a no-op.
+func (NilSample) Min() int64 { return 0 }
+
+// Percentile is a no-op.
+func (NilSample) Percentile(p float64) float64 { return 0.0 }
+
+// Percentiles is a no-op.
+func (NilSample) Percentiles(ps []float64) []float64 {
+ return make([]float64, len(ps))
+}
+
+// Size is a no-op.
+func (NilSample) Size() int { return 0 }
+
+// Sample is a no-op.
+func (NilSample) Snapshot() Sample { return NilSample{} }
+
+// StdDev is a no-op.
+func (NilSample) StdDev() float64 { return 0.0 }
+
+// Sum is a no-op.
+func (NilSample) Sum() int64 { return 0 }
+
+// Update is a no-op.
+func (NilSample) Update(v int64) {}
+
+// Values is a no-op.
+func (NilSample) Values() []int64 { return []int64{} }
+
+// Variance is a no-op.
+func (NilSample) Variance() float64 { return 0.0 }
+
+// SampleMax returns the maximum value of the slice of int64.
+func SampleMax(values []int64) int64 {
+ if 0 == len(values) {
+ return 0
+ }
+ var max int64 = math.MinInt64
+ for _, v := range values {
+ if max < v {
+ max = v
+ }
+ }
+ return max
+}
+
+// SampleMean returns the mean value of the slice of int64.
+func SampleMean(values []int64) float64 {
+ if 0 == len(values) {
+ return 0.0
+ }
+ return float64(SampleSum(values)) / float64(len(values))
+}
+
+// SampleMin returns the minimum value of the slice of int64.
+func SampleMin(values []int64) int64 {
+ if 0 == len(values) {
+ return 0
+ }
+ var min int64 = math.MaxInt64
+ for _, v := range values {
+ if min > v {
+ min = v
+ }
+ }
+ return min
+}
+
+// SamplePercentiles returns an arbitrary percentile of the slice of int64.
+func SamplePercentile(values int64Slice, p float64) float64 {
+ return SamplePercentiles(values, []float64{p})[0]
+}
+
+// SamplePercentiles returns a slice of arbitrary percentiles of the slice of
+// int64.
+func SamplePercentiles(values int64Slice, ps []float64) []float64 {
+ scores := make([]float64, len(ps))
+ size := len(values)
+ if size > 0 {
+ sort.Sort(values)
+ for i, p := range ps {
+ pos := p * float64(size+1)
+ if pos < 1.0 {
+ scores[i] = float64(values[0])
+ } else if pos >= float64(size) {
+ scores[i] = float64(values[size-1])
+ } else {
+ lower := float64(values[int(pos)-1])
+ upper := float64(values[int(pos)])
+ scores[i] = lower + (pos-math.Floor(pos))*(upper-lower)
+ }
+ }
+ }
+ return scores
+}
+
+// SampleSnapshot is a read-only copy of another Sample.
+type SampleSnapshot struct {
+ count int64
+ values []int64
+}
+
+func NewSampleSnapshot(count int64, values []int64) *SampleSnapshot {
+ return &SampleSnapshot{
+ count: count,
+ values: values,
+ }
+}
+
+// Clear panics.
+func (*SampleSnapshot) Clear() {
+ panic("Clear called on a SampleSnapshot")
+}
+
+// Count returns the count of inputs at the time the snapshot was taken.
+func (s *SampleSnapshot) Count() int64 { return s.count }
+
+// Max returns the maximal value at the time the snapshot was taken.
+func (s *SampleSnapshot) Max() int64 { return SampleMax(s.values) }
+
+// Mean returns the mean value at the time the snapshot was taken.
+func (s *SampleSnapshot) Mean() float64 { return SampleMean(s.values) }
+
+// Min returns the minimal value at the time the snapshot was taken.
+func (s *SampleSnapshot) Min() int64 { return SampleMin(s.values) }
+
+// Percentile returns an arbitrary percentile of values at the time the
+// snapshot was taken.
+func (s *SampleSnapshot) Percentile(p float64) float64 {
+ return SamplePercentile(s.values, p)
+}
+
+// Percentiles returns a slice of arbitrary percentiles of values at the time
+// the snapshot was taken.
+func (s *SampleSnapshot) Percentiles(ps []float64) []float64 {
+ return SamplePercentiles(s.values, ps)
+}
+
+// Size returns the size of the sample at the time the snapshot was taken.
+func (s *SampleSnapshot) Size() int { return len(s.values) }
+
+// Snapshot returns the snapshot.
+func (s *SampleSnapshot) Snapshot() Sample { return s }
+
+// StdDev returns the standard deviation of values at the time the snapshot was
+// taken.
+func (s *SampleSnapshot) StdDev() float64 { return SampleStdDev(s.values) }
+
+// Sum returns the sum of values at the time the snapshot was taken.
+func (s *SampleSnapshot) Sum() int64 { return SampleSum(s.values) }
+
+// Update panics.
+func (*SampleSnapshot) Update(int64) {
+ panic("Update called on a SampleSnapshot")
+}
+
+// Values returns a copy of the values in the sample.
+func (s *SampleSnapshot) Values() []int64 {
+ values := make([]int64, len(s.values))
+ copy(values, s.values)
+ return values
+}
+
+// Variance returns the variance of values at the time the snapshot was taken.
+func (s *SampleSnapshot) Variance() float64 { return SampleVariance(s.values) }
+
+// SampleStdDev returns the standard deviation of the slice of int64.
+func SampleStdDev(values []int64) float64 {
+ return math.Sqrt(SampleVariance(values))
+}
+
+// SampleSum returns the sum of the slice of int64.
+func SampleSum(values []int64) int64 {
+ var sum int64
+ for _, v := range values {
+ sum += v
+ }
+ return sum
+}
+
+// SampleVariance returns the variance of the slice of int64.
+func SampleVariance(values []int64) float64 {
+ if 0 == len(values) {
+ return 0.0
+ }
+ m := SampleMean(values)
+ var sum float64
+ for _, v := range values {
+ d := float64(v) - m
+ sum += d * d
+ }
+ return sum / float64(len(values))
+}
+
+// A uniform sample using Vitter's Algorithm R.
+//
+//
+type UniformSample struct {
+ count int64
+ mutex sync.Mutex
+ reservoirSize int
+ values []int64
+}
+
+// NewUniformSample constructs a new uniform sample with the given reservoir
+// size.
+func NewUniformSample(reservoirSize int) Sample {
+ if UseNilMetrics {
+ return NilSample{}
+ }
+ return &UniformSample{
+ reservoirSize: reservoirSize,
+ values: make([]int64, 0, reservoirSize),
+ }
+}
+
+// Clear clears all samples.
+func (s *UniformSample) Clear() {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ s.count = 0
+ s.values = make([]int64, 0, s.reservoirSize)
+}
+
+// Count returns the number of samples recorded, which may exceed the
+// reservoir size.
+func (s *UniformSample) Count() int64 {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ return s.count
+}
+
+// Max returns the maximum value in the sample, which may not be the maximum
+// value ever to be part of the sample.
+func (s *UniformSample) Max() int64 {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ return SampleMax(s.values)
+}
+
+// Mean returns the mean of the values in the sample.
+func (s *UniformSample) Mean() float64 {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ return SampleMean(s.values)
+}
+
+// Min returns the minimum value in the sample, which may not be the minimum
+// value ever to be part of the sample.
+func (s *UniformSample) Min() int64 {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ return SampleMin(s.values)
+}
+
+// Percentile returns an arbitrary percentile of values in the sample.
+func (s *UniformSample) Percentile(p float64) float64 {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ return SamplePercentile(s.values, p)
+}
+
+// Percentiles returns a slice of arbitrary percentiles of values in the
+// sample.
+func (s *UniformSample) Percentiles(ps []float64) []float64 {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ return SamplePercentiles(s.values, ps)
+}
+
+// Size returns the size of the sample, which is at most the reservoir size.
+func (s *UniformSample) Size() int {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ return len(s.values)
+}
+
+// Snapshot returns a read-only copy of the sample.
+func (s *UniformSample) Snapshot() Sample {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ values := make([]int64, len(s.values))
+ copy(values, s.values)
+ return &SampleSnapshot{
+ count: s.count,
+ values: values,
+ }
+}
+
+// StdDev returns the standard deviation of the values in the sample.
+func (s *UniformSample) StdDev() float64 {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ return SampleStdDev(s.values)
+}
+
+// Sum returns the sum of the values in the sample.
+func (s *UniformSample) Sum() int64 {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ return SampleSum(s.values)
+}
+
+// Update samples a new value.
+func (s *UniformSample) Update(v int64) {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ s.count++
+ if len(s.values) < s.reservoirSize {
+ s.values = append(s.values, v)
+ } else {
+ r := rand.Int63n(s.count)
+ if r < int64(len(s.values)) {
+ s.values[int(r)] = v
+ }
+ }
+}
+
+// Values returns a copy of the values in the sample.
+func (s *UniformSample) Values() []int64 {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ values := make([]int64, len(s.values))
+ copy(values, s.values)
+ return values
+}
+
+// Variance returns the variance of the values in the sample.
+func (s *UniformSample) Variance() float64 {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ return SampleVariance(s.values)
+}
+
+// expDecaySample represents an individual sample in a heap.
+type expDecaySample struct {
+ k float64
+ v int64
+}
+
+func newExpDecaySampleHeap(reservoirSize int) *expDecaySampleHeap {
+ return &expDecaySampleHeap{make([]expDecaySample, 0, reservoirSize)}
+}
+
+// expDecaySampleHeap is a min-heap of expDecaySamples.
+// The internal implementation is copied from the standard library's container/heap
+type expDecaySampleHeap struct {
+ s []expDecaySample
+}
+
+func (h *expDecaySampleHeap) Clear() {
+ h.s = h.s[:0]
+}
+
+func (h *expDecaySampleHeap) Push(s expDecaySample) {
+ n := len(h.s)
+ h.s = h.s[0 : n+1]
+ h.s[n] = s
+ h.up(n)
+}
+
+func (h *expDecaySampleHeap) Pop() expDecaySample {
+ n := len(h.s) - 1
+ h.s[0], h.s[n] = h.s[n], h.s[0]
+ h.down(0, n)
+
+ n = len(h.s)
+ s := h.s[n-1]
+ h.s = h.s[0 : n-1]
+ return s
+}
+
+func (h *expDecaySampleHeap) Size() int {
+ return len(h.s)
+}
+
+func (h *expDecaySampleHeap) Values() []expDecaySample {
+ return h.s
+}
+
+func (h *expDecaySampleHeap) up(j int) {
+ for {
+ i := (j - 1) / 2 // parent
+ if i == j || !(h.s[j].k < h.s[i].k) {
+ break
+ }
+ h.s[i], h.s[j] = h.s[j], h.s[i]
+ j = i
+ }
+}
+
+func (h *expDecaySampleHeap) down(i, n int) {
+ for {
+ j1 := 2*i + 1
+ if j1 >= n || j1 < 0 { // j1 < 0 after int overflow
+ break
+ }
+ j := j1 // left child
+ if j2 := j1 + 1; j2 < n && !(h.s[j1].k < h.s[j2].k) {
+ j = j2 // = 2*i + 2 // right child
+ }
+ if !(h.s[j].k < h.s[i].k) {
+ break
+ }
+ h.s[i], h.s[j] = h.s[j], h.s[i]
+ i = j
+ }
+}
+
+type int64Slice []int64
+
+func (p int64Slice) Len() int { return len(p) }
+func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] }
+func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
diff --git a/vendor/github.com/rcrowley/go-metrics/syslog.go b/vendor/github.com/rcrowley/go-metrics/syslog.go
new file mode 100644
index 00000000..693f1908
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/syslog.go
@@ -0,0 +1,78 @@
+// +build !windows
+
+package metrics
+
+import (
+ "fmt"
+ "log/syslog"
+ "time"
+)
+
+// Output each metric in the given registry to syslog periodically using
+// the given syslogger.
+func Syslog(r Registry, d time.Duration, w *syslog.Writer) {
+ for _ = range time.Tick(d) {
+ r.Each(func(name string, i interface{}) {
+ switch metric := i.(type) {
+ case Counter:
+ w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Count()))
+ case Gauge:
+ w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Value()))
+ case GaugeFloat64:
+ w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Value()))
+ case Healthcheck:
+ metric.Check()
+ w.Info(fmt.Sprintf("healthcheck %s: error: %v", name, metric.Error()))
+ case Histogram:
+ h := metric.Snapshot()
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
+ w.Info(fmt.Sprintf(
+ "histogram %s: count: %d min: %d max: %d mean: %.2f stddev: %.2f median: %.2f 75%%: %.2f 95%%: %.2f 99%%: %.2f 99.9%%: %.2f",
+ name,
+ h.Count(),
+ h.Min(),
+ h.Max(),
+ h.Mean(),
+ h.StdDev(),
+ ps[0],
+ ps[1],
+ ps[2],
+ ps[3],
+ ps[4],
+ ))
+ case Meter:
+ m := metric.Snapshot()
+ w.Info(fmt.Sprintf(
+ "meter %s: count: %d 1-min: %.2f 5-min: %.2f 15-min: %.2f mean: %.2f",
+ name,
+ m.Count(),
+ m.Rate1(),
+ m.Rate5(),
+ m.Rate15(),
+ m.RateMean(),
+ ))
+ case Timer:
+ t := metric.Snapshot()
+ ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
+ w.Info(fmt.Sprintf(
+ "timer %s: count: %d min: %d max: %d mean: %.2f stddev: %.2f median: %.2f 75%%: %.2f 95%%: %.2f 99%%: %.2f 99.9%%: %.2f 1-min: %.2f 5-min: %.2f 15-min: %.2f mean-rate: %.2f",
+ name,
+ t.Count(),
+ t.Min(),
+ t.Max(),
+ t.Mean(),
+ t.StdDev(),
+ ps[0],
+ ps[1],
+ ps[2],
+ ps[3],
+ ps[4],
+ t.Rate1(),
+ t.Rate5(),
+ t.Rate15(),
+ t.RateMean(),
+ ))
+ }
+ })
+ }
+}
diff --git a/vendor/github.com/rcrowley/go-metrics/timer.go b/vendor/github.com/rcrowley/go-metrics/timer.go
new file mode 100644
index 00000000..17db8f8d
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/timer.go
@@ -0,0 +1,311 @@
+package metrics
+
+import (
+ "sync"
+ "time"
+)
+
+// Timers capture the duration and rate of events.
+type Timer interface {
+ Count() int64
+ Max() int64
+ Mean() float64
+ Min() int64
+ Percentile(float64) float64
+ Percentiles([]float64) []float64
+ Rate1() float64
+ Rate5() float64
+ Rate15() float64
+ RateMean() float64
+ Snapshot() Timer
+ StdDev() float64
+ Sum() int64
+ Time(func())
+ Update(time.Duration)
+ UpdateSince(time.Time)
+ Variance() float64
+}
+
+// GetOrRegisterTimer returns an existing Timer or constructs and registers a
+// new StandardTimer.
+func GetOrRegisterTimer(name string, r Registry) Timer {
+ if nil == r {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, NewTimer).(Timer)
+}
+
+// NewCustomTimer constructs a new StandardTimer from a Histogram and a Meter.
+func NewCustomTimer(h Histogram, m Meter) Timer {
+ if UseNilMetrics {
+ return NilTimer{}
+ }
+ return &StandardTimer{
+ histogram: h,
+ meter: m,
+ }
+}
+
+// NewRegisteredTimer constructs and registers a new StandardTimer.
+func NewRegisteredTimer(name string, r Registry) Timer {
+ c := NewTimer()
+ if nil == r {
+ r = DefaultRegistry
+ }
+ r.Register(name, c)
+ return c
+}
+
+// NewTimer constructs a new StandardTimer using an exponentially-decaying
+// sample with the same reservoir size and alpha as UNIX load averages.
+func NewTimer() Timer {
+ if UseNilMetrics {
+ return NilTimer{}
+ }
+ return &StandardTimer{
+ histogram: NewHistogram(NewExpDecaySample(1028, 0.015)),
+ meter: NewMeter(),
+ }
+}
+
+// NilTimer is a no-op Timer.
+type NilTimer struct {
+ h Histogram
+ m Meter
+}
+
+// Count is a no-op.
+func (NilTimer) Count() int64 { return 0 }
+
+// Max is a no-op.
+func (NilTimer) Max() int64 { return 0 }
+
+// Mean is a no-op.
+func (NilTimer) Mean() float64 { return 0.0 }
+
+// Min is a no-op.
+func (NilTimer) Min() int64 { return 0 }
+
+// Percentile is a no-op.
+func (NilTimer) Percentile(p float64) float64 { return 0.0 }
+
+// Percentiles is a no-op.
+func (NilTimer) Percentiles(ps []float64) []float64 {
+ return make([]float64, len(ps))
+}
+
+// Rate1 is a no-op.
+func (NilTimer) Rate1() float64 { return 0.0 }
+
+// Rate5 is a no-op.
+func (NilTimer) Rate5() float64 { return 0.0 }
+
+// Rate15 is a no-op.
+func (NilTimer) Rate15() float64 { return 0.0 }
+
+// RateMean is a no-op.
+func (NilTimer) RateMean() float64 { return 0.0 }
+
+// Snapshot is a no-op.
+func (NilTimer) Snapshot() Timer { return NilTimer{} }
+
+// StdDev is a no-op.
+func (NilTimer) StdDev() float64 { return 0.0 }
+
+// Sum is a no-op.
+func (NilTimer) Sum() int64 { return 0 }
+
+// Time is a no-op.
+func (NilTimer) Time(func()) {}
+
+// Update is a no-op.
+func (NilTimer) Update(time.Duration) {}
+
+// UpdateSince is a no-op.
+func (NilTimer) UpdateSince(time.Time) {}
+
+// Variance is a no-op.
+func (NilTimer) Variance() float64 { return 0.0 }
+
+// StandardTimer is the standard implementation of a Timer and uses a Histogram
+// and Meter.
+type StandardTimer struct {
+ histogram Histogram
+ meter Meter
+ mutex sync.Mutex
+}
+
+// Count returns the number of events recorded.
+func (t *StandardTimer) Count() int64 {
+ return t.histogram.Count()
+}
+
+// Max returns the maximum value in the sample.
+func (t *StandardTimer) Max() int64 {
+ return t.histogram.Max()
+}
+
+// Mean returns the mean of the values in the sample.
+func (t *StandardTimer) Mean() float64 {
+ return t.histogram.Mean()
+}
+
+// Min returns the minimum value in the sample.
+func (t *StandardTimer) Min() int64 {
+ return t.histogram.Min()
+}
+
+// Percentile returns an arbitrary percentile of the values in the sample.
+func (t *StandardTimer) Percentile(p float64) float64 {
+ return t.histogram.Percentile(p)
+}
+
+// Percentiles returns a slice of arbitrary percentiles of the values in the
+// sample.
+func (t *StandardTimer) Percentiles(ps []float64) []float64 {
+ return t.histogram.Percentiles(ps)
+}
+
+// Rate1 returns the one-minute moving average rate of events per second.
+func (t *StandardTimer) Rate1() float64 {
+ return t.meter.Rate1()
+}
+
+// Rate5 returns the five-minute moving average rate of events per second.
+func (t *StandardTimer) Rate5() float64 {
+ return t.meter.Rate5()
+}
+
+// Rate15 returns the fifteen-minute moving average rate of events per second.
+func (t *StandardTimer) Rate15() float64 {
+ return t.meter.Rate15()
+}
+
+// RateMean returns the meter's mean rate of events per second.
+func (t *StandardTimer) RateMean() float64 {
+ return t.meter.RateMean()
+}
+
+// Snapshot returns a read-only copy of the timer.
+func (t *StandardTimer) Snapshot() Timer {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+ return &TimerSnapshot{
+ histogram: t.histogram.Snapshot().(*HistogramSnapshot),
+ meter: t.meter.Snapshot().(*MeterSnapshot),
+ }
+}
+
+// StdDev returns the standard deviation of the values in the sample.
+func (t *StandardTimer) StdDev() float64 {
+ return t.histogram.StdDev()
+}
+
+// Sum returns the sum in the sample.
+func (t *StandardTimer) Sum() int64 {
+ return t.histogram.Sum()
+}
+
+// Record the duration of the execution of the given function.
+func (t *StandardTimer) Time(f func()) {
+ ts := time.Now()
+ f()
+ t.Update(time.Since(ts))
+}
+
+// Record the duration of an event.
+func (t *StandardTimer) Update(d time.Duration) {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+ t.histogram.Update(int64(d))
+ t.meter.Mark(1)
+}
+
+// Record the duration of an event that started at a time and ends now.
+func (t *StandardTimer) UpdateSince(ts time.Time) {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+ t.histogram.Update(int64(time.Since(ts)))
+ t.meter.Mark(1)
+}
+
+// Variance returns the variance of the values in the sample.
+func (t *StandardTimer) Variance() float64 {
+ return t.histogram.Variance()
+}
+
+// TimerSnapshot is a read-only copy of another Timer.
+type TimerSnapshot struct {
+ histogram *HistogramSnapshot
+ meter *MeterSnapshot
+}
+
+// Count returns the number of events recorded at the time the snapshot was
+// taken.
+func (t *TimerSnapshot) Count() int64 { return t.histogram.Count() }
+
+// Max returns the maximum value at the time the snapshot was taken.
+func (t *TimerSnapshot) Max() int64 { return t.histogram.Max() }
+
+// Mean returns the mean value at the time the snapshot was taken.
+func (t *TimerSnapshot) Mean() float64 { return t.histogram.Mean() }
+
+// Min returns the minimum value at the time the snapshot was taken.
+func (t *TimerSnapshot) Min() int64 { return t.histogram.Min() }
+
+// Percentile returns an arbitrary percentile of sampled values at the time the
+// snapshot was taken.
+func (t *TimerSnapshot) Percentile(p float64) float64 {
+ return t.histogram.Percentile(p)
+}
+
+// Percentiles returns a slice of arbitrary percentiles of sampled values at
+// the time the snapshot was taken.
+func (t *TimerSnapshot) Percentiles(ps []float64) []float64 {
+ return t.histogram.Percentiles(ps)
+}
+
+// Rate1 returns the one-minute moving average rate of events per second at the
+// time the snapshot was taken.
+func (t *TimerSnapshot) Rate1() float64 { return t.meter.Rate1() }
+
+// Rate5 returns the five-minute moving average rate of events per second at
+// the time the snapshot was taken.
+func (t *TimerSnapshot) Rate5() float64 { return t.meter.Rate5() }
+
+// Rate15 returns the fifteen-minute moving average rate of events per second
+// at the time the snapshot was taken.
+func (t *TimerSnapshot) Rate15() float64 { return t.meter.Rate15() }
+
+// RateMean returns the meter's mean rate of events per second at the time the
+// snapshot was taken.
+func (t *TimerSnapshot) RateMean() float64 { return t.meter.RateMean() }
+
+// Snapshot returns the snapshot.
+func (t *TimerSnapshot) Snapshot() Timer { return t }
+
+// StdDev returns the standard deviation of the values at the time the snapshot
+// was taken.
+func (t *TimerSnapshot) StdDev() float64 { return t.histogram.StdDev() }
+
+// Sum returns the sum at the time the snapshot was taken.
+func (t *TimerSnapshot) Sum() int64 { return t.histogram.Sum() }
+
+// Time panics.
+func (*TimerSnapshot) Time(func()) {
+ panic("Time called on a TimerSnapshot")
+}
+
+// Update panics.
+func (*TimerSnapshot) Update(time.Duration) {
+ panic("Update called on a TimerSnapshot")
+}
+
+// UpdateSince panics.
+func (*TimerSnapshot) UpdateSince(time.Time) {
+ panic("UpdateSince called on a TimerSnapshot")
+}
+
+// Variance returns the variance of the values at the time the snapshot was
+// taken.
+func (t *TimerSnapshot) Variance() float64 { return t.histogram.Variance() }
diff --git a/vendor/github.com/rcrowley/go-metrics/validate.sh b/vendor/github.com/rcrowley/go-metrics/validate.sh
new file mode 100644
index 00000000..f6499982
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/validate.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -e
+
+# check there are no formatting issues
+GOFMT_LINES=`gofmt -l . | wc -l | xargs`
+test $GOFMT_LINES -eq 0 || echo "gofmt needs to be run, ${GOFMT_LINES} files have issues"
+
+# run the tests for the root package
+go test .
diff --git a/vendor/github.com/rcrowley/go-metrics/writer.go b/vendor/github.com/rcrowley/go-metrics/writer.go
new file mode 100644
index 00000000..091e971d
--- /dev/null
+++ b/vendor/github.com/rcrowley/go-metrics/writer.go
@@ -0,0 +1,100 @@
+package metrics
+
+import (
+ "fmt"
+ "io"
+ "sort"
+ "time"
+)
+
+// Write sorts writes each metric in the given registry periodically to the
+// given io.Writer.
+func Write(r Registry, d time.Duration, w io.Writer) {
+ for _ = range time.Tick(d) {
+ WriteOnce(r, w)
+ }
+}
+
+// WriteOnce sorts and writes metrics in the given registry to the given
+// io.Writer.
+func WriteOnce(r Registry, w io.Writer) {
+ var namedMetrics namedMetricSlice
+ r.Each(func(name string, i interface{}) {
+ namedMetrics = append(namedMetrics, namedMetric{name, i})
+ })
+
+ sort.Sort(namedMetrics)
+ for _, namedMetric := range namedMetrics {
+ switch metric := namedMetric.m.(type) {
+ case Counter:
+ fmt.Fprintf(w, "counter %s\n", namedMetric.name)
+ fmt.Fprintf(w, " count: %9d\n", metric.Count())
+ case Gauge:
+ fmt.Fprintf(w, "gauge %s\n", namedMetric.name)
+ fmt.Fprintf(w, " value: %9d\n", metric.Value())
+ case GaugeFloat64:
+ fmt.Fprintf(w, "gauge %s\n", namedMetric.name)
+ fmt.Fprintf(w, " value: %f\n", metric.Value())
+ case Healthcheck:
+ metric.Check()
+ fmt.Fprintf(w, "healthcheck %s\n", namedMetric.name)
+ fmt.Fprintf(w, " error: %v\n", metric.Error())
+ case Histogram:
+ h := metric.Snapshot()
+ ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
+ fmt.Fprintf(w, "histogram %s\n", namedMetric.name)
+ fmt.Fprintf(w, " count: %9d\n", h.Count())
+ fmt.Fprintf(w, " min: %9d\n", h.Min())
+ fmt.Fprintf(w, " max: %9d\n", h.Max())
+ fmt.Fprintf(w, " mean: %12.2f\n", h.Mean())
+ fmt.Fprintf(w, " stddev: %12.2f\n", h.StdDev())
+ fmt.Fprintf(w, " median: %12.2f\n", ps[0])
+ fmt.Fprintf(w, " 75%%: %12.2f\n", ps[1])
+ fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2])
+ fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3])
+ fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4])
+ case Meter:
+ m := metric.Snapshot()
+ fmt.Fprintf(w, "meter %s\n", namedMetric.name)
+ fmt.Fprintf(w, " count: %9d\n", m.Count())
+ fmt.Fprintf(w, " 1-min rate: %12.2f\n", m.Rate1())
+ fmt.Fprintf(w, " 5-min rate: %12.2f\n", m.Rate5())
+ fmt.Fprintf(w, " 15-min rate: %12.2f\n", m.Rate15())
+ fmt.Fprintf(w, " mean rate: %12.2f\n", m.RateMean())
+ case Timer:
+ t := metric.Snapshot()
+ ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
+ fmt.Fprintf(w, "timer %s\n", namedMetric.name)
+ fmt.Fprintf(w, " count: %9d\n", t.Count())
+ fmt.Fprintf(w, " min: %9d\n", t.Min())
+ fmt.Fprintf(w, " max: %9d\n", t.Max())
+ fmt.Fprintf(w, " mean: %12.2f\n", t.Mean())
+ fmt.Fprintf(w, " stddev: %12.2f\n", t.StdDev())
+ fmt.Fprintf(w, " median: %12.2f\n", ps[0])
+ fmt.Fprintf(w, " 75%%: %12.2f\n", ps[1])
+ fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2])
+ fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3])
+ fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4])
+ fmt.Fprintf(w, " 1-min rate: %12.2f\n", t.Rate1())
+ fmt.Fprintf(w, " 5-min rate: %12.2f\n", t.Rate5())
+ fmt.Fprintf(w, " 15-min rate: %12.2f\n", t.Rate15())
+ fmt.Fprintf(w, " mean rate: %12.2f\n", t.RateMean())
+ }
+ }
+}
+
+type namedMetric struct {
+ name string
+ m interface{}
+}
+
+// namedMetricSlice is a slice of namedMetrics that implements sort.Interface.
+type namedMetricSlice []namedMetric
+
+func (nms namedMetricSlice) Len() int { return len(nms) }
+
+func (nms namedMetricSlice) Swap(i, j int) { nms[i], nms[j] = nms[j], nms[i] }
+
+func (nms namedMetricSlice) Less(i, j int) bool {
+ return nms[i].name < nms[j].name
+}
diff --git a/vendor/github.com/streadway/amqp/.gitignore b/vendor/github.com/streadway/amqp/.gitignore
new file mode 100644
index 00000000..ba8a7056
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/.gitignore
@@ -0,0 +1,4 @@
+certs/*
+spec/spec
+examples/simple-consumer/simple-consumer
+examples/simple-producer/simple-producer
diff --git a/vendor/github.com/streadway/amqp/.travis.yml b/vendor/github.com/streadway/amqp/.travis.yml
new file mode 100644
index 00000000..a2c53e81
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/.travis.yml
@@ -0,0 +1,19 @@
+language: go
+
+go:
+ - 1.8
+ - 1.9
+ - tip
+
+services:
+ - rabbitmq
+
+env:
+ - AMQP_URL=amqp://guest:guest@127.0.0.1:5672/
+
+before_install:
+ - go get -v github.com/golang/lint/golint
+
+script:
+ - ./pre-commit
+ - go test -cpu=1,2 -v -tags integration ./...
diff --git a/vendor/github.com/streadway/amqp/CONTRIBUTING.md b/vendor/github.com/streadway/amqp/CONTRIBUTING.md
new file mode 100644
index 00000000..c87f3d7e
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/CONTRIBUTING.md
@@ -0,0 +1,35 @@
+## Prequisites
+
+1. Go: [https://golang.org/dl/](https://golang.org/dl/)
+1. Golint `go get -u -v github.com/golang/lint/golint`
+
+## Contributing
+
+The workflow is pretty standard:
+
+1. Fork github.com/streadway/amqp
+1. Add the pre-commit hook: `ln -s ../../pre-commit .git/hooks/pre-commit`
+1. Create your feature branch (`git checkout -b my-new-feature`)
+1. Run integration tests (see below)
+1. **Implement tests**
+1. Implement fixs
+1. Commit your changes (`git commit -am 'Add some feature'`)
+1. Push to a branch (`git push -u origin my-new-feature`)
+1. Submit a pull request
+
+## Running Tests
+
+The test suite assumes that:
+
+ * A RabbitMQ node is running on localhost with all defaults: [https://www.rabbitmq.com/download.html](https://www.rabbitmq.com/download.html)
+ * `AMQP_URL` is exported to `amqp://guest:guest@127.0.0.1:5672/`
+
+### Integration Tests
+
+After starting a local RabbitMQ, run integration tests with the following:
+
+ env AMQP_URL=amqp://guest:guest@127.0.0.1:5672/ go test -v -cpu 2 -tags integration -race
+
+All integration tests should use the `integrationConnection(...)` test
+helpers defined in `integration_test.go` to setup the integration environment
+and logging.
diff --git a/vendor/github.com/streadway/amqp/LICENSE b/vendor/github.com/streadway/amqp/LICENSE
new file mode 100644
index 00000000..243c0ce7
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+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.
+
+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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
diff --git a/vendor/github.com/streadway/amqp/README.md b/vendor/github.com/streadway/amqp/README.md
new file mode 100644
index 00000000..099db276
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/README.md
@@ -0,0 +1,93 @@
+[](http://travis-ci.org/streadway/amqp) [](http://godoc.org/github.com/streadway/amqp)
+
+# Go RabbitMQ Client Library
+
+This is an AMQP 0.9.1 client with RabbitMQ extensions in Go.
+
+## Project Maturity
+
+This project has been used in production systems for many years. It is reasonably mature
+and feature complete, and as of November 2016 has [a team of maintainers](https://github.com/streadway/amqp/issues/215).
+
+Future API changes are unlikely but possible. They will be discussed on [Github
+issues](https://github.com/streadway/amqp/issues) along with any bugs or
+enhancements.
+
+## Supported Go Versions
+
+This library supports two most recent Go release series, currently 1.8 and 1.9.
+
+
+## Supported RabbitMQ Versions
+
+This project supports RabbitMQ versions starting with `2.0` but primarily tested
+against reasonably recent `3.x` releases. Some features and behaviours may be
+server version-specific.
+
+## Goals
+
+Provide a functional interface that closely represents the AMQP 0.9.1 model
+targeted to RabbitMQ as a server. This includes the minimum necessary to
+interact the semantics of the protocol.
+
+## Non-goals
+
+Things not intended to be supported.
+
+ * Auto reconnect and re-synchronization of client and server topologies.
+ * Reconnection would require understanding the error paths when the
+ topology cannot be declared on reconnect. This would require a new set
+ of types and code paths that are best suited at the call-site of this
+ package. AMQP has a dynamic topology that needs all peers to agree. If
+ this doesn't happen, the behavior is undefined. Instead of producing a
+ possible interface with undefined behavior, this package is designed to
+ be simple for the caller to implement the necessary connection-time
+ topology declaration so that reconnection is trivial and encapsulated in
+ the caller's application code.
+ * AMQP Protocol negotiation for forward or backward compatibility.
+ * 0.9.1 is stable and widely deployed. Versions 0.10 and 1.0 are divergent
+ specifications that change the semantics and wire format of the protocol.
+ We will accept patches for other protocol support but have no plans for
+ implementation ourselves.
+ * Anything other than PLAIN and EXTERNAL authentication mechanisms.
+ * Keeping the mechanisms interface modular makes it possible to extend
+ outside of this package. If other mechanisms prove to be popular, then
+ we would accept patches to include them in this package.
+
+## Usage
+
+See the 'examples' subdirectory for simple producers and consumers executables.
+If you have a use-case in mind which isn't well-represented by the examples,
+please file an issue.
+
+## Documentation
+
+Use [Godoc documentation](http://godoc.org/github.com/streadway/amqp) for
+reference and usage.
+
+[RabbitMQ tutorials in
+Go](https://github.com/rabbitmq/rabbitmq-tutorials/tree/master/go) are also
+available.
+
+## Contributing
+
+Pull requests are very much welcomed. Create your pull request on a non-master
+branch, make sure a test or example is included that covers your change and
+your commits represent coherent changes that include a reason for the change.
+
+To run the integration tests, make sure you have RabbitMQ running on any host,
+export the environment variable `AMQP_URL=amqp://host/` and run `go test -tags
+integration`. TravisCI will also run the integration tests.
+
+Thanks to the [community of contributors](https://github.com/streadway/amqp/graphs/contributors).
+
+## External packages
+
+ * [Google App Engine Dialer support](https://github.com/soundtrackyourbrand/gaeamqp)
+ * [RabbitMQ examples in Go](https://github.com/rabbitmq/rabbitmq-tutorials/tree/master/go)
+
+## License
+
+BSD 2 clause - see LICENSE for more details.
+
+
diff --git a/vendor/github.com/streadway/amqp/allocator.go b/vendor/github.com/streadway/amqp/allocator.go
new file mode 100644
index 00000000..53620e7d
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/allocator.go
@@ -0,0 +1,106 @@
+package amqp
+
+import (
+ "bytes"
+ "fmt"
+ "math/big"
+)
+
+const (
+ free = 0
+ allocated = 1
+)
+
+// allocator maintains a bitset of allocated numbers.
+type allocator struct {
+ pool *big.Int
+ last int
+ low int
+ high int
+}
+
+// NewAllocator reserves and frees integers out of a range between low and
+// high.
+//
+// O(N) worst case space used, where N is maximum allocated, divided by
+// sizeof(big.Word)
+func newAllocator(low, high int) *allocator {
+ return &allocator{
+ pool: big.NewInt(0),
+ last: low,
+ low: low,
+ high: high,
+ }
+}
+
+// String returns a string describing the contents of the allocator like
+// "allocator[low..high] reserved..until"
+//
+// O(N) where N is high-low
+func (a allocator) String() string {
+ b := &bytes.Buffer{}
+ fmt.Fprintf(b, "allocator[%d..%d]", a.low, a.high)
+
+ for low := a.low; low <= a.high; low++ {
+ high := low
+ for a.reserved(high) && high <= a.high {
+ high++
+ }
+
+ if high > low+1 {
+ fmt.Fprintf(b, " %d..%d", low, high-1)
+ } else if high > low {
+ fmt.Fprintf(b, " %d", high-1)
+ }
+
+ low = high
+ }
+ return b.String()
+}
+
+// Next reserves and returns the next available number out of the range between
+// low and high. If no number is available, false is returned.
+//
+// O(N) worst case runtime where N is allocated, but usually O(1) due to a
+// rolling index into the oldest allocation.
+func (a *allocator) next() (int, bool) {
+ wrapped := a.last
+
+ // Find trailing bit
+ for ; a.last <= a.high; a.last++ {
+ if a.reserve(a.last) {
+ return a.last, true
+ }
+ }
+
+ // Find preceding free'd pool
+ a.last = a.low
+
+ for ; a.last < wrapped; a.last++ {
+ if a.reserve(a.last) {
+ return a.last, true
+ }
+ }
+
+ return 0, false
+}
+
+// reserve claims the bit if it is not already claimed, returning true if
+// successfully claimed.
+func (a *allocator) reserve(n int) bool {
+ if a.reserved(n) {
+ return false
+ }
+ a.pool.SetBit(a.pool, n-a.low, allocated)
+ return true
+}
+
+// reserved returns true if the integer has been allocated
+func (a *allocator) reserved(n int) bool {
+ return a.pool.Bit(n-a.low) == allocated
+}
+
+// release frees the use of the number for another allocation
+func (a *allocator) release(n int) {
+ a.pool.SetBit(a.pool, n-a.low, free)
+}
diff --git a/vendor/github.com/streadway/amqp/auth.go b/vendor/github.com/streadway/amqp/auth.go
new file mode 100644
index 00000000..ebc765b6
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/auth.go
@@ -0,0 +1,46 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "fmt"
+)
+
+// Authentication interface provides a means for different SASL authentication
+// mechanisms to be used during connection tuning.
+type Authentication interface {
+ Mechanism() string
+ Response() string
+}
+
+// PlainAuth is a similar to Basic Auth in HTTP.
+type PlainAuth struct {
+ Username string
+ Password string
+}
+
+// Mechanism returns "PLAIN"
+func (auth *PlainAuth) Mechanism() string {
+ return "PLAIN"
+}
+
+// Response returns the null character delimited encoding for the SASL PLAIN Mechanism.
+func (auth *PlainAuth) Response() string {
+ return fmt.Sprintf("\000%s\000%s", auth.Username, auth.Password)
+}
+
+// Finds the first mechanism preferred by the client that the server supports.
+func pickSASLMechanism(client []Authentication, serverMechanisms []string) (auth Authentication, ok bool) {
+ for _, auth = range client {
+ for _, mech := range serverMechanisms {
+ if auth.Mechanism() == mech {
+ return auth, true
+ }
+ }
+ }
+
+ return
+}
diff --git a/vendor/github.com/streadway/amqp/certs.sh b/vendor/github.com/streadway/amqp/certs.sh
new file mode 100644
index 00000000..834f4224
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/certs.sh
@@ -0,0 +1,159 @@
+#!/bin/sh
+#
+# Creates the CA, server and client certs to be used by tls_test.go
+# http://www.rabbitmq.com/ssl.html
+#
+# Copy stdout into the const section of tls_test.go or use for RabbitMQ
+#
+root=$PWD/certs
+
+if [ -f $root/ca/serial ]; then
+ echo >&2 "Previous installation found"
+ echo >&2 "Remove $root/ca and rerun to overwrite"
+ exit 1
+fi
+
+mkdir -p $root/ca/private
+mkdir -p $root/ca/certs
+mkdir -p $root/server
+mkdir -p $root/client
+
+cd $root/ca
+
+chmod 700 private
+touch index.txt
+echo 'unique_subject = no' > index.txt.attr
+echo '01' > serial
+echo >openssl.cnf '
+[ ca ]
+default_ca = testca
+
+[ testca ]
+dir = .
+certificate = $dir/cacert.pem
+database = $dir/index.txt
+new_certs_dir = $dir/certs
+private_key = $dir/private/cakey.pem
+serial = $dir/serial
+
+default_crl_days = 7
+default_days = 3650
+default_md = sha1
+
+policy = testca_policy
+x509_extensions = certificate_extensions
+
+[ testca_policy ]
+commonName = supplied
+stateOrProvinceName = optional
+countryName = optional
+emailAddress = optional
+organizationName = optional
+organizationalUnitName = optional
+
+[ certificate_extensions ]
+basicConstraints = CA:false
+
+[ req ]
+default_bits = 2048
+default_keyfile = ./private/cakey.pem
+default_md = sha1
+prompt = yes
+distinguished_name = root_ca_distinguished_name
+x509_extensions = root_ca_extensions
+
+[ root_ca_distinguished_name ]
+commonName = hostname
+
+[ root_ca_extensions ]
+basicConstraints = CA:true
+keyUsage = keyCertSign, cRLSign
+
+[ client_ca_extensions ]
+basicConstraints = CA:false
+keyUsage = digitalSignature
+extendedKeyUsage = 1.3.6.1.5.5.7.3.2
+
+[ server_ca_extensions ]
+basicConstraints = CA:false
+keyUsage = keyEncipherment
+extendedKeyUsage = 1.3.6.1.5.5.7.3.1
+subjectAltName = @alt_names
+
+[ alt_names ]
+IP.1 = 127.0.0.1
+'
+
+openssl req \
+ -x509 \
+ -nodes \
+ -config openssl.cnf \
+ -newkey rsa:2048 \
+ -days 3650 \
+ -subj "/CN=MyTestCA/" \
+ -out cacert.pem \
+ -outform PEM
+
+openssl x509 \
+ -in cacert.pem \
+ -out cacert.cer \
+ -outform DER
+
+openssl genrsa -out $root/server/key.pem 2048
+openssl genrsa -out $root/client/key.pem 2048
+
+openssl req \
+ -new \
+ -nodes \
+ -config openssl.cnf \
+ -subj "/CN=127.0.0.1/O=server/" \
+ -key $root/server/key.pem \
+ -out $root/server/req.pem \
+ -outform PEM
+
+openssl req \
+ -new \
+ -nodes \
+ -config openssl.cnf \
+ -subj "/CN=127.0.0.1/O=client/" \
+ -key $root/client/key.pem \
+ -out $root/client/req.pem \
+ -outform PEM
+
+openssl ca \
+ -config openssl.cnf \
+ -in $root/server/req.pem \
+ -out $root/server/cert.pem \
+ -notext \
+ -batch \
+ -extensions server_ca_extensions
+
+openssl ca \
+ -config openssl.cnf \
+ -in $root/client/req.pem \
+ -out $root/client/cert.pem \
+ -notext \
+ -batch \
+ -extensions client_ca_extensions
+
+cat <<-END
+const caCert = \`
+`cat $root/ca/cacert.pem`
+\`
+
+const serverCert = \`
+`cat $root/server/cert.pem`
+\`
+
+const serverKey = \`
+`cat $root/server/key.pem`
+\`
+
+const clientCert = \`
+`cat $root/client/cert.pem`
+\`
+
+const clientKey = \`
+`cat $root/client/key.pem`
+\`
+END
diff --git a/vendor/github.com/streadway/amqp/channel.go b/vendor/github.com/streadway/amqp/channel.go
new file mode 100644
index 00000000..6e158af8
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/channel.go
@@ -0,0 +1,1577 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "reflect"
+ "sync"
+ "sync/atomic"
+)
+
+// 0 1 3 7 size+7 size+8
+// +------+---------+-------------+ +------------+ +-----------+
+// | type | channel | size | | payload | | frame-end |
+// +------+---------+-------------+ +------------+ +-----------+
+// octet short long size octets octet
+const frameHeaderSize = 1 + 2 + 4 + 1
+
+/*
+Channel represents an AMQP channel. Used as a context for valid message
+exchange. Errors on methods with this Channel as a receiver means this channel
+should be discarded and a new channel established.
+
+*/
+type Channel struct {
+ destructor sync.Once
+ m sync.Mutex // struct field mutex
+ confirmM sync.Mutex // publisher confirms state mutex
+ notifyM sync.RWMutex
+
+ connection *Connection
+
+ rpc chan message
+ consumers *consumers
+
+ id uint16
+
+ // closed is set to 1 when the channel has been closed - see Channel.send()
+ closed int32
+
+ // true when we will never notify again
+ noNotify bool
+
+ // Channel and Connection exceptions will be broadcast on these listeners.
+ closes []chan *Error
+
+ // Listeners for active=true flow control. When true is sent to a listener,
+ // publishing should pause until false is sent to listeners.
+ flows []chan bool
+
+ // Listeners for returned publishings for unroutable messages on mandatory
+ // publishings or undeliverable messages on immediate publishings.
+ returns []chan Return
+
+ // Listeners for when the server notifies the client that
+ // a consumer has been cancelled.
+ cancels []chan string
+
+ // Allocated when in confirm mode in order to track publish counter and order confirms
+ confirms *confirms
+ confirming bool
+
+ // Selects on any errors from shutdown during RPC
+ errors chan *Error
+
+ // State machine that manages frame order, must only be mutated by the connection
+ recv func(*Channel, frame) error
+
+ // Current state for frame re-assembly, only mutated from recv
+ message messageWithContent
+ header *headerFrame
+ body []byte
+}
+
+// Constructs a new channel with the given framing rules
+func newChannel(c *Connection, id uint16) *Channel {
+ return &Channel{
+ connection: c,
+ id: id,
+ rpc: make(chan message),
+ consumers: makeConsumers(),
+ confirms: newConfirms(),
+ recv: (*Channel).recvMethod,
+ errors: make(chan *Error, 1),
+ }
+}
+
+// shutdown is called by Connection after the channel has been removed from the
+// connection registry.
+func (ch *Channel) shutdown(e *Error) {
+ ch.destructor.Do(func() {
+ ch.m.Lock()
+ defer ch.m.Unlock()
+
+ // Grab an exclusive lock for the notify channels
+ ch.notifyM.Lock()
+ defer ch.notifyM.Unlock()
+
+ // Broadcast abnormal shutdown
+ if e != nil {
+ for _, c := range ch.closes {
+ c <- e
+ }
+ }
+
+ // Signal that from now on, Channel.send() should call
+ // Channel.sendClosed()
+ atomic.StoreInt32(&ch.closed, 1)
+
+ // Notify RPC if we're selecting
+ if e != nil {
+ ch.errors <- e
+ }
+
+ ch.consumers.close()
+
+ for _, c := range ch.closes {
+ close(c)
+ }
+
+ for _, c := range ch.flows {
+ close(c)
+ }
+
+ for _, c := range ch.returns {
+ close(c)
+ }
+
+ for _, c := range ch.cancels {
+ close(c)
+ }
+
+ // Set the slices to nil to prevent the dispatch() range from sending on
+ // the now closed channels after we release the notifyM mutex
+ ch.flows = nil
+ ch.closes = nil
+ ch.returns = nil
+ ch.cancels = nil
+
+ if ch.confirms != nil {
+ ch.confirms.Close()
+ }
+
+ close(ch.errors)
+ ch.noNotify = true
+ })
+}
+
+// send calls Channel.sendOpen() during normal operation.
+//
+// After the channel has been closed, send calls Channel.sendClosed(), ensuring
+// only 'channel.close' is sent to the server.
+func (ch *Channel) send(msg message) (err error) {
+ // If the channel is closed, use Channel.sendClosed()
+ if atomic.LoadInt32(&ch.closed) == 1 {
+ return ch.sendClosed(msg)
+ }
+
+ return ch.sendOpen(msg)
+}
+
+func (ch *Channel) open() error {
+ return ch.call(&channelOpen{}, &channelOpenOk{})
+}
+
+// Performs a request/response call for when the message is not NoWait and is
+// specified as Synchronous.
+func (ch *Channel) call(req message, res ...message) error {
+ if err := ch.send(req); err != nil {
+ return err
+ }
+
+ if req.wait() {
+ select {
+ case e, ok := <-ch.errors:
+ if ok {
+ return e
+ }
+ return ErrClosed
+
+ case msg := <-ch.rpc:
+ if msg != nil {
+ for _, try := range res {
+ if reflect.TypeOf(msg) == reflect.TypeOf(try) {
+ // *res = *msg
+ vres := reflect.ValueOf(try).Elem()
+ vmsg := reflect.ValueOf(msg).Elem()
+ vres.Set(vmsg)
+ return nil
+ }
+ }
+ return ErrCommandInvalid
+ }
+ // RPC channel has been closed without an error, likely due to a hard
+ // error on the Connection. This indicates we have already been
+ // shutdown and if were waiting, will have returned from the errors chan.
+ return ErrClosed
+ }
+ }
+
+ return nil
+}
+
+func (ch *Channel) sendClosed(msg message) (err error) {
+ // After a 'channel.close' is sent or received the only valid response is
+ // channel.close-ok
+ if _, ok := msg.(*channelCloseOk); ok {
+ return ch.connection.send(&methodFrame{
+ ChannelId: ch.id,
+ Method: msg,
+ })
+ }
+
+ return ErrClosed
+}
+
+func (ch *Channel) sendOpen(msg message) (err error) {
+ if content, ok := msg.(messageWithContent); ok {
+ props, body := content.getContent()
+ class, _ := content.id()
+
+ // catch client max frame size==0 and server max frame size==0
+ // set size to length of what we're trying to publish
+ var size int
+ if ch.connection.Config.FrameSize > 0 {
+ size = ch.connection.Config.FrameSize - frameHeaderSize
+ } else {
+ size = len(body)
+ }
+
+ if err = ch.connection.send(&methodFrame{
+ ChannelId: ch.id,
+ Method: content,
+ }); err != nil {
+ return
+ }
+
+ if err = ch.connection.send(&headerFrame{
+ ChannelId: ch.id,
+ ClassId: class,
+ Size: uint64(len(body)),
+ Properties: props,
+ }); err != nil {
+ return
+ }
+
+ // chunk body into size (max frame size - frame header size)
+ for i, j := 0, size; i < len(body); i, j = j, j+size {
+ if j > len(body) {
+ j = len(body)
+ }
+
+ if err = ch.connection.send(&bodyFrame{
+ ChannelId: ch.id,
+ Body: body[i:j],
+ }); err != nil {
+ return
+ }
+ }
+ } else {
+ err = ch.connection.send(&methodFrame{
+ ChannelId: ch.id,
+ Method: msg,
+ })
+ }
+
+ return
+}
+
+// Eventually called via the state machine from the connection's reader
+// goroutine, so assumes serialized access.
+func (ch *Channel) dispatch(msg message) {
+ switch m := msg.(type) {
+ case *channelClose:
+ // lock before sending connection.close-ok
+ // to avoid unexpected interleaving with basic.publish frames if
+ // publishing is happening concurrently
+ ch.m.Lock()
+ ch.send(&channelCloseOk{})
+ ch.m.Unlock()
+ ch.connection.closeChannel(ch, newError(m.ReplyCode, m.ReplyText))
+
+ case *channelFlow:
+ ch.notifyM.RLock()
+ for _, c := range ch.flows {
+ c <- m.Active
+ }
+ ch.notifyM.RUnlock()
+ ch.send(&channelFlowOk{Active: m.Active})
+
+ case *basicCancel:
+ ch.notifyM.RLock()
+ for _, c := range ch.cancels {
+ c <- m.ConsumerTag
+ }
+ ch.notifyM.RUnlock()
+ ch.consumers.cancel(m.ConsumerTag)
+
+ case *basicReturn:
+ ret := newReturn(*m)
+ ch.notifyM.RLock()
+ for _, c := range ch.returns {
+ c <- *ret
+ }
+ ch.notifyM.RUnlock()
+
+ case *basicAck:
+ if ch.confirming {
+ if m.Multiple {
+ ch.confirms.Multiple(Confirmation{m.DeliveryTag, true})
+ } else {
+ ch.confirms.One(Confirmation{m.DeliveryTag, true})
+ }
+ }
+
+ case *basicNack:
+ if ch.confirming {
+ if m.Multiple {
+ ch.confirms.Multiple(Confirmation{m.DeliveryTag, false})
+ } else {
+ ch.confirms.One(Confirmation{m.DeliveryTag, false})
+ }
+ }
+
+ case *basicDeliver:
+ ch.consumers.send(m.ConsumerTag, newDelivery(ch, m))
+ // TODO log failed consumer and close channel, this can happen when
+ // deliveries are in flight and a no-wait cancel has happened
+
+ default:
+ ch.rpc <- msg
+ }
+}
+
+func (ch *Channel) transition(f func(*Channel, frame) error) error {
+ ch.recv = f
+ return nil
+}
+
+func (ch *Channel) recvMethod(f frame) error {
+ switch frame := f.(type) {
+ case *methodFrame:
+ if msg, ok := frame.Method.(messageWithContent); ok {
+ ch.body = make([]byte, 0)
+ ch.message = msg
+ return ch.transition((*Channel).recvHeader)
+ }
+
+ ch.dispatch(frame.Method) // termination state
+ return ch.transition((*Channel).recvMethod)
+
+ case *headerFrame:
+ // drop
+ return ch.transition((*Channel).recvMethod)
+
+ case *bodyFrame:
+ // drop
+ return ch.transition((*Channel).recvMethod)
+ }
+
+ panic("unexpected frame type")
+}
+
+func (ch *Channel) recvHeader(f frame) error {
+ switch frame := f.(type) {
+ case *methodFrame:
+ // interrupt content and handle method
+ return ch.recvMethod(f)
+
+ case *headerFrame:
+ // start collecting if we expect body frames
+ ch.header = frame
+
+ if frame.Size == 0 {
+ ch.message.setContent(ch.header.Properties, ch.body)
+ ch.dispatch(ch.message) // termination state
+ return ch.transition((*Channel).recvMethod)
+ }
+ return ch.transition((*Channel).recvContent)
+
+ case *bodyFrame:
+ // drop and reset
+ return ch.transition((*Channel).recvMethod)
+ }
+
+ panic("unexpected frame type")
+}
+
+// state after method + header and before the length
+// defined by the header has been reached
+func (ch *Channel) recvContent(f frame) error {
+ switch frame := f.(type) {
+ case *methodFrame:
+ // interrupt content and handle method
+ return ch.recvMethod(f)
+
+ case *headerFrame:
+ // drop and reset
+ return ch.transition((*Channel).recvMethod)
+
+ case *bodyFrame:
+ ch.body = append(ch.body, frame.Body...)
+
+ if uint64(len(ch.body)) >= ch.header.Size {
+ ch.message.setContent(ch.header.Properties, ch.body)
+ ch.dispatch(ch.message) // termination state
+ return ch.transition((*Channel).recvMethod)
+ }
+
+ return ch.transition((*Channel).recvContent)
+ }
+
+ panic("unexpected frame type")
+}
+
+/*
+Close initiate a clean channel closure by sending a close message with the error
+code set to '200'.
+
+It is safe to call this method multiple times.
+
+*/
+func (ch *Channel) Close() error {
+ defer ch.connection.closeChannel(ch, nil)
+ return ch.call(
+ &channelClose{ReplyCode: replySuccess},
+ &channelCloseOk{},
+ )
+}
+
+/*
+NotifyClose registers a listener for when the server sends a channel or
+connection exception in the form of a Connection.Close or Channel.Close method.
+Connection exceptions will be broadcast to all open channels and all channels
+will be closed, where channel exceptions will only be broadcast to listeners to
+this channel.
+
+The chan provided will be closed when the Channel is closed and on a
+graceful close, no error will be sent.
+
+*/
+func (ch *Channel) NotifyClose(c chan *Error) chan *Error {
+ ch.notifyM.Lock()
+ defer ch.notifyM.Unlock()
+
+ if ch.noNotify {
+ close(c)
+ } else {
+ ch.closes = append(ch.closes, c)
+ }
+
+ return c
+}
+
+/*
+NotifyFlow registers a listener for basic.flow methods sent by the server.
+When `true` is sent on one of the listener channels, all publishers should
+pause until a `false` is sent.
+
+The server may ask the producer to pause or restart the flow of Publishings
+sent by on a channel. This is a simple flow-control mechanism that a server can
+use to avoid overflowing its queues or otherwise finding itself receiving more
+messages than it can process. Note that this method is not intended for window
+control. It does not affect contents returned by basic.get-ok methods.
+
+When a new channel is opened, it is active (flow is active). Some
+applications assume that channels are inactive until started. To emulate
+this behavior a client MAY open the channel, then pause it.
+
+Publishers should respond to a flow messages as rapidly as possible and the
+server may disconnect over producing channels that do not respect these
+messages.
+
+basic.flow-ok methods will always be returned to the server regardless of
+the number of listeners there are.
+
+To control the flow of deliveries from the server, use the Channel.Flow()
+method instead.
+
+Note: RabbitMQ will rather use TCP pushback on the network connection instead
+of sending basic.flow. This means that if a single channel is producing too
+much on the same connection, all channels using that connection will suffer,
+including acknowledgments from deliveries. Use different Connections if you
+desire to interleave consumers and producers in the same process to avoid your
+basic.ack messages from getting rate limited with your basic.publish messages.
+
+*/
+func (ch *Channel) NotifyFlow(c chan bool) chan bool {
+ ch.notifyM.Lock()
+ defer ch.notifyM.Unlock()
+
+ if ch.noNotify {
+ close(c)
+ } else {
+ ch.flows = append(ch.flows, c)
+ }
+
+ return c
+}
+
+/*
+NotifyReturn registers a listener for basic.return methods. These can be sent
+from the server when a publish is undeliverable either from the mandatory or
+immediate flags.
+
+A return struct has a copy of the Publishing along with some error
+information about why the publishing failed.
+
+*/
+func (ch *Channel) NotifyReturn(c chan Return) chan Return {
+ ch.notifyM.Lock()
+ defer ch.notifyM.Unlock()
+
+ if ch.noNotify {
+ close(c)
+ } else {
+ ch.returns = append(ch.returns, c)
+ }
+
+ return c
+}
+
+/*
+NotifyCancel registers a listener for basic.cancel methods. These can be sent
+from the server when a queue is deleted or when consuming from a mirrored queue
+where the master has just failed (and was moved to another node).
+
+The subscription tag is returned to the listener.
+
+*/
+func (ch *Channel) NotifyCancel(c chan string) chan string {
+ ch.notifyM.Lock()
+ defer ch.notifyM.Unlock()
+
+ if ch.noNotify {
+ close(c)
+ } else {
+ ch.cancels = append(ch.cancels, c)
+ }
+
+ return c
+}
+
+/*
+NotifyConfirm calls NotifyPublish and starts a goroutine sending
+ordered Ack and Nack DeliveryTag to the respective channels.
+
+For strict ordering, use NotifyPublish instead.
+*/
+func (ch *Channel) NotifyConfirm(ack, nack chan uint64) (chan uint64, chan uint64) {
+ confirms := ch.NotifyPublish(make(chan Confirmation, len(ack)+len(nack)))
+
+ go func() {
+ for c := range confirms {
+ if c.Ack {
+ ack <- c.DeliveryTag
+ } else {
+ nack <- c.DeliveryTag
+ }
+ }
+ close(ack)
+ if nack != ack {
+ close(nack)
+ }
+ }()
+
+ return ack, nack
+}
+
+/*
+NotifyPublish registers a listener for reliable publishing. Receives from this
+chan for every publish after Channel.Confirm will be in order starting with
+DeliveryTag 1.
+
+There will be one and only one Confirmation Publishing starting with the
+delivery tag of 1 and progressing sequentially until the total number of
+Publishings have been seen by the server.
+
+Acknowledgments will be received in the order of delivery from the
+NotifyPublish channels even if the server acknowledges them out of order.
+
+The listener chan will be closed when the Channel is closed.
+
+The capacity of the chan Confirmation must be at least as large as the
+number of outstanding publishings. Not having enough buffered chans will
+create a deadlock if you attempt to perform other operations on the Connection
+or Channel while confirms are in-flight.
+
+It's advisable to wait for all Confirmations to arrive before calling
+Channel.Close() or Connection.Close().
+
+*/
+func (ch *Channel) NotifyPublish(confirm chan Confirmation) chan Confirmation {
+ ch.notifyM.Lock()
+ defer ch.notifyM.Unlock()
+
+ if ch.noNotify {
+ close(confirm)
+ } else {
+ ch.confirms.Listen(confirm)
+ }
+
+ return confirm
+
+}
+
+/*
+Qos controls how many messages or how many bytes the server will try to keep on
+the network for consumers before receiving delivery acks. The intent of Qos is
+to make sure the network buffers stay full between the server and client.
+
+With a prefetch count greater than zero, the server will deliver that many
+messages to consumers before acknowledgments are received. The server ignores
+this option when consumers are started with noAck because no acknowledgments
+are expected or sent.
+
+With a prefetch size greater than zero, the server will try to keep at least
+that many bytes of deliveries flushed to the network before receiving
+acknowledgments from the consumers. This option is ignored when consumers are
+started with noAck.
+
+When global is true, these Qos settings apply to all existing and future
+consumers on all channels on the same connection. When false, the Channel.Qos
+settings will apply to all existing and future consumers on this channel.
+RabbitMQ does not implement the global flag.
+
+To get round-robin behavior between consumers consuming from the same queue on
+different connections, set the prefetch count to 1, and the next available
+message on the server will be delivered to the next available consumer.
+
+If your consumer work time is reasonably consistent and not much greater
+than two times your network round trip time, you will see significant
+throughput improvements starting with a prefetch count of 2 or slightly
+greater as described by benchmarks on RabbitMQ.
+
+http://www.rabbitmq.com/blog/2012/04/25/rabbitmq-performance-measurements-part-2/
+*/
+func (ch *Channel) Qos(prefetchCount, prefetchSize int, global bool) error {
+ return ch.call(
+ &basicQos{
+ PrefetchCount: uint16(prefetchCount),
+ PrefetchSize: uint32(prefetchSize),
+ Global: global,
+ },
+ &basicQosOk{},
+ )
+}
+
+/*
+Cancel stops deliveries to the consumer chan established in Channel.Consume and
+identified by consumer.
+
+Only use this method to cleanly stop receiving deliveries from the server and
+cleanly shut down the consumer chan identified by this tag. Using this method
+and waiting for remaining messages to flush from the consumer chan will ensure
+all messages received on the network will be delivered to the receiver of your
+consumer chan.
+
+Continue consuming from the chan Delivery provided by Channel.Consume until the
+chan closes.
+
+When noWait is true, do not wait for the server to acknowledge the cancel.
+Only use this when you are certain there are no deliveries in flight that
+require an acknowledgment, otherwise they will arrive and be dropped in the
+client without an ack, and will not be redelivered to other consumers.
+
+*/
+func (ch *Channel) Cancel(consumer string, noWait bool) error {
+ req := &basicCancel{
+ ConsumerTag: consumer,
+ NoWait: noWait,
+ }
+ res := &basicCancelOk{}
+
+ if err := ch.call(req, res); err != nil {
+ return err
+ }
+
+ if req.wait() {
+ ch.consumers.cancel(res.ConsumerTag)
+ } else {
+ // Potentially could drop deliveries in flight
+ ch.consumers.cancel(consumer)
+ }
+
+ return nil
+}
+
+/*
+QueueDeclare declares a queue to hold messages and deliver to consumers.
+Declaring creates a queue if it doesn't already exist, or ensures that an
+existing queue matches the same parameters.
+
+Every queue declared gets a default binding to the empty exchange "" which has
+the type "direct" with the routing key matching the queue's name. With this
+default binding, it is possible to publish messages that route directly to
+this queue by publishing to "" with the routing key of the queue name.
+
+ QueueDeclare("alerts", true, false, false, false, nil)
+ Publish("", "alerts", false, false, Publishing{Body: []byte("...")})
+
+ Delivery Exchange Key Queue
+ -----------------------------------------------
+ key: alerts -> "" -> alerts -> alerts
+
+The queue name may be empty, in which case the server will generate a unique name
+which will be returned in the Name field of Queue struct.
+
+Durable and Non-Auto-Deleted queues will survive server restarts and remain
+when there are no remaining consumers or bindings. Persistent publishings will
+be restored in this queue on server restart. These queues are only able to be
+bound to durable exchanges.
+
+Non-Durable and Auto-Deleted queues will not be redeclared on server restart
+and will be deleted by the server after a short time when the last consumer is
+canceled or the last consumer's channel is closed. Queues with this lifetime
+can also be deleted normally with QueueDelete. These durable queues can only
+be bound to non-durable exchanges.
+
+Non-Durable and Non-Auto-Deleted queues will remain declared as long as the
+server is running regardless of how many consumers. This lifetime is useful
+for temporary topologies that may have long delays between consumer activity.
+These queues can only be bound to non-durable exchanges.
+
+Durable and Auto-Deleted queues will be restored on server restart, but without
+active consumers will not survive and be removed. This Lifetime is unlikely
+to be useful.
+
+Exclusive queues are only accessible by the connection that declares them and
+will be deleted when the connection closes. Channels on other connections
+will receive an error when attempting to declare, bind, consume, purge or
+delete a queue with the same name.
+
+When noWait is true, the queue will assume to be declared on the server. A
+channel exception will arrive if the conditions are met for existing queues
+or attempting to modify an existing queue from a different connection.
+
+When the error return value is not nil, you can assume the queue could not be
+declared with these parameters, and the channel will be closed.
+
+*/
+func (ch *Channel) QueueDeclare(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) {
+ if err := args.Validate(); err != nil {
+ return Queue{}, err
+ }
+
+ req := &queueDeclare{
+ Queue: name,
+ Passive: false,
+ Durable: durable,
+ AutoDelete: autoDelete,
+ Exclusive: exclusive,
+ NoWait: noWait,
+ Arguments: args,
+ }
+ res := &queueDeclareOk{}
+
+ if err := ch.call(req, res); err != nil {
+ return Queue{}, err
+ }
+
+ if req.wait() {
+ return Queue{
+ Name: res.Queue,
+ Messages: int(res.MessageCount),
+ Consumers: int(res.ConsumerCount),
+ }, nil
+ }
+
+ return Queue{Name: name}, nil
+}
+
+/*
+
+QueueDeclarePassive is functionally and parametrically equivalent to
+QueueDeclare, except that it sets the "passive" attribute to true. A passive
+queue is assumed by RabbitMQ to already exist, and attempting to connect to a
+non-existent queue will cause RabbitMQ to throw an exception. This function
+can be used to test for the existence of a queue.
+
+*/
+func (ch *Channel) QueueDeclarePassive(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) {
+ if err := args.Validate(); err != nil {
+ return Queue{}, err
+ }
+
+ req := &queueDeclare{
+ Queue: name,
+ Passive: true,
+ Durable: durable,
+ AutoDelete: autoDelete,
+ Exclusive: exclusive,
+ NoWait: noWait,
+ Arguments: args,
+ }
+ res := &queueDeclareOk{}
+
+ if err := ch.call(req, res); err != nil {
+ return Queue{}, err
+ }
+
+ if req.wait() {
+ return Queue{
+ Name: res.Queue,
+ Messages: int(res.MessageCount),
+ Consumers: int(res.ConsumerCount),
+ }, nil
+ }
+
+ return Queue{Name: name}, nil
+}
+
+/*
+QueueInspect passively declares a queue by name to inspect the current message
+count and consumer count.
+
+Use this method to check how many unacknowledged messages reside in the queue,
+how many consumers are receiving deliveries, and whether a queue by this
+name already exists.
+
+If the queue by this name exists, use Channel.QueueDeclare check if it is
+declared with specific parameters.
+
+If a queue by this name does not exist, an error will be returned and the
+channel will be closed.
+
+*/
+func (ch *Channel) QueueInspect(name string) (Queue, error) {
+ req := &queueDeclare{
+ Queue: name,
+ Passive: true,
+ }
+ res := &queueDeclareOk{}
+
+ err := ch.call(req, res)
+
+ state := Queue{
+ Name: name,
+ Messages: int(res.MessageCount),
+ Consumers: int(res.ConsumerCount),
+ }
+
+ return state, err
+}
+
+/*
+QueueBind binds an exchange to a queue so that publishings to the exchange will
+be routed to the queue when the publishing routing key matches the binding
+routing key.
+
+ QueueBind("pagers", "alert", "log", false, nil)
+ QueueBind("emails", "info", "log", false, nil)
+
+ Delivery Exchange Key Queue
+ -----------------------------------------------
+ key: alert --> log ----> alert --> pagers
+ key: info ---> log ----> info ---> emails
+ key: debug --> log (none) (dropped)
+
+If a binding with the same key and arguments already exists between the
+exchange and queue, the attempt to rebind will be ignored and the existing
+binding will be retained.
+
+In the case that multiple bindings may cause the message to be routed to the
+same queue, the server will only route the publishing once. This is possible
+with topic exchanges.
+
+ QueueBind("pagers", "alert", "amq.topic", false, nil)
+ QueueBind("emails", "info", "amq.topic", false, nil)
+ QueueBind("emails", "#", "amq.topic", false, nil) // match everything
+
+ Delivery Exchange Key Queue
+ -----------------------------------------------
+ key: alert --> amq.topic ----> alert --> pagers
+ key: info ---> amq.topic ----> # ------> emails
+ \---> info ---/
+ key: debug --> amq.topic ----> # ------> emails
+
+It is only possible to bind a durable queue to a durable exchange regardless of
+whether the queue or exchange is auto-deleted. Bindings between durable queues
+and exchanges will also be restored on server restart.
+
+If the binding could not complete, an error will be returned and the channel
+will be closed.
+
+When noWait is true and the queue could not be bound, the channel will be
+closed with an error.
+
+*/
+func (ch *Channel) QueueBind(name, key, exchange string, noWait bool, args Table) error {
+ if err := args.Validate(); err != nil {
+ return err
+ }
+
+ return ch.call(
+ &queueBind{
+ Queue: name,
+ Exchange: exchange,
+ RoutingKey: key,
+ NoWait: noWait,
+ Arguments: args,
+ },
+ &queueBindOk{},
+ )
+}
+
+/*
+QueueUnbind removes a binding between an exchange and queue matching the key and
+arguments.
+
+It is possible to send and empty string for the exchange name which means to
+unbind the queue from the default exchange.
+
+*/
+func (ch *Channel) QueueUnbind(name, key, exchange string, args Table) error {
+ if err := args.Validate(); err != nil {
+ return err
+ }
+
+ return ch.call(
+ &queueUnbind{
+ Queue: name,
+ Exchange: exchange,
+ RoutingKey: key,
+ Arguments: args,
+ },
+ &queueUnbindOk{},
+ )
+}
+
+/*
+QueuePurge removes all messages from the named queue which are not waiting to
+be acknowledged. Messages that have been delivered but have not yet been
+acknowledged will not be removed.
+
+When successful, returns the number of messages purged.
+
+If noWait is true, do not wait for the server response and the number of
+messages purged will not be meaningful.
+*/
+func (ch *Channel) QueuePurge(name string, noWait bool) (int, error) {
+ req := &queuePurge{
+ Queue: name,
+ NoWait: noWait,
+ }
+ res := &queuePurgeOk{}
+
+ err := ch.call(req, res)
+
+ return int(res.MessageCount), err
+}
+
+/*
+QueueDelete removes the queue from the server including all bindings then
+purges the messages based on server configuration, returning the number of
+messages purged.
+
+When ifUnused is true, the queue will not be deleted if there are any
+consumers on the queue. If there are consumers, an error will be returned and
+the channel will be closed.
+
+When ifEmpty is true, the queue will not be deleted if there are any messages
+remaining on the queue. If there are messages, an error will be returned and
+the channel will be closed.
+
+When noWait is true, the queue will be deleted without waiting for a response
+from the server. The purged message count will not be meaningful. If the queue
+could not be deleted, a channel exception will be raised and the channel will
+be closed.
+
+*/
+func (ch *Channel) QueueDelete(name string, ifUnused, ifEmpty, noWait bool) (int, error) {
+ req := &queueDelete{
+ Queue: name,
+ IfUnused: ifUnused,
+ IfEmpty: ifEmpty,
+ NoWait: noWait,
+ }
+ res := &queueDeleteOk{}
+
+ err := ch.call(req, res)
+
+ return int(res.MessageCount), err
+}
+
+/*
+Consume immediately starts delivering queued messages.
+
+Begin receiving on the returned chan Delivery before any other operation on the
+Connection or Channel.
+
+Continues deliveries to the returned chan Delivery until Channel.Cancel,
+Connection.Close, Channel.Close, or an AMQP exception occurs. Consumers must
+range over the chan to ensure all deliveries are received. Unreceived
+deliveries will block all methods on the same connection.
+
+All deliveries in AMQP must be acknowledged. It is expected of the consumer to
+call Delivery.Ack after it has successfully processed the delivery. If the
+consumer is cancelled or the channel or connection is closed any unacknowledged
+deliveries will be requeued at the end of the same queue.
+
+The consumer is identified by a string that is unique and scoped for all
+consumers on this channel. If you wish to eventually cancel the consumer, use
+the same non-empty identifier in Channel.Cancel. An empty string will cause
+the library to generate a unique identity. The consumer identity will be
+included in every Delivery in the ConsumerTag field
+
+When autoAck (also known as noAck) is true, the server will acknowledge
+deliveries to this consumer prior to writing the delivery to the network. When
+autoAck is true, the consumer should not call Delivery.Ack. Automatically
+acknowledging deliveries means that some deliveries may get lost if the
+consumer is unable to process them after the server delivers them.
+See http://www.rabbitmq.com/confirms.html for more details.
+
+When exclusive is true, the server will ensure that this is the sole consumer
+from this queue. When exclusive is false, the server will fairly distribute
+deliveries across multiple consumers.
+
+The noLocal flag is not supported by RabbitMQ.
+
+It's advisable to use separate connections for
+Channel.Publish and Channel.Consume so not to have TCP pushback on publishing
+affect the ability to consume messages, so this parameter is here mostly for
+completeness.
+
+When noWait is true, do not wait for the server to confirm the request and
+immediately begin deliveries. If it is not possible to consume, a channel
+exception will be raised and the channel will be closed.
+
+Optional arguments can be provided that have specific semantics for the queue
+or server.
+
+Inflight messages, limited by Channel.Qos will be buffered until received from
+the returned chan.
+
+When the Channel or Connection is closed, all buffered and inflight messages will
+be dropped.
+
+When the consumer tag is cancelled, all inflight messages will be delivered until
+the returned chan is closed.
+
+*/
+func (ch *Channel) Consume(queue, consumer string, autoAck, exclusive, noLocal, noWait bool, args Table) (<-chan Delivery, error) {
+ // When we return from ch.call, there may be a delivery already for the
+ // consumer that hasn't been added to the consumer hash yet. Because of
+ // this, we never rely on the server picking a consumer tag for us.
+
+ if err := args.Validate(); err != nil {
+ return nil, err
+ }
+
+ if consumer == "" {
+ consumer = uniqueConsumerTag()
+ }
+
+ req := &basicConsume{
+ Queue: queue,
+ ConsumerTag: consumer,
+ NoLocal: noLocal,
+ NoAck: autoAck,
+ Exclusive: exclusive,
+ NoWait: noWait,
+ Arguments: args,
+ }
+ res := &basicConsumeOk{}
+
+ deliveries := make(chan Delivery)
+
+ ch.consumers.add(consumer, deliveries)
+
+ if err := ch.call(req, res); err != nil {
+ ch.consumers.cancel(consumer)
+ return nil, err
+ }
+
+ return (<-chan Delivery)(deliveries), nil
+}
+
+/*
+ExchangeDeclare declares an exchange on the server. If the exchange does not
+already exist, the server will create it. If the exchange exists, the server
+verifies that it is of the provided type, durability and auto-delete flags.
+
+Errors returned from this method will close the channel.
+
+Exchange names starting with "amq." are reserved for pre-declared and
+standardized exchanges. The client MAY declare an exchange starting with
+"amq." if the passive option is set, or the exchange already exists. Names can
+consist of a non-empty sequence of letters, digits, hyphen, underscore,
+period, or colon.
+
+Each exchange belongs to one of a set of exchange kinds/types implemented by
+the server. The exchange types define the functionality of the exchange - i.e.
+how messages are routed through it. Once an exchange is declared, its type
+cannot be changed. The common types are "direct", "fanout", "topic" and
+"headers".
+
+Durable and Non-Auto-Deleted exchanges will survive server restarts and remain
+declared when there are no remaining bindings. This is the best lifetime for
+long-lived exchange configurations like stable routes and default exchanges.
+
+Non-Durable and Auto-Deleted exchanges will be deleted when there are no
+remaining bindings and not restored on server restart. This lifetime is
+useful for temporary topologies that should not pollute the virtual host on
+failure or after the consumers have completed.
+
+Non-Durable and Non-Auto-deleted exchanges will remain as long as the server is
+running including when there are no remaining bindings. This is useful for
+temporary topologies that may have long delays between bindings.
+
+Durable and Auto-Deleted exchanges will survive server restarts and will be
+removed before and after server restarts when there are no remaining bindings.
+These exchanges are useful for robust temporary topologies or when you require
+binding durable queues to auto-deleted exchanges.
+
+Note: RabbitMQ declares the default exchange types like 'amq.fanout' as
+durable, so queues that bind to these pre-declared exchanges must also be
+durable.
+
+Exchanges declared as `internal` do not accept accept publishings. Internal
+exchanges are useful when you wish to implement inter-exchange topologies
+that should not be exposed to users of the broker.
+
+When noWait is true, declare without waiting for a confirmation from the server.
+The channel may be closed as a result of an error. Add a NotifyClose listener
+to respond to any exceptions.
+
+Optional amqp.Table of arguments that are specific to the server's implementation of
+the exchange can be sent for exchange types that require extra parameters.
+*/
+func (ch *Channel) ExchangeDeclare(name, kind string, durable, autoDelete, internal, noWait bool, args Table) error {
+ if err := args.Validate(); err != nil {
+ return err
+ }
+
+ return ch.call(
+ &exchangeDeclare{
+ Exchange: name,
+ Type: kind,
+ Passive: false,
+ Durable: durable,
+ AutoDelete: autoDelete,
+ Internal: internal,
+ NoWait: noWait,
+ Arguments: args,
+ },
+ &exchangeDeclareOk{},
+ )
+}
+
+/*
+
+ExchangeDeclarePassive is functionally and parametrically equivalent to
+ExchangeDeclare, except that it sets the "passive" attribute to true. A passive
+exchange is assumed by RabbitMQ to already exist, and attempting to connect to a
+non-existent exchange will cause RabbitMQ to throw an exception. This function
+can be used to detect the existence of an exchange.
+
+*/
+func (ch *Channel) ExchangeDeclarePassive(name, kind string, durable, autoDelete, internal, noWait bool, args Table) error {
+ if err := args.Validate(); err != nil {
+ return err
+ }
+
+ return ch.call(
+ &exchangeDeclare{
+ Exchange: name,
+ Type: kind,
+ Passive: true,
+ Durable: durable,
+ AutoDelete: autoDelete,
+ Internal: internal,
+ NoWait: noWait,
+ Arguments: args,
+ },
+ &exchangeDeclareOk{},
+ )
+}
+
+/*
+ExchangeDelete removes the named exchange from the server. When an exchange is
+deleted all queue bindings on the exchange are also deleted. If this exchange
+does not exist, the channel will be closed with an error.
+
+When ifUnused is true, the server will only delete the exchange if it has no queue
+bindings. If the exchange has queue bindings the server does not delete it
+but close the channel with an exception instead. Set this to true if you are
+not the sole owner of the exchange.
+
+When noWait is true, do not wait for a server confirmation that the exchange has
+been deleted. Failing to delete the channel could close the channel. Add a
+NotifyClose listener to respond to these channel exceptions.
+*/
+func (ch *Channel) ExchangeDelete(name string, ifUnused, noWait bool) error {
+ return ch.call(
+ &exchangeDelete{
+ Exchange: name,
+ IfUnused: ifUnused,
+ NoWait: noWait,
+ },
+ &exchangeDeleteOk{},
+ )
+}
+
+/*
+ExchangeBind binds an exchange to another exchange to create inter-exchange
+routing topologies on the server. This can decouple the private topology and
+routing exchanges from exchanges intended solely for publishing endpoints.
+
+Binding two exchanges with identical arguments will not create duplicate
+bindings.
+
+Binding one exchange to another with multiple bindings will only deliver a
+message once. For example if you bind your exchange to `amq.fanout` with two
+different binding keys, only a single message will be delivered to your
+exchange even though multiple bindings will match.
+
+Given a message delivered to the source exchange, the message will be forwarded
+to the destination exchange when the routing key is matched.
+
+ ExchangeBind("sell", "MSFT", "trade", false, nil)
+ ExchangeBind("buy", "AAPL", "trade", false, nil)
+
+ Delivery Source Key Destination
+ example exchange exchange
+ -----------------------------------------------
+ key: AAPL --> trade ----> MSFT sell
+ \---> AAPL --> buy
+
+When noWait is true, do not wait for the server to confirm the binding. If any
+error occurs the channel will be closed. Add a listener to NotifyClose to
+handle these errors.
+
+Optional arguments specific to the exchanges bound can also be specified.
+*/
+func (ch *Channel) ExchangeBind(destination, key, source string, noWait bool, args Table) error {
+ if err := args.Validate(); err != nil {
+ return err
+ }
+
+ return ch.call(
+ &exchangeBind{
+ Destination: destination,
+ Source: source,
+ RoutingKey: key,
+ NoWait: noWait,
+ Arguments: args,
+ },
+ &exchangeBindOk{},
+ )
+}
+
+/*
+ExchangeUnbind unbinds the destination exchange from the source exchange on the
+server by removing the routing key between them. This is the inverse of
+ExchangeBind. If the binding does not currently exist, an error will be
+returned.
+
+When noWait is true, do not wait for the server to confirm the deletion of the
+binding. If any error occurs the channel will be closed. Add a listener to
+NotifyClose to handle these errors.
+
+Optional arguments that are specific to the type of exchanges bound can also be
+provided. These must match the same arguments specified in ExchangeBind to
+identify the binding.
+*/
+func (ch *Channel) ExchangeUnbind(destination, key, source string, noWait bool, args Table) error {
+ if err := args.Validate(); err != nil {
+ return err
+ }
+
+ return ch.call(
+ &exchangeUnbind{
+ Destination: destination,
+ Source: source,
+ RoutingKey: key,
+ NoWait: noWait,
+ Arguments: args,
+ },
+ &exchangeUnbindOk{},
+ )
+}
+
+/*
+Publish sends a Publishing from the client to an exchange on the server.
+
+When you want a single message to be delivered to a single queue, you can
+publish to the default exchange with the routingKey of the queue name. This is
+because every declared queue gets an implicit route to the default exchange.
+
+Since publishings are asynchronous, any undeliverable message will get returned
+by the server. Add a listener with Channel.NotifyReturn to handle any
+undeliverable message when calling publish with either the mandatory or
+immediate parameters as true.
+
+Publishings can be undeliverable when the mandatory flag is true and no queue is
+bound that matches the routing key, or when the immediate flag is true and no
+consumer on the matched queue is ready to accept the delivery.
+
+This can return an error when the channel, connection or socket is closed. The
+error or lack of an error does not indicate whether the server has received this
+publishing.
+
+It is possible for publishing to not reach the broker if the underlying socket
+is shut down without pending publishing packets being flushed from the kernel
+buffers. The easy way of making it probable that all publishings reach the
+server is to always call Connection.Close before terminating your publishing
+application. The way to ensure that all publishings reach the server is to add
+a listener to Channel.NotifyPublish and put the channel in confirm mode with
+Channel.Confirm. Publishing delivery tags and their corresponding
+confirmations start at 1. Exit when all publishings are confirmed.
+
+When Publish does not return an error and the channel is in confirm mode, the
+internal counter for DeliveryTags with the first confirmation starts at 1.
+
+*/
+func (ch *Channel) Publish(exchange, key string, mandatory, immediate bool, msg Publishing) error {
+ if err := msg.Headers.Validate(); err != nil {
+ return err
+ }
+
+ ch.m.Lock()
+ defer ch.m.Unlock()
+
+ if err := ch.send(&basicPublish{
+ Exchange: exchange,
+ RoutingKey: key,
+ Mandatory: mandatory,
+ Immediate: immediate,
+ Body: msg.Body,
+ Properties: properties{
+ Headers: msg.Headers,
+ ContentType: msg.ContentType,
+ ContentEncoding: msg.ContentEncoding,
+ DeliveryMode: msg.DeliveryMode,
+ Priority: msg.Priority,
+ CorrelationId: msg.CorrelationId,
+ ReplyTo: msg.ReplyTo,
+ Expiration: msg.Expiration,
+ MessageId: msg.MessageId,
+ Timestamp: msg.Timestamp,
+ Type: msg.Type,
+ UserId: msg.UserId,
+ AppId: msg.AppId,
+ },
+ }); err != nil {
+ return err
+ }
+
+ if ch.confirming {
+ ch.confirms.Publish()
+ }
+
+ return nil
+}
+
+/*
+Get synchronously receives a single Delivery from the head of a queue from the
+server to the client. In almost all cases, using Channel.Consume will be
+preferred.
+
+If there was a delivery waiting on the queue and that delivery was received, the
+second return value will be true. If there was no delivery waiting or an error
+occurred, the ok bool will be false.
+
+All deliveries must be acknowledged including those from Channel.Get. Call
+Delivery.Ack on the returned delivery when you have fully processed this
+delivery.
+
+When autoAck is true, the server will automatically acknowledge this message so
+you don't have to. But if you are unable to fully process this message before
+the channel or connection is closed, the message will not get requeued.
+
+*/
+func (ch *Channel) Get(queue string, autoAck bool) (msg Delivery, ok bool, err error) {
+ req := &basicGet{Queue: queue, NoAck: autoAck}
+ res := &basicGetOk{}
+ empty := &basicGetEmpty{}
+
+ if err := ch.call(req, res, empty); err != nil {
+ return Delivery{}, false, err
+ }
+
+ if res.DeliveryTag > 0 {
+ return *(newDelivery(ch, res)), true, nil
+ }
+
+ return Delivery{}, false, nil
+}
+
+/*
+Tx puts the channel into transaction mode on the server. All publishings and
+acknowledgments following this method will be atomically committed or rolled
+back for a single queue. Call either Channel.TxCommit or Channel.TxRollback to
+leave a this transaction and immediately start a new transaction.
+
+The atomicity across multiple queues is not defined as queue declarations and
+bindings are not included in the transaction.
+
+The behavior of publishings that are delivered as mandatory or immediate while
+the channel is in a transaction is not defined.
+
+Once a channel has been put into transaction mode, it cannot be taken out of
+transaction mode. Use a different channel for non-transactional semantics.
+
+*/
+func (ch *Channel) Tx() error {
+ return ch.call(
+ &txSelect{},
+ &txSelectOk{},
+ )
+}
+
+/*
+TxCommit atomically commits all publishings and acknowledgments for a single
+queue and immediately start a new transaction.
+
+Calling this method without having called Channel.Tx is an error.
+
+*/
+func (ch *Channel) TxCommit() error {
+ return ch.call(
+ &txCommit{},
+ &txCommitOk{},
+ )
+}
+
+/*
+TxRollback atomically rolls back all publishings and acknowledgments for a
+single queue and immediately start a new transaction.
+
+Calling this method without having called Channel.Tx is an error.
+
+*/
+func (ch *Channel) TxRollback() error {
+ return ch.call(
+ &txRollback{},
+ &txRollbackOk{},
+ )
+}
+
+/*
+Flow pauses the delivery of messages to consumers on this channel. Channels
+are opened with flow control not active, to open a channel with paused
+deliveries immediately call this method with true after calling
+Connection.Channel.
+
+When active is true, this method asks the server to temporarily pause deliveries
+until called again with active as false.
+
+Channel.Get methods will not be affected by flow control.
+
+This method is not intended to act as window control. Use Channel.Qos to limit
+the number of unacknowledged messages or bytes in flight instead.
+
+The server may also send us flow methods to throttle our publishings. A well
+behaving publishing client should add a listener with Channel.NotifyFlow and
+pause its publishings when true is sent on that channel.
+
+Note: RabbitMQ prefers to use TCP push back to control flow for all channels on
+a connection, so under high volume scenarios, it's wise to open separate
+Connections for publishings and deliveries.
+
+*/
+func (ch *Channel) Flow(active bool) error {
+ return ch.call(
+ &channelFlow{Active: active},
+ &channelFlowOk{},
+ )
+}
+
+/*
+Confirm puts this channel into confirm mode so that the client can ensure all
+publishings have successfully been received by the server. After entering this
+mode, the server will send a basic.ack or basic.nack message with the deliver
+tag set to a 1 based incremental index corresponding to every publishing
+received after the this method returns.
+
+Add a listener to Channel.NotifyPublish to respond to the Confirmations. If
+Channel.NotifyPublish is not called, the Confirmations will be silently
+ignored.
+
+The order of acknowledgments is not bound to the order of deliveries.
+
+Ack and Nack confirmations will arrive at some point in the future.
+
+Unroutable mandatory or immediate messages are acknowledged immediately after
+any Channel.NotifyReturn listeners have been notified. Other messages are
+acknowledged when all queues that should have the message routed to them have
+either received acknowledgment of delivery or have enqueued the message,
+persisting the message if necessary.
+
+When noWait is true, the client will not wait for a response. A channel
+exception could occur if the server does not support this method.
+
+*/
+func (ch *Channel) Confirm(noWait bool) error {
+ if err := ch.call(
+ &confirmSelect{Nowait: noWait},
+ &confirmSelectOk{},
+ ); err != nil {
+ return err
+ }
+
+ ch.confirmM.Lock()
+ ch.confirming = true
+ ch.confirmM.Unlock()
+
+ return nil
+}
+
+/*
+Recover redelivers all unacknowledged deliveries on this channel.
+
+When requeue is false, messages will be redelivered to the original consumer.
+
+When requeue is true, messages will be redelivered to any available consumer,
+potentially including the original.
+
+If the deliveries cannot be recovered, an error will be returned and the channel
+will be closed.
+
+Note: this method is not implemented on RabbitMQ, use Delivery.Nack instead
+*/
+func (ch *Channel) Recover(requeue bool) error {
+ return ch.call(
+ &basicRecover{Requeue: requeue},
+ &basicRecoverOk{},
+ )
+}
+
+/*
+Ack acknowledges a delivery by its delivery tag when having been consumed with
+Channel.Consume or Channel.Get.
+
+Ack acknowledges all message received prior to the delivery tag when multiple
+is true.
+
+See also Delivery.Ack
+*/
+func (ch *Channel) Ack(tag uint64, multiple bool) error {
+ return ch.send(&basicAck{
+ DeliveryTag: tag,
+ Multiple: multiple,
+ })
+}
+
+/*
+Nack negatively acknowledges a delivery by its delivery tag. Prefer this
+method to notify the server that you were not able to process this delivery and
+it must be redelivered or dropped.
+
+See also Delivery.Nack
+*/
+func (ch *Channel) Nack(tag uint64, multiple bool, requeue bool) error {
+ return ch.send(&basicNack{
+ DeliveryTag: tag,
+ Multiple: multiple,
+ Requeue: requeue,
+ })
+}
+
+/*
+Reject negatively acknowledges a delivery by its delivery tag. Prefer Nack
+over Reject when communicating with a RabbitMQ server because you can Nack
+multiple messages, reducing the amount of protocol messages to exchange.
+
+See also Delivery.Reject
+*/
+func (ch *Channel) Reject(tag uint64, requeue bool) error {
+ return ch.send(&basicReject{
+ DeliveryTag: tag,
+ Requeue: requeue,
+ })
+}
diff --git a/vendor/github.com/streadway/amqp/confirms.go b/vendor/github.com/streadway/amqp/confirms.go
new file mode 100644
index 00000000..06cbaa71
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/confirms.go
@@ -0,0 +1,94 @@
+package amqp
+
+import "sync"
+
+// confirms resequences and notifies one or multiple publisher confirmation listeners
+type confirms struct {
+ m sync.Mutex
+ listeners []chan Confirmation
+ sequencer map[uint64]Confirmation
+ published uint64
+ expecting uint64
+}
+
+// newConfirms allocates a confirms
+func newConfirms() *confirms {
+ return &confirms{
+ sequencer: map[uint64]Confirmation{},
+ published: 0,
+ expecting: 1,
+ }
+}
+
+func (c *confirms) Listen(l chan Confirmation) {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ c.listeners = append(c.listeners, l)
+}
+
+// publish increments the publishing counter
+func (c *confirms) Publish() uint64 {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ c.published++
+ return c.published
+}
+
+// confirm confirms one publishing, increments the expecting delivery tag, and
+// removes bookkeeping for that delivery tag.
+func (c *confirms) confirm(confirmation Confirmation) {
+ delete(c.sequencer, c.expecting)
+ c.expecting++
+ for _, l := range c.listeners {
+ l <- confirmation
+ }
+}
+
+// resequence confirms any out of order delivered confirmations
+func (c *confirms) resequence() {
+ for c.expecting <= c.published {
+ sequenced, found := c.sequencer[c.expecting]
+ if !found {
+ return
+ }
+ c.confirm(sequenced)
+ }
+}
+
+// one confirms one publishing and all following in the publishing sequence
+func (c *confirms) One(confirmed Confirmation) {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ if c.expecting == confirmed.DeliveryTag {
+ c.confirm(confirmed)
+ } else {
+ c.sequencer[confirmed.DeliveryTag] = confirmed
+ }
+ c.resequence()
+}
+
+// multiple confirms all publishings up until the delivery tag
+func (c *confirms) Multiple(confirmed Confirmation) {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ for c.expecting <= confirmed.DeliveryTag {
+ c.confirm(Confirmation{c.expecting, confirmed.Ack})
+ }
+ c.resequence()
+}
+
+// Close closes all listeners, discarding any out of sequence confirmations
+func (c *confirms) Close() error {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ for _, l := range c.listeners {
+ close(l)
+ }
+ c.listeners = nil
+ return nil
+}
diff --git a/vendor/github.com/streadway/amqp/connection.go b/vendor/github.com/streadway/amqp/connection.go
new file mode 100644
index 00000000..8a84fd2f
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/connection.go
@@ -0,0 +1,840 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "bufio"
+ "crypto/tls"
+ "io"
+ "net"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+const (
+ maxChannelMax = (2 << 15) - 1
+
+ defaultHeartbeat = 10 * time.Second
+ defaultConnectionTimeout = 30 * time.Second
+ defaultProduct = "https://github.com/streadway/amqp"
+ defaultVersion = "β"
+ defaultChannelMax = maxChannelMax
+ defaultLocale = "en_US"
+)
+
+// Config is used in DialConfig and Open to specify the desired tuning
+// parameters used during a connection open handshake. The negotiated tuning
+// will be stored in the returned connection's Config field.
+type Config struct {
+ // The SASL mechanisms to try in the client request, and the successful
+ // mechanism used on the Connection object.
+ // If SASL is nil, PlainAuth from the URL is used.
+ SASL []Authentication
+
+ // Vhost specifies the namespace of permissions, exchanges, queues and
+ // bindings on the server. Dial sets this to the path parsed from the URL.
+ Vhost string
+
+ ChannelMax int // 0 max channels means 2^16 - 1
+ FrameSize int // 0 max bytes means unlimited
+ Heartbeat time.Duration // less than 1s uses the server's interval
+
+ // TLSClientConfig specifies the client configuration of the TLS connection
+ // when establishing a tls transport.
+ // If the URL uses an amqps scheme, then an empty tls.Config with the
+ // ServerName from the URL is used.
+ TLSClientConfig *tls.Config
+
+ // Properties is table of properties that the client advertises to the server.
+ // This is an optional setting - if the application does not set this,
+ // the underlying library will use a generic set of client properties.
+ Properties Table
+
+ // Connection locale that we expect to always be en_US
+ // Even though servers must return it as per the AMQP 0-9-1 spec,
+ // we are not aware of it being used other than to satisfy the spec requirements
+ Locale string
+
+ // Dial returns a net.Conn prepared for a TLS handshake with TSLClientConfig,
+ // then an AMQP connection handshake.
+ // If Dial is nil, net.DialTimeout with a 30s connection and 30s deadline is
+ // used during TLS and AMQP handshaking.
+ Dial func(network, addr string) (net.Conn, error)
+}
+
+// Connection manages the serialization and deserialization of frames from IO
+// and dispatches the frames to the appropriate channel. All RPC methods and
+// asyncronous Publishing, Delivery, Ack, Nack and Return messages are
+// multiplexed on this channel. There must always be active receivers for
+// every asynchronous message on this connection.
+type Connection struct {
+ destructor sync.Once // shutdown once
+ sendM sync.Mutex // conn writer mutex
+ m sync.Mutex // struct field mutex
+
+ conn io.ReadWriteCloser
+
+ rpc chan message
+ writer *writer
+ sends chan time.Time // timestamps of each frame sent
+ deadlines chan readDeadliner // heartbeater updates read deadlines
+
+ allocator *allocator // id generator valid after openTune
+ channels map[uint16]*Channel
+
+ noNotify bool // true when we will never notify again
+ closes []chan *Error
+ blocks []chan Blocking
+
+ errors chan *Error
+
+ Config Config // The negotiated Config after connection.open
+
+ Major int // Server's major version
+ Minor int // Server's minor version
+ Properties Table // Server properties
+ Locales []string // Server locales
+
+ closed int32 // Will be 1 if the connection is closed, 0 otherwise. Should only be accessed as atomic
+}
+
+type readDeadliner interface {
+ SetReadDeadline(time.Time) error
+}
+
+// defaultDial establishes a connection when config.Dial is not provided
+func defaultDial(network, addr string) (net.Conn, error) {
+ conn, err := net.DialTimeout(network, addr, defaultConnectionTimeout)
+ if err != nil {
+ return nil, err
+ }
+
+ // Heartbeating hasn't started yet, don't stall forever on a dead server.
+ // A deadline is set for TLS and AMQP handshaking. After AMQP is established,
+ // the deadline is cleared in openComplete.
+ if err := conn.SetDeadline(time.Now().Add(defaultConnectionTimeout)); err != nil {
+ return nil, err
+ }
+
+ return conn, nil
+}
+
+// Dial accepts a string in the AMQP URI format and returns a new Connection
+// over TCP using PlainAuth. Defaults to a server heartbeat interval of 10
+// seconds and sets the handshake deadline to 30 seconds. After handshake,
+// deadlines are cleared.
+//
+// Dial uses the zero value of tls.Config when it encounters an amqps://
+// scheme. It is equivalent to calling DialTLS(amqp, nil).
+func Dial(url string) (*Connection, error) {
+ return DialConfig(url, Config{
+ Heartbeat: defaultHeartbeat,
+ Locale: defaultLocale,
+ })
+}
+
+// DialTLS accepts a string in the AMQP URI format and returns a new Connection
+// over TCP using PlainAuth. Defaults to a server heartbeat interval of 10
+// seconds and sets the initial read deadline to 30 seconds.
+//
+// DialTLS uses the provided tls.Config when encountering an amqps:// scheme.
+func DialTLS(url string, amqps *tls.Config) (*Connection, error) {
+ return DialConfig(url, Config{
+ Heartbeat: defaultHeartbeat,
+ TLSClientConfig: amqps,
+ Locale: defaultLocale,
+ })
+}
+
+// DialConfig accepts a string in the AMQP URI format and a configuration for
+// the transport and connection setup, returning a new Connection. Defaults to
+// a server heartbeat interval of 10 seconds and sets the initial read deadline
+// to 30 seconds.
+func DialConfig(url string, config Config) (*Connection, error) {
+ var err error
+ var conn net.Conn
+
+ uri, err := ParseURI(url)
+ if err != nil {
+ return nil, err
+ }
+
+ if config.SASL == nil {
+ config.SASL = []Authentication{uri.PlainAuth()}
+ }
+
+ if config.Vhost == "" {
+ config.Vhost = uri.Vhost
+ }
+
+ addr := net.JoinHostPort(uri.Host, strconv.FormatInt(int64(uri.Port), 10))
+
+ dialer := config.Dial
+ if dialer == nil {
+ dialer = defaultDial
+ }
+
+ conn, err = dialer("tcp", addr)
+ if err != nil {
+ return nil, err
+ }
+
+ if uri.Scheme == "amqps" {
+ if config.TLSClientConfig == nil {
+ config.TLSClientConfig = new(tls.Config)
+ }
+
+ // If ServerName has not been specified in TLSClientConfig,
+ // set it to the URI host used for this connection.
+ if config.TLSClientConfig.ServerName == "" {
+ config.TLSClientConfig.ServerName = uri.Host
+ }
+
+ client := tls.Client(conn, config.TLSClientConfig)
+ if err := client.Handshake(); err != nil {
+ conn.Close()
+ return nil, err
+ }
+
+ conn = client
+ }
+
+ return Open(conn, config)
+}
+
+/*
+Open accepts an already established connection, or other io.ReadWriteCloser as
+a transport. Use this method if you have established a TLS connection or wish
+to use your own custom transport.
+
+*/
+func Open(conn io.ReadWriteCloser, config Config) (*Connection, error) {
+ c := &Connection{
+ conn: conn,
+ writer: &writer{bufio.NewWriter(conn)},
+ channels: make(map[uint16]*Channel),
+ rpc: make(chan message),
+ sends: make(chan time.Time),
+ errors: make(chan *Error, 1),
+ deadlines: make(chan readDeadliner, 1),
+ }
+ go c.reader(conn)
+ return c, c.open(config)
+}
+
+/*
+LocalAddr returns the local TCP peer address, or ":0" (the zero value of net.TCPAddr)
+as a fallback default value if the underlying transport does not support LocalAddr().
+*/
+func (c *Connection) LocalAddr() net.Addr {
+ if conn, ok := c.conn.(interface {
+ LocalAddr() net.Addr
+ }); ok {
+ return conn.LocalAddr()
+ }
+ return &net.TCPAddr{}
+}
+
+// ConnectionState returns basic TLS details of the underlying transport.
+// Returns a zero value when the underlying connection does not implement
+// ConnectionState() tls.ConnectionState.
+func (c *Connection) ConnectionState() tls.ConnectionState {
+ if conn, ok := c.conn.(interface {
+ ConnectionState() tls.ConnectionState
+ }); ok {
+ return conn.ConnectionState()
+ }
+ return tls.ConnectionState{}
+}
+
+/*
+NotifyClose registers a listener for close events either initiated by an error
+accompaning a connection.close method or by a normal shutdown.
+
+On normal shutdowns, the chan will be closed.
+
+To reconnect after a transport or protocol error, register a listener here and
+re-run your setup process.
+
+*/
+func (c *Connection) NotifyClose(receiver chan *Error) chan *Error {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ if c.noNotify {
+ close(receiver)
+ } else {
+ c.closes = append(c.closes, receiver)
+ }
+
+ return receiver
+}
+
+/*
+NotifyBlocked registers a listener for RabbitMQ specific TCP flow control
+method extensions connection.blocked and connection.unblocked. Flow control is
+active with a reason when Blocking.Blocked is true. When a Connection is
+blocked, all methods will block across all connections until server resources
+become free again.
+
+This optional extension is supported by the server when the
+"connection.blocked" server capability key is true.
+
+*/
+func (c *Connection) NotifyBlocked(receiver chan Blocking) chan Blocking {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ if c.noNotify {
+ close(receiver)
+ } else {
+ c.blocks = append(c.blocks, receiver)
+ }
+
+ return receiver
+}
+
+/*
+Close requests and waits for the response to close the AMQP connection.
+
+It's advisable to use this message when publishing to ensure all kernel buffers
+have been flushed on the server and client before exiting.
+
+An error indicates that server may not have received this request to close but
+the connection should be treated as closed regardless.
+
+After returning from this call, all resources associated with this connection,
+including the underlying io, Channels, Notify listeners and Channel consumers
+will also be closed.
+*/
+func (c *Connection) Close() error {
+ if c.isClosed() {
+ return ErrClosed
+ }
+
+ defer c.shutdown(nil)
+ return c.call(
+ &connectionClose{
+ ReplyCode: replySuccess,
+ ReplyText: "kthxbai",
+ },
+ &connectionCloseOk{},
+ )
+}
+
+func (c *Connection) closeWith(err *Error) error {
+ if c.isClosed() {
+ return ErrClosed
+ }
+
+ defer c.shutdown(err)
+ return c.call(
+ &connectionClose{
+ ReplyCode: uint16(err.Code),
+ ReplyText: err.Reason,
+ },
+ &connectionCloseOk{},
+ )
+}
+
+func (c *Connection) isClosed() bool {
+ return (atomic.LoadInt32(&c.closed) == 1)
+}
+
+func (c *Connection) send(f frame) error {
+ if c.isClosed() {
+ return ErrClosed
+ }
+
+ c.sendM.Lock()
+ err := c.writer.WriteFrame(f)
+ c.sendM.Unlock()
+
+ if err != nil {
+ // shutdown could be re-entrant from signaling notify chans
+ go c.shutdown(&Error{
+ Code: FrameError,
+ Reason: err.Error(),
+ })
+ } else {
+ // Broadcast we sent a frame, reducing heartbeats, only
+ // if there is something that can receive - like a non-reentrant
+ // call or if the heartbeater isn't running
+ select {
+ case c.sends <- time.Now():
+ default:
+ }
+ }
+
+ return err
+}
+
+func (c *Connection) shutdown(err *Error) {
+ atomic.StoreInt32(&c.closed, 1)
+
+ c.destructor.Do(func() {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ if err != nil {
+ for _, c := range c.closes {
+ c <- err
+ }
+ }
+
+ if err != nil {
+ c.errors <- err
+ }
+ // Shutdown handler goroutine can still receive the result.
+ close(c.errors)
+
+ for _, c := range c.closes {
+ close(c)
+ }
+
+ for _, c := range c.blocks {
+ close(c)
+ }
+
+ // Shutdown the channel, but do not use closeChannel() as it calls
+ // releaseChannel() which requires the connection lock.
+ //
+ // Ranging over c.channels and calling releaseChannel() that mutates
+ // c.channels is racy - see commit 6063341 for an example.
+ for _, ch := range c.channels {
+ ch.shutdown(err)
+ }
+
+ c.conn.Close()
+
+ c.channels = map[uint16]*Channel{}
+ c.allocator = newAllocator(1, c.Config.ChannelMax)
+ c.noNotify = true
+ })
+}
+
+// All methods sent to the connection channel should be synchronous so we
+// can handle them directly without a framing component
+func (c *Connection) demux(f frame) {
+ if f.channel() == 0 {
+ c.dispatch0(f)
+ } else {
+ c.dispatchN(f)
+ }
+}
+
+func (c *Connection) dispatch0(f frame) {
+ switch mf := f.(type) {
+ case *methodFrame:
+ switch m := mf.Method.(type) {
+ case *connectionClose:
+ // Send immediately as shutdown will close our side of the writer.
+ c.send(&methodFrame{
+ ChannelId: 0,
+ Method: &connectionCloseOk{},
+ })
+
+ c.shutdown(newError(m.ReplyCode, m.ReplyText))
+ case *connectionBlocked:
+ for _, c := range c.blocks {
+ c <- Blocking{Active: true, Reason: m.Reason}
+ }
+ case *connectionUnblocked:
+ for _, c := range c.blocks {
+ c <- Blocking{Active: false}
+ }
+ default:
+ c.rpc <- m
+ }
+ case *heartbeatFrame:
+ // kthx - all reads reset our deadline. so we can drop this
+ default:
+ // lolwat - channel0 only responds to methods and heartbeats
+ c.closeWith(ErrUnexpectedFrame)
+ }
+}
+
+func (c *Connection) dispatchN(f frame) {
+ c.m.Lock()
+ channel := c.channels[f.channel()]
+ c.m.Unlock()
+
+ if channel != nil {
+ channel.recv(channel, f)
+ } else {
+ c.dispatchClosed(f)
+ }
+}
+
+// section 2.3.7: "When a peer decides to close a channel or connection, it
+// sends a Close method. The receiving peer MUST respond to a Close with a
+// Close-Ok, and then both parties can close their channel or connection. Note
+// that if peers ignore Close, deadlock can happen when both peers send Close
+// at the same time."
+//
+// When we don't have a channel, so we must respond with close-ok on a close
+// method. This can happen between a channel exception on an asynchronous
+// method like basic.publish and a synchronous close with channel.close.
+// In that case, we'll get both a channel.close and channel.close-ok in any
+// order.
+func (c *Connection) dispatchClosed(f frame) {
+ // Only consider method frames, drop content/header frames
+ if mf, ok := f.(*methodFrame); ok {
+ switch mf.Method.(type) {
+ case *channelClose:
+ c.send(&methodFrame{
+ ChannelId: f.channel(),
+ Method: &channelCloseOk{},
+ })
+ case *channelCloseOk:
+ // we are already closed, so do nothing
+ default:
+ // unexpected method on closed channel
+ c.closeWith(ErrClosed)
+ }
+ }
+}
+
+// Reads each frame off the IO and hand off to the connection object that
+// will demux the streams and dispatch to one of the opened channels or
+// handle on channel 0 (the connection channel).
+func (c *Connection) reader(r io.Reader) {
+ buf := bufio.NewReader(r)
+ frames := &reader{buf}
+ conn, haveDeadliner := r.(readDeadliner)
+
+ for {
+ frame, err := frames.ReadFrame()
+
+ if err != nil {
+ c.shutdown(&Error{Code: FrameError, Reason: err.Error()})
+ return
+ }
+
+ c.demux(frame)
+
+ if haveDeadliner {
+ c.deadlines <- conn
+ }
+ }
+}
+
+// Ensures that at least one frame is being sent at the tuned interval with a
+// jitter tolerance of 1s
+func (c *Connection) heartbeater(interval time.Duration, done chan *Error) {
+ const maxServerHeartbeatsInFlight = 3
+
+ var sendTicks <-chan time.Time
+ if interval > 0 {
+ ticker := time.NewTicker(interval)
+ defer ticker.Stop()
+ sendTicks = ticker.C
+ }
+
+ lastSent := time.Now()
+
+ for {
+ select {
+ case at, stillSending := <-c.sends:
+ // When actively sending, depend on sent frames to reset server timer
+ if stillSending {
+ lastSent = at
+ } else {
+ return
+ }
+
+ case at := <-sendTicks:
+ // When idle, fill the space with a heartbeat frame
+ if at.Sub(lastSent) > interval-time.Second {
+ if err := c.send(&heartbeatFrame{}); err != nil {
+ // send heartbeats even after close/closeOk so we
+ // tick until the connection starts erroring
+ return
+ }
+ }
+
+ case conn := <-c.deadlines:
+ // When reading, reset our side of the deadline, if we've negotiated one with
+ // a deadline that covers at least 2 server heartbeats
+ if interval > 0 {
+ conn.SetReadDeadline(time.Now().Add(maxServerHeartbeatsInFlight * interval))
+ }
+
+ case <-done:
+ return
+ }
+ }
+}
+
+// Convenience method to inspect the Connection.Properties["capabilities"]
+// Table for server identified capabilities like "basic.ack" or
+// "confirm.select".
+func (c *Connection) isCapable(featureName string) bool {
+ capabilities, _ := c.Properties["capabilities"].(Table)
+ hasFeature, _ := capabilities[featureName].(bool)
+ return hasFeature
+}
+
+// allocateChannel records but does not open a new channel with a unique id.
+// This method is the initial part of the channel lifecycle and paired with
+// releaseChannel
+func (c *Connection) allocateChannel() (*Channel, error) {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ if c.isClosed() {
+ return nil, ErrClosed
+ }
+
+ id, ok := c.allocator.next()
+ if !ok {
+ return nil, ErrChannelMax
+ }
+
+ ch := newChannel(c, uint16(id))
+ c.channels[uint16(id)] = ch
+
+ return ch, nil
+}
+
+// releaseChannel removes a channel from the registry as the final part of the
+// channel lifecycle
+func (c *Connection) releaseChannel(id uint16) {
+ c.m.Lock()
+ defer c.m.Unlock()
+
+ delete(c.channels, id)
+ c.allocator.release(int(id))
+}
+
+// openChannel allocates and opens a channel, must be paired with closeChannel
+func (c *Connection) openChannel() (*Channel, error) {
+ ch, err := c.allocateChannel()
+ if err != nil {
+ return nil, err
+ }
+
+ if err := ch.open(); err != nil {
+ c.releaseChannel(ch.id)
+ return nil, err
+ }
+ return ch, nil
+}
+
+// closeChannel releases and initiates a shutdown of the channel. All channel
+// closures should be initiated here for proper channel lifecycle management on
+// this connection.
+func (c *Connection) closeChannel(ch *Channel, e *Error) {
+ ch.shutdown(e)
+ c.releaseChannel(ch.id)
+}
+
+/*
+Channel opens a unique, concurrent server channel to process the bulk of AMQP
+messages. Any error from methods on this receiver will render the receiver
+invalid and a new Channel should be opened.
+
+*/
+func (c *Connection) Channel() (*Channel, error) {
+ return c.openChannel()
+}
+
+func (c *Connection) call(req message, res ...message) error {
+ // Special case for when the protocol header frame is sent insted of a
+ // request method
+ if req != nil {
+ if err := c.send(&methodFrame{ChannelId: 0, Method: req}); err != nil {
+ return err
+ }
+ }
+
+ select {
+ case err, ok := <-c.errors:
+ if !ok {
+ return ErrClosed
+ }
+ return err
+
+ case msg := <-c.rpc:
+ // Try to match one of the result types
+ for _, try := range res {
+ if reflect.TypeOf(msg) == reflect.TypeOf(try) {
+ // *res = *msg
+ vres := reflect.ValueOf(try).Elem()
+ vmsg := reflect.ValueOf(msg).Elem()
+ vres.Set(vmsg)
+ return nil
+ }
+ }
+ return ErrCommandInvalid
+ }
+ // unreachable
+}
+
+// Connection = open-Connection *use-Connection close-Connection
+// open-Connection = C:protocol-header
+// S:START C:START-OK
+// *challenge
+// S:TUNE C:TUNE-OK
+// C:OPEN S:OPEN-OK
+// challenge = S:SECURE C:SECURE-OK
+// use-Connection = *channel
+// close-Connection = C:CLOSE S:CLOSE-OK
+// / S:CLOSE C:CLOSE-OK
+func (c *Connection) open(config Config) error {
+ if err := c.send(&protocolHeader{}); err != nil {
+ return err
+ }
+
+ return c.openStart(config)
+}
+
+func (c *Connection) openStart(config Config) error {
+ start := &connectionStart{}
+
+ if err := c.call(nil, start); err != nil {
+ return err
+ }
+
+ c.Major = int(start.VersionMajor)
+ c.Minor = int(start.VersionMinor)
+ c.Properties = Table(start.ServerProperties)
+ c.Locales = strings.Split(start.Locales, " ")
+
+ // eventually support challenge/response here by also responding to
+ // connectionSecure.
+ auth, ok := pickSASLMechanism(config.SASL, strings.Split(start.Mechanisms, " "))
+ if !ok {
+ return ErrSASL
+ }
+
+ // Save this mechanism off as the one we chose
+ c.Config.SASL = []Authentication{auth}
+
+ // Set the connection locale to client locale
+ c.Config.Locale = config.Locale
+
+ return c.openTune(config, auth)
+}
+
+func (c *Connection) openTune(config Config, auth Authentication) error {
+ if len(config.Properties) == 0 {
+ config.Properties = Table{
+ "product": defaultProduct,
+ "version": defaultVersion,
+ }
+ }
+
+ config.Properties["capabilities"] = Table{
+ "connection.blocked": true,
+ "consumer_cancel_notify": true,
+ }
+
+ ok := &connectionStartOk{
+ ClientProperties: config.Properties,
+ Mechanism: auth.Mechanism(),
+ Response: auth.Response(),
+ Locale: config.Locale,
+ }
+ tune := &connectionTune{}
+
+ if err := c.call(ok, tune); err != nil {
+ // per spec, a connection can only be closed when it has been opened
+ // so at this point, we know it's an auth error, but the socket
+ // was closed instead. Return a meaningful error.
+ return ErrCredentials
+ }
+
+ // When the server and client both use default 0, then the max channel is
+ // only limited by uint16.
+ c.Config.ChannelMax = pick(config.ChannelMax, int(tune.ChannelMax))
+ if c.Config.ChannelMax == 0 {
+ c.Config.ChannelMax = defaultChannelMax
+ }
+ c.Config.ChannelMax = min(c.Config.ChannelMax, maxChannelMax)
+
+ // Frame size includes headers and end byte (len(payload)+8), even if
+ // this is less than FrameMinSize, use what the server sends because the
+ // alternative is to stop the handshake here.
+ c.Config.FrameSize = pick(config.FrameSize, int(tune.FrameMax))
+
+ // Save this off for resetDeadline()
+ c.Config.Heartbeat = time.Second * time.Duration(pick(
+ int(config.Heartbeat/time.Second),
+ int(tune.Heartbeat)))
+
+ // "The client should start sending heartbeats after receiving a
+ // Connection.Tune method"
+ go c.heartbeater(c.Config.Heartbeat, c.NotifyClose(make(chan *Error, 1)))
+
+ if err := c.send(&methodFrame{
+ ChannelId: 0,
+ Method: &connectionTuneOk{
+ ChannelMax: uint16(c.Config.ChannelMax),
+ FrameMax: uint32(c.Config.FrameSize),
+ Heartbeat: uint16(c.Config.Heartbeat / time.Second),
+ },
+ }); err != nil {
+ return err
+ }
+
+ return c.openVhost(config)
+}
+
+func (c *Connection) openVhost(config Config) error {
+ req := &connectionOpen{VirtualHost: config.Vhost}
+ res := &connectionOpenOk{}
+
+ if err := c.call(req, res); err != nil {
+ // Cannot be closed yet, but we know it's a vhost problem
+ return ErrVhost
+ }
+
+ c.Config.Vhost = config.Vhost
+
+ return c.openComplete()
+}
+
+// openComplete performs any final Connection initialization dependent on the
+// connection handshake and clears any state needed for TLS and AMQP handshaking.
+func (c *Connection) openComplete() error {
+ // We clear the deadlines and let the heartbeater reset the read deadline if requested.
+ // RabbitMQ uses TCP flow control at this point for pushback so Writes can
+ // intentionally block.
+ if deadliner, ok := c.conn.(interface {
+ SetDeadline(time.Time) error
+ }); ok {
+ _ = deadliner.SetDeadline(time.Time{})
+ }
+
+ c.allocator = newAllocator(1, c.Config.ChannelMax)
+ return nil
+}
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func pick(client, server int) int {
+ if client == 0 || server == 0 {
+ return max(client, server)
+ }
+ return min(client, server)
+}
diff --git a/vendor/github.com/streadway/amqp/consumers.go b/vendor/github.com/streadway/amqp/consumers.go
new file mode 100644
index 00000000..7ab6b35a
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/consumers.go
@@ -0,0 +1,128 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "fmt"
+ "os"
+ "sync"
+ "sync/atomic"
+)
+
+var consumerSeq uint64
+
+func uniqueConsumerTag() string {
+ return fmt.Sprintf("ctag-%s-%d", os.Args[0], atomic.AddUint64(&consumerSeq, 1))
+}
+
+type consumerBuffers map[string]chan *Delivery
+
+// Concurrent type that manages the consumerTag ->
+// ingress consumerBuffer mapping
+type consumers struct {
+ sync.WaitGroup // one for buffer
+ closed chan struct{} // signal buffer
+
+ sync.Mutex // protects below
+ chans consumerBuffers
+}
+
+func makeConsumers() *consumers {
+ return &consumers{
+ closed: make(chan struct{}),
+ chans: make(consumerBuffers),
+ }
+}
+
+func (subs *consumers) buffer(in chan *Delivery, out chan Delivery) {
+ defer close(out)
+ defer subs.Done()
+
+ var inflight = in
+ var queue []*Delivery
+
+ for delivery := range in {
+ queue = append(queue, delivery)
+
+ for len(queue) > 0 {
+ select {
+ case <-subs.closed:
+ // closed before drained, drop in-flight
+ return
+
+ case delivery, consuming := <-inflight:
+ if consuming {
+ queue = append(queue, delivery)
+ } else {
+ inflight = nil
+ }
+
+ case out <- *queue[0]:
+ queue = queue[1:]
+ }
+ }
+ }
+}
+
+// On key conflict, close the previous channel.
+func (subs *consumers) add(tag string, consumer chan Delivery) {
+ subs.Lock()
+ defer subs.Unlock()
+
+ if prev, found := subs.chans[tag]; found {
+ close(prev)
+ }
+
+ in := make(chan *Delivery)
+ subs.chans[tag] = in
+
+ subs.Add(1)
+ go subs.buffer(in, consumer)
+}
+
+func (subs *consumers) cancel(tag string) (found bool) {
+ subs.Lock()
+ defer subs.Unlock()
+
+ ch, found := subs.chans[tag]
+
+ if found {
+ delete(subs.chans, tag)
+ close(ch)
+ }
+
+ return found
+}
+
+func (subs *consumers) close() {
+ subs.Lock()
+ defer subs.Unlock()
+
+ close(subs.closed)
+
+ for tag, ch := range subs.chans {
+ delete(subs.chans, tag)
+ close(ch)
+ }
+
+ subs.Wait()
+}
+
+// Sends a delivery to a the consumer identified by `tag`.
+// If unbuffered channels are used for Consume this method
+// could block all deliveries until the consumer
+// receives on the other end of the channel.
+func (subs *consumers) send(tag string, msg *Delivery) bool {
+ subs.Lock()
+ defer subs.Unlock()
+
+ buffer, found := subs.chans[tag]
+ if found {
+ buffer <- msg
+ }
+
+ return found
+}
diff --git a/vendor/github.com/streadway/amqp/delivery.go b/vendor/github.com/streadway/amqp/delivery.go
new file mode 100644
index 00000000..304c8346
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/delivery.go
@@ -0,0 +1,173 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "errors"
+ "time"
+)
+
+var errDeliveryNotInitialized = errors.New("delivery not initialized")
+
+// Acknowledger notifies the server of successful or failed consumption of
+// delivieries via identifier found in the Delivery.DeliveryTag field.
+//
+// Applications can provide mock implementations in tests of Delivery handlers.
+type Acknowledger interface {
+ Ack(tag uint64, multiple bool) error
+ Nack(tag uint64, multiple bool, requeue bool) error
+ Reject(tag uint64, requeue bool) error
+}
+
+// Delivery captures the fields for a previously delivered message resident in
+// a queue to be delivered by the server to a consumer from Channel.Consume or
+// Channel.Get.
+type Delivery struct {
+ Acknowledger Acknowledger // the channel from which this delivery arrived
+
+ Headers Table // Application or header exchange table
+
+ // Properties
+ ContentType string // MIME content type
+ ContentEncoding string // MIME content encoding
+ DeliveryMode uint8 // queue implementation use - non-persistent (1) or persistent (2)
+ Priority uint8 // queue implementation use - 0 to 9
+ CorrelationId string // application use - correlation identifier
+ ReplyTo string // application use - address to reply to (ex: RPC)
+ Expiration string // implementation use - message expiration spec
+ MessageId string // application use - message identifier
+ Timestamp time.Time // application use - message timestamp
+ Type string // application use - message type name
+ UserId string // application use - creating user - should be authenticated user
+ AppId string // application use - creating application id
+
+ // Valid only with Channel.Consume
+ ConsumerTag string
+
+ // Valid only with Channel.Get
+ MessageCount uint32
+
+ DeliveryTag uint64
+ Redelivered bool
+ Exchange string // basic.publish exhange
+ RoutingKey string // basic.publish routing key
+
+ Body []byte
+}
+
+func newDelivery(channel *Channel, msg messageWithContent) *Delivery {
+ props, body := msg.getContent()
+
+ delivery := Delivery{
+ Acknowledger: channel,
+
+ Headers: props.Headers,
+ ContentType: props.ContentType,
+ ContentEncoding: props.ContentEncoding,
+ DeliveryMode: props.DeliveryMode,
+ Priority: props.Priority,
+ CorrelationId: props.CorrelationId,
+ ReplyTo: props.ReplyTo,
+ Expiration: props.Expiration,
+ MessageId: props.MessageId,
+ Timestamp: props.Timestamp,
+ Type: props.Type,
+ UserId: props.UserId,
+ AppId: props.AppId,
+
+ Body: body,
+ }
+
+ // Properties for the delivery types
+ switch m := msg.(type) {
+ case *basicDeliver:
+ delivery.ConsumerTag = m.ConsumerTag
+ delivery.DeliveryTag = m.DeliveryTag
+ delivery.Redelivered = m.Redelivered
+ delivery.Exchange = m.Exchange
+ delivery.RoutingKey = m.RoutingKey
+
+ case *basicGetOk:
+ delivery.MessageCount = m.MessageCount
+ delivery.DeliveryTag = m.DeliveryTag
+ delivery.Redelivered = m.Redelivered
+ delivery.Exchange = m.Exchange
+ delivery.RoutingKey = m.RoutingKey
+ }
+
+ return &delivery
+}
+
+/*
+Ack delegates an acknowledgement through the Acknowledger interface that the
+client or server has finished work on a delivery.
+
+All deliveries in AMQP must be acknowledged. If you called Channel.Consume
+with autoAck true then the server will be automatically ack each message and
+this method should not be called. Otherwise, you must call Delivery.Ack after
+you have successfully processed this delivery.
+
+When multiple is true, this delivery and all prior unacknowledged deliveries
+on the same channel will be acknowledged. This is useful for batch processing
+of deliveries.
+
+An error will indicate that the acknowledge could not be delivered to the
+channel it was sent from.
+
+Either Delivery.Ack, Delivery.Reject or Delivery.Nack must be called for every
+delivery that is not automatically acknowledged.
+*/
+func (d Delivery) Ack(multiple bool) error {
+ if d.Acknowledger == nil {
+ return errDeliveryNotInitialized
+ }
+ return d.Acknowledger.Ack(d.DeliveryTag, multiple)
+}
+
+/*
+Reject delegates a negatively acknowledgement through the Acknowledger interface.
+
+When requeue is true, queue this message to be delivered to a consumer on a
+different channel. When requeue is false or the server is unable to queue this
+message, it will be dropped.
+
+If you are batch processing deliveries, and your server supports it, prefer
+Delivery.Nack.
+
+Either Delivery.Ack, Delivery.Reject or Delivery.Nack must be called for every
+delivery that is not automatically acknowledged.
+*/
+func (d Delivery) Reject(requeue bool) error {
+ if d.Acknowledger == nil {
+ return errDeliveryNotInitialized
+ }
+ return d.Acknowledger.Reject(d.DeliveryTag, requeue)
+}
+
+/*
+Nack negatively acknowledge the delivery of message(s) identified by the
+delivery tag from either the client or server.
+
+When multiple is true, nack messages up to and including delivered messages up
+until the delivery tag delivered on the same channel.
+
+When requeue is true, request the server to deliver this message to a different
+consumer. If it is not possible or requeue is false, the message will be
+dropped or delivered to a server configured dead-letter queue.
+
+This method must not be used to select or requeue messages the client wishes
+not to handle, rather it is to inform the server that the client is incapable
+of handling this message at this time.
+
+Either Delivery.Ack, Delivery.Reject or Delivery.Nack must be called for every
+delivery that is not automatically acknowledged.
+*/
+func (d Delivery) Nack(multiple, requeue bool) error {
+ if d.Acknowledger == nil {
+ return errDeliveryNotInitialized
+ }
+ return d.Acknowledger.Nack(d.DeliveryTag, multiple, requeue)
+}
diff --git a/vendor/github.com/streadway/amqp/doc.go b/vendor/github.com/streadway/amqp/doc.go
new file mode 100644
index 00000000..76bf3e58
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/doc.go
@@ -0,0 +1,108 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+/*
+Package amqp is an AMQP 0.9.1 client with RabbitMQ extensions
+
+Understand the AMQP 0.9.1 messaging model by reviewing these links first. Much
+of the terminology in this library directly relates to AMQP concepts.
+
+ Resources
+
+ http://www.rabbitmq.com/tutorials/amqp-concepts.html
+ http://www.rabbitmq.com/getstarted.html
+ http://www.rabbitmq.com/amqp-0-9-1-reference.html
+
+Design
+
+Most other broker clients publish to queues, but in AMQP, clients publish
+Exchanges instead. AMQP is programmable, meaning that both the producers and
+consumers agree on the configuration of the broker, instead requiring an
+operator or system configuration that declares the logical topology in the
+broker. The routing between producers and consumer queues is via Bindings.
+These bindings form the logical topology of the broker.
+
+In this library, a message sent from publisher is called a "Publishing" and a
+message received to a consumer is called a "Delivery". The fields of
+Publishings and Deliveries are close but not exact mappings to the underlying
+wire format to maintain stronger types. Many other libraries will combine
+message properties with message headers. In this library, the message well
+known properties are strongly typed fields on the Publishings and Deliveries,
+whereas the user defined headers are in the Headers field.
+
+The method naming closely matches the protocol's method name with positional
+parameters mapping to named protocol message fields. The motivation here is to
+present a comprehensive view over all possible interactions with the server.
+
+Generally, methods that map to protocol methods of the "basic" class will be
+elided in this interface, and "select" methods of various channel mode selectors
+will be elided for example Channel.Confirm and Channel.Tx.
+
+The library is intentionally designed to be synchronous, where responses for
+each protocol message are required to be received in an RPC manner. Some
+methods have a noWait parameter like Channel.QueueDeclare, and some methods are
+asynchronous like Channel.Publish. The error values should still be checked for
+these methods as they will indicate IO failures like when the underlying
+connection closes.
+
+Asynchronous Events
+
+Clients of this library may be interested in receiving some of the protocol
+messages other than Deliveries like basic.ack methods while a channel is in
+confirm mode.
+
+The Notify* methods with Connection and Channel receivers model the pattern of
+asynchronous events like closes due to exceptions, or messages that are sent out
+of band from an RPC call like basic.ack or basic.flow.
+
+Any asynchronous events, including Deliveries and Publishings must always have
+a receiver until the corresponding chans are closed. Without asynchronous
+receivers, the sychronous methods will block.
+
+Use Case
+
+It's important as a client to an AMQP topology to ensure the state of the
+broker matches your expectations. For both publish and consume use cases,
+make sure you declare the queues, exchanges and bindings you expect to exist
+prior to calling Channel.Publish or Channel.Consume.
+
+ // Connections start with amqp.Dial() typically from a command line argument
+ // or environment variable.
+ connection, err := amqp.Dial(os.Getenv("AMQP_URL"))
+
+ // To cleanly shutdown by flushing kernel buffers, make sure to close and
+ // wait for the response.
+ defer connection.Close()
+
+ // Most operations happen on a channel. If any error is returned on a
+ // channel, the channel will no longer be valid, throw it away and try with
+ // a different channel. If you use many channels, it's useful for the
+ // server to
+ channel, err := connection.Channel()
+
+ // Declare your topology here, if it doesn't exist, it will be created, if
+ // it existed already and is not what you expect, then that's considered an
+ // error.
+
+ // Use your connection on this topology with either Publish or Consume, or
+ // inspect your queues with QueueInspect. It's unwise to mix Publish and
+ // Consume to let TCP do its job well.
+
+SSL/TLS - Secure connections
+
+When Dial encounters an amqps:// scheme, it will use the zero value of a
+tls.Config. This will only perform server certificate and host verification.
+
+Use DialTLS when you wish to provide a client certificate (recommended),
+include a private certificate authority's certificate in the cert chain for
+server validity, or run insecure by not verifying the server certificate dial
+your own connection. DialTLS will use the provided tls.Config when it
+encounters an amqps:// scheme and will dial a plain connection when it
+encounters an amqp:// scheme.
+
+SSL/TLS in RabbitMQ is documented here: http://www.rabbitmq.com/ssl.html
+
+*/
+package amqp
diff --git a/vendor/github.com/streadway/amqp/fuzz.go b/vendor/github.com/streadway/amqp/fuzz.go
new file mode 100644
index 00000000..16e626ce
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/fuzz.go
@@ -0,0 +1,17 @@
+// +build gofuzz
+
+package amqp
+
+import "bytes"
+
+func Fuzz(data []byte) int {
+ r := reader{bytes.NewReader(data)}
+ frame, err := r.ReadFrame()
+ if err != nil {
+ if frame != nil {
+ panic("frame is not nil")
+ }
+ return 0
+ }
+ return 1
+}
diff --git a/vendor/github.com/streadway/amqp/gen.sh b/vendor/github.com/streadway/amqp/gen.sh
new file mode 100644
index 00000000..d46e19bd
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/gen.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+go run spec/gen.go < spec/amqp0-9-1.stripped.extended.xml | gofmt > spec091.go
diff --git a/vendor/github.com/streadway/amqp/pre-commit b/vendor/github.com/streadway/amqp/pre-commit
new file mode 100644
index 00000000..7607f467
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/pre-commit
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+GOFMT_FILES=$(gofmt -l .)
+if [ -n "${GOFMT_FILES}" ]; then
+ printf >&2 'gofmt failed for the following files:\n%s\n\nplease run "gofmt -w ." on your changes before committing.\n' "${GOFMT_FILES}"
+ exit 1
+fi
+
+GOLINT_ERRORS=$(golint ./... | grep -v "Id should be")
+if [ -n "${GOLINT_ERRORS}" ]; then
+ printf >&2 'golint failed for the following reasons:\n%s\n\nplease run 'golint ./...' on your changes before committing.\n' "${GOLINT_ERRORS}"
+ exit 1
+fi
+
+GOVET_ERRORS=$(go tool vet *.go 2>&1)
+if [ -n "${GOVET_ERRORS}" ]; then
+ printf >&2 'go vet failed for the following reasons:\n%s\n\nplease run "go tool vet *.go" on your changes before committing.\n' "${GOVET_ERRORS}"
+ exit 1
+fi
+
+if [ -z "${NOTEST}" ]; then
+ printf >&2 'Running short tests...\n'
+ env AMQP_URL= go test -short -v | egrep 'PASS|ok'
+
+ if [ $? -ne 0 ]; then
+ printf >&2 'go test failed, please fix before committing.\n'
+ exit 1
+ fi
+fi
diff --git a/vendor/github.com/streadway/amqp/read.go b/vendor/github.com/streadway/amqp/read.go
new file mode 100644
index 00000000..3aa0b338
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/read.go
@@ -0,0 +1,456 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "io"
+ "time"
+)
+
+/*
+Reads a frame from an input stream and returns an interface that can be cast into
+one of the following:
+
+ methodFrame
+ PropertiesFrame
+ bodyFrame
+ heartbeatFrame
+
+2.3.5 frame Details
+
+All frames consist of a header (7 octets), a payload of arbitrary size, and a
+'frame-end' octet that detects malformed frames:
+
+ 0 1 3 7 size+7 size+8
+ +------+---------+-------------+ +------------+ +-----------+
+ | type | channel | size | | payload | | frame-end |
+ +------+---------+-------------+ +------------+ +-----------+
+ octet short long size octets octet
+
+To read a frame, we:
+ 1. Read the header and check the frame type and channel.
+ 2. Depending on the frame type, we read the payload and process it.
+ 3. Read the frame end octet.
+
+In realistic implementations where performance is a concern, we would use
+“read-ahead buffering” or
+
+“gathering reads” to avoid doing three separate system calls to read a frame.
+*/
+func (r *reader) ReadFrame() (frame frame, err error) {
+ var scratch [7]byte
+
+ if _, err = io.ReadFull(r.r, scratch[:7]); err != nil {
+ return
+ }
+
+ typ := uint8(scratch[0])
+ channel := binary.BigEndian.Uint16(scratch[1:3])
+ size := binary.BigEndian.Uint32(scratch[3:7])
+
+ switch typ {
+ case frameMethod:
+ if frame, err = r.parseMethodFrame(channel, size); err != nil {
+ return
+ }
+
+ case frameHeader:
+ if frame, err = r.parseHeaderFrame(channel, size); err != nil {
+ return
+ }
+
+ case frameBody:
+ if frame, err = r.parseBodyFrame(channel, size); err != nil {
+ return nil, err
+ }
+
+ case frameHeartbeat:
+ if frame, err = r.parseHeartbeatFrame(channel, size); err != nil {
+ return
+ }
+
+ default:
+ return nil, ErrFrame
+ }
+
+ if _, err = io.ReadFull(r.r, scratch[:1]); err != nil {
+ return nil, err
+ }
+
+ if scratch[0] != frameEnd {
+ return nil, ErrFrame
+ }
+
+ return
+}
+
+func readShortstr(r io.Reader) (v string, err error) {
+ var length uint8
+ if err = binary.Read(r, binary.BigEndian, &length); err != nil {
+ return
+ }
+
+ bytes := make([]byte, length)
+ if _, err = io.ReadFull(r, bytes); err != nil {
+ return
+ }
+ return string(bytes), nil
+}
+
+func readLongstr(r io.Reader) (v string, err error) {
+ var length uint32
+ if err = binary.Read(r, binary.BigEndian, &length); err != nil {
+ return
+ }
+
+ // slices can't be longer than max int32 value
+ if length > (^uint32(0) >> 1) {
+ return
+ }
+
+ bytes := make([]byte, length)
+ if _, err = io.ReadFull(r, bytes); err != nil {
+ return
+ }
+ return string(bytes), nil
+}
+
+func readDecimal(r io.Reader) (v Decimal, err error) {
+ if err = binary.Read(r, binary.BigEndian, &v.Scale); err != nil {
+ return
+ }
+ if err = binary.Read(r, binary.BigEndian, &v.Value); err != nil {
+ return
+ }
+ return
+}
+
+func readFloat32(r io.Reader) (v float32, err error) {
+ if err = binary.Read(r, binary.BigEndian, &v); err != nil {
+ return
+ }
+ return
+}
+
+func readFloat64(r io.Reader) (v float64, err error) {
+ if err = binary.Read(r, binary.BigEndian, &v); err != nil {
+ return
+ }
+ return
+}
+
+func readTimestamp(r io.Reader) (v time.Time, err error) {
+ var sec int64
+ if err = binary.Read(r, binary.BigEndian, &sec); err != nil {
+ return
+ }
+ return time.Unix(sec, 0), nil
+}
+
+/*
+'A': []interface{}
+'D': Decimal
+'F': Table
+'I': int32
+'S': string
+'T': time.Time
+'V': nil
+'b': byte
+'d': float64
+'f': float32
+'l': int64
+'s': int16
+'t': bool
+'x': []byte
+*/
+func readField(r io.Reader) (v interface{}, err error) {
+ var typ byte
+ if err = binary.Read(r, binary.BigEndian, &typ); err != nil {
+ return
+ }
+
+ switch typ {
+ case 't':
+ var value uint8
+ if err = binary.Read(r, binary.BigEndian, &value); err != nil {
+ return
+ }
+ return (value != 0), nil
+
+ case 'b':
+ var value [1]byte
+ if _, err = io.ReadFull(r, value[0:1]); err != nil {
+ return
+ }
+ return value[0], nil
+
+ case 's':
+ var value int16
+ if err = binary.Read(r, binary.BigEndian, &value); err != nil {
+ return
+ }
+ return value, nil
+
+ case 'I':
+ var value int32
+ if err = binary.Read(r, binary.BigEndian, &value); err != nil {
+ return
+ }
+ return value, nil
+
+ case 'l':
+ var value int64
+ if err = binary.Read(r, binary.BigEndian, &value); err != nil {
+ return
+ }
+ return value, nil
+
+ case 'f':
+ var value float32
+ if err = binary.Read(r, binary.BigEndian, &value); err != nil {
+ return
+ }
+ return value, nil
+
+ case 'd':
+ var value float64
+ if err = binary.Read(r, binary.BigEndian, &value); err != nil {
+ return
+ }
+ return value, nil
+
+ case 'D':
+ return readDecimal(r)
+
+ case 'S':
+ return readLongstr(r)
+
+ case 'A':
+ return readArray(r)
+
+ case 'T':
+ return readTimestamp(r)
+
+ case 'F':
+ return readTable(r)
+
+ case 'x':
+ var len int32
+ if err = binary.Read(r, binary.BigEndian, &len); err != nil {
+ return nil, err
+ }
+
+ value := make([]byte, len)
+ if _, err = io.ReadFull(r, value); err != nil {
+ return nil, err
+ }
+ return value, err
+
+ case 'V':
+ return nil, nil
+ }
+
+ return nil, ErrSyntax
+}
+
+/*
+ Field tables are long strings that contain packed name-value pairs. The
+ name-value pairs are encoded as short string defining the name, and octet
+ defining the values type and then the value itself. The valid field types for
+ tables are an extension of the native integer, bit, string, and timestamp
+ types, and are shown in the grammar. Multi-octet integer fields are always
+ held in network byte order.
+*/
+func readTable(r io.Reader) (table Table, err error) {
+ var nested bytes.Buffer
+ var str string
+
+ if str, err = readLongstr(r); err != nil {
+ return
+ }
+
+ nested.Write([]byte(str))
+
+ table = make(Table)
+
+ for nested.Len() > 0 {
+ var key string
+ var value interface{}
+
+ if key, err = readShortstr(&nested); err != nil {
+ return
+ }
+
+ if value, err = readField(&nested); err != nil {
+ return
+ }
+
+ table[key] = value
+ }
+
+ return
+}
+
+func readArray(r io.Reader) ([]interface{}, error) {
+ var (
+ size uint32
+ err error
+ )
+
+ if err = binary.Read(r, binary.BigEndian, &size); err != nil {
+ return nil, err
+ }
+
+ var (
+ lim = &io.LimitedReader{R: r, N: int64(size)}
+ arr = []interface{}{}
+ field interface{}
+ )
+
+ for {
+ if field, err = readField(lim); err != nil {
+ if err == io.EOF {
+ break
+ }
+ return nil, err
+ }
+ arr = append(arr, field)
+ }
+
+ return arr, nil
+}
+
+// Checks if this bit mask matches the flags bitset
+func hasProperty(mask uint16, prop int) bool {
+ return int(mask)&prop > 0
+}
+
+func (r *reader) parseHeaderFrame(channel uint16, size uint32) (frame frame, err error) {
+ hf := &headerFrame{
+ ChannelId: channel,
+ }
+
+ if err = binary.Read(r.r, binary.BigEndian, &hf.ClassId); err != nil {
+ return
+ }
+
+ if err = binary.Read(r.r, binary.BigEndian, &hf.weight); err != nil {
+ return
+ }
+
+ if err = binary.Read(r.r, binary.BigEndian, &hf.Size); err != nil {
+ return
+ }
+
+ var flags uint16
+
+ if err = binary.Read(r.r, binary.BigEndian, &flags); err != nil {
+ return
+ }
+
+ if hasProperty(flags, flagContentType) {
+ if hf.Properties.ContentType, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagContentEncoding) {
+ if hf.Properties.ContentEncoding, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagHeaders) {
+ if hf.Properties.Headers, err = readTable(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagDeliveryMode) {
+ if err = binary.Read(r.r, binary.BigEndian, &hf.Properties.DeliveryMode); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagPriority) {
+ if err = binary.Read(r.r, binary.BigEndian, &hf.Properties.Priority); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagCorrelationId) {
+ if hf.Properties.CorrelationId, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagReplyTo) {
+ if hf.Properties.ReplyTo, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagExpiration) {
+ if hf.Properties.Expiration, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagMessageId) {
+ if hf.Properties.MessageId, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagTimestamp) {
+ if hf.Properties.Timestamp, err = readTimestamp(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagType) {
+ if hf.Properties.Type, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagUserId) {
+ if hf.Properties.UserId, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagAppId) {
+ if hf.Properties.AppId, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+ if hasProperty(flags, flagReserved1) {
+ if hf.Properties.reserved1, err = readShortstr(r.r); err != nil {
+ return
+ }
+ }
+
+ return hf, nil
+}
+
+func (r *reader) parseBodyFrame(channel uint16, size uint32) (frame frame, err error) {
+ bf := &bodyFrame{
+ ChannelId: channel,
+ Body: make([]byte, size),
+ }
+
+ if _, err = io.ReadFull(r.r, bf.Body); err != nil {
+ return nil, err
+ }
+
+ return bf, nil
+}
+
+var errHeartbeatPayload = errors.New("Heartbeats should not have a payload")
+
+func (r *reader) parseHeartbeatFrame(channel uint16, size uint32) (frame frame, err error) {
+ hf := &heartbeatFrame{
+ ChannelId: channel,
+ }
+
+ if size > 0 {
+ return nil, errHeartbeatPayload
+ }
+
+ return hf, nil
+}
diff --git a/vendor/github.com/streadway/amqp/return.go b/vendor/github.com/streadway/amqp/return.go
new file mode 100644
index 00000000..10dcedb2
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/return.go
@@ -0,0 +1,64 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "time"
+)
+
+// Return captures a flattened struct of fields returned by the server when a
+// Publishing is unable to be delivered either due to the `mandatory` flag set
+// and no route found, or `immediate` flag set and no free consumer.
+type Return struct {
+ ReplyCode uint16 // reason
+ ReplyText string // description
+ Exchange string // basic.publish exchange
+ RoutingKey string // basic.publish routing key
+
+ // Properties
+ ContentType string // MIME content type
+ ContentEncoding string // MIME content encoding
+ Headers Table // Application or header exchange table
+ DeliveryMode uint8 // queue implementation use - non-persistent (1) or persistent (2)
+ Priority uint8 // queue implementation use - 0 to 9
+ CorrelationId string // application use - correlation identifier
+ ReplyTo string // application use - address to to reply to (ex: RPC)
+ Expiration string // implementation use - message expiration spec
+ MessageId string // application use - message identifier
+ Timestamp time.Time // application use - message timestamp
+ Type string // application use - message type name
+ UserId string // application use - creating user id
+ AppId string // application use - creating application
+
+ Body []byte
+}
+
+func newReturn(msg basicReturn) *Return {
+ props, body := msg.getContent()
+
+ return &Return{
+ ReplyCode: msg.ReplyCode,
+ ReplyText: msg.ReplyText,
+ Exchange: msg.Exchange,
+ RoutingKey: msg.RoutingKey,
+
+ Headers: props.Headers,
+ ContentType: props.ContentType,
+ ContentEncoding: props.ContentEncoding,
+ DeliveryMode: props.DeliveryMode,
+ Priority: props.Priority,
+ CorrelationId: props.CorrelationId,
+ ReplyTo: props.ReplyTo,
+ Expiration: props.Expiration,
+ MessageId: props.MessageId,
+ Timestamp: props.Timestamp,
+ Type: props.Type,
+ UserId: props.UserId,
+ AppId: props.AppId,
+
+ Body: body,
+ }
+}
diff --git a/vendor/github.com/streadway/amqp/spec091.go b/vendor/github.com/streadway/amqp/spec091.go
new file mode 100644
index 00000000..cd53ebe7
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/spec091.go
@@ -0,0 +1,3306 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+/* GENERATED FILE - DO NOT EDIT */
+/* Rebuild from the spec/gen.go tool */
+
+package amqp
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+)
+
+// Error codes that can be sent from the server during a connection or
+// channel exception or used by the client to indicate a class of error like
+// ErrCredentials. The text of the error is likely more interesting than
+// these constants.
+const (
+ frameMethod = 1
+ frameHeader = 2
+ frameBody = 3
+ frameHeartbeat = 8
+ frameMinSize = 4096
+ frameEnd = 206
+ replySuccess = 200
+ ContentTooLarge = 311
+ NoRoute = 312
+ NoConsumers = 313
+ ConnectionForced = 320
+ InvalidPath = 402
+ AccessRefused = 403
+ NotFound = 404
+ ResourceLocked = 405
+ PreconditionFailed = 406
+ FrameError = 501
+ SyntaxError = 502
+ CommandInvalid = 503
+ ChannelError = 504
+ UnexpectedFrame = 505
+ ResourceError = 506
+ NotAllowed = 530
+ NotImplemented = 540
+ InternalError = 541
+)
+
+func isSoftExceptionCode(code int) bool {
+ switch code {
+ case 311:
+ return true
+ case 312:
+ return true
+ case 313:
+ return true
+ case 403:
+ return true
+ case 404:
+ return true
+ case 405:
+ return true
+ case 406:
+ return true
+
+ }
+ return false
+}
+
+type connectionStart struct {
+ VersionMajor byte
+ VersionMinor byte
+ ServerProperties Table
+ Mechanisms string
+ Locales string
+}
+
+func (msg *connectionStart) id() (uint16, uint16) {
+ return 10, 10
+}
+
+func (msg *connectionStart) wait() bool {
+ return true
+}
+
+func (msg *connectionStart) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.VersionMajor); err != nil {
+ return
+ }
+ if err = binary.Write(w, binary.BigEndian, msg.VersionMinor); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.ServerProperties); err != nil {
+ return
+ }
+
+ if err = writeLongstr(w, msg.Mechanisms); err != nil {
+ return
+ }
+ if err = writeLongstr(w, msg.Locales); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionStart) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.VersionMajor); err != nil {
+ return
+ }
+ if err = binary.Read(r, binary.BigEndian, &msg.VersionMinor); err != nil {
+ return
+ }
+
+ if msg.ServerProperties, err = readTable(r); err != nil {
+ return
+ }
+
+ if msg.Mechanisms, err = readLongstr(r); err != nil {
+ return
+ }
+ if msg.Locales, err = readLongstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionStartOk struct {
+ ClientProperties Table
+ Mechanism string
+ Response string
+ Locale string
+}
+
+func (msg *connectionStartOk) id() (uint16, uint16) {
+ return 10, 11
+}
+
+func (msg *connectionStartOk) wait() bool {
+ return true
+}
+
+func (msg *connectionStartOk) write(w io.Writer) (err error) {
+
+ if err = writeTable(w, msg.ClientProperties); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Mechanism); err != nil {
+ return
+ }
+
+ if err = writeLongstr(w, msg.Response); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Locale); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionStartOk) read(r io.Reader) (err error) {
+
+ if msg.ClientProperties, err = readTable(r); err != nil {
+ return
+ }
+
+ if msg.Mechanism, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if msg.Response, err = readLongstr(r); err != nil {
+ return
+ }
+
+ if msg.Locale, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionSecure struct {
+ Challenge string
+}
+
+func (msg *connectionSecure) id() (uint16, uint16) {
+ return 10, 20
+}
+
+func (msg *connectionSecure) wait() bool {
+ return true
+}
+
+func (msg *connectionSecure) write(w io.Writer) (err error) {
+
+ if err = writeLongstr(w, msg.Challenge); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionSecure) read(r io.Reader) (err error) {
+
+ if msg.Challenge, err = readLongstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionSecureOk struct {
+ Response string
+}
+
+func (msg *connectionSecureOk) id() (uint16, uint16) {
+ return 10, 21
+}
+
+func (msg *connectionSecureOk) wait() bool {
+ return true
+}
+
+func (msg *connectionSecureOk) write(w io.Writer) (err error) {
+
+ if err = writeLongstr(w, msg.Response); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionSecureOk) read(r io.Reader) (err error) {
+
+ if msg.Response, err = readLongstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionTune struct {
+ ChannelMax uint16
+ FrameMax uint32
+ Heartbeat uint16
+}
+
+func (msg *connectionTune) id() (uint16, uint16) {
+ return 10, 30
+}
+
+func (msg *connectionTune) wait() bool {
+ return true
+}
+
+func (msg *connectionTune) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.ChannelMax); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.FrameMax); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.Heartbeat); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionTune) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.ChannelMax); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.FrameMax); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.Heartbeat); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionTuneOk struct {
+ ChannelMax uint16
+ FrameMax uint32
+ Heartbeat uint16
+}
+
+func (msg *connectionTuneOk) id() (uint16, uint16) {
+ return 10, 31
+}
+
+func (msg *connectionTuneOk) wait() bool {
+ return true
+}
+
+func (msg *connectionTuneOk) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.ChannelMax); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.FrameMax); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.Heartbeat); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionTuneOk) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.ChannelMax); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.FrameMax); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.Heartbeat); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionOpen struct {
+ VirtualHost string
+ reserved1 string
+ reserved2 bool
+}
+
+func (msg *connectionOpen) id() (uint16, uint16) {
+ return 10, 40
+}
+
+func (msg *connectionOpen) wait() bool {
+ return true
+}
+
+func (msg *connectionOpen) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = writeShortstr(w, msg.VirtualHost); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.reserved2 {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionOpen) read(r io.Reader) (err error) {
+ var bits byte
+
+ if msg.VirtualHost, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.reserved1, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.reserved2 = (bits&(1<<0) > 0)
+
+ return
+}
+
+type connectionOpenOk struct {
+ reserved1 string
+}
+
+func (msg *connectionOpenOk) id() (uint16, uint16) {
+ return 10, 41
+}
+
+func (msg *connectionOpenOk) wait() bool {
+ return true
+}
+
+func (msg *connectionOpenOk) write(w io.Writer) (err error) {
+
+ if err = writeShortstr(w, msg.reserved1); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionOpenOk) read(r io.Reader) (err error) {
+
+ if msg.reserved1, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionClose struct {
+ ReplyCode uint16
+ ReplyText string
+ ClassId uint16
+ MethodId uint16
+}
+
+func (msg *connectionClose) id() (uint16, uint16) {
+ return 10, 50
+}
+
+func (msg *connectionClose) wait() bool {
+ return true
+}
+
+func (msg *connectionClose) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.ReplyCode); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.ReplyText); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.ClassId); err != nil {
+ return
+ }
+ if err = binary.Write(w, binary.BigEndian, msg.MethodId); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionClose) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.ReplyCode); err != nil {
+ return
+ }
+
+ if msg.ReplyText, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.ClassId); err != nil {
+ return
+ }
+ if err = binary.Read(r, binary.BigEndian, &msg.MethodId); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionCloseOk struct {
+}
+
+func (msg *connectionCloseOk) id() (uint16, uint16) {
+ return 10, 51
+}
+
+func (msg *connectionCloseOk) wait() bool {
+ return true
+}
+
+func (msg *connectionCloseOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *connectionCloseOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type connectionBlocked struct {
+ Reason string
+}
+
+func (msg *connectionBlocked) id() (uint16, uint16) {
+ return 10, 60
+}
+
+func (msg *connectionBlocked) wait() bool {
+ return false
+}
+
+func (msg *connectionBlocked) write(w io.Writer) (err error) {
+
+ if err = writeShortstr(w, msg.Reason); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *connectionBlocked) read(r io.Reader) (err error) {
+
+ if msg.Reason, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type connectionUnblocked struct {
+}
+
+func (msg *connectionUnblocked) id() (uint16, uint16) {
+ return 10, 61
+}
+
+func (msg *connectionUnblocked) wait() bool {
+ return false
+}
+
+func (msg *connectionUnblocked) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *connectionUnblocked) read(r io.Reader) (err error) {
+
+ return
+}
+
+type channelOpen struct {
+ reserved1 string
+}
+
+func (msg *channelOpen) id() (uint16, uint16) {
+ return 20, 10
+}
+
+func (msg *channelOpen) wait() bool {
+ return true
+}
+
+func (msg *channelOpen) write(w io.Writer) (err error) {
+
+ if err = writeShortstr(w, msg.reserved1); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *channelOpen) read(r io.Reader) (err error) {
+
+ if msg.reserved1, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type channelOpenOk struct {
+ reserved1 string
+}
+
+func (msg *channelOpenOk) id() (uint16, uint16) {
+ return 20, 11
+}
+
+func (msg *channelOpenOk) wait() bool {
+ return true
+}
+
+func (msg *channelOpenOk) write(w io.Writer) (err error) {
+
+ if err = writeLongstr(w, msg.reserved1); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *channelOpenOk) read(r io.Reader) (err error) {
+
+ if msg.reserved1, err = readLongstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type channelFlow struct {
+ Active bool
+}
+
+func (msg *channelFlow) id() (uint16, uint16) {
+ return 20, 20
+}
+
+func (msg *channelFlow) wait() bool {
+ return true
+}
+
+func (msg *channelFlow) write(w io.Writer) (err error) {
+ var bits byte
+
+ if msg.Active {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *channelFlow) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Active = (bits&(1<<0) > 0)
+
+ return
+}
+
+type channelFlowOk struct {
+ Active bool
+}
+
+func (msg *channelFlowOk) id() (uint16, uint16) {
+ return 20, 21
+}
+
+func (msg *channelFlowOk) wait() bool {
+ return false
+}
+
+func (msg *channelFlowOk) write(w io.Writer) (err error) {
+ var bits byte
+
+ if msg.Active {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *channelFlowOk) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Active = (bits&(1<<0) > 0)
+
+ return
+}
+
+type channelClose struct {
+ ReplyCode uint16
+ ReplyText string
+ ClassId uint16
+ MethodId uint16
+}
+
+func (msg *channelClose) id() (uint16, uint16) {
+ return 20, 40
+}
+
+func (msg *channelClose) wait() bool {
+ return true
+}
+
+func (msg *channelClose) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.ReplyCode); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.ReplyText); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.ClassId); err != nil {
+ return
+ }
+ if err = binary.Write(w, binary.BigEndian, msg.MethodId); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *channelClose) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.ReplyCode); err != nil {
+ return
+ }
+
+ if msg.ReplyText, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.ClassId); err != nil {
+ return
+ }
+ if err = binary.Read(r, binary.BigEndian, &msg.MethodId); err != nil {
+ return
+ }
+
+ return
+}
+
+type channelCloseOk struct {
+}
+
+func (msg *channelCloseOk) id() (uint16, uint16) {
+ return 20, 41
+}
+
+func (msg *channelCloseOk) wait() bool {
+ return true
+}
+
+func (msg *channelCloseOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *channelCloseOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type exchangeDeclare struct {
+ reserved1 uint16
+ Exchange string
+ Type string
+ Passive bool
+ Durable bool
+ AutoDelete bool
+ Internal bool
+ NoWait bool
+ Arguments Table
+}
+
+func (msg *exchangeDeclare) id() (uint16, uint16) {
+ return 40, 10
+}
+
+func (msg *exchangeDeclare) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *exchangeDeclare) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.Type); err != nil {
+ return
+ }
+
+ if msg.Passive {
+ bits |= 1 << 0
+ }
+
+ if msg.Durable {
+ bits |= 1 << 1
+ }
+
+ if msg.AutoDelete {
+ bits |= 1 << 2
+ }
+
+ if msg.Internal {
+ bits |= 1 << 3
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 4
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.Arguments); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *exchangeDeclare) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.Type, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Passive = (bits&(1<<0) > 0)
+ msg.Durable = (bits&(1<<1) > 0)
+ msg.AutoDelete = (bits&(1<<2) > 0)
+ msg.Internal = (bits&(1<<3) > 0)
+ msg.NoWait = (bits&(1<<4) > 0)
+
+ if msg.Arguments, err = readTable(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type exchangeDeclareOk struct {
+}
+
+func (msg *exchangeDeclareOk) id() (uint16, uint16) {
+ return 40, 11
+}
+
+func (msg *exchangeDeclareOk) wait() bool {
+ return true
+}
+
+func (msg *exchangeDeclareOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *exchangeDeclareOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type exchangeDelete struct {
+ reserved1 uint16
+ Exchange string
+ IfUnused bool
+ NoWait bool
+}
+
+func (msg *exchangeDelete) id() (uint16, uint16) {
+ return 40, 20
+}
+
+func (msg *exchangeDelete) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *exchangeDelete) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+
+ if msg.IfUnused {
+ bits |= 1 << 0
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 1
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *exchangeDelete) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.IfUnused = (bits&(1<<0) > 0)
+ msg.NoWait = (bits&(1<<1) > 0)
+
+ return
+}
+
+type exchangeDeleteOk struct {
+}
+
+func (msg *exchangeDeleteOk) id() (uint16, uint16) {
+ return 40, 21
+}
+
+func (msg *exchangeDeleteOk) wait() bool {
+ return true
+}
+
+func (msg *exchangeDeleteOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *exchangeDeleteOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type exchangeBind struct {
+ reserved1 uint16
+ Destination string
+ Source string
+ RoutingKey string
+ NoWait bool
+ Arguments Table
+}
+
+func (msg *exchangeBind) id() (uint16, uint16) {
+ return 40, 30
+}
+
+func (msg *exchangeBind) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *exchangeBind) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Destination); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.Source); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.Arguments); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *exchangeBind) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Destination, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.Source, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.NoWait = (bits&(1<<0) > 0)
+
+ if msg.Arguments, err = readTable(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type exchangeBindOk struct {
+}
+
+func (msg *exchangeBindOk) id() (uint16, uint16) {
+ return 40, 31
+}
+
+func (msg *exchangeBindOk) wait() bool {
+ return true
+}
+
+func (msg *exchangeBindOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *exchangeBindOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type exchangeUnbind struct {
+ reserved1 uint16
+ Destination string
+ Source string
+ RoutingKey string
+ NoWait bool
+ Arguments Table
+}
+
+func (msg *exchangeUnbind) id() (uint16, uint16) {
+ return 40, 40
+}
+
+func (msg *exchangeUnbind) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *exchangeUnbind) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Destination); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.Source); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.Arguments); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *exchangeUnbind) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Destination, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.Source, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.NoWait = (bits&(1<<0) > 0)
+
+ if msg.Arguments, err = readTable(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type exchangeUnbindOk struct {
+}
+
+func (msg *exchangeUnbindOk) id() (uint16, uint16) {
+ return 40, 51
+}
+
+func (msg *exchangeUnbindOk) wait() bool {
+ return true
+}
+
+func (msg *exchangeUnbindOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *exchangeUnbindOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type queueDeclare struct {
+ reserved1 uint16
+ Queue string
+ Passive bool
+ Durable bool
+ Exclusive bool
+ AutoDelete bool
+ NoWait bool
+ Arguments Table
+}
+
+func (msg *queueDeclare) id() (uint16, uint16) {
+ return 50, 10
+}
+
+func (msg *queueDeclare) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *queueDeclare) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+
+ if msg.Passive {
+ bits |= 1 << 0
+ }
+
+ if msg.Durable {
+ bits |= 1 << 1
+ }
+
+ if msg.Exclusive {
+ bits |= 1 << 2
+ }
+
+ if msg.AutoDelete {
+ bits |= 1 << 3
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 4
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.Arguments); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queueDeclare) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Passive = (bits&(1<<0) > 0)
+ msg.Durable = (bits&(1<<1) > 0)
+ msg.Exclusive = (bits&(1<<2) > 0)
+ msg.AutoDelete = (bits&(1<<3) > 0)
+ msg.NoWait = (bits&(1<<4) > 0)
+
+ if msg.Arguments, err = readTable(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type queueDeclareOk struct {
+ Queue string
+ MessageCount uint32
+ ConsumerCount uint32
+}
+
+func (msg *queueDeclareOk) id() (uint16, uint16) {
+ return 50, 11
+}
+
+func (msg *queueDeclareOk) wait() bool {
+ return true
+}
+
+func (msg *queueDeclareOk) write(w io.Writer) (err error) {
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.MessageCount); err != nil {
+ return
+ }
+ if err = binary.Write(w, binary.BigEndian, msg.ConsumerCount); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queueDeclareOk) read(r io.Reader) (err error) {
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.MessageCount); err != nil {
+ return
+ }
+ if err = binary.Read(r, binary.BigEndian, &msg.ConsumerCount); err != nil {
+ return
+ }
+
+ return
+}
+
+type queueBind struct {
+ reserved1 uint16
+ Queue string
+ Exchange string
+ RoutingKey string
+ NoWait bool
+ Arguments Table
+}
+
+func (msg *queueBind) id() (uint16, uint16) {
+ return 50, 20
+}
+
+func (msg *queueBind) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *queueBind) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.Arguments); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queueBind) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.NoWait = (bits&(1<<0) > 0)
+
+ if msg.Arguments, err = readTable(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type queueBindOk struct {
+}
+
+func (msg *queueBindOk) id() (uint16, uint16) {
+ return 50, 21
+}
+
+func (msg *queueBindOk) wait() bool {
+ return true
+}
+
+func (msg *queueBindOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *queueBindOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type queueUnbind struct {
+ reserved1 uint16
+ Queue string
+ Exchange string
+ RoutingKey string
+ Arguments Table
+}
+
+func (msg *queueUnbind) id() (uint16, uint16) {
+ return 50, 50
+}
+
+func (msg *queueUnbind) wait() bool {
+ return true
+}
+
+func (msg *queueUnbind) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.Arguments); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queueUnbind) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if msg.Arguments, err = readTable(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type queueUnbindOk struct {
+}
+
+func (msg *queueUnbindOk) id() (uint16, uint16) {
+ return 50, 51
+}
+
+func (msg *queueUnbindOk) wait() bool {
+ return true
+}
+
+func (msg *queueUnbindOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *queueUnbindOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type queuePurge struct {
+ reserved1 uint16
+ Queue string
+ NoWait bool
+}
+
+func (msg *queuePurge) id() (uint16, uint16) {
+ return 50, 30
+}
+
+func (msg *queuePurge) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *queuePurge) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queuePurge) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.NoWait = (bits&(1<<0) > 0)
+
+ return
+}
+
+type queuePurgeOk struct {
+ MessageCount uint32
+}
+
+func (msg *queuePurgeOk) id() (uint16, uint16) {
+ return 50, 31
+}
+
+func (msg *queuePurgeOk) wait() bool {
+ return true
+}
+
+func (msg *queuePurgeOk) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.MessageCount); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queuePurgeOk) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.MessageCount); err != nil {
+ return
+ }
+
+ return
+}
+
+type queueDelete struct {
+ reserved1 uint16
+ Queue string
+ IfUnused bool
+ IfEmpty bool
+ NoWait bool
+}
+
+func (msg *queueDelete) id() (uint16, uint16) {
+ return 50, 40
+}
+
+func (msg *queueDelete) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *queueDelete) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+
+ if msg.IfUnused {
+ bits |= 1 << 0
+ }
+
+ if msg.IfEmpty {
+ bits |= 1 << 1
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 2
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queueDelete) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.IfUnused = (bits&(1<<0) > 0)
+ msg.IfEmpty = (bits&(1<<1) > 0)
+ msg.NoWait = (bits&(1<<2) > 0)
+
+ return
+}
+
+type queueDeleteOk struct {
+ MessageCount uint32
+}
+
+func (msg *queueDeleteOk) id() (uint16, uint16) {
+ return 50, 41
+}
+
+func (msg *queueDeleteOk) wait() bool {
+ return true
+}
+
+func (msg *queueDeleteOk) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.MessageCount); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *queueDeleteOk) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.MessageCount); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicQos struct {
+ PrefetchSize uint32
+ PrefetchCount uint16
+ Global bool
+}
+
+func (msg *basicQos) id() (uint16, uint16) {
+ return 60, 10
+}
+
+func (msg *basicQos) wait() bool {
+ return true
+}
+
+func (msg *basicQos) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.PrefetchSize); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.PrefetchCount); err != nil {
+ return
+ }
+
+ if msg.Global {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicQos) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.PrefetchSize); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.PrefetchCount); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Global = (bits&(1<<0) > 0)
+
+ return
+}
+
+type basicQosOk struct {
+}
+
+func (msg *basicQosOk) id() (uint16, uint16) {
+ return 60, 11
+}
+
+func (msg *basicQosOk) wait() bool {
+ return true
+}
+
+func (msg *basicQosOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *basicQosOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type basicConsume struct {
+ reserved1 uint16
+ Queue string
+ ConsumerTag string
+ NoLocal bool
+ NoAck bool
+ Exclusive bool
+ NoWait bool
+ Arguments Table
+}
+
+func (msg *basicConsume) id() (uint16, uint16) {
+ return 60, 20
+}
+
+func (msg *basicConsume) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *basicConsume) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.ConsumerTag); err != nil {
+ return
+ }
+
+ if msg.NoLocal {
+ bits |= 1 << 0
+ }
+
+ if msg.NoAck {
+ bits |= 1 << 1
+ }
+
+ if msg.Exclusive {
+ bits |= 1 << 2
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 3
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeTable(w, msg.Arguments); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicConsume) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.ConsumerTag, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.NoLocal = (bits&(1<<0) > 0)
+ msg.NoAck = (bits&(1<<1) > 0)
+ msg.Exclusive = (bits&(1<<2) > 0)
+ msg.NoWait = (bits&(1<<3) > 0)
+
+ if msg.Arguments, err = readTable(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicConsumeOk struct {
+ ConsumerTag string
+}
+
+func (msg *basicConsumeOk) id() (uint16, uint16) {
+ return 60, 21
+}
+
+func (msg *basicConsumeOk) wait() bool {
+ return true
+}
+
+func (msg *basicConsumeOk) write(w io.Writer) (err error) {
+
+ if err = writeShortstr(w, msg.ConsumerTag); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicConsumeOk) read(r io.Reader) (err error) {
+
+ if msg.ConsumerTag, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicCancel struct {
+ ConsumerTag string
+ NoWait bool
+}
+
+func (msg *basicCancel) id() (uint16, uint16) {
+ return 60, 30
+}
+
+func (msg *basicCancel) wait() bool {
+ return true && !msg.NoWait
+}
+
+func (msg *basicCancel) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = writeShortstr(w, msg.ConsumerTag); err != nil {
+ return
+ }
+
+ if msg.NoWait {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicCancel) read(r io.Reader) (err error) {
+ var bits byte
+
+ if msg.ConsumerTag, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.NoWait = (bits&(1<<0) > 0)
+
+ return
+}
+
+type basicCancelOk struct {
+ ConsumerTag string
+}
+
+func (msg *basicCancelOk) id() (uint16, uint16) {
+ return 60, 31
+}
+
+func (msg *basicCancelOk) wait() bool {
+ return true
+}
+
+func (msg *basicCancelOk) write(w io.Writer) (err error) {
+
+ if err = writeShortstr(w, msg.ConsumerTag); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicCancelOk) read(r io.Reader) (err error) {
+
+ if msg.ConsumerTag, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicPublish struct {
+ reserved1 uint16
+ Exchange string
+ RoutingKey string
+ Mandatory bool
+ Immediate bool
+ Properties properties
+ Body []byte
+}
+
+func (msg *basicPublish) id() (uint16, uint16) {
+ return 60, 40
+}
+
+func (msg *basicPublish) wait() bool {
+ return false
+}
+
+func (msg *basicPublish) getContent() (properties, []byte) {
+ return msg.Properties, msg.Body
+}
+
+func (msg *basicPublish) setContent(props properties, body []byte) {
+ msg.Properties, msg.Body = props, body
+}
+
+func (msg *basicPublish) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ if msg.Mandatory {
+ bits |= 1 << 0
+ }
+
+ if msg.Immediate {
+ bits |= 1 << 1
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicPublish) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Mandatory = (bits&(1<<0) > 0)
+ msg.Immediate = (bits&(1<<1) > 0)
+
+ return
+}
+
+type basicReturn struct {
+ ReplyCode uint16
+ ReplyText string
+ Exchange string
+ RoutingKey string
+ Properties properties
+ Body []byte
+}
+
+func (msg *basicReturn) id() (uint16, uint16) {
+ return 60, 50
+}
+
+func (msg *basicReturn) wait() bool {
+ return false
+}
+
+func (msg *basicReturn) getContent() (properties, []byte) {
+ return msg.Properties, msg.Body
+}
+
+func (msg *basicReturn) setContent(props properties, body []byte) {
+ msg.Properties, msg.Body = props, body
+}
+
+func (msg *basicReturn) write(w io.Writer) (err error) {
+
+ if err = binary.Write(w, binary.BigEndian, msg.ReplyCode); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.ReplyText); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicReturn) read(r io.Reader) (err error) {
+
+ if err = binary.Read(r, binary.BigEndian, &msg.ReplyCode); err != nil {
+ return
+ }
+
+ if msg.ReplyText, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicDeliver struct {
+ ConsumerTag string
+ DeliveryTag uint64
+ Redelivered bool
+ Exchange string
+ RoutingKey string
+ Properties properties
+ Body []byte
+}
+
+func (msg *basicDeliver) id() (uint16, uint16) {
+ return 60, 60
+}
+
+func (msg *basicDeliver) wait() bool {
+ return false
+}
+
+func (msg *basicDeliver) getContent() (properties, []byte) {
+ return msg.Properties, msg.Body
+}
+
+func (msg *basicDeliver) setContent(props properties, body []byte) {
+ msg.Properties, msg.Body = props, body
+}
+
+func (msg *basicDeliver) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = writeShortstr(w, msg.ConsumerTag); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if msg.Redelivered {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicDeliver) read(r io.Reader) (err error) {
+ var bits byte
+
+ if msg.ConsumerTag, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Redelivered = (bits&(1<<0) > 0)
+
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicGet struct {
+ reserved1 uint16
+ Queue string
+ NoAck bool
+}
+
+func (msg *basicGet) id() (uint16, uint16) {
+ return 60, 70
+}
+
+func (msg *basicGet) wait() bool {
+ return true
+}
+
+func (msg *basicGet) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.reserved1); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Queue); err != nil {
+ return
+ }
+
+ if msg.NoAck {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicGet) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.reserved1); err != nil {
+ return
+ }
+
+ if msg.Queue, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.NoAck = (bits&(1<<0) > 0)
+
+ return
+}
+
+type basicGetOk struct {
+ DeliveryTag uint64
+ Redelivered bool
+ Exchange string
+ RoutingKey string
+ MessageCount uint32
+ Properties properties
+ Body []byte
+}
+
+func (msg *basicGetOk) id() (uint16, uint16) {
+ return 60, 71
+}
+
+func (msg *basicGetOk) wait() bool {
+ return true
+}
+
+func (msg *basicGetOk) getContent() (properties, []byte) {
+ return msg.Properties, msg.Body
+}
+
+func (msg *basicGetOk) setContent(props properties, body []byte) {
+ msg.Properties, msg.Body = props, body
+}
+
+func (msg *basicGetOk) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if msg.Redelivered {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ if err = writeShortstr(w, msg.Exchange); err != nil {
+ return
+ }
+ if err = writeShortstr(w, msg.RoutingKey); err != nil {
+ return
+ }
+
+ if err = binary.Write(w, binary.BigEndian, msg.MessageCount); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicGetOk) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Redelivered = (bits&(1<<0) > 0)
+
+ if msg.Exchange, err = readShortstr(r); err != nil {
+ return
+ }
+ if msg.RoutingKey, err = readShortstr(r); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &msg.MessageCount); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicGetEmpty struct {
+ reserved1 string
+}
+
+func (msg *basicGetEmpty) id() (uint16, uint16) {
+ return 60, 72
+}
+
+func (msg *basicGetEmpty) wait() bool {
+ return true
+}
+
+func (msg *basicGetEmpty) write(w io.Writer) (err error) {
+
+ if err = writeShortstr(w, msg.reserved1); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicGetEmpty) read(r io.Reader) (err error) {
+
+ if msg.reserved1, err = readShortstr(r); err != nil {
+ return
+ }
+
+ return
+}
+
+type basicAck struct {
+ DeliveryTag uint64
+ Multiple bool
+}
+
+func (msg *basicAck) id() (uint16, uint16) {
+ return 60, 80
+}
+
+func (msg *basicAck) wait() bool {
+ return false
+}
+
+func (msg *basicAck) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if msg.Multiple {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicAck) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Multiple = (bits&(1<<0) > 0)
+
+ return
+}
+
+type basicReject struct {
+ DeliveryTag uint64
+ Requeue bool
+}
+
+func (msg *basicReject) id() (uint16, uint16) {
+ return 60, 90
+}
+
+func (msg *basicReject) wait() bool {
+ return false
+}
+
+func (msg *basicReject) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if msg.Requeue {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicReject) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Requeue = (bits&(1<<0) > 0)
+
+ return
+}
+
+type basicRecoverAsync struct {
+ Requeue bool
+}
+
+func (msg *basicRecoverAsync) id() (uint16, uint16) {
+ return 60, 100
+}
+
+func (msg *basicRecoverAsync) wait() bool {
+ return false
+}
+
+func (msg *basicRecoverAsync) write(w io.Writer) (err error) {
+ var bits byte
+
+ if msg.Requeue {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicRecoverAsync) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Requeue = (bits&(1<<0) > 0)
+
+ return
+}
+
+type basicRecover struct {
+ Requeue bool
+}
+
+func (msg *basicRecover) id() (uint16, uint16) {
+ return 60, 110
+}
+
+func (msg *basicRecover) wait() bool {
+ return true
+}
+
+func (msg *basicRecover) write(w io.Writer) (err error) {
+ var bits byte
+
+ if msg.Requeue {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicRecover) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Requeue = (bits&(1<<0) > 0)
+
+ return
+}
+
+type basicRecoverOk struct {
+}
+
+func (msg *basicRecoverOk) id() (uint16, uint16) {
+ return 60, 111
+}
+
+func (msg *basicRecoverOk) wait() bool {
+ return true
+}
+
+func (msg *basicRecoverOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *basicRecoverOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type basicNack struct {
+ DeliveryTag uint64
+ Multiple bool
+ Requeue bool
+}
+
+func (msg *basicNack) id() (uint16, uint16) {
+ return 60, 120
+}
+
+func (msg *basicNack) wait() bool {
+ return false
+}
+
+func (msg *basicNack) write(w io.Writer) (err error) {
+ var bits byte
+
+ if err = binary.Write(w, binary.BigEndian, msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if msg.Multiple {
+ bits |= 1 << 0
+ }
+
+ if msg.Requeue {
+ bits |= 1 << 1
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *basicNack) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &msg.DeliveryTag); err != nil {
+ return
+ }
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Multiple = (bits&(1<<0) > 0)
+ msg.Requeue = (bits&(1<<1) > 0)
+
+ return
+}
+
+type txSelect struct {
+}
+
+func (msg *txSelect) id() (uint16, uint16) {
+ return 90, 10
+}
+
+func (msg *txSelect) wait() bool {
+ return true
+}
+
+func (msg *txSelect) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *txSelect) read(r io.Reader) (err error) {
+
+ return
+}
+
+type txSelectOk struct {
+}
+
+func (msg *txSelectOk) id() (uint16, uint16) {
+ return 90, 11
+}
+
+func (msg *txSelectOk) wait() bool {
+ return true
+}
+
+func (msg *txSelectOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *txSelectOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type txCommit struct {
+}
+
+func (msg *txCommit) id() (uint16, uint16) {
+ return 90, 20
+}
+
+func (msg *txCommit) wait() bool {
+ return true
+}
+
+func (msg *txCommit) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *txCommit) read(r io.Reader) (err error) {
+
+ return
+}
+
+type txCommitOk struct {
+}
+
+func (msg *txCommitOk) id() (uint16, uint16) {
+ return 90, 21
+}
+
+func (msg *txCommitOk) wait() bool {
+ return true
+}
+
+func (msg *txCommitOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *txCommitOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type txRollback struct {
+}
+
+func (msg *txRollback) id() (uint16, uint16) {
+ return 90, 30
+}
+
+func (msg *txRollback) wait() bool {
+ return true
+}
+
+func (msg *txRollback) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *txRollback) read(r io.Reader) (err error) {
+
+ return
+}
+
+type txRollbackOk struct {
+}
+
+func (msg *txRollbackOk) id() (uint16, uint16) {
+ return 90, 31
+}
+
+func (msg *txRollbackOk) wait() bool {
+ return true
+}
+
+func (msg *txRollbackOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *txRollbackOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+type confirmSelect struct {
+ Nowait bool
+}
+
+func (msg *confirmSelect) id() (uint16, uint16) {
+ return 85, 10
+}
+
+func (msg *confirmSelect) wait() bool {
+ return true
+}
+
+func (msg *confirmSelect) write(w io.Writer) (err error) {
+ var bits byte
+
+ if msg.Nowait {
+ bits |= 1 << 0
+ }
+
+ if err = binary.Write(w, binary.BigEndian, bits); err != nil {
+ return
+ }
+
+ return
+}
+
+func (msg *confirmSelect) read(r io.Reader) (err error) {
+ var bits byte
+
+ if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
+ return
+ }
+ msg.Nowait = (bits&(1<<0) > 0)
+
+ return
+}
+
+type confirmSelectOk struct {
+}
+
+func (msg *confirmSelectOk) id() (uint16, uint16) {
+ return 85, 11
+}
+
+func (msg *confirmSelectOk) wait() bool {
+ return true
+}
+
+func (msg *confirmSelectOk) write(w io.Writer) (err error) {
+
+ return
+}
+
+func (msg *confirmSelectOk) read(r io.Reader) (err error) {
+
+ return
+}
+
+func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err error) {
+ mf := &methodFrame{
+ ChannelId: channel,
+ }
+
+ if err = binary.Read(r.r, binary.BigEndian, &mf.ClassId); err != nil {
+ return
+ }
+
+ if err = binary.Read(r.r, binary.BigEndian, &mf.MethodId); err != nil {
+ return
+ }
+
+ switch mf.ClassId {
+
+ case 10: // connection
+ switch mf.MethodId {
+
+ case 10: // connection start
+ //fmt.Println("NextMethod: class:10 method:10")
+ method := &connectionStart{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 11: // connection start-ok
+ //fmt.Println("NextMethod: class:10 method:11")
+ method := &connectionStartOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 20: // connection secure
+ //fmt.Println("NextMethod: class:10 method:20")
+ method := &connectionSecure{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 21: // connection secure-ok
+ //fmt.Println("NextMethod: class:10 method:21")
+ method := &connectionSecureOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 30: // connection tune
+ //fmt.Println("NextMethod: class:10 method:30")
+ method := &connectionTune{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 31: // connection tune-ok
+ //fmt.Println("NextMethod: class:10 method:31")
+ method := &connectionTuneOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 40: // connection open
+ //fmt.Println("NextMethod: class:10 method:40")
+ method := &connectionOpen{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 41: // connection open-ok
+ //fmt.Println("NextMethod: class:10 method:41")
+ method := &connectionOpenOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 50: // connection close
+ //fmt.Println("NextMethod: class:10 method:50")
+ method := &connectionClose{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 51: // connection close-ok
+ //fmt.Println("NextMethod: class:10 method:51")
+ method := &connectionCloseOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 60: // connection blocked
+ //fmt.Println("NextMethod: class:10 method:60")
+ method := &connectionBlocked{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 61: // connection unblocked
+ //fmt.Println("NextMethod: class:10 method:61")
+ method := &connectionUnblocked{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+
+ case 20: // channel
+ switch mf.MethodId {
+
+ case 10: // channel open
+ //fmt.Println("NextMethod: class:20 method:10")
+ method := &channelOpen{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 11: // channel open-ok
+ //fmt.Println("NextMethod: class:20 method:11")
+ method := &channelOpenOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 20: // channel flow
+ //fmt.Println("NextMethod: class:20 method:20")
+ method := &channelFlow{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 21: // channel flow-ok
+ //fmt.Println("NextMethod: class:20 method:21")
+ method := &channelFlowOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 40: // channel close
+ //fmt.Println("NextMethod: class:20 method:40")
+ method := &channelClose{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 41: // channel close-ok
+ //fmt.Println("NextMethod: class:20 method:41")
+ method := &channelCloseOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+
+ case 40: // exchange
+ switch mf.MethodId {
+
+ case 10: // exchange declare
+ //fmt.Println("NextMethod: class:40 method:10")
+ method := &exchangeDeclare{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 11: // exchange declare-ok
+ //fmt.Println("NextMethod: class:40 method:11")
+ method := &exchangeDeclareOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 20: // exchange delete
+ //fmt.Println("NextMethod: class:40 method:20")
+ method := &exchangeDelete{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 21: // exchange delete-ok
+ //fmt.Println("NextMethod: class:40 method:21")
+ method := &exchangeDeleteOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 30: // exchange bind
+ //fmt.Println("NextMethod: class:40 method:30")
+ method := &exchangeBind{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 31: // exchange bind-ok
+ //fmt.Println("NextMethod: class:40 method:31")
+ method := &exchangeBindOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 40: // exchange unbind
+ //fmt.Println("NextMethod: class:40 method:40")
+ method := &exchangeUnbind{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 51: // exchange unbind-ok
+ //fmt.Println("NextMethod: class:40 method:51")
+ method := &exchangeUnbindOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+
+ case 50: // queue
+ switch mf.MethodId {
+
+ case 10: // queue declare
+ //fmt.Println("NextMethod: class:50 method:10")
+ method := &queueDeclare{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 11: // queue declare-ok
+ //fmt.Println("NextMethod: class:50 method:11")
+ method := &queueDeclareOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 20: // queue bind
+ //fmt.Println("NextMethod: class:50 method:20")
+ method := &queueBind{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 21: // queue bind-ok
+ //fmt.Println("NextMethod: class:50 method:21")
+ method := &queueBindOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 50: // queue unbind
+ //fmt.Println("NextMethod: class:50 method:50")
+ method := &queueUnbind{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 51: // queue unbind-ok
+ //fmt.Println("NextMethod: class:50 method:51")
+ method := &queueUnbindOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 30: // queue purge
+ //fmt.Println("NextMethod: class:50 method:30")
+ method := &queuePurge{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 31: // queue purge-ok
+ //fmt.Println("NextMethod: class:50 method:31")
+ method := &queuePurgeOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 40: // queue delete
+ //fmt.Println("NextMethod: class:50 method:40")
+ method := &queueDelete{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 41: // queue delete-ok
+ //fmt.Println("NextMethod: class:50 method:41")
+ method := &queueDeleteOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+
+ case 60: // basic
+ switch mf.MethodId {
+
+ case 10: // basic qos
+ //fmt.Println("NextMethod: class:60 method:10")
+ method := &basicQos{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 11: // basic qos-ok
+ //fmt.Println("NextMethod: class:60 method:11")
+ method := &basicQosOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 20: // basic consume
+ //fmt.Println("NextMethod: class:60 method:20")
+ method := &basicConsume{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 21: // basic consume-ok
+ //fmt.Println("NextMethod: class:60 method:21")
+ method := &basicConsumeOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 30: // basic cancel
+ //fmt.Println("NextMethod: class:60 method:30")
+ method := &basicCancel{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 31: // basic cancel-ok
+ //fmt.Println("NextMethod: class:60 method:31")
+ method := &basicCancelOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 40: // basic publish
+ //fmt.Println("NextMethod: class:60 method:40")
+ method := &basicPublish{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 50: // basic return
+ //fmt.Println("NextMethod: class:60 method:50")
+ method := &basicReturn{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 60: // basic deliver
+ //fmt.Println("NextMethod: class:60 method:60")
+ method := &basicDeliver{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 70: // basic get
+ //fmt.Println("NextMethod: class:60 method:70")
+ method := &basicGet{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 71: // basic get-ok
+ //fmt.Println("NextMethod: class:60 method:71")
+ method := &basicGetOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 72: // basic get-empty
+ //fmt.Println("NextMethod: class:60 method:72")
+ method := &basicGetEmpty{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 80: // basic ack
+ //fmt.Println("NextMethod: class:60 method:80")
+ method := &basicAck{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 90: // basic reject
+ //fmt.Println("NextMethod: class:60 method:90")
+ method := &basicReject{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 100: // basic recover-async
+ //fmt.Println("NextMethod: class:60 method:100")
+ method := &basicRecoverAsync{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 110: // basic recover
+ //fmt.Println("NextMethod: class:60 method:110")
+ method := &basicRecover{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 111: // basic recover-ok
+ //fmt.Println("NextMethod: class:60 method:111")
+ method := &basicRecoverOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 120: // basic nack
+ //fmt.Println("NextMethod: class:60 method:120")
+ method := &basicNack{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+
+ case 90: // tx
+ switch mf.MethodId {
+
+ case 10: // tx select
+ //fmt.Println("NextMethod: class:90 method:10")
+ method := &txSelect{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 11: // tx select-ok
+ //fmt.Println("NextMethod: class:90 method:11")
+ method := &txSelectOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 20: // tx commit
+ //fmt.Println("NextMethod: class:90 method:20")
+ method := &txCommit{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 21: // tx commit-ok
+ //fmt.Println("NextMethod: class:90 method:21")
+ method := &txCommitOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 30: // tx rollback
+ //fmt.Println("NextMethod: class:90 method:30")
+ method := &txRollback{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 31: // tx rollback-ok
+ //fmt.Println("NextMethod: class:90 method:31")
+ method := &txRollbackOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+
+ case 85: // confirm
+ switch mf.MethodId {
+
+ case 10: // confirm select
+ //fmt.Println("NextMethod: class:85 method:10")
+ method := &confirmSelect{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ case 11: // confirm select-ok
+ //fmt.Println("NextMethod: class:85 method:11")
+ method := &confirmSelectOk{}
+ if err = method.read(r.r); err != nil {
+ return
+ }
+ mf.Method = method
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
+ }
+
+ default:
+ return nil, fmt.Errorf("Bad method frame, unknown class %d", mf.ClassId)
+ }
+
+ return mf, nil
+}
diff --git a/vendor/github.com/streadway/amqp/types.go b/vendor/github.com/streadway/amqp/types.go
new file mode 100644
index 00000000..ff5ea3cb
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/types.go
@@ -0,0 +1,427 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "fmt"
+ "io"
+ "time"
+)
+
+// Constants for standard AMQP 0-9-1 exchange types.
+const (
+ ExchangeDirect = "direct"
+ ExchangeFanout = "fanout"
+ ExchangeTopic = "topic"
+ ExchangeHeaders = "headers"
+)
+
+var (
+ // ErrClosed is returned when the channel or connection is not open
+ ErrClosed = &Error{Code: ChannelError, Reason: "channel/connection is not open"}
+
+ // ErrChannelMax is returned when Connection.Channel has been called enough
+ // times that all channel IDs have been exhausted in the client or the
+ // server.
+ ErrChannelMax = &Error{Code: ChannelError, Reason: "channel id space exhausted"}
+
+ // ErrSASL is returned from Dial when the authentication mechanism could not
+ // be negoated.
+ ErrSASL = &Error{Code: AccessRefused, Reason: "SASL could not negotiate a shared mechanism"}
+
+ // ErrCredentials is returned when the authenticated client is not authorized
+ // to any vhost.
+ ErrCredentials = &Error{Code: AccessRefused, Reason: "username or password not allowed"}
+
+ // ErrVhost is returned when the authenticated user is not permitted to
+ // access the requested Vhost.
+ ErrVhost = &Error{Code: AccessRefused, Reason: "no access to this vhost"}
+
+ // ErrSyntax is hard protocol error, indicating an unsupported protocol,
+ // implementation or encoding.
+ ErrSyntax = &Error{Code: SyntaxError, Reason: "invalid field or value inside of a frame"}
+
+ // ErrFrame is returned when the protocol frame cannot be read from the
+ // server, indicating an unsupported protocol or unsupported frame type.
+ ErrFrame = &Error{Code: FrameError, Reason: "frame could not be parsed"}
+
+ // ErrCommandInvalid is returned when the server sends an unexpected response
+ // to this requested message type. This indicates a bug in this client.
+ ErrCommandInvalid = &Error{Code: CommandInvalid, Reason: "unexpected command received"}
+
+ // ErrUnexpectedFrame is returned when something other than a method or
+ // heartbeat frame is delivered to the Connection, indicating a bug in the
+ // client.
+ ErrUnexpectedFrame = &Error{Code: UnexpectedFrame, Reason: "unexpected frame received"}
+
+ // ErrFieldType is returned when writing a message containing a Go type unsupported by AMQP.
+ ErrFieldType = &Error{Code: SyntaxError, Reason: "unsupported table field type"}
+)
+
+// Error captures the code and reason a channel or connection has been closed
+// by the server.
+type Error struct {
+ Code int // constant code from the specification
+ Reason string // description of the error
+ Server bool // true when initiated from the server, false when from this library
+ Recover bool // true when this error can be recovered by retrying later or with different parameters
+}
+
+func newError(code uint16, text string) *Error {
+ return &Error{
+ Code: int(code),
+ Reason: text,
+ Recover: isSoftExceptionCode(int(code)),
+ Server: true,
+ }
+}
+
+func (e Error) Error() string {
+ return fmt.Sprintf("Exception (%d) Reason: %q", e.Code, e.Reason)
+}
+
+// Used by header frames to capture routing and header information
+type properties struct {
+ ContentType string // MIME content type
+ ContentEncoding string // MIME content encoding
+ Headers Table // Application or header exchange table
+ DeliveryMode uint8 // queue implementation use - Transient (1) or Persistent (2)
+ Priority uint8 // queue implementation use - 0 to 9
+ CorrelationId string // application use - correlation identifier
+ ReplyTo string // application use - address to to reply to (ex: RPC)
+ Expiration string // implementation use - message expiration spec
+ MessageId string // application use - message identifier
+ Timestamp time.Time // application use - message timestamp
+ Type string // application use - message type name
+ UserId string // application use - creating user id
+ AppId string // application use - creating application
+ reserved1 string // was cluster-id - process for buffer consumption
+}
+
+// DeliveryMode. Transient means higher throughput but messages will not be
+// restored on broker restart. The delivery mode of publishings is unrelated
+// to the durability of the queues they reside on. Transient messages will
+// not be restored to durable queues, persistent messages will be restored to
+// durable queues and lost on non-durable queues during server restart.
+//
+// This remains typed as uint8 to match Publishing.DeliveryMode. Other
+// delivery modes specific to custom queue implementations are not enumerated
+// here.
+const (
+ Transient uint8 = 1
+ Persistent uint8 = 2
+)
+
+// The property flags are an array of bits that indicate the presence or
+// absence of each property value in sequence. The bits are ordered from most
+// high to low - bit 15 indicates the first property.
+const (
+ flagContentType = 0x8000
+ flagContentEncoding = 0x4000
+ flagHeaders = 0x2000
+ flagDeliveryMode = 0x1000
+ flagPriority = 0x0800
+ flagCorrelationId = 0x0400
+ flagReplyTo = 0x0200
+ flagExpiration = 0x0100
+ flagMessageId = 0x0080
+ flagTimestamp = 0x0040
+ flagType = 0x0020
+ flagUserId = 0x0010
+ flagAppId = 0x0008
+ flagReserved1 = 0x0004
+)
+
+// Queue captures the current server state of the queue on the server returned
+// from Channel.QueueDeclare or Channel.QueueInspect.
+type Queue struct {
+ Name string // server confirmed or generated name
+ Messages int // count of messages not awaiting acknowledgment
+ Consumers int // number of consumers receiving deliveries
+}
+
+// Publishing captures the client message sent to the server. The fields
+// outside of the Headers table included in this struct mirror the underlying
+// fields in the content frame. They use native types for convenience and
+// efficiency.
+type Publishing struct {
+ // Application or exchange specific fields,
+ // the headers exchange will inspect this field.
+ Headers Table
+
+ // Properties
+ ContentType string // MIME content type
+ ContentEncoding string // MIME content encoding
+ DeliveryMode uint8 // Transient (0 or 1) or Persistent (2)
+ Priority uint8 // 0 to 9
+ CorrelationId string // correlation identifier
+ ReplyTo string // address to to reply to (ex: RPC)
+ Expiration string // message expiration spec
+ MessageId string // message identifier
+ Timestamp time.Time // message timestamp
+ Type string // message type name
+ UserId string // creating user id - ex: "guest"
+ AppId string // creating application id
+
+ // The application specific payload of the message
+ Body []byte
+}
+
+// Blocking notifies the server's TCP flow control of the Connection. When a
+// server hits a memory or disk alarm it will block all connections until the
+// resources are reclaimed. Use NotifyBlock on the Connection to receive these
+// events.
+type Blocking struct {
+ Active bool // TCP pushback active/inactive on server
+ Reason string // Server reason for activation
+}
+
+// Confirmation notifies the acknowledgment or negative acknowledgement of a
+// publishing identified by its delivery tag. Use NotifyPublish on the Channel
+// to consume these events.
+type Confirmation struct {
+ DeliveryTag uint64 // A 1 based counter of publishings from when the channel was put in Confirm mode
+ Ack bool // True when the server successfully received the publishing
+}
+
+// Decimal matches the AMQP decimal type. Scale is the number of decimal
+// digits Scale == 2, Value == 12345, Decimal == 123.45
+type Decimal struct {
+ Scale uint8
+ Value int32
+}
+
+// Table stores user supplied fields of the following types:
+//
+// bool
+// byte
+// float32
+// float64
+// int16
+// int32
+// int64
+// nil
+// string
+// time.Time
+// amqp.Decimal
+// amqp.Table
+// []byte
+// []interface{} - containing above types
+//
+// Functions taking a table will immediately fail when the table contains a
+// value of an unsupported type.
+//
+// The caller must be specific in which precision of integer it wishes to
+// encode.
+//
+// Use a type assertion when reading values from a table for type conversion.
+//
+// RabbitMQ expects int32 for integer values.
+//
+type Table map[string]interface{}
+
+func validateField(f interface{}) error {
+ switch fv := f.(type) {
+ case nil, bool, byte, int16, int32, int64, float32, float64, string, []byte, Decimal, time.Time:
+ return nil
+
+ case []interface{}:
+ for _, v := range fv {
+ if err := validateField(v); err != nil {
+ return fmt.Errorf("in array %s", err)
+ }
+ }
+ return nil
+
+ case Table:
+ for k, v := range fv {
+ if err := validateField(v); err != nil {
+ return fmt.Errorf("table field %q %s", k, err)
+ }
+ }
+ return nil
+ }
+
+ return fmt.Errorf("value %t not supported", f)
+}
+
+// Validate returns and error if any Go types in the table are incompatible with AMQP types.
+func (t Table) Validate() error {
+ return validateField(t)
+}
+
+// Heap interface for maintaining delivery tags
+type tagSet []uint64
+
+func (set tagSet) Len() int { return len(set) }
+func (set tagSet) Less(i, j int) bool { return (set)[i] < (set)[j] }
+func (set tagSet) Swap(i, j int) { (set)[i], (set)[j] = (set)[j], (set)[i] }
+func (set *tagSet) Push(tag interface{}) { *set = append(*set, tag.(uint64)) }
+func (set *tagSet) Pop() interface{} {
+ val := (*set)[len(*set)-1]
+ *set = (*set)[:len(*set)-1]
+ return val
+}
+
+type message interface {
+ id() (uint16, uint16)
+ wait() bool
+ read(io.Reader) error
+ write(io.Writer) error
+}
+
+type messageWithContent interface {
+ message
+ getContent() (properties, []byte)
+ setContent(properties, []byte)
+}
+
+/*
+The base interface implemented as:
+
+2.3.5 frame Details
+
+All frames consist of a header (7 octets), a payload of arbitrary size, and a 'frame-end' octet that detects
+malformed frames:
+
+ 0 1 3 7 size+7 size+8
+ +------+---------+-------------+ +------------+ +-----------+
+ | type | channel | size | | payload | | frame-end |
+ +------+---------+-------------+ +------------+ +-----------+
+ octet short long size octets octet
+
+To read a frame, we:
+
+ 1. Read the header and check the frame type and channel.
+ 2. Depending on the frame type, we read the payload and process it.
+ 3. Read the frame end octet.
+
+In realistic implementations where performance is a concern, we would use
+“read-ahead buffering” or “gathering reads” to avoid doing three separate
+system calls to read a frame.
+
+*/
+type frame interface {
+ write(io.Writer) error
+ channel() uint16
+}
+
+type reader struct {
+ r io.Reader
+}
+
+type writer struct {
+ w io.Writer
+}
+
+// Implements the frame interface for Connection RPC
+type protocolHeader struct{}
+
+func (protocolHeader) write(w io.Writer) error {
+ _, err := w.Write([]byte{'A', 'M', 'Q', 'P', 0, 0, 9, 1})
+ return err
+}
+
+func (protocolHeader) channel() uint16 {
+ panic("only valid as initial handshake")
+}
+
+/*
+Method frames carry the high-level protocol commands (which we call "methods").
+One method frame carries one command. The method frame payload has this format:
+
+ 0 2 4
+ +----------+-----------+-------------- - -
+ | class-id | method-id | arguments...
+ +----------+-----------+-------------- - -
+ short short ...
+
+To process a method frame, we:
+ 1. Read the method frame payload.
+ 2. Unpack it into a structure. A given method always has the same structure,
+ so we can unpack the method rapidly. 3. Check that the method is allowed in
+ the current context.
+ 4. Check that the method arguments are valid.
+ 5. Execute the method.
+
+Method frame bodies are constructed as a list of AMQP data fields (bits,
+integers, strings and string tables). The marshalling code is trivially
+generated directly from the protocol specifications, and can be very rapid.
+*/
+type methodFrame struct {
+ ChannelId uint16
+ ClassId uint16
+ MethodId uint16
+ Method message
+}
+
+func (f *methodFrame) channel() uint16 { return f.ChannelId }
+
+/*
+Heartbeating is a technique designed to undo one of TCP/IP's features, namely
+its ability to recover from a broken physical connection by closing only after
+a quite long time-out. In some scenarios we need to know very rapidly if a
+peer is disconnected or not responding for other reasons (e.g. it is looping).
+Since heartbeating can be done at a low level, we implement this as a special
+type of frame that peers exchange at the transport level, rather than as a
+class method.
+*/
+type heartbeatFrame struct {
+ ChannelId uint16
+}
+
+func (f *heartbeatFrame) channel() uint16 { return f.ChannelId }
+
+/*
+Certain methods (such as Basic.Publish, Basic.Deliver, etc.) are formally
+defined as carrying content. When a peer sends such a method frame, it always
+follows it with a content header and zero or more content body frames.
+
+A content header frame has this format:
+
+ 0 2 4 12 14
+ +----------+--------+-----------+----------------+------------- - -
+ | class-id | weight | body size | property flags | property list...
+ +----------+--------+-----------+----------------+------------- - -
+ short short long long short remainder...
+
+We place content body in distinct frames (rather than including it in the
+method) so that AMQP may support "zero copy" techniques in which content is
+never marshalled or encoded. We place the content properties in their own
+frame so that recipients can selectively discard contents they do not want to
+process
+*/
+type headerFrame struct {
+ ChannelId uint16
+ ClassId uint16
+ weight uint16
+ Size uint64
+ Properties properties
+}
+
+func (f *headerFrame) channel() uint16 { return f.ChannelId }
+
+/*
+Content is the application data we carry from client-to-client via the AMQP
+server. Content is, roughly speaking, a set of properties plus a binary data
+part. The set of allowed properties are defined by the Basic class, and these
+form the "content header frame". The data can be any size, and MAY be broken
+into several (or many) chunks, each forming a "content body frame".
+
+Looking at the frames for a specific channel, as they pass on the wire, we
+might see something like this:
+
+ [method]
+ [method] [header] [body] [body]
+ [method]
+ ...
+*/
+type bodyFrame struct {
+ ChannelId uint16
+ Body []byte
+}
+
+func (f *bodyFrame) channel() uint16 { return f.ChannelId }
diff --git a/vendor/github.com/streadway/amqp/uri.go b/vendor/github.com/streadway/amqp/uri.go
new file mode 100644
index 00000000..35fefdc2
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/uri.go
@@ -0,0 +1,167 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "errors"
+ "net"
+ "net/url"
+ "strconv"
+ "strings"
+)
+
+var errURIScheme = errors.New("AMQP scheme must be either 'amqp://' or 'amqps://'")
+var errURIWhitespace = errors.New("URI must not contain whitespace")
+
+var schemePorts = map[string]int{
+ "amqp": 5672,
+ "amqps": 5671,
+}
+
+var defaultURI = URI{
+ Scheme: "amqp",
+ Host: "localhost",
+ Port: 5672,
+ Username: "guest",
+ Password: "guest",
+ Vhost: "/",
+}
+
+// URI represents a parsed AMQP URI string.
+type URI struct {
+ Scheme string
+ Host string
+ Port int
+ Username string
+ Password string
+ Vhost string
+}
+
+// ParseURI attempts to parse the given AMQP URI according to the spec.
+// See http://www.rabbitmq.com/uri-spec.html.
+//
+// Default values for the fields are:
+//
+// Scheme: amqp
+// Host: localhost
+// Port: 5672
+// Username: guest
+// Password: guest
+// Vhost: /
+//
+func ParseURI(uri string) (URI, error) {
+ builder := defaultURI
+
+ if strings.Contains(uri, " ") == true {
+ return builder, errURIWhitespace
+ }
+
+ u, err := url.Parse(uri)
+ if err != nil {
+ return builder, err
+ }
+
+ defaultPort, okScheme := schemePorts[u.Scheme]
+
+ if okScheme {
+ builder.Scheme = u.Scheme
+ } else {
+ return builder, errURIScheme
+ }
+
+ host := u.Hostname()
+ port := u.Port()
+
+ if host != "" {
+ builder.Host = host
+ }
+
+ if port != "" {
+ port32, err := strconv.ParseInt(port, 10, 32)
+ if err != nil {
+ return builder, err
+ }
+ builder.Port = int(port32)
+ } else {
+ builder.Port = defaultPort
+ }
+
+ if u.User != nil {
+ builder.Username = u.User.Username()
+ if password, ok := u.User.Password(); ok {
+ builder.Password = password
+ }
+ }
+
+ if u.Path != "" {
+ if strings.HasPrefix(u.Path, "/") {
+ if u.Host == "" && strings.HasPrefix(u.Path, "///") {
+ // net/url doesn't handle local context authorities and leaves that up
+ // to the scheme handler. In our case, we translate amqp:/// into the
+ // default host and whatever the vhost should be
+ if len(u.Path) > 3 {
+ builder.Vhost = u.Path[3:]
+ }
+ } else if len(u.Path) > 1 {
+ builder.Vhost = u.Path[1:]
+ }
+ } else {
+ builder.Vhost = u.Path
+ }
+ }
+
+ return builder, nil
+}
+
+// PlainAuth returns a PlainAuth structure based on the parsed URI's
+// Username and Password fields.
+func (uri URI) PlainAuth() *PlainAuth {
+ return &PlainAuth{
+ Username: uri.Username,
+ Password: uri.Password,
+ }
+}
+
+func (uri URI) String() string {
+ authority, err := url.Parse("")
+ if err != nil {
+ return err.Error()
+ }
+
+ authority.Scheme = uri.Scheme
+
+ if uri.Username != defaultURI.Username || uri.Password != defaultURI.Password {
+ authority.User = url.User(uri.Username)
+
+ if uri.Password != defaultURI.Password {
+ authority.User = url.UserPassword(uri.Username, uri.Password)
+ }
+ }
+
+ authority.Host = net.JoinHostPort(uri.Host, strconv.Itoa(uri.Port))
+
+ if defaultPort, found := schemePorts[uri.Scheme]; !found || defaultPort != uri.Port {
+ authority.Host = net.JoinHostPort(uri.Host, strconv.Itoa(uri.Port))
+ } else {
+ // JoinHostPort() automatically add brackets to the host if it's
+ // an IPv6 address.
+ //
+ // If not port is specified, JoinHostPort() return an IP address in the
+ // form of "[::1]:", so we use TrimSuffix() to remove the extra ":".
+ authority.Host = strings.TrimSuffix(net.JoinHostPort(uri.Host, ""), ":")
+ }
+
+ if uri.Vhost != defaultURI.Vhost {
+ // Make sure net/url does not double escape, e.g.
+ // "%2F" does not become "%252F".
+ authority.Path = uri.Vhost
+ authority.RawPath = url.QueryEscape(uri.Vhost)
+ } else {
+ authority.Path = "/"
+ }
+
+ return authority.String()
+}
diff --git a/vendor/github.com/streadway/amqp/write.go b/vendor/github.com/streadway/amqp/write.go
new file mode 100644
index 00000000..58ed20d6
--- /dev/null
+++ b/vendor/github.com/streadway/amqp/write.go
@@ -0,0 +1,411 @@
+// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// Source code and contact info at http://github.com/streadway/amqp
+
+package amqp
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "io"
+ "math"
+ "time"
+)
+
+func (w *writer) WriteFrame(frame frame) (err error) {
+ if err = frame.write(w.w); err != nil {
+ return
+ }
+
+ if buf, ok := w.w.(*bufio.Writer); ok {
+ err = buf.Flush()
+ }
+
+ return
+}
+
+func (f *methodFrame) write(w io.Writer) (err error) {
+ var payload bytes.Buffer
+
+ if f.Method == nil {
+ return errors.New("malformed frame: missing method")
+ }
+
+ class, method := f.Method.id()
+
+ if err = binary.Write(&payload, binary.BigEndian, class); err != nil {
+ return
+ }
+
+ if err = binary.Write(&payload, binary.BigEndian, method); err != nil {
+ return
+ }
+
+ if err = f.Method.write(&payload); err != nil {
+ return
+ }
+
+ return writeFrame(w, frameMethod, f.ChannelId, payload.Bytes())
+}
+
+// Heartbeat
+//
+// Payload is empty
+func (f *heartbeatFrame) write(w io.Writer) (err error) {
+ return writeFrame(w, frameHeartbeat, f.ChannelId, []byte{})
+}
+
+// CONTENT HEADER
+// 0 2 4 12 14
+// +----------+--------+-----------+----------------+------------- - -
+// | class-id | weight | body size | property flags | property list...
+// +----------+--------+-----------+----------------+------------- - -
+// short short long long short remainder...
+//
+func (f *headerFrame) write(w io.Writer) (err error) {
+ var payload bytes.Buffer
+ var zeroTime time.Time
+
+ if err = binary.Write(&payload, binary.BigEndian, f.ClassId); err != nil {
+ return
+ }
+
+ if err = binary.Write(&payload, binary.BigEndian, f.weight); err != nil {
+ return
+ }
+
+ if err = binary.Write(&payload, binary.BigEndian, f.Size); err != nil {
+ return
+ }
+
+ // First pass will build the mask to be serialized, second pass will serialize
+ // each of the fields that appear in the mask.
+
+ var mask uint16
+
+ if len(f.Properties.ContentType) > 0 {
+ mask = mask | flagContentType
+ }
+ if len(f.Properties.ContentEncoding) > 0 {
+ mask = mask | flagContentEncoding
+ }
+ if f.Properties.Headers != nil && len(f.Properties.Headers) > 0 {
+ mask = mask | flagHeaders
+ }
+ if f.Properties.DeliveryMode > 0 {
+ mask = mask | flagDeliveryMode
+ }
+ if f.Properties.Priority > 0 {
+ mask = mask | flagPriority
+ }
+ if len(f.Properties.CorrelationId) > 0 {
+ mask = mask | flagCorrelationId
+ }
+ if len(f.Properties.ReplyTo) > 0 {
+ mask = mask | flagReplyTo
+ }
+ if len(f.Properties.Expiration) > 0 {
+ mask = mask | flagExpiration
+ }
+ if len(f.Properties.MessageId) > 0 {
+ mask = mask | flagMessageId
+ }
+ if f.Properties.Timestamp != zeroTime {
+ mask = mask | flagTimestamp
+ }
+ if len(f.Properties.Type) > 0 {
+ mask = mask | flagType
+ }
+ if len(f.Properties.UserId) > 0 {
+ mask = mask | flagUserId
+ }
+ if len(f.Properties.AppId) > 0 {
+ mask = mask | flagAppId
+ }
+
+ if err = binary.Write(&payload, binary.BigEndian, mask); err != nil {
+ return
+ }
+
+ if hasProperty(mask, flagContentType) {
+ if err = writeShortstr(&payload, f.Properties.ContentType); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagContentEncoding) {
+ if err = writeShortstr(&payload, f.Properties.ContentEncoding); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagHeaders) {
+ if err = writeTable(&payload, f.Properties.Headers); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagDeliveryMode) {
+ if err = binary.Write(&payload, binary.BigEndian, f.Properties.DeliveryMode); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagPriority) {
+ if err = binary.Write(&payload, binary.BigEndian, f.Properties.Priority); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagCorrelationId) {
+ if err = writeShortstr(&payload, f.Properties.CorrelationId); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagReplyTo) {
+ if err = writeShortstr(&payload, f.Properties.ReplyTo); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagExpiration) {
+ if err = writeShortstr(&payload, f.Properties.Expiration); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagMessageId) {
+ if err = writeShortstr(&payload, f.Properties.MessageId); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagTimestamp) {
+ if err = binary.Write(&payload, binary.BigEndian, uint64(f.Properties.Timestamp.Unix())); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagType) {
+ if err = writeShortstr(&payload, f.Properties.Type); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagUserId) {
+ if err = writeShortstr(&payload, f.Properties.UserId); err != nil {
+ return
+ }
+ }
+ if hasProperty(mask, flagAppId) {
+ if err = writeShortstr(&payload, f.Properties.AppId); err != nil {
+ return
+ }
+ }
+
+ return writeFrame(w, frameHeader, f.ChannelId, payload.Bytes())
+}
+
+// Body
+//
+// Payload is one byterange from the full body who's size is declared in the
+// Header frame
+func (f *bodyFrame) write(w io.Writer) (err error) {
+ return writeFrame(w, frameBody, f.ChannelId, f.Body)
+}
+
+func writeFrame(w io.Writer, typ uint8, channel uint16, payload []byte) (err error) {
+ end := []byte{frameEnd}
+ size := uint(len(payload))
+
+ _, err = w.Write([]byte{
+ byte(typ),
+ byte((channel & 0xff00) >> 8),
+ byte((channel & 0x00ff) >> 0),
+ byte((size & 0xff000000) >> 24),
+ byte((size & 0x00ff0000) >> 16),
+ byte((size & 0x0000ff00) >> 8),
+ byte((size & 0x000000ff) >> 0),
+ })
+
+ if err != nil {
+ return
+ }
+
+ if _, err = w.Write(payload); err != nil {
+ return
+ }
+
+ if _, err = w.Write(end); err != nil {
+ return
+ }
+
+ return
+}
+
+func writeShortstr(w io.Writer, s string) (err error) {
+ b := []byte(s)
+
+ var length = uint8(len(b))
+
+ if err = binary.Write(w, binary.BigEndian, length); err != nil {
+ return
+ }
+
+ if _, err = w.Write(b[:length]); err != nil {
+ return
+ }
+
+ return
+}
+
+func writeLongstr(w io.Writer, s string) (err error) {
+ b := []byte(s)
+
+ var length = uint32(len(b))
+
+ if err = binary.Write(w, binary.BigEndian, length); err != nil {
+ return
+ }
+
+ if _, err = w.Write(b[:length]); err != nil {
+ return
+ }
+
+ return
+}
+
+/*
+'A': []interface{}
+'D': Decimal
+'F': Table
+'I': int32
+'S': string
+'T': time.Time
+'V': nil
+'b': byte
+'d': float64
+'f': float32
+'l': int64
+'s': int16
+'t': bool
+'x': []byte
+*/
+func writeField(w io.Writer, value interface{}) (err error) {
+ var buf [9]byte
+ var enc []byte
+
+ switch v := value.(type) {
+ case bool:
+ buf[0] = 't'
+ if v {
+ buf[1] = byte(1)
+ } else {
+ buf[1] = byte(0)
+ }
+ enc = buf[:2]
+
+ case byte:
+ buf[0] = 'b'
+ buf[1] = byte(v)
+ enc = buf[:2]
+
+ case int16:
+ buf[0] = 's'
+ binary.BigEndian.PutUint16(buf[1:3], uint16(v))
+ enc = buf[:3]
+
+ case int32:
+ buf[0] = 'I'
+ binary.BigEndian.PutUint32(buf[1:5], uint32(v))
+ enc = buf[:5]
+
+ case int64:
+ buf[0] = 'l'
+ binary.BigEndian.PutUint64(buf[1:9], uint64(v))
+ enc = buf[:9]
+
+ case float32:
+ buf[0] = 'f'
+ binary.BigEndian.PutUint32(buf[1:5], math.Float32bits(v))
+ enc = buf[:5]
+
+ case float64:
+ buf[0] = 'd'
+ binary.BigEndian.PutUint64(buf[1:9], math.Float64bits(v))
+ enc = buf[:9]
+
+ case Decimal:
+ buf[0] = 'D'
+ buf[1] = byte(v.Scale)
+ binary.BigEndian.PutUint32(buf[2:6], uint32(v.Value))
+ enc = buf[:6]
+
+ case string:
+ buf[0] = 'S'
+ binary.BigEndian.PutUint32(buf[1:5], uint32(len(v)))
+ enc = append(buf[:5], []byte(v)...)
+
+ case []interface{}: // field-array
+ buf[0] = 'A'
+
+ sec := new(bytes.Buffer)
+ for _, val := range v {
+ if err = writeField(sec, val); err != nil {
+ return
+ }
+ }
+
+ binary.BigEndian.PutUint32(buf[1:5], uint32(sec.Len()))
+ if _, err = w.Write(buf[:5]); err != nil {
+ return
+ }
+
+ if _, err = w.Write(sec.Bytes()); err != nil {
+ return
+ }
+
+ return
+
+ case time.Time:
+ buf[0] = 'T'
+ binary.BigEndian.PutUint64(buf[1:9], uint64(v.Unix()))
+ enc = buf[:9]
+
+ case Table:
+ if _, err = w.Write([]byte{'F'}); err != nil {
+ return
+ }
+ return writeTable(w, v)
+
+ case []byte:
+ buf[0] = 'x'
+ binary.BigEndian.PutUint32(buf[1:5], uint32(len(v)))
+ if _, err = w.Write(buf[0:5]); err != nil {
+ return
+ }
+ if _, err = w.Write(v); err != nil {
+ return
+ }
+ return
+
+ case nil:
+ buf[0] = 'V'
+ enc = buf[:1]
+
+ default:
+ return ErrFieldType
+ }
+
+ _, err = w.Write(enc)
+
+ return
+}
+
+func writeTable(w io.Writer, table Table) (err error) {
+ var buf bytes.Buffer
+
+ for key, val := range table {
+ if err = writeShortstr(&buf, key); err != nil {
+ return
+ }
+ if err = writeField(&buf, val); err != nil {
+ return
+ }
+ }
+
+ return writeLongstr(w, string(buf.Bytes()))
+}
diff --git a/vendor/github.com/tidwall/btree/.travis.yml b/vendor/github.com/tidwall/btree/.travis.yml
new file mode 100644
index 00000000..4f2ee4d9
--- /dev/null
+++ b/vendor/github.com/tidwall/btree/.travis.yml
@@ -0,0 +1 @@
+language: go
diff --git a/vendor/github.com/tidwall/btree/LICENSE b/vendor/github.com/tidwall/btree/LICENSE
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/vendor/github.com/tidwall/btree/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/tidwall/btree/README.md b/vendor/github.com/tidwall/btree/README.md
new file mode 100644
index 00000000..deb1e886
--- /dev/null
+++ b/vendor/github.com/tidwall/btree/README.md
@@ -0,0 +1,107 @@
+BTree implementation for Go
+===========================
+
+
+[](https://godoc.org/github.com/tidwall/btree)
+
+This package provides an in-memory B-Tree implementation for Go, useful as
+an ordered, mutable data structure.
+
+This is a fork of the wonderful [google/btree](https://github.com/google/btree) package. It's has all the same great features and adds a few more.
+
+- Descend* functions for iterating backwards.
+- Iteration performance boost.
+- User defined context.
+
+User defined context
+--------------------
+This is a great new feature that allows for entering the same item into multiple B-trees, and each B-tree have a different ordering formula.
+
+For example:
+
+```go
+package main
+
+import (
+ "fmt"
+
+ "github.com/tidwall/btree"
+)
+
+type Item struct {
+ Key, Val string
+}
+
+func (i1 *Item) Less(item btree.Item, ctx interface{}) bool {
+ i2 := item.(*Item)
+ switch tag := ctx.(type) {
+ case string:
+ if tag == "vals" {
+ if i1.Val < i2.Val {
+ return true
+ } else if i1.Val > i2.Val {
+ return false
+ }
+ // Both vals are equal so we should fall though
+ // and let the key comparison take over.
+ }
+ }
+ return i1.Key < i2.Key
+}
+
+func main() {
+
+ // Create a tree for keys and a tree for values.
+ // The "keys" tree will be sorted on the Keys field.
+ // The "values" tree will be sorted on the Values field.
+ keys := btree.New(16, "keys")
+ vals := btree.New(16, "vals")
+
+ // Create some items.
+ users := []*Item{
+ &Item{Key: "user:1", Val: "Jane"},
+ &Item{Key: "user:2", Val: "Andy"},
+ &Item{Key: "user:3", Val: "Steve"},
+ &Item{Key: "user:4", Val: "Andrea"},
+ &Item{Key: "user:5", Val: "Janet"},
+ &Item{Key: "user:6", Val: "Andy"},
+ }
+
+ // Insert each user into both trees
+ for _, user := range users {
+ keys.ReplaceOrInsert(user)
+ vals.ReplaceOrInsert(user)
+ }
+
+ // Iterate over each user in the key tree
+ keys.Ascend(func(item btree.Item) bool {
+ kvi := item.(*Item)
+ fmt.Printf("%s %s\n", kvi.Key, kvi.Val)
+ return true
+ })
+
+ fmt.Printf("\n")
+ // Iterate over each user in the val tree
+ vals.Ascend(func(item btree.Item) bool {
+ kvi := item.(*Item)
+ fmt.Printf("%s %s\n", kvi.Key, kvi.Val)
+ return true
+ })
+}
+
+// Should see the results
+/*
+user:1 Jane
+user:2 Andy
+user:3 Steve
+user:4 Andrea
+user:5 Janet
+user:6 Andy
+
+user:4 Andrea
+user:2 Andy
+user:6 Andy
+user:1 Jane
+user:3 Steve
+*/
+```
diff --git a/vendor/github.com/tidwall/btree/btree.go b/vendor/github.com/tidwall/btree/btree.go
new file mode 100644
index 00000000..26f0d23a
--- /dev/null
+++ b/vendor/github.com/tidwall/btree/btree.go
@@ -0,0 +1,968 @@
+// Copyright 2014 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package btree implements in-memory B-Trees of arbitrary degree.
+//
+// btree implements an in-memory B-Tree for use as an ordered data structure.
+// It is not meant for persistent storage solutions.
+//
+// It has a flatter structure than an equivalent red-black or other binary tree,
+// which in some cases yields better memory usage and/or performance.
+// See some discussion on the matter here:
+// http://google-opensource.blogspot.com/2013/01/c-containers-that-save-memory-and-time.html
+// Note, though, that this project is in no way related to the C++ B-Tree
+// implementation written about there.
+//
+// Within this tree, each node contains a slice of items and a (possibly nil)
+// slice of children. For basic numeric values or raw structs, this can cause
+// efficiency differences when compared to equivalent C++ template code that
+// stores values in arrays within the node:
+// * Due to the overhead of storing values as interfaces (each
+// value needs to be stored as the value itself, then 2 words for the
+// interface pointing to that value and its type), resulting in higher
+// memory use.
+// * Since interfaces can point to values anywhere in memory, values are
+// most likely not stored in contiguous blocks, resulting in a higher
+// number of cache misses.
+// These issues don't tend to matter, though, when working with strings or other
+// heap-allocated structures, since C++-equivalent structures also must store
+// pointers and also distribute their values across the heap.
+//
+// This implementation is designed to be a drop-in replacement to gollrb.LLRB
+// trees, (http://github.com/petar/gollrb), an excellent and probably the most
+// widely used ordered tree implementation in the Go ecosystem currently.
+// Its functions, therefore, exactly mirror those of
+// llrb.LLRB where possible. Unlike gollrb, though, we currently don't
+// support storing multiple equivalent values.
+package btree
+
+import (
+ "fmt"
+ "io"
+ "strings"
+ "sync"
+)
+
+// Item represents a single object in the tree.
+type Item interface {
+ // Less tests whether the current item is less than the given argument.
+ //
+ // This must provide a strict weak ordering.
+ // If !a.Less(b) && !b.Less(a), we treat this to mean a == b (i.e. we can only
+ // hold one of either a or b in the tree).
+ //
+ // There is a user-defined ctx argument that is equal to the ctx value which
+ // is set at time of the btree contruction.
+ Less(than Item, ctx interface{}) bool
+}
+
+const (
+ DefaultFreeListSize = 32
+)
+
+var (
+ nilItems = make(items, 16)
+ nilChildren = make(children, 16)
+)
+
+// FreeList represents a free list of btree nodes. By default each
+// BTree has its own FreeList, but multiple BTrees can share the same
+// FreeList.
+// Two Btrees using the same freelist are safe for concurrent write access.
+type FreeList struct {
+ mu sync.Mutex
+ freelist []*node
+}
+
+// NewFreeList creates a new free list.
+// size is the maximum size of the returned free list.
+func NewFreeList(size int) *FreeList {
+ return &FreeList{freelist: make([]*node, 0, size)}
+}
+
+func (f *FreeList) newNode() (n *node) {
+ f.mu.Lock()
+ index := len(f.freelist) - 1
+ if index < 0 {
+ f.mu.Unlock()
+ return new(node)
+ }
+ n = f.freelist[index]
+ f.freelist[index] = nil
+ f.freelist = f.freelist[:index]
+ f.mu.Unlock()
+ return
+}
+
+func (f *FreeList) freeNode(n *node) {
+ f.mu.Lock()
+ if len(f.freelist) < cap(f.freelist) {
+ f.freelist = append(f.freelist, n)
+ }
+ f.mu.Unlock()
+}
+
+// ItemIterator allows callers of Ascend* to iterate in-order over portions of
+// the tree. When this function returns false, iteration will stop and the
+// associated Ascend* function will immediately return.
+type ItemIterator func(i Item) bool
+
+// New creates a new B-Tree with the given degree.
+//
+// New(2), for example, will create a 2-3-4 tree (each node contains 1-3 items
+// and 2-4 children).
+func New(degree int, ctx interface{}) *BTree {
+ return NewWithFreeList(degree, NewFreeList(DefaultFreeListSize), ctx)
+}
+
+// NewWithFreeList creates a new B-Tree that uses the given node free list.
+func NewWithFreeList(degree int, f *FreeList, ctx interface{}) *BTree {
+ if degree <= 1 {
+ panic("bad degree")
+ }
+ return &BTree{
+ degree: degree,
+ cow: ©OnWriteContext{freelist: f},
+ ctx: ctx,
+ }
+}
+
+// items stores items in a node.
+type items []Item
+
+// insertAt inserts a value into the given index, pushing all subsequent values
+// forward.
+func (s *items) insertAt(index int, item Item) {
+ *s = append(*s, nil)
+ if index < len(*s) {
+ copy((*s)[index+1:], (*s)[index:])
+ }
+ (*s)[index] = item
+}
+
+// removeAt removes a value at a given index, pulling all subsequent values
+// back.
+func (s *items) removeAt(index int) Item {
+ item := (*s)[index]
+ copy((*s)[index:], (*s)[index+1:])
+ (*s)[len(*s)-1] = nil
+ *s = (*s)[:len(*s)-1]
+ return item
+}
+
+// pop removes and returns the last element in the list.
+func (s *items) pop() (out Item) {
+ index := len(*s) - 1
+ out = (*s)[index]
+ (*s)[index] = nil
+ *s = (*s)[:index]
+ return
+}
+
+// truncate truncates this instance at index so that it contains only the
+// first index items. index must be less than or equal to length.
+func (s *items) truncate(index int) {
+ var toClear items
+ *s, toClear = (*s)[:index], (*s)[index:]
+ for len(toClear) > 0 {
+ toClear = toClear[copy(toClear, nilItems):]
+ }
+}
+
+// find returns the index where the given item should be inserted into this
+// list. 'found' is true if the item already exists in the list at the given
+// index.
+func (s items) find(item Item, ctx interface{}) (index int, found bool) {
+ i, j := 0, len(s)
+ for i < j {
+ h := i + (j-i)/2
+ if !item.Less(s[h], ctx) {
+ i = h + 1
+ } else {
+ j = h
+ }
+ }
+ if i > 0 && !s[i-1].Less(item, ctx) {
+ return i - 1, true
+ }
+ return i, false
+}
+
+// children stores child nodes in a node.
+type children []*node
+
+// insertAt inserts a value into the given index, pushing all subsequent values
+// forward.
+func (s *children) insertAt(index int, n *node) {
+ *s = append(*s, nil)
+ if index < len(*s) {
+ copy((*s)[index+1:], (*s)[index:])
+ }
+ (*s)[index] = n
+}
+
+// removeAt removes a value at a given index, pulling all subsequent values
+// back.
+func (s *children) removeAt(index int) *node {
+ n := (*s)[index]
+ copy((*s)[index:], (*s)[index+1:])
+ (*s)[len(*s)-1] = nil
+ *s = (*s)[:len(*s)-1]
+ return n
+}
+
+// pop removes and returns the last element in the list.
+func (s *children) pop() (out *node) {
+ index := len(*s) - 1
+ out = (*s)[index]
+ (*s)[index] = nil
+ *s = (*s)[:index]
+ return
+}
+
+// truncate truncates this instance at index so that it contains only the
+// first index children. index must be less than or equal to length.
+func (s *children) truncate(index int) {
+ var toClear children
+ *s, toClear = (*s)[:index], (*s)[index:]
+ for len(toClear) > 0 {
+ toClear = toClear[copy(toClear, nilChildren):]
+ }
+}
+
+// node is an internal node in a tree.
+//
+// It must at all times maintain the invariant that either
+// * len(children) == 0, len(items) unconstrained
+// * len(children) == len(items) + 1
+type node struct {
+ items items
+ children children
+ cow *copyOnWriteContext
+}
+
+func (n *node) mutableFor(cow *copyOnWriteContext) *node {
+ if n.cow == cow {
+ return n
+ }
+ out := cow.newNode()
+ if cap(out.items) >= len(n.items) {
+ out.items = out.items[:len(n.items)]
+ } else {
+ out.items = make(items, len(n.items), cap(n.items))
+ }
+ copy(out.items, n.items)
+ // Copy children
+ if cap(out.children) >= len(n.children) {
+ out.children = out.children[:len(n.children)]
+ } else {
+ out.children = make(children, len(n.children), cap(n.children))
+ }
+ copy(out.children, n.children)
+ return out
+}
+
+func (n *node) mutableChild(i int) *node {
+ c := n.children[i].mutableFor(n.cow)
+ n.children[i] = c
+ return c
+}
+
+// split splits the given node at the given index. The current node shrinks,
+// and this function returns the item that existed at that index and a new node
+// containing all items/children after it.
+func (n *node) split(i int) (Item, *node) {
+ item := n.items[i]
+ next := n.cow.newNode()
+ next.items = append(next.items, n.items[i+1:]...)
+ n.items.truncate(i)
+ if len(n.children) > 0 {
+ next.children = append(next.children, n.children[i+1:]...)
+ n.children.truncate(i + 1)
+ }
+ return item, next
+}
+
+// maybeSplitChild checks if a child should be split, and if so splits it.
+// Returns whether or not a split occurred.
+func (n *node) maybeSplitChild(i, maxItems int) bool {
+ if len(n.children[i].items) < maxItems {
+ return false
+ }
+ first := n.mutableChild(i)
+ item, second := first.split(maxItems / 2)
+ n.items.insertAt(i, item)
+ n.children.insertAt(i+1, second)
+ return true
+}
+
+// insert inserts an item into the subtree rooted at this node, making sure
+// no nodes in the subtree exceed maxItems items. Should an equivalent item be
+// be found/replaced by insert, it will be returned.
+func (n *node) insert(item Item, maxItems int, ctx interface{}) Item {
+ i, found := n.items.find(item, ctx)
+ if found {
+ out := n.items[i]
+ n.items[i] = item
+ return out
+ }
+ if len(n.children) == 0 {
+ n.items.insertAt(i, item)
+ return nil
+ }
+ if n.maybeSplitChild(i, maxItems) {
+ inTree := n.items[i]
+ switch {
+ case item.Less(inTree, ctx):
+ // no change, we want first split node
+ case inTree.Less(item, ctx):
+ i++ // we want second split node
+ default:
+ out := n.items[i]
+ n.items[i] = item
+ return out
+ }
+ }
+ return n.mutableChild(i).insert(item, maxItems, ctx)
+}
+
+// get finds the given key in the subtree and returns it.
+func (n *node) get(key Item, ctx interface{}) Item {
+ i, found := n.items.find(key, ctx)
+ if found {
+ return n.items[i]
+ } else if len(n.children) > 0 {
+ return n.children[i].get(key, ctx)
+ }
+ return nil
+}
+
+// min returns the first item in the subtree.
+func min(n *node) Item {
+ if n == nil {
+ return nil
+ }
+ for len(n.children) > 0 {
+ n = n.children[0]
+ }
+ if len(n.items) == 0 {
+ return nil
+ }
+ return n.items[0]
+}
+
+// max returns the last item in the subtree.
+func max(n *node) Item {
+ if n == nil {
+ return nil
+ }
+ for len(n.children) > 0 {
+ n = n.children[len(n.children)-1]
+ }
+ if len(n.items) == 0 {
+ return nil
+ }
+ return n.items[len(n.items)-1]
+}
+
+// toRemove details what item to remove in a node.remove call.
+type toRemove int
+
+const (
+ removeItem toRemove = iota // removes the given item
+ removeMin // removes smallest item in the subtree
+ removeMax // removes largest item in the subtree
+)
+
+// remove removes an item from the subtree rooted at this node.
+func (n *node) remove(item Item, minItems int, typ toRemove, ctx interface{}) Item {
+ var i int
+ var found bool
+ switch typ {
+ case removeMax:
+ if len(n.children) == 0 {
+ return n.items.pop()
+ }
+ i = len(n.items)
+ case removeMin:
+ if len(n.children) == 0 {
+ return n.items.removeAt(0)
+ }
+ i = 0
+ case removeItem:
+ i, found = n.items.find(item, ctx)
+ if len(n.children) == 0 {
+ if found {
+ return n.items.removeAt(i)
+ }
+ return nil
+ }
+ default:
+ panic("invalid type")
+ }
+ // If we get to here, we have children.
+ if len(n.children[i].items) <= minItems {
+ return n.growChildAndRemove(i, item, minItems, typ, ctx)
+ }
+ child := n.mutableChild(i)
+ // Either we had enough items to begin with, or we've done some
+ // merging/stealing, because we've got enough now and we're ready to return
+ // stuff.
+ if found {
+ // The item exists at index 'i', and the child we've selected can give us a
+ // predecessor, since if we've gotten here it's got > minItems items in it.
+ out := n.items[i]
+ // We use our special-case 'remove' call with typ=maxItem to pull the
+ // predecessor of item i (the rightmost leaf of our immediate left child)
+ // and set it into where we pulled the item from.
+ n.items[i] = child.remove(nil, minItems, removeMax, ctx)
+ return out
+ }
+ // Final recursive call. Once we're here, we know that the item isn't in this
+ // node and that the child is big enough to remove from.
+ return child.remove(item, minItems, typ, ctx)
+}
+
+// growChildAndRemove grows child 'i' to make sure it's possible to remove an
+// item from it while keeping it at minItems, then calls remove to actually
+// remove it.
+//
+// Most documentation says we have to do two sets of special casing:
+// 1) item is in this node
+// 2) item is in child
+// In both cases, we need to handle the two subcases:
+// A) node has enough values that it can spare one
+// B) node doesn't have enough values
+// For the latter, we have to check:
+// a) left sibling has node to spare
+// b) right sibling has node to spare
+// c) we must merge
+// To simplify our code here, we handle cases #1 and #2 the same:
+// If a node doesn't have enough items, we make sure it does (using a,b,c).
+// We then simply redo our remove call, and the second time (regardless of
+// whether we're in case 1 or 2), we'll have enough items and can guarantee
+// that we hit case A.
+func (n *node) growChildAndRemove(i int, item Item, minItems int, typ toRemove, ctx interface{}) Item {
+ if i > 0 && len(n.children[i-1].items) > minItems {
+ // Steal from left child
+ child := n.mutableChild(i)
+ stealFrom := n.mutableChild(i - 1)
+ stolenItem := stealFrom.items.pop()
+ child.items.insertAt(0, n.items[i-1])
+ n.items[i-1] = stolenItem
+ if len(stealFrom.children) > 0 {
+ child.children.insertAt(0, stealFrom.children.pop())
+ }
+ } else if i < len(n.items) && len(n.children[i+1].items) > minItems {
+ // steal from right child
+ child := n.mutableChild(i)
+ stealFrom := n.mutableChild(i + 1)
+ stolenItem := stealFrom.items.removeAt(0)
+ child.items = append(child.items, n.items[i])
+ n.items[i] = stolenItem
+ if len(stealFrom.children) > 0 {
+ child.children = append(child.children, stealFrom.children.removeAt(0))
+ }
+ } else {
+ if i >= len(n.items) {
+ i--
+ }
+ child := n.mutableChild(i)
+ // merge with right child
+ mergeItem := n.items.removeAt(i)
+ mergeChild := n.children.removeAt(i + 1)
+ child.items = append(child.items, mergeItem)
+ child.items = append(child.items, mergeChild.items...)
+ child.children = append(child.children, mergeChild.children...)
+ n.cow.freeNode(mergeChild)
+ }
+ return n.remove(item, minItems, typ, ctx)
+}
+
+type direction int
+
+const (
+ descend = direction(-1)
+ ascend = direction(+1)
+)
+
+// iterate provides a simple method for iterating over elements in the tree.
+//
+// When ascending, the 'start' should be less than 'stop' and when descending,
+// the 'start' should be greater than 'stop'. Setting 'includeStart' to true
+// will force the iterator to include the first item when it equals 'start',
+// thus creating a "greaterOrEqual" or "lessThanEqual" rather than just a
+// "greaterThan" or "lessThan" queries.
+func (n *node) iterate(dir direction, start, stop Item, includeStart bool, hit bool, iter ItemIterator, ctx interface{}) (bool, bool) {
+ var ok bool
+ switch dir {
+ case ascend:
+ for i := 0; i < len(n.items); i++ {
+ if start != nil && n.items[i].Less(start, ctx) {
+ continue
+ }
+ if len(n.children) > 0 {
+ if hit, ok = n.children[i].iterate(dir, start, stop, includeStart, hit, iter, ctx); !ok {
+ return hit, false
+ }
+ }
+ if !includeStart && !hit && start != nil && !start.Less(n.items[i], ctx) {
+ hit = true
+ continue
+ }
+ hit = true
+ if stop != nil && !n.items[i].Less(stop, ctx) {
+ return hit, false
+ }
+ if !iter(n.items[i]) {
+ return hit, false
+ }
+ }
+ if len(n.children) > 0 {
+ if hit, ok = n.children[len(n.children)-1].iterate(dir, start, stop, includeStart, hit, iter, ctx); !ok {
+ return hit, false
+ }
+ }
+ case descend:
+ for i := len(n.items) - 1; i >= 0; i-- {
+ if start != nil && !n.items[i].Less(start, ctx) {
+ if !includeStart || hit || start.Less(n.items[i], ctx) {
+ continue
+ }
+ }
+ if len(n.children) > 0 {
+ if hit, ok = n.children[i+1].iterate(dir, start, stop, includeStart, hit, iter, ctx); !ok {
+ return hit, false
+ }
+ }
+ if stop != nil && !stop.Less(n.items[i], ctx) {
+ return hit, false // continue
+ }
+ hit = true
+ if !iter(n.items[i]) {
+ return hit, false
+ }
+ }
+ if len(n.children) > 0 {
+ if hit, ok = n.children[0].iterate(dir, start, stop, includeStart, hit, iter, ctx); !ok {
+ return hit, false
+ }
+ }
+ }
+ return hit, true
+}
+
+// Used for testing/debugging purposes.
+func (n *node) print(w io.Writer, level int) {
+ fmt.Fprintf(w, "%sNODE:%v\n", strings.Repeat(" ", level), n.items)
+ for _, c := range n.children {
+ c.print(w, level+1)
+ }
+}
+
+// BTree is an implementation of a B-Tree.
+//
+// BTree stores Item instances in an ordered structure, allowing easy insertion,
+// removal, and iteration.
+//
+// Write operations are not safe for concurrent mutation by multiple
+// goroutines, but Read operations are.
+type BTree struct {
+ degree int
+ length int
+ root *node
+ ctx interface{}
+ cow *copyOnWriteContext
+}
+
+// copyOnWriteContext pointers determine node ownership... a tree with a write
+// context equivalent to a node's write context is allowed to modify that node.
+// A tree whose write context does not match a node's is not allowed to modify
+// it, and must create a new, writable copy (IE: it's a Clone).
+//
+// When doing any write operation, we maintain the invariant that the current
+// node's context is equal to the context of the tree that requested the write.
+// We do this by, before we descend into any node, creating a copy with the
+// correct context if the contexts don't match.
+//
+// Since the node we're currently visiting on any write has the requesting
+// tree's context, that node is modifiable in place. Children of that node may
+// not share context, but before we descend into them, we'll make a mutable
+// copy.
+type copyOnWriteContext struct {
+ freelist *FreeList
+}
+
+// Clone clones the btree, lazily. Clone should not be called concurrently,
+// but the original tree (t) and the new tree (t2) can be used concurrently
+// once the Clone call completes.
+//
+// The internal tree structure of b is marked read-only and shared between t and
+// t2. Writes to both t and t2 use copy-on-write logic, creating new nodes
+// whenever one of b's original nodes would have been modified. Read operations
+// should have no performance degredation. Write operations for both t and t2
+// will initially experience minor slow-downs caused by additional allocs and
+// copies due to the aforementioned copy-on-write logic, but should converge to
+// the original performance characteristics of the original tree.
+func (t *BTree) Clone() (t2 *BTree) {
+ // Create two entirely new copy-on-write contexts.
+ // This operation effectively creates three trees:
+ // the original, shared nodes (old b.cow)
+ // the new b.cow nodes
+ // the new out.cow nodes
+ cow1, cow2 := *t.cow, *t.cow
+ out := *t
+ t.cow = &cow1
+ out.cow = &cow2
+ return &out
+}
+
+// maxItems returns the max number of items to allow per node.
+func (t *BTree) maxItems() int {
+ return t.degree*2 - 1
+}
+
+// minItems returns the min number of items to allow per node (ignored for the
+// root node).
+func (t *BTree) minItems() int {
+ return t.degree - 1
+}
+
+func (c *copyOnWriteContext) newNode() (n *node) {
+ n = c.freelist.newNode()
+ n.cow = c
+ return
+}
+
+func (c *copyOnWriteContext) freeNode(n *node) {
+ if n.cow == c {
+ // clear to allow GC
+ n.items.truncate(0)
+ n.children.truncate(0)
+ n.cow = nil
+ c.freelist.freeNode(n)
+ }
+}
+
+// ReplaceOrInsert adds the given item to the tree. If an item in the tree
+// already equals the given one, it is removed from the tree and returned.
+// Otherwise, nil is returned.
+//
+// nil cannot be added to the tree (will panic).
+func (t *BTree) ReplaceOrInsert(item Item) Item {
+ if item == nil {
+ panic("nil item being added to BTree")
+ }
+ if t.root == nil {
+ t.root = t.cow.newNode()
+ t.root.items = append(t.root.items, item)
+ t.length++
+ return nil
+ } else {
+ t.root = t.root.mutableFor(t.cow)
+ if len(t.root.items) >= t.maxItems() {
+ item2, second := t.root.split(t.maxItems() / 2)
+ oldroot := t.root
+ t.root = t.cow.newNode()
+ t.root.items = append(t.root.items, item2)
+ t.root.children = append(t.root.children, oldroot, second)
+ }
+ }
+ out := t.root.insert(item, t.maxItems(), t.ctx)
+ if out == nil {
+ t.length++
+ }
+ return out
+}
+
+// Delete removes an item equal to the passed in item from the tree, returning
+// it. If no such item exists, returns nil.
+func (t *BTree) Delete(item Item) Item {
+ return t.deleteItem(item, removeItem, t.ctx)
+}
+
+// DeleteMin removes the smallest item in the tree and returns it.
+// If no such item exists, returns nil.
+func (t *BTree) DeleteMin() Item {
+ return t.deleteItem(nil, removeMin, t.ctx)
+}
+
+// DeleteMax removes the largest item in the tree and returns it.
+// If no such item exists, returns nil.
+func (t *BTree) DeleteMax() Item {
+ return t.deleteItem(nil, removeMax, t.ctx)
+}
+
+func (t *BTree) deleteItem(item Item, typ toRemove, ctx interface{}) Item {
+ if t.root == nil || len(t.root.items) == 0 {
+ return nil
+ }
+ t.root = t.root.mutableFor(t.cow)
+ out := t.root.remove(item, t.minItems(), typ, ctx)
+ if len(t.root.items) == 0 && len(t.root.children) > 0 {
+ oldroot := t.root
+ t.root = t.root.children[0]
+ t.cow.freeNode(oldroot)
+ }
+ if out != nil {
+ t.length--
+ }
+ return out
+}
+
+// AscendRange calls the iterator for every value in the tree within the range
+// [greaterOrEqual, lessThan), until iterator returns false.
+func (t *BTree) AscendRange(greaterOrEqual, lessThan Item, iterator ItemIterator) {
+ if t.root == nil {
+ return
+ }
+ t.root.iterate(ascend, greaterOrEqual, lessThan, true, false, iterator, t.ctx)
+}
+
+// AscendLessThan calls the iterator for every value in the tree within the range
+// [first, pivot), until iterator returns false.
+func (t *BTree) AscendLessThan(pivot Item, iterator ItemIterator) {
+ if t.root == nil {
+ return
+ }
+ t.root.iterate(ascend, nil, pivot, false, false, iterator, t.ctx)
+}
+
+// AscendGreaterOrEqual calls the iterator for every value in the tree within
+// the range [pivot, last], until iterator returns false.
+func (t *BTree) AscendGreaterOrEqual(pivot Item, iterator ItemIterator) {
+ if t.root == nil {
+ return
+ }
+ t.root.iterate(ascend, pivot, nil, true, false, iterator, t.ctx)
+}
+
+// Ascend calls the iterator for every value in the tree within the range
+// [first, last], until iterator returns false.
+func (t *BTree) Ascend(iterator ItemIterator) {
+ if t.root == nil {
+ return
+ }
+ t.root.iterate(ascend, nil, nil, false, false, iterator, t.ctx)
+}
+
+// DescendRange calls the iterator for every value in the tree within the range
+// [lessOrEqual, greaterThan), until iterator returns false.
+func (t *BTree) DescendRange(lessOrEqual, greaterThan Item, iterator ItemIterator) {
+ if t.root == nil {
+ return
+ }
+ t.root.iterate(descend, lessOrEqual, greaterThan, true, false, iterator, t.ctx)
+}
+
+// DescendLessOrEqual calls the iterator for every value in the tree within the range
+// [pivot, first], until iterator returns false.
+func (t *BTree) DescendLessOrEqual(pivot Item, iterator ItemIterator) {
+ if t.root == nil {
+ return
+ }
+ t.root.iterate(descend, pivot, nil, true, false, iterator, t.ctx)
+}
+
+// DescendGreaterThan calls the iterator for every value in the tree within
+// the range (pivot, last], until iterator returns false.
+func (t *BTree) DescendGreaterThan(pivot Item, iterator ItemIterator) {
+ if t.root == nil {
+ return
+ }
+ t.root.iterate(descend, nil, pivot, false, false, iterator, t.ctx)
+}
+
+// Descend calls the iterator for every value in the tree within the range
+// [last, first], until iterator returns false.
+func (t *BTree) Descend(iterator ItemIterator) {
+ if t.root == nil {
+ return
+ }
+ t.root.iterate(descend, nil, nil, false, false, iterator, t.ctx)
+}
+
+// Get looks for the key item in the tree, returning it. It returns nil if
+// unable to find that item.
+func (t *BTree) Get(key Item) Item {
+ if t.root == nil {
+ return nil
+ }
+ return t.root.get(key, t.ctx)
+}
+
+// Min returns the smallest item in the tree, or nil if the tree is empty.
+func (t *BTree) Min() Item {
+ return min(t.root)
+}
+
+// Max returns the largest item in the tree, or nil if the tree is empty.
+func (t *BTree) Max() Item {
+ return max(t.root)
+}
+
+// Has returns true if the given key is in the tree.
+func (t *BTree) Has(key Item) bool {
+ return t.Get(key) != nil
+}
+
+// Len returns the number of items currently in the tree.
+func (t *BTree) Len() int {
+ return t.length
+}
+
+// Int implements the Item interface for integers.
+type Int int
+
+// Less returns true if int(a) < int(b).
+func (a Int) Less(b Item, ctx interface{}) bool {
+ return a < b.(Int)
+}
+
+type stackItem struct {
+ n *node // current node
+ i int // index of the next child/item.
+}
+
+// Cursor represents an iterator that can traverse over all items in the tree
+// in sorted order.
+//
+// Changing data while traversing a cursor may result in unexpected items to
+// be returned. You must reposition your cursor after mutating data.
+type Cursor struct {
+ t *BTree
+ stack []stackItem
+}
+
+// Cursor returns a new cursor used to traverse over items in the tree.
+func (t *BTree) Cursor() *Cursor {
+ return &Cursor{t: t}
+}
+
+// First moves the cursor to the first item in the tree and returns that item.
+func (c *Cursor) First() Item {
+ c.stack = c.stack[:0]
+ n := c.t.root
+ if n == nil {
+ return nil
+ }
+ c.stack = append(c.stack, stackItem{n: n})
+ for len(n.children) > 0 {
+ n = n.children[0]
+ c.stack = append(c.stack, stackItem{n: n})
+ }
+ if len(n.items) == 0 {
+ return nil
+ }
+ return n.items[0]
+}
+
+// Next moves the cursor to the next item and returns that item.
+func (c *Cursor) Next() Item {
+ if len(c.stack) == 0 {
+ return nil
+ }
+ si := len(c.stack) - 1
+ c.stack[si].i++
+ n := c.stack[si].n
+ i := c.stack[si].i
+ if i == len(n.children)+len(n.items) {
+ c.stack = c.stack[:len(c.stack)-1]
+ return c.Next()
+ }
+ if len(n.children) == 0 {
+ if i >= len(n.items) {
+ c.stack = c.stack[:len(c.stack)-1]
+ return c.Next()
+ }
+ return n.items[i]
+ } else if i%2 == 1 {
+ return n.items[i/2]
+ }
+ c.stack = append(c.stack, stackItem{n: n.children[i/2], i: -1})
+ return c.Next()
+
+}
+
+// Last moves the cursor to the last item in the tree and returns that item.
+func (c *Cursor) Last() Item {
+ c.stack = c.stack[:0]
+ n := c.t.root
+ if n == nil {
+ return nil
+ }
+ c.stack = append(c.stack, stackItem{n: n, i: len(n.children) + len(n.items) - 1})
+ for len(n.children) > 0 {
+ n = n.children[len(n.children)-1]
+ c.stack = append(c.stack, stackItem{n: n, i: len(n.children) + len(n.items) - 1})
+ }
+ if len(n.items) == 0 {
+ return nil
+ }
+ return n.items[len(n.items)-1]
+}
+
+// Prev moves the cursor to the previous item and returns that item.
+func (c *Cursor) Prev() Item {
+ if len(c.stack) == 0 {
+ return nil
+ }
+ si := len(c.stack) - 1
+ c.stack[si].i--
+ n := c.stack[si].n
+ i := c.stack[si].i
+ if i == -1 {
+ c.stack = c.stack[:len(c.stack)-1]
+ return c.Prev()
+ }
+ if len(n.children) == 0 {
+ return n.items[i]
+ } else if i%2 == 1 {
+ return n.items[i/2]
+ }
+ child := n.children[i/2]
+ c.stack = append(c.stack, stackItem{n: child,
+ i: len(child.children) + len(child.items)})
+ return c.Prev()
+}
+
+// Seek moves the cursor to provided item and returns that item.
+// If the item does not exist then the next item is returned.
+func (c *Cursor) Seek(pivot Item) Item {
+ c.stack = c.stack[:0]
+ n := c.t.root
+ for n != nil {
+ i, found := n.items.find(pivot, c.t.ctx)
+ c.stack = append(c.stack, stackItem{n: n})
+ if found {
+ if len(n.children) == 0 {
+ c.stack[len(c.stack)-1].i = i
+ } else {
+ c.stack[len(c.stack)-1].i = i*2 + 1
+ }
+ return n.items[i]
+ }
+ if len(n.children) == 0 {
+ if i == len(n.items) {
+ c.stack[len(c.stack)-1].i = i + 1
+ return c.Next()
+ }
+ c.stack[len(c.stack)-1].i = i
+ return n.items[i]
+ }
+ c.stack[len(c.stack)-1].i = i * 2
+ n = n.children[i]
+ }
+ return nil
+}
diff --git a/vendor/github.com/tidwall/buntdb/.travis.yml b/vendor/github.com/tidwall/buntdb/.travis.yml
new file mode 100644
index 00000000..4f2ee4d9
--- /dev/null
+++ b/vendor/github.com/tidwall/buntdb/.travis.yml
@@ -0,0 +1 @@
+language: go
diff --git a/vendor/github.com/tidwall/buntdb/LICENSE b/vendor/github.com/tidwall/buntdb/LICENSE
new file mode 100644
index 00000000..58f5819a
--- /dev/null
+++ b/vendor/github.com/tidwall/buntdb/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Josh Baker
+
+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.
diff --git a/vendor/github.com/tidwall/buntdb/README.md b/vendor/github.com/tidwall/buntdb/README.md
new file mode 100644
index 00000000..31798093
--- /dev/null
+++ b/vendor/github.com/tidwall/buntdb/README.md
@@ -0,0 +1,634 @@
+
+
+
+
+
+
+
+
+
+BuntDB is a low-level, in-memory, key/value store in pure Go.
+It persists to disk, is ACID compliant, and uses locking for multiple
+readers and a single writer. It supports custom indexes and geospatial
+data. It's ideal for projects that need a dependable database and favor
+speed over data size.
+
+Features
+========
+
+- In-memory database for [fast reads and writes](#performance)
+- Embeddable with a [simple API](https://godoc.org/github.com/tidwall/buntdb)
+- [Spatial indexing](#spatial-indexes) for up to 20 dimensions; Useful for Geospatial data
+- Index fields inside [JSON](#json-indexes) documents
+- [Collate i18n Indexes](#collate-i18n-indexes) using the optional [collate package](https://github.com/tidwall/collate)
+- Create [custom indexes](#custom-indexes) for any data type
+- Support for [multi value indexes](#multi-value-index); Similar to a SQL multi column index
+- [Built-in types](#built-in-types) that are easy to get up & running; String, Uint, Int, Float
+- Flexible [iteration](#iterating) of data; ascending, descending, and ranges
+- [Durable append-only file](#append-only-file) format for persistence
+- Option to evict old items with an [expiration](#data-expiration) TTL
+- Tight codebase, under 2K loc using the `cloc` command
+- ACID semantics with locking [transactions](#transactions) that support rollbacks
+
+
+Getting Started
+===============
+
+## Installing
+
+To start using BuntDB, install Go and run `go get`:
+
+```sh
+$ go get -u github.com/tidwall/buntdb
+```
+
+This will retrieve the library.
+
+
+## Opening a database
+
+The primary object in BuntDB is a `DB`. To open or create your
+database, use the `buntdb.Open()` function:
+
+```go
+package main
+
+import (
+ "log"
+
+ "github.com/tidwall/buntdb"
+)
+
+func main() {
+ // Open the data.db file. It will be created if it doesn't exist.
+ db, err := buntdb.Open("data.db")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer db.Close()
+
+ ...
+}
+```
+
+It's also possible to open a database that does not persist to disk by using `:memory:` as the path of the file.
+
+```go
+buntdb.Open(":memory:") // Open a file that does not persist to disk.
+```
+
+## Transactions
+All reads and writes must be performed from inside a transaction. BuntDB can have one write transaction opened at a time, but can have many concurrent read transactions. Each transaction maintains a stable view of the database. In other words, once a transaction has begun, the data for that transaction cannot be changed by other transactions.
+
+Transactions run in a function that exposes a `Tx` object, which represents the transaction state. While inside a transaction, all database operations should be performed using this object. You should never access the origin `DB` object while inside a transaction. Doing so may have side-effects, such as blocking your application.
+
+When a transaction fails, it will roll back, and revert all changes that occurred to the database during that transaction. There's a single return value that you can use to close the transaction. For read/write transactions, returning an error this way will force the transaction to roll back. When a read/write transaction succeeds all changes are persisted to disk.
+
+### Read-only Transactions
+A read-only transaction should be used when you don't need to make changes to the data. The advantage of a read-only transaction is that there can be many running concurrently.
+
+```go
+err := db.View(func(tx *buntdb.Tx) error {
+ ...
+ return nil
+})
+```
+
+### Read/write Transactions
+A read/write transaction is used when you need to make changes to your data. There can only be one read/write transaction running at a time. So make sure you close it as soon as you are done with it.
+
+```go
+err := db.Update(func(tx *buntdb.Tx) error {
+ ...
+ return nil
+})
+```
+
+## Setting and getting key/values
+
+To set a value you must open a read/write transaction:
+
+```go
+err := db.Update(func(tx *buntdb.Tx) error {
+ _, _, err := tx.Set("mykey", "myvalue", nil)
+ return err
+})
+```
+
+
+To get the value:
+
+```go
+err := db.View(func(tx *buntdb.Tx) error {
+ val, err := tx.Get("mykey")
+ if err != nil{
+ return err
+ }
+ fmt.Printf("value is %s\n", val)
+ return nil
+})
+```
+
+Getting non-existent values will cause an `ErrNotFound` error.
+
+### Iterating
+All keys/value pairs are ordered in the database by the key. To iterate over the keys:
+
+```go
+err := db.View(func(tx *buntdb.Tx) error {
+ err := tx.Ascend("", func(key, value string) bool {
+ fmt.Printf("key: %s, value: %s\n", key, value)
+ })
+ return err
+})
+```
+
+There is also `AscendGreaterOrEqual`, `AscendLessThan`, `AscendRange`, `AscendEqual`, `Descend`, `DescendLessOrEqual`, `DescendGreaterThan`, `DescendRange`, and `DescendEqual`. Please see the [documentation](https://godoc.org/github.com/tidwall/buntdb) for more information on these functions.
+
+
+## Custom Indexes
+Initially all data is stored in a single [B-tree](https://en.wikipedia.org/wiki/B-tree) with each item having one key and one value. All of these items are ordered by the key. This is great for quickly getting a value from a key or [iterating](#iterating) over the keys. Feel free to peruse the [B-tree implementation](https://github.com/tidwall/btree).
+
+You can also create custom indexes that allow for ordering and [iterating](#iterating) over values. A custom index also uses a B-tree, but it's more flexible because it allows for custom ordering.
+
+For example, let's say you want to create an index for ordering names:
+
+```go
+db.CreateIndex("names", "*", buntdb.IndexString)
+```
+
+This will create an index named `names` which stores and sorts all values. The second parameter is a pattern that is used to filter on keys. A `*` wildcard argument means that we want to accept all keys. `IndexString` is a built-in function that performs case-insensitive ordering on the values
+
+Now you can add various names:
+
+```go
+db.Update(func(tx *buntdb.Tx) error {
+ tx.Set("user:0:name", "tom", nil)
+ tx.Set("user:1:name", "Randi", nil)
+ tx.Set("user:2:name", "jane", nil)
+ tx.Set("user:4:name", "Janet", nil)
+ tx.Set("user:5:name", "Paula", nil)
+ tx.Set("user:6:name", "peter", nil)
+ tx.Set("user:7:name", "Terri", nil)
+ return nil
+})
+```
+
+Finally you can iterate over the index:
+
+```go
+db.View(func(tx *buntdb.Tx) error {
+ tx.Ascend("names", func(key, val string) bool {
+ fmt.Printf(buf, "%s %s\n", key, val)
+ return true
+ })
+ return nil
+})
+```
+The output should be:
+```
+user:2:name jane
+user:4:name Janet
+user:5:name Paula
+user:6:name peter
+user:1:name Randi
+user:7:name Terri
+user:0:name tom
+```
+
+The pattern parameter can be used to filter on keys like this:
+
+```go
+db.CreateIndex("names", "user:*", buntdb.IndexString)
+```
+
+Now only items with keys that have the prefix `user:` will be added to the `names` index.
+
+
+### Built-in types
+Along with `IndexString`, there is also `IndexInt`, `IndexUint`, and `IndexFloat`.
+These are built-in types for indexing. You can choose to use these or create your own.
+
+So to create an index that is numerically ordered on an age key, we could use:
+
+```go
+db.CreateIndex("ages", "user:*:age", buntdb.IndexInt)
+```
+
+And then add values:
+
+```go
+db.Update(func(tx *buntdb.Tx) error {
+ tx.Set("user:0:age", "35", nil)
+ tx.Set("user:1:age", "49", nil)
+ tx.Set("user:2:age", "13", nil)
+ tx.Set("user:4:age", "63", nil)
+ tx.Set("user:5:age", "8", nil)
+ tx.Set("user:6:age", "3", nil)
+ tx.Set("user:7:age", "16", nil)
+ return nil
+})
+```
+
+```go
+db.View(func(tx *buntdb.Tx) error {
+ tx.Ascend("ages", func(key, val string) bool {
+ fmt.Printf(buf, "%s %s\n", key, val)
+ return true
+ })
+ return nil
+})
+```
+
+The output should be:
+```
+user:6:age 3
+user:5:age 8
+user:2:age 13
+user:7:age 16
+user:0:age 35
+user:1:age 49
+user:4:age 63
+```
+
+## Spatial Indexes
+BuntDB has support for spatial indexes by storing rectangles in an [R-tree](https://en.wikipedia.org/wiki/R-tree). An R-tree is organized in a similar manner as a [B-tree](https://en.wikipedia.org/wiki/B-tree), and both are balanced trees. But, an R-tree is special because it can operate on data that is in multiple dimensions. This is super handy for Geospatial applications.
+
+To create a spatial index use the `CreateSpatialIndex` function:
+
+```go
+db.CreateSpatialIndex("fleet", "fleet:*:pos", buntdb.IndexRect)
+```
+
+Then `IndexRect` is a built-in function that converts rect strings to a format that the R-tree can use. It's easy to use this function out of the box, but you might find it better to create a custom one that renders from a different format, such as [Well-known text](https://en.wikipedia.org/wiki/Well-known_text) or [GeoJSON](http://geojson.org/).
+
+To add some lon,lat points to the `fleet` index:
+
+```go
+db.Update(func(tx *buntdb.Tx) error {
+ tx.Set("fleet:0:pos", "[-115.567 33.532]", nil)
+ tx.Set("fleet:1:pos", "[-116.671 35.735]", nil)
+ tx.Set("fleet:2:pos", "[-113.902 31.234]", nil)
+ return nil
+})
+```
+
+And then you can run the `Intersects` function on the index:
+
+```go
+db.View(func(tx *buntdb.Tx) error {
+ tx.Intersects("fleet", "[-117 30],[-112 36]", func(key, val string) bool {
+ ...
+ return true
+ })
+ return nil
+})
+```
+
+This will get all three positions.
+
+### k-Nearest Neighbors
+
+Use the `Nearby` function to get all the positions in order of nearest to farthest :
+
+```go
+db.View(func(tx *buntdb.Tx) error {
+ tx.Nearby("fleet", "[-113 33]", func(key, val string, dist float64) bool {
+ ...
+ return true
+ })
+ return nil
+})
+```
+
+### Spatial bracket syntax
+
+The bracket syntax `[-117 30],[-112 36]` is unique to BuntDB, and it's how the built-in rectangles are processed. But, you are not limited to this syntax. Whatever Rect function you choose to use during `CreateSpatialIndex` will be used to process the parameter, in this case it's `IndexRect`.
+
+- **2D rectangle:** `[10 15],[20 25]`
+*Min XY: "10x15", Max XY: "20x25"*
+
+- **3D rectangle:** `[10 15 12],[20 25 18]`
+*Min XYZ: "10x15x12", Max XYZ: "20x25x18"*
+
+- **2D point:** `[10 15]`
+*XY: "10x15"*
+
+- **LonLat point:** `[-112.2693 33.5123]`
+*LatLon: "33.5123 -112.2693"*
+
+- **LonLat bounding box:** `[-112.26 33.51],[-112.18 33.67]`
+*Min LatLon: "33.51 -112.26", Max LatLon: "33.67 -112.18"*
+
+**Notice:** The longitude is the Y axis and is on the left, and latitude is the X axis and is on the right.
+
+You can also represent `Infinity` by using `-inf` and `+inf`.
+For example, you might have the following points (`[X Y M]` where XY is a point and M is a timestamp):
+```
+[3 9 1]
+[3 8 2]
+[4 8 3]
+[4 7 4]
+[5 7 5]
+[5 6 6]
+```
+
+You can then do a search for all points with `M` between 2-4 by calling `Intersects`.
+
+```go
+tx.Intersects("points", "[-inf -inf 2],[+inf +inf 4]", func(key, val string) bool {
+ println(val)
+ return true
+})
+```
+
+Which will return:
+
+```
+[3 8 2]
+[4 8 3]
+[4 7 4]
+```
+
+## JSON Indexes
+Indexes can be created on individual fields inside JSON documents. BuntDB uses [GJSON](https://github.com/tidwall/gjson) under the hood.
+
+For example:
+
+```go
+package main
+
+import (
+ "fmt"
+
+ "github.com/tidwall/buntdb"
+)
+
+func main() {
+ db, _ := buntdb.Open(":memory:")
+ db.CreateIndex("last_name", "*", buntdb.IndexJSON("name.last"))
+ db.CreateIndex("age", "*", buntdb.IndexJSON("age"))
+ db.Update(func(tx *buntdb.Tx) error {
+ tx.Set("1", `{"name":{"first":"Tom","last":"Johnson"},"age":38}`, nil)
+ tx.Set("2", `{"name":{"first":"Janet","last":"Prichard"},"age":47}`, nil)
+ tx.Set("3", `{"name":{"first":"Carol","last":"Anderson"},"age":52}`, nil)
+ tx.Set("4", `{"name":{"first":"Alan","last":"Cooper"},"age":28}`, nil)
+ return nil
+ })
+ db.View(func(tx *buntdb.Tx) error {
+ fmt.Println("Order by last name")
+ tx.Ascend("last_name", func(key, value string) bool {
+ fmt.Printf("%s: %s\n", key, value)
+ return true
+ })
+ fmt.Println("Order by age")
+ tx.Ascend("age", func(key, value string) bool {
+ fmt.Printf("%s: %s\n", key, value)
+ return true
+ })
+ fmt.Println("Order by age range 30-50")
+ tx.AscendRange("age", `{"age":30}`, `{"age":50}`, func(key, value string) bool {
+ fmt.Printf("%s: %s\n", key, value)
+ return true
+ })
+ return nil
+ })
+}
+```
+
+Results:
+
+```
+Order by last name
+3: {"name":{"first":"Carol","last":"Anderson"},"age":52}
+4: {"name":{"first":"Alan","last":"Cooper"},"age":28}
+1: {"name":{"first":"Tom","last":"Johnson"},"age":38}
+2: {"name":{"first":"Janet","last":"Prichard"},"age":47}
+
+Order by age
+4: {"name":{"first":"Alan","last":"Cooper"},"age":28}
+1: {"name":{"first":"Tom","last":"Johnson"},"age":38}
+2: {"name":{"first":"Janet","last":"Prichard"},"age":47}
+3: {"name":{"first":"Carol","last":"Anderson"},"age":52}
+
+Order by age range 30-50
+1: {"name":{"first":"Tom","last":"Johnson"},"age":38}
+2: {"name":{"first":"Janet","last":"Prichard"},"age":47}
+```
+
+## Multi Value Index
+With BuntDB it's possible to join multiple values on a single index.
+This is similar to a [multi column index](http://dev.mysql.com/doc/refman/5.7/en/multiple-column-indexes.html) in a traditional SQL database.
+
+In this example we are creating a multi value index on "name.last" and "age":
+
+```go
+db, _ := buntdb.Open(":memory:")
+db.CreateIndex("last_name_age", "*", buntdb.IndexJSON("name.last"), buntdb.IndexJSON("age"))
+db.Update(func(tx *buntdb.Tx) error {
+ tx.Set("1", `{"name":{"first":"Tom","last":"Johnson"},"age":38}`, nil)
+ tx.Set("2", `{"name":{"first":"Janet","last":"Prichard"},"age":47}`, nil)
+ tx.Set("3", `{"name":{"first":"Carol","last":"Anderson"},"age":52}`, nil)
+ tx.Set("4", `{"name":{"first":"Alan","last":"Cooper"},"age":28}`, nil)
+ tx.Set("5", `{"name":{"first":"Sam","last":"Anderson"},"age":51}`, nil)
+ tx.Set("6", `{"name":{"first":"Melinda","last":"Prichard"},"age":44}`, nil)
+ return nil
+})
+db.View(func(tx *buntdb.Tx) error {
+ tx.Ascend("last_name_age", func(key, value string) bool {
+ fmt.Printf("%s: %s\n", key, value)
+ return true
+ })
+ return nil
+})
+
+// Output:
+// 5: {"name":{"first":"Sam","last":"Anderson"},"age":51}
+// 3: {"name":{"first":"Carol","last":"Anderson"},"age":52}
+// 4: {"name":{"first":"Alan","last":"Cooper"},"age":28}
+// 1: {"name":{"first":"Tom","last":"Johnson"},"age":38}
+// 6: {"name":{"first":"Melinda","last":"Prichard"},"age":44}
+// 2: {"name":{"first":"Janet","last":"Prichard"},"age":47}
+```
+
+## Descending Ordered Index
+Any index can be put in descending order by wrapping it's less function with `buntdb.Desc`.
+
+```go
+db.CreateIndex("last_name_age", "*",
+buntdb.IndexJSON("name.last"),
+buntdb.Desc(buntdb.IndexJSON("age")))
+```
+
+This will create a multi value index where the last name is ascending and the age is descending.
+
+## Collate i18n Indexes
+
+Using the external [collate package](https://github.com/tidwall/collate) it's possible to create
+indexes that are sorted by the specified language. This is similar to the [SQL COLLATE keyword](https://msdn.microsoft.com/en-us/library/ms174596.aspx) found in traditional databases.
+
+To install:
+
+```
+go get -u github.com/tidwall/collate
+```
+
+For example:
+
+```go
+import "github.com/tidwall/collate"
+
+// To sort case-insensitive in French.
+db.CreateIndex("name", "*", collate.IndexString("FRENCH_CI"))
+
+// To specify that numbers should sort numerically ("2" < "12")
+// and use a comma to represent a decimal point.
+db.CreateIndex("amount", "*", collate.IndexString("FRENCH_NUM"))
+```
+
+There's also support for Collation on JSON indexes:
+
+```go
+db.CreateIndex("last_name", "*", collate.IndexJSON("CHINESE_CI", "name.last"))
+```
+
+Check out the [collate project](https://github.com/tidwall/collate) for more information.
+
+## Data Expiration
+Items can be automatically evicted by using the `SetOptions` object in the `Set` function to set a `TTL`.
+
+```go
+db.Update(func(tx *buntdb.Tx) error {
+ tx.Set("mykey", "myval", &buntdb.SetOptions{Expires:true, TTL:time.Second})
+ return nil
+})
+```
+
+Now `mykey` will automatically be deleted after one second. You can remove the TTL by setting the value again with the same key/value, but with the options parameter set to nil.
+
+## Delete while iterating
+BuntDB does not currently support deleting a key while in the process of iterating.
+As a workaround you'll need to delete keys following the completion of the iterator.
+
+```go
+var delkeys []string
+tx.AscendKeys("object:*", func(k, v string) bool {
+ if someCondition(k) == true {
+ delkeys = append(delkeys, k)
+ }
+ return true // continue
+})
+for _, k := range delkeys {
+ if _, err = tx.Delete(k); err != nil {
+ return err
+ }
+}
+```
+
+## Append-only File
+
+BuntDB uses an AOF (append-only file) which is a log of all database changes that occur from operations like `Set()` and `Delete()`.
+
+The format of this file looks like:
+```
+set key:1 value1
+set key:2 value2
+set key:1 value3
+del key:2
+...
+```
+
+When the database opens again, it will read back the aof file and process each command in exact order.
+This read process happens one time when the database opens.
+From there on the file is only appended.
+
+As you may guess this log file can grow large over time.
+There's a background routine that automatically shrinks the log file when it gets too large.
+There is also a `Shrink()` function which will rewrite the aof file so that it contains only the items in the database.
+The shrink operation does not lock up the database so read and write transactions can continue while shrinking is in process.
+
+### Durability and fsync
+
+By default BuntDB executes an `fsync` once every second on the [aof file](#append-only-file). Which simply means that there's a chance that up to one second of data might be lost. If you need higher durability then there's an optional database config setting `Config.SyncPolicy` which can be set to `Always`.
+
+The `Config.SyncPolicy` has the following options:
+
+- `Never` - fsync is managed by the operating system, less safe
+- `EverySecond` - fsync every second, fast and safer, this is the default
+- `Always` - fsync after every write, very durable, slower
+
+## Config
+
+Here are some configuration options that can be use to change various behaviors of the database.
+
+- **SyncPolicy** adjusts how often the data is synced to disk. This value can be Never, EverySecond, or Always. Default is EverySecond.
+- **AutoShrinkPercentage** is used by the background process to trigger a shrink of the aof file when the size of the file is larger than the percentage of the result of the previous shrunk file. For example, if this value is 100, and the last shrink process resulted in a 100mb file, then the new aof file must be 200mb before a shrink is triggered. Default is 100.
+- **AutoShrinkMinSize** defines the minimum size of the aof file before an automatic shrink can occur. Default is 32MB.
+- **AutoShrinkDisabled** turns off automatic background shrinking. Default is false.
+
+To update the configuration you should call `ReadConfig` followed by `SetConfig`. For example:
+
+```go
+
+var config buntdb.Config
+if err := db.ReadConfig(&config); err != nil{
+ log.Fatal(err)
+}
+if err := db.WriteConfig(config); err != nil{
+ log.Fatal(err)
+}
+```
+
+## Performance
+
+How fast is BuntDB?
+
+Here are some example [benchmarks](https://github.com/tidwall/raft-buntdb#raftstore-performance-comparison) when using BuntDB in a Raft Store implementation.
+
+You can also run the standard Go benchmark tool from the project root directory:
+
+```
+go test --bench=.
+```
+
+### BuntDB-Benchmark
+
+There's a [custom utility](https://github.com/tidwall/buntdb-benchmark) that was created specifically for benchmarking BuntDB.
+
+*These are the results from running the benchmarks on a MacBook Pro 15" 2.8 GHz Intel Core i7:*
+
+```
+$ buntdb-benchmark -q
+GET: 4609604.74 operations per second
+SET: 248500.33 operations per second
+ASCEND_100: 2268998.79 operations per second
+ASCEND_200: 1178388.14 operations per second
+ASCEND_400: 679134.20 operations per second
+ASCEND_800: 348445.55 operations per second
+DESCEND_100: 2313821.69 operations per second
+DESCEND_200: 1292738.38 operations per second
+DESCEND_400: 675258.76 operations per second
+DESCEND_800: 337481.67 operations per second
+SPATIAL_SET: 134824.60 operations per second
+SPATIAL_INTERSECTS_100: 939491.47 operations per second
+SPATIAL_INTERSECTS_200: 561590.40 operations per second
+SPATIAL_INTERSECTS_400: 306951.15 operations per second
+SPATIAL_INTERSECTS_800: 159673.91 operations per second
+```
+
+To install this utility:
+
+```
+go get github.com/tidwall/buntdb-benchmark
+```
+
+
+
+## Contact
+Josh Baker [@tidwall](http://twitter.com/tidwall)
+
+## License
+
+BuntDB source code is available under the MIT [License](/LICENSE).
diff --git a/vendor/github.com/tidwall/buntdb/buntdb.go b/vendor/github.com/tidwall/buntdb/buntdb.go
new file mode 100644
index 00000000..35f85200
--- /dev/null
+++ b/vendor/github.com/tidwall/buntdb/buntdb.go
@@ -0,0 +1,2195 @@
+// Package buntdb implements a low-level in-memory key/value store in pure Go.
+// It persists to disk, is ACID compliant, and uses locking for multiple
+// readers and a single writer. Bunt is ideal for projects that need
+// a dependable database, and favor speed over data size.
+package buntdb
+
+import (
+ "bufio"
+ "errors"
+ "io"
+ "os"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/tidwall/btree"
+ "github.com/tidwall/gjson"
+ "github.com/tidwall/grect"
+ "github.com/tidwall/match"
+ "github.com/tidwall/rtree"
+)
+
+var (
+ // ErrTxNotWritable is returned when performing a write operation on a
+ // read-only transaction.
+ ErrTxNotWritable = errors.New("tx not writable")
+
+ // ErrTxClosed is returned when committing or rolling back a transaction
+ // that has already been committed or rolled back.
+ ErrTxClosed = errors.New("tx closed")
+
+ // ErrNotFound is returned when an item or index is not in the database.
+ ErrNotFound = errors.New("not found")
+
+ // ErrInvalid is returned when the database file is an invalid format.
+ ErrInvalid = errors.New("invalid database")
+
+ // ErrDatabaseClosed is returned when the database is closed.
+ ErrDatabaseClosed = errors.New("database closed")
+
+ // ErrIndexExists is returned when an index already exists in the database.
+ ErrIndexExists = errors.New("index exists")
+
+ // ErrInvalidOperation is returned when an operation cannot be completed.
+ ErrInvalidOperation = errors.New("invalid operation")
+
+ // ErrInvalidSyncPolicy is returned for an invalid SyncPolicy value.
+ ErrInvalidSyncPolicy = errors.New("invalid sync policy")
+
+ // ErrShrinkInProcess is returned when a shrink operation is in-process.
+ ErrShrinkInProcess = errors.New("shrink is in-process")
+
+ // ErrPersistenceActive is returned when post-loading data from an database
+ // not opened with Open(":memory:").
+ ErrPersistenceActive = errors.New("persistence active")
+
+ // ErrTxIterating is returned when Set or Delete are called while iterating.
+ ErrTxIterating = errors.New("tx is iterating")
+)
+
+// DB represents a collection of key-value pairs that persist on disk.
+// Transactions are used for all forms of data access to the DB.
+type DB struct {
+ mu sync.RWMutex // the gatekeeper for all fields
+ file *os.File // the underlying file
+ buf []byte // a buffer to write to
+ keys *btree.BTree // a tree of all item ordered by key
+ exps *btree.BTree // a tree of items ordered by expiration
+ idxs map[string]*index // the index trees.
+ exmgr bool // indicates that expires manager is running.
+ flushes int // a count of the number of disk flushes
+ closed bool // set when the database has been closed
+ config Config // the database configuration
+ persist bool // do we write to disk
+ shrinking bool // when an aof shrink is in-process.
+ lastaofsz int // the size of the last shrink aof size
+}
+
+// SyncPolicy represents how often data is synced to disk.
+type SyncPolicy int
+
+const (
+ // Never is used to disable syncing data to disk.
+ // The faster and less safe method.
+ Never SyncPolicy = 0
+ // EverySecond is used to sync data to disk every second.
+ // It's pretty fast and you can lose 1 second of data if there
+ // is a disaster.
+ // This is the recommended setting.
+ EverySecond = 1
+ // Always is used to sync data after every write to disk.
+ // Slow. Very safe.
+ Always = 2
+)
+
+// Config represents database configuration options. These
+// options are used to change various behaviors of the database.
+type Config struct {
+ // SyncPolicy adjusts how often the data is synced to disk.
+ // This value can be Never, EverySecond, or Always.
+ // The default is EverySecond.
+ SyncPolicy SyncPolicy
+
+ // AutoShrinkPercentage is used by the background process to trigger
+ // a shrink of the aof file when the size of the file is larger than the
+ // percentage of the result of the previous shrunk file.
+ // For example, if this value is 100, and the last shrink process
+ // resulted in a 100mb file, then the new aof file must be 200mb before
+ // a shrink is triggered.
+ AutoShrinkPercentage int
+
+ // AutoShrinkMinSize defines the minimum size of the aof file before
+ // an automatic shrink can occur.
+ AutoShrinkMinSize int
+
+ // AutoShrinkDisabled turns off automatic background shrinking
+ AutoShrinkDisabled bool
+
+ // OnExpired is used to custom handle the deletion option when a key
+ // has been expired.
+ OnExpired func(keys []string)
+
+ // OnExpiredSync will be called inside the same transaction that is performing
+ // the deletion of expired items. If OnExpired is present then this callback
+ // will not be called. If this callback is present, then the deletion of the
+ // timeed-out item is the explicit responsibility of this callback.
+ OnExpiredSync func(key, value string, tx *Tx) error
+}
+
+// exctx is a simple b-tree context for ordering by expiration.
+type exctx struct {
+ db *DB
+}
+
+// Default number of btree degrees
+const btreeDegrees = 64
+
+// Open opens a database at the provided path.
+// If the file does not exist then it will be created automatically.
+func Open(path string) (*DB, error) {
+ db := &DB{}
+ // initialize trees and indexes
+ db.keys = btree.New(btreeDegrees, nil)
+ db.exps = btree.New(btreeDegrees, &exctx{db})
+ db.idxs = make(map[string]*index)
+ // initialize default configuration
+ db.config = Config{
+ SyncPolicy: EverySecond,
+ AutoShrinkPercentage: 100,
+ AutoShrinkMinSize: 32 * 1024 * 1024,
+ }
+ // turn off persistence for pure in-memory
+ db.persist = path != ":memory:"
+ if db.persist {
+ var err error
+ // hardcoding 0666 as the default mode.
+ db.file, err = os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0666)
+ if err != nil {
+ return nil, err
+ }
+ // load the database from disk
+ if err := db.load(); err != nil {
+ // close on error, ignore close error
+ _ = db.file.Close()
+ return nil, err
+ }
+ }
+ // start the background manager.
+ go db.backgroundManager()
+ return db, nil
+}
+
+// Close releases all database resources.
+// All transactions must be closed before closing the database.
+func (db *DB) Close() error {
+ db.mu.Lock()
+ defer db.mu.Unlock()
+ if db.closed {
+ return ErrDatabaseClosed
+ }
+ db.closed = true
+ if db.persist {
+ db.file.Sync() // do a sync but ignore the error
+ if err := db.file.Close(); err != nil {
+ return err
+ }
+ }
+ // Let's release all references to nil. This will help both with debugging
+ // late usage panics and it provides a hint to the garbage collector
+ db.keys, db.exps, db.idxs, db.file = nil, nil, nil, nil
+ return nil
+}
+
+// Save writes a snapshot of the database to a writer. This operation blocks all
+// writes, but not reads. This can be used for snapshots and backups for pure
+// in-memory databases using the ":memory:". Database that persist to disk
+// can be snapshotted by simply copying the database file.
+func (db *DB) Save(wr io.Writer) error {
+ var err error
+ db.mu.RLock()
+ defer db.mu.RUnlock()
+ // use a buffered writer and flush every 4MB
+ var buf []byte
+ // iterated through every item in the database and write to the buffer
+ db.keys.Ascend(func(item btree.Item) bool {
+ dbi := item.(*dbItem)
+ buf = dbi.writeSetTo(buf)
+ if len(buf) > 1024*1024*4 {
+ // flush when buffer is over 4MB
+ _, err = wr.Write(buf)
+ if err != nil {
+ return false
+ }
+ buf = buf[:0]
+ }
+ return true
+ })
+ if err != nil {
+ return err
+ }
+ // one final flush
+ if len(buf) > 0 {
+ _, err = wr.Write(buf)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Load loads commands from reader. This operation blocks all reads and writes.
+// Note that this can only work for fully in-memory databases opened with
+// Open(":memory:").
+func (db *DB) Load(rd io.Reader) error {
+ db.mu.Lock()
+ defer db.mu.Unlock()
+ if db.persist {
+ // cannot load into databases that persist to disk
+ return ErrPersistenceActive
+ }
+ return db.readLoad(rd, time.Now())
+}
+
+// index represents a b-tree or r-tree index and also acts as the
+// b-tree/r-tree context for itself.
+type index struct {
+ btr *btree.BTree // contains the items
+ rtr *rtree.RTree // contains the items
+ name string // name of the index
+ pattern string // a required key pattern
+ less func(a, b string) bool // less comparison function
+ rect func(item string) (min, max []float64) // rect from string function
+ db *DB // the origin database
+ opts IndexOptions // index options
+}
+
+// match matches the pattern to the key
+func (idx *index) match(key string) bool {
+ if idx.pattern == "*" {
+ return true
+ }
+ if idx.opts.CaseInsensitiveKeyMatching {
+ for i := 0; i < len(key); i++ {
+ if key[i] >= 'A' && key[i] <= 'Z' {
+ key = strings.ToLower(key)
+ break
+ }
+ }
+ }
+ return match.Match(key, idx.pattern)
+}
+
+// clearCopy creates a copy of the index, but with an empty dataset.
+func (idx *index) clearCopy() *index {
+ // copy the index meta information
+ nidx := &index{
+ name: idx.name,
+ pattern: idx.pattern,
+ db: idx.db,
+ less: idx.less,
+ rect: idx.rect,
+ opts: idx.opts,
+ }
+ // initialize with empty trees
+ if nidx.less != nil {
+ nidx.btr = btree.New(btreeDegrees, nidx)
+ }
+ if nidx.rect != nil {
+ nidx.rtr = rtree.New(nidx)
+ }
+ return nidx
+}
+
+// rebuild rebuilds the index
+func (idx *index) rebuild() {
+ // initialize trees
+ if idx.less != nil {
+ idx.btr = btree.New(btreeDegrees, idx)
+ }
+ if idx.rect != nil {
+ idx.rtr = rtree.New(idx)
+ }
+ // iterate through all keys and fill the index
+ idx.db.keys.Ascend(func(item btree.Item) bool {
+ dbi := item.(*dbItem)
+ if !idx.match(dbi.key) {
+ // does not match the pattern, conintue
+ return true
+ }
+ if idx.less != nil {
+ idx.btr.ReplaceOrInsert(dbi)
+ }
+ if idx.rect != nil {
+ idx.rtr.Insert(dbi)
+ }
+ return true
+ })
+}
+
+// CreateIndex builds a new index and populates it with items.
+// The items are ordered in an b-tree and can be retrieved using the
+// Ascend* and Descend* methods.
+// An error will occur if an index with the same name already exists.
+//
+// When a pattern is provided, the index will be populated with
+// keys that match the specified pattern. This is a very simple pattern
+// match where '*' matches on any number characters and '?' matches on
+// any one character.
+// The less function compares if string 'a' is less than string 'b'.
+// It allows for indexes to create custom ordering. It's possible
+// that the strings may be textual or binary. It's up to the provided
+// less function to handle the content format and comparison.
+// There are some default less function that can be used such as
+// IndexString, IndexBinary, etc.
+//
+// Deprecated: Use Transactions
+func (db *DB) CreateIndex(name, pattern string,
+ less ...func(a, b string) bool) error {
+ return db.Update(func(tx *Tx) error {
+ return tx.CreateIndex(name, pattern, less...)
+ })
+}
+
+// ReplaceIndex builds a new index and populates it with items.
+// The items are ordered in an b-tree and can be retrieved using the
+// Ascend* and Descend* methods.
+// If a previous index with the same name exists, that index will be deleted.
+//
+// Deprecated: Use Transactions
+func (db *DB) ReplaceIndex(name, pattern string,
+ less ...func(a, b string) bool) error {
+ return db.Update(func(tx *Tx) error {
+ err := tx.CreateIndex(name, pattern, less...)
+ if err != nil {
+ if err == ErrIndexExists {
+ err := tx.DropIndex(name)
+ if err != nil {
+ return err
+ }
+ return tx.CreateIndex(name, pattern, less...)
+ }
+ return err
+ }
+ return nil
+ })
+}
+
+// CreateSpatialIndex builds a new index and populates it with items.
+// The items are organized in an r-tree and can be retrieved using the
+// Intersects method.
+// An error will occur if an index with the same name already exists.
+//
+// The rect function converts a string to a rectangle. The rectangle is
+// represented by two arrays, min and max. Both arrays may have a length
+// between 1 and 20, and both arrays must match in length. A length of 1 is a
+// one dimensional rectangle, and a length of 4 is a four dimension rectangle.
+// There is support for up to 20 dimensions.
+// The values of min must be less than the values of max at the same dimension.
+// Thus min[0] must be less-than-or-equal-to max[0].
+// The IndexRect is a default function that can be used for the rect
+// parameter.
+//
+// Deprecated: Use Transactions
+func (db *DB) CreateSpatialIndex(name, pattern string,
+ rect func(item string) (min, max []float64)) error {
+ return db.Update(func(tx *Tx) error {
+ return tx.CreateSpatialIndex(name, pattern, rect)
+ })
+}
+
+// ReplaceSpatialIndex builds a new index and populates it with items.
+// The items are organized in an r-tree and can be retrieved using the
+// Intersects method.
+// If a previous index with the same name exists, that index will be deleted.
+//
+// Deprecated: Use Transactions
+func (db *DB) ReplaceSpatialIndex(name, pattern string,
+ rect func(item string) (min, max []float64)) error {
+ return db.Update(func(tx *Tx) error {
+ err := tx.CreateSpatialIndex(name, pattern, rect)
+ if err != nil {
+ if err == ErrIndexExists {
+ err := tx.DropIndex(name)
+ if err != nil {
+ return err
+ }
+ return tx.CreateSpatialIndex(name, pattern, rect)
+ }
+ return err
+ }
+ return nil
+ })
+}
+
+// DropIndex removes an index.
+//
+// Deprecated: Use Transactions
+func (db *DB) DropIndex(name string) error {
+ return db.Update(func(tx *Tx) error {
+ return tx.DropIndex(name)
+ })
+}
+
+// Indexes returns a list of index names.
+//
+// Deprecated: Use Transactions
+func (db *DB) Indexes() ([]string, error) {
+ var names []string
+ var err = db.View(func(tx *Tx) error {
+ var err error
+ names, err = tx.Indexes()
+ return err
+ })
+ return names, err
+}
+
+// ReadConfig returns the database configuration.
+func (db *DB) ReadConfig(config *Config) error {
+ db.mu.RLock()
+ defer db.mu.RUnlock()
+ if db.closed {
+ return ErrDatabaseClosed
+ }
+ *config = db.config
+ return nil
+}
+
+// SetConfig updates the database configuration.
+func (db *DB) SetConfig(config Config) error {
+ db.mu.Lock()
+ defer db.mu.Unlock()
+ if db.closed {
+ return ErrDatabaseClosed
+ }
+ switch config.SyncPolicy {
+ default:
+ return ErrInvalidSyncPolicy
+ case Never, EverySecond, Always:
+ }
+ db.config = config
+ return nil
+}
+
+// insertIntoDatabase performs inserts an item in to the database and updates
+// all indexes. If a previous item with the same key already exists, that item
+// will be replaced with the new one, and return the previous item.
+func (db *DB) insertIntoDatabase(item *dbItem) *dbItem {
+ var pdbi *dbItem
+ prev := db.keys.ReplaceOrInsert(item)
+ if prev != nil {
+ // A previous item was removed from the keys tree. Let's
+ // fully delete this item from all indexes.
+ pdbi = prev.(*dbItem)
+ if pdbi.opts != nil && pdbi.opts.ex {
+ // Remove it from the exipres tree.
+ db.exps.Delete(pdbi)
+ }
+ for _, idx := range db.idxs {
+ if idx.btr != nil {
+ // Remove it from the btree index.
+ idx.btr.Delete(pdbi)
+ }
+ if idx.rtr != nil {
+ // Remove it from the rtree index.
+ idx.rtr.Remove(pdbi)
+ }
+ }
+ }
+ if item.opts != nil && item.opts.ex {
+ // The new item has eviction options. Add it to the
+ // expires tree
+ db.exps.ReplaceOrInsert(item)
+ }
+ for _, idx := range db.idxs {
+ if !idx.match(item.key) {
+ continue
+ }
+ if idx.btr != nil {
+ // Add new item to btree index.
+ idx.btr.ReplaceOrInsert(item)
+ }
+ if idx.rtr != nil {
+ // Add new item to rtree index.
+ idx.rtr.Insert(item)
+ }
+ }
+ // we must return the previous item to the caller.
+ return pdbi
+}
+
+// deleteFromDatabase removes and item from the database and indexes. The input
+// item must only have the key field specified thus "&dbItem{key: key}" is all
+// that is needed to fully remove the item with the matching key. If an item
+// with the matching key was found in the database, it will be removed and
+// returned to the caller. A nil return value means that the item was not
+// found in the database
+func (db *DB) deleteFromDatabase(item *dbItem) *dbItem {
+ var pdbi *dbItem
+ prev := db.keys.Delete(item)
+ if prev != nil {
+ pdbi = prev.(*dbItem)
+ if pdbi.opts != nil && pdbi.opts.ex {
+ // Remove it from the exipres tree.
+ db.exps.Delete(pdbi)
+ }
+ for _, idx := range db.idxs {
+ if idx.btr != nil {
+ // Remove it from the btree index.
+ idx.btr.Delete(pdbi)
+ }
+ if idx.rtr != nil {
+ // Remove it from the rtree index.
+ idx.rtr.Remove(pdbi)
+ }
+ }
+ }
+ return pdbi
+}
+
+// backgroundManager runs continuously in the background and performs various
+// operations such as removing expired items and syncing to disk.
+func (db *DB) backgroundManager() {
+ flushes := 0
+ t := time.NewTicker(time.Second)
+ defer t.Stop()
+ for range t.C {
+ var shrink bool
+ // Open a standard view. This will take a full lock of the
+ // database thus allowing for access to anything we need.
+ var onExpired func([]string)
+ var expired []*dbItem
+ var onExpiredSync func(key, value string, tx *Tx) error
+ err := db.Update(func(tx *Tx) error {
+ onExpired = db.config.OnExpired
+ if onExpired == nil {
+ onExpiredSync = db.config.OnExpiredSync
+ }
+ if db.persist && !db.config.AutoShrinkDisabled {
+ pos, err := db.file.Seek(0, 1)
+ if err != nil {
+ return err
+ }
+ aofsz := int(pos)
+ if aofsz > db.config.AutoShrinkMinSize {
+ prc := float64(db.config.AutoShrinkPercentage) / 100.0
+ shrink = aofsz > db.lastaofsz+int(float64(db.lastaofsz)*prc)
+ }
+ }
+ // produce a list of expired items that need removing
+ db.exps.AscendLessThan(&dbItem{
+ opts: &dbItemOpts{ex: true, exat: time.Now()},
+ }, func(item btree.Item) bool {
+ expired = append(expired, item.(*dbItem))
+ return true
+ })
+ if onExpired == nil && onExpiredSync == nil {
+ for _, itm := range expired {
+ if _, err := tx.Delete(itm.key); err != nil {
+ // it's ok to get a "not found" because the
+ // 'Delete' method reports "not found" for
+ // expired items.
+ if err != ErrNotFound {
+ return err
+ }
+ }
+ }
+ } else if onExpiredSync != nil {
+ for _, itm := range expired {
+ if err := onExpiredSync(itm.key, itm.val, tx); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+ })
+ if err == ErrDatabaseClosed {
+ break
+ }
+
+ // send expired event, if needed
+ if onExpired != nil && len(expired) > 0 {
+ keys := make([]string, 0, 32)
+ for _, itm := range expired {
+ keys = append(keys, itm.key)
+ }
+ onExpired(keys)
+ }
+
+ // execute a disk sync, if needed
+ func() {
+ db.mu.Lock()
+ defer db.mu.Unlock()
+ if db.persist && db.config.SyncPolicy == EverySecond &&
+ flushes != db.flushes {
+ _ = db.file.Sync()
+ flushes = db.flushes
+ }
+ }()
+ if shrink {
+ if err = db.Shrink(); err != nil {
+ if err == ErrDatabaseClosed {
+ break
+ }
+ }
+ }
+ }
+}
+
+// Shrink will make the database file smaller by removing redundant
+// log entries. This operation does not block the database.
+func (db *DB) Shrink() error {
+ db.mu.Lock()
+ if db.closed {
+ db.mu.Unlock()
+ return ErrDatabaseClosed
+ }
+ if !db.persist {
+ // The database was opened with ":memory:" as the path.
+ // There is no persistence, and no need to do anything here.
+ db.mu.Unlock()
+ return nil
+ }
+ if db.shrinking {
+ // The database is already in the process of shrinking.
+ db.mu.Unlock()
+ return ErrShrinkInProcess
+ }
+ db.shrinking = true
+ defer func() {
+ db.mu.Lock()
+ db.shrinking = false
+ db.mu.Unlock()
+ }()
+ fname := db.file.Name()
+ tmpname := fname + ".tmp"
+ // the endpos is used to return to the end of the file when we are
+ // finished writing all of the current items.
+ endpos, err := db.file.Seek(0, 2)
+ if err != nil {
+ return err
+ }
+ db.mu.Unlock()
+ time.Sleep(time.Second / 4) // wait just a bit before starting
+ f, err := os.Create(tmpname)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ _ = f.Close()
+ _ = os.RemoveAll(tmpname)
+ }()
+
+ // we are going to read items in as chunks as to not hold up the database
+ // for too long.
+ var buf []byte
+ pivot := ""
+ done := false
+ for !done {
+ err := func() error {
+ db.mu.RLock()
+ defer db.mu.RUnlock()
+ if db.closed {
+ return ErrDatabaseClosed
+ }
+ done = true
+ var n int
+ db.keys.AscendGreaterOrEqual(&dbItem{key: pivot},
+ func(item btree.Item) bool {
+ dbi := item.(*dbItem)
+ // 1000 items or 64MB buffer
+ if n > 1000 || len(buf) > 64*1024*1024 {
+ pivot = dbi.key
+ done = false
+ return false
+ }
+ buf = dbi.writeSetTo(buf)
+ n++
+ return true
+ },
+ )
+ if len(buf) > 0 {
+ if _, err := f.Write(buf); err != nil {
+ return err
+ }
+ buf = buf[:0]
+ }
+ return nil
+ }()
+ if err != nil {
+ return err
+ }
+ }
+ // We reached this far so all of the items have been written to a new tmp
+ // There's some more work to do by appending the new line from the aof
+ // to the tmp file and finally swap the files out.
+ return func() error {
+ // We're wrapping this in a function to get the benefit of a defered
+ // lock/unlock.
+ db.mu.Lock()
+ defer db.mu.Unlock()
+ if db.closed {
+ return ErrDatabaseClosed
+ }
+ // We are going to open a new version of the aof file so that we do
+ // not change the seek position of the previous. This may cause a
+ // problem in the future if we choose to use syscall file locking.
+ aof, err := os.Open(fname)
+ if err != nil {
+ return err
+ }
+ defer func() { _ = aof.Close() }()
+ if _, err := aof.Seek(endpos, 0); err != nil {
+ return err
+ }
+ // Just copy all of the new commands that have occurred since we
+ // started the shrink process.
+ if _, err := io.Copy(f, aof); err != nil {
+ return err
+ }
+ // Close all files
+ if err := aof.Close(); err != nil {
+ return err
+ }
+ if err := f.Close(); err != nil {
+ return err
+ }
+ if err := db.file.Close(); err != nil {
+ return err
+ }
+ // Any failures below here is really bad. So just panic.
+ if err := os.Rename(tmpname, fname); err != nil {
+ panic(err)
+ }
+ db.file, err = os.OpenFile(fname, os.O_CREATE|os.O_RDWR, 0666)
+ if err != nil {
+ panic(err)
+ }
+ pos, err := db.file.Seek(0, 2)
+ if err != nil {
+ return err
+ }
+ db.lastaofsz = int(pos)
+ return nil
+ }()
+}
+
+var errValidEOF = errors.New("valid eof")
+
+// readLoad reads from the reader and loads commands into the database.
+// modTime is the modified time of the reader, should be no greater than
+// the current time.Now().
+func (db *DB) readLoad(rd io.Reader, modTime time.Time) error {
+ data := make([]byte, 4096)
+ parts := make([]string, 0, 8)
+ r := bufio.NewReader(rd)
+ for {
+ // read a single command.
+ // first we should read the number of parts that the of the command
+ line, err := r.ReadBytes('\n')
+ if err != nil {
+ if len(line) > 0 {
+ // got an eof but also data. this should be an unexpected eof.
+ return io.ErrUnexpectedEOF
+ }
+ if err == io.EOF {
+ break
+ }
+ return err
+ }
+ if line[0] != '*' {
+ return ErrInvalid
+ }
+ // convert the string number to and int
+ var n int
+ if len(line) == 4 && line[len(line)-2] == '\r' {
+ if line[1] < '0' || line[1] > '9' {
+ return ErrInvalid
+ }
+ n = int(line[1] - '0')
+ } else {
+ if len(line) < 5 || line[len(line)-2] != '\r' {
+ return ErrInvalid
+ }
+ for i := 1; i < len(line)-2; i++ {
+ if line[i] < '0' || line[i] > '9' {
+ return ErrInvalid
+ }
+ n = n*10 + int(line[i]-'0')
+ }
+ }
+ // read each part of the command.
+ parts = parts[:0]
+ for i := 0; i < n; i++ {
+ // read the number of bytes of the part.
+ line, err := r.ReadBytes('\n')
+ if err != nil {
+ return err
+ }
+ if line[0] != '$' {
+ return ErrInvalid
+ }
+ // convert the string number to and int
+ var n int
+ if len(line) == 4 && line[len(line)-2] == '\r' {
+ if line[1] < '0' || line[1] > '9' {
+ return ErrInvalid
+ }
+ n = int(line[1] - '0')
+ } else {
+ if len(line) < 5 || line[len(line)-2] != '\r' {
+ return ErrInvalid
+ }
+ for i := 1; i < len(line)-2; i++ {
+ if line[i] < '0' || line[i] > '9' {
+ return ErrInvalid
+ }
+ n = n*10 + int(line[i]-'0')
+ }
+ }
+ // resize the read buffer
+ if len(data) < n+2 {
+ dataln := len(data)
+ for dataln < n+2 {
+ dataln *= 2
+ }
+ data = make([]byte, dataln)
+ }
+ if _, err = io.ReadFull(r, data[:n+2]); err != nil {
+ return err
+ }
+ if data[n] != '\r' || data[n+1] != '\n' {
+ return ErrInvalid
+ }
+ // copy string
+ parts = append(parts, string(data[:n]))
+ }
+ // finished reading the command
+
+ if len(parts) == 0 {
+ continue
+ }
+ if (parts[0][0] == 's' || parts[0][1] == 'S') &&
+ (parts[0][1] == 'e' || parts[0][1] == 'E') &&
+ (parts[0][2] == 't' || parts[0][2] == 'T') {
+ // SET
+ if len(parts) < 3 || len(parts) == 4 || len(parts) > 5 {
+ return ErrInvalid
+ }
+ if len(parts) == 5 {
+ if strings.ToLower(parts[3]) != "ex" {
+ return ErrInvalid
+ }
+ ex, err := strconv.ParseInt(parts[4], 10, 64)
+ if err != nil {
+ return err
+ }
+ now := time.Now()
+ dur := (time.Duration(ex) * time.Second) - now.Sub(modTime)
+ if dur > 0 {
+ db.insertIntoDatabase(&dbItem{
+ key: parts[1],
+ val: parts[2],
+ opts: &dbItemOpts{
+ ex: true,
+ exat: now.Add(dur),
+ },
+ })
+ }
+ } else {
+ db.insertIntoDatabase(&dbItem{key: parts[1], val: parts[2]})
+ }
+ } else if (parts[0][0] == 'd' || parts[0][1] == 'D') &&
+ (parts[0][1] == 'e' || parts[0][1] == 'E') &&
+ (parts[0][2] == 'l' || parts[0][2] == 'L') {
+ // DEL
+ if len(parts) != 2 {
+ return ErrInvalid
+ }
+ db.deleteFromDatabase(&dbItem{key: parts[1]})
+ } else if (parts[0][0] == 'f' || parts[0][1] == 'F') &&
+ strings.ToLower(parts[0]) == "flushdb" {
+ db.keys = btree.New(btreeDegrees, nil)
+ db.exps = btree.New(btreeDegrees, &exctx{db})
+ db.idxs = make(map[string]*index)
+ } else {
+ return ErrInvalid
+ }
+ }
+ return nil
+}
+
+// load reads entries from the append only database file and fills the database.
+// The file format uses the Redis append only file format, which is and a series
+// of RESP commands. For more information on RESP please read
+// http://redis.io/topics/protocol. The only supported RESP commands are DEL and
+// SET.
+func (db *DB) load() error {
+ fi, err := db.file.Stat()
+ if err != nil {
+ return err
+ }
+ if err := db.readLoad(db.file, fi.ModTime()); err != nil {
+ return err
+ }
+ pos, err := db.file.Seek(0, 2)
+ if err != nil {
+ return err
+ }
+ db.lastaofsz = int(pos)
+ return nil
+}
+
+// managed calls a block of code that is fully contained in a transaction.
+// This method is intended to be wrapped by Update and View
+func (db *DB) managed(writable bool, fn func(tx *Tx) error) (err error) {
+ var tx *Tx
+ tx, err = db.Begin(writable)
+ if err != nil {
+ return
+ }
+ defer func() {
+ if err != nil {
+ // The caller returned an error. We must rollback.
+ _ = tx.Rollback()
+ return
+ }
+ if writable {
+ // Everything went well. Lets Commit()
+ err = tx.Commit()
+ } else {
+ // read-only transaction can only roll back.
+ err = tx.Rollback()
+ }
+ }()
+ tx.funcd = true
+ defer func() {
+ tx.funcd = false
+ }()
+ err = fn(tx)
+ return
+}
+
+// View executes a function within a managed read-only transaction.
+// When a non-nil error is returned from the function that error will be return
+// to the caller of View().
+//
+// Executing a manual commit or rollback from inside the function will result
+// in a panic.
+func (db *DB) View(fn func(tx *Tx) error) error {
+ return db.managed(false, fn)
+}
+
+// Update executes a function within a managed read/write transaction.
+// The transaction has been committed when no error is returned.
+// In the event that an error is returned, the transaction will be rolled back.
+// When a non-nil error is returned from the function, the transaction will be
+// rolled back and the that error will be return to the caller of Update().
+//
+// Executing a manual commit or rollback from inside the function will result
+// in a panic.
+func (db *DB) Update(fn func(tx *Tx) error) error {
+ return db.managed(true, fn)
+}
+
+// get return an item or nil if not found.
+func (db *DB) get(key string) *dbItem {
+ item := db.keys.Get(&dbItem{key: key})
+ if item != nil {
+ return item.(*dbItem)
+ }
+ return nil
+}
+
+// Tx represents a transaction on the database. This transaction can either be
+// read-only or read/write. Read-only transactions can be used for retrieving
+// values for keys and iterating through keys and values. Read/write
+// transactions can set and delete keys.
+//
+// All transactions must be committed or rolled-back when done.
+type Tx struct {
+ db *DB // the underlying database.
+ writable bool // when false mutable operations fail.
+ funcd bool // when true Commit and Rollback panic.
+ wc *txWriteContext // context for writable transactions.
+}
+
+type txWriteContext struct {
+ // rollback when deleteAll is called
+ rbkeys *btree.BTree // a tree of all item ordered by key
+ rbexps *btree.BTree // a tree of items ordered by expiration
+ rbidxs map[string]*index // the index trees.
+
+ rollbackItems map[string]*dbItem // details for rolling back tx.
+ commitItems map[string]*dbItem // details for committing tx.
+ itercount int // stack of iterators
+ rollbackIndexes map[string]*index // details for dropped indexes.
+}
+
+// DeleteAll deletes all items from the database.
+func (tx *Tx) DeleteAll() error {
+ if tx.db == nil {
+ return ErrTxClosed
+ } else if !tx.writable {
+ return ErrTxNotWritable
+ } else if tx.wc.itercount > 0 {
+ return ErrTxIterating
+ }
+
+ // check to see if we've already deleted everything
+ if tx.wc.rbkeys == nil {
+ // we need to backup the live data in case of a rollback.
+ tx.wc.rbkeys = tx.db.keys
+ tx.wc.rbexps = tx.db.exps
+ tx.wc.rbidxs = tx.db.idxs
+ }
+
+ // now reset the live database trees
+ tx.db.keys = btree.New(btreeDegrees, nil)
+ tx.db.exps = btree.New(btreeDegrees, &exctx{tx.db})
+ tx.db.idxs = make(map[string]*index)
+
+ // finally re-create the indexes
+ for name, idx := range tx.wc.rbidxs {
+ tx.db.idxs[name] = idx.clearCopy()
+ }
+
+ // always clear out the commits
+ tx.wc.commitItems = make(map[string]*dbItem)
+
+ return nil
+}
+
+// Begin opens a new transaction.
+// Multiple read-only transactions can be opened at the same time but there can
+// only be one read/write transaction at a time. Attempting to open a read/write
+// transactions while another one is in progress will result in blocking until
+// the current read/write transaction is completed.
+//
+// All transactions must be closed by calling Commit() or Rollback() when done.
+func (db *DB) Begin(writable bool) (*Tx, error) {
+ tx := &Tx{
+ db: db,
+ writable: writable,
+ }
+ tx.lock()
+ if db.closed {
+ tx.unlock()
+ return nil, ErrDatabaseClosed
+ }
+ if writable {
+ // writable transactions have a writeContext object that
+ // contains information about changes to the database.
+ tx.wc = &txWriteContext{}
+ tx.wc.rollbackItems = make(map[string]*dbItem)
+ tx.wc.rollbackIndexes = make(map[string]*index)
+ if db.persist {
+ tx.wc.commitItems = make(map[string]*dbItem)
+ }
+ }
+ return tx, nil
+}
+
+// lock locks the database based on the transaction type.
+func (tx *Tx) lock() {
+ if tx.writable {
+ tx.db.mu.Lock()
+ } else {
+ tx.db.mu.RLock()
+ }
+}
+
+// unlock unlocks the database based on the transaction type.
+func (tx *Tx) unlock() {
+ if tx.writable {
+ tx.db.mu.Unlock()
+ } else {
+ tx.db.mu.RUnlock()
+ }
+}
+
+// rollbackInner handles the underlying rollback logic.
+// Intended to be called from Commit() and Rollback().
+func (tx *Tx) rollbackInner() {
+ // rollback the deleteAll if needed
+ if tx.wc.rbkeys != nil {
+ tx.db.keys = tx.wc.rbkeys
+ tx.db.idxs = tx.wc.rbidxs
+ tx.db.exps = tx.wc.rbexps
+ }
+ for key, item := range tx.wc.rollbackItems {
+ tx.db.deleteFromDatabase(&dbItem{key: key})
+ if item != nil {
+ // When an item is not nil, we will need to reinsert that item
+ // into the database overwriting the current one.
+ tx.db.insertIntoDatabase(item)
+ }
+ }
+ for name, idx := range tx.wc.rollbackIndexes {
+ delete(tx.db.idxs, name)
+ if idx != nil {
+ // When an index is not nil, we will need to rebuilt that index
+ // this could be an expensive process if the database has many
+ // items or the index is complex.
+ tx.db.idxs[name] = idx
+ idx.rebuild()
+ }
+ }
+}
+
+// Commit writes all changes to disk.
+// An error is returned when a write error occurs, or when a Commit() is called
+// from a read-only transaction.
+func (tx *Tx) Commit() error {
+ if tx.funcd {
+ panic("managed tx commit not allowed")
+ }
+ if tx.db == nil {
+ return ErrTxClosed
+ } else if !tx.writable {
+ return ErrTxNotWritable
+ }
+ var err error
+ if tx.db.persist && (len(tx.wc.commitItems) > 0 || tx.wc.rbkeys != nil) {
+ tx.db.buf = tx.db.buf[:0]
+ // write a flushdb if a deleteAll was called.
+ if tx.wc.rbkeys != nil {
+ tx.db.buf = append(tx.db.buf, "*1\r\n$7\r\nflushdb\r\n"...)
+ }
+ // Each committed record is written to disk
+ for key, item := range tx.wc.commitItems {
+ if item == nil {
+ tx.db.buf = (&dbItem{key: key}).writeDeleteTo(tx.db.buf)
+ } else {
+ tx.db.buf = item.writeSetTo(tx.db.buf)
+ }
+ }
+ // Flushing the buffer only once per transaction.
+ // If this operation fails then the write did failed and we must
+ // rollback.
+ if _, err = tx.db.file.Write(tx.db.buf); err != nil {
+ tx.rollbackInner()
+ }
+ if tx.db.config.SyncPolicy == Always {
+ _ = tx.db.file.Sync()
+ }
+ // Increment the number of flushes. The background syncing uses this.
+ tx.db.flushes++
+ }
+ // Unlock the database and allow for another writable transaction.
+ tx.unlock()
+ // Clear the db field to disable this transaction from future use.
+ tx.db = nil
+ return err
+}
+
+// Rollback closes the transaction and reverts all mutable operations that
+// were performed on the transaction such as Set() and Delete().
+//
+// Read-only transactions can only be rolled back, not committed.
+func (tx *Tx) Rollback() error {
+ if tx.funcd {
+ panic("managed tx rollback not allowed")
+ }
+ if tx.db == nil {
+ return ErrTxClosed
+ }
+ // The rollback func does the heavy lifting.
+ if tx.writable {
+ tx.rollbackInner()
+ }
+ // unlock the database for more transactions.
+ tx.unlock()
+ // Clear the db field to disable this transaction from future use.
+ tx.db = nil
+ return nil
+}
+
+// dbItemOpts holds various meta information about an item.
+type dbItemOpts struct {
+ ex bool // does this item expire?
+ exat time.Time // when does this item expire?
+}
+type dbItem struct {
+ key, val string // the binary key and value
+ opts *dbItemOpts // optional meta information
+ keyless bool // keyless item for scanning
+}
+
+func appendArray(buf []byte, count int) []byte {
+ buf = append(buf, '*')
+ buf = append(buf, strconv.FormatInt(int64(count), 10)...)
+ buf = append(buf, '\r', '\n')
+ return buf
+}
+
+func appendBulkString(buf []byte, s string) []byte {
+ buf = append(buf, '$')
+ buf = append(buf, strconv.FormatInt(int64(len(s)), 10)...)
+ buf = append(buf, '\r', '\n')
+ buf = append(buf, s...)
+ buf = append(buf, '\r', '\n')
+ return buf
+}
+
+// writeSetTo writes an item as a single SET record to the a bufio Writer.
+func (dbi *dbItem) writeSetTo(buf []byte) []byte {
+ if dbi.opts != nil && dbi.opts.ex {
+ ex := dbi.opts.exat.Sub(time.Now()) / time.Second
+ buf = appendArray(buf, 5)
+ buf = appendBulkString(buf, "set")
+ buf = appendBulkString(buf, dbi.key)
+ buf = appendBulkString(buf, dbi.val)
+ buf = appendBulkString(buf, "ex")
+ buf = appendBulkString(buf, strconv.FormatUint(uint64(ex), 10))
+ } else {
+ buf = appendArray(buf, 3)
+ buf = appendBulkString(buf, "set")
+ buf = appendBulkString(buf, dbi.key)
+ buf = appendBulkString(buf, dbi.val)
+ }
+ return buf
+}
+
+// writeSetTo writes an item as a single DEL record to the a bufio Writer.
+func (dbi *dbItem) writeDeleteTo(buf []byte) []byte {
+ buf = appendArray(buf, 2)
+ buf = appendBulkString(buf, "del")
+ buf = appendBulkString(buf, dbi.key)
+ return buf
+}
+
+// expired evaluates id the item has expired. This will always return false when
+// the item does not have `opts.ex` set to true.
+func (dbi *dbItem) expired() bool {
+ return dbi.opts != nil && dbi.opts.ex && time.Now().After(dbi.opts.exat)
+}
+
+// MaxTime from http://stackoverflow.com/questions/25065055#32620397
+// This is a long time in the future. It's an imaginary number that is
+// used for b-tree ordering.
+var maxTime = time.Unix(1<<63-62135596801, 999999999)
+
+// expiresAt will return the time when the item will expire. When an item does
+// not expire `maxTime` is used.
+func (dbi *dbItem) expiresAt() time.Time {
+ if dbi.opts == nil || !dbi.opts.ex {
+ return maxTime
+ }
+ return dbi.opts.exat
+}
+
+// Less determines if a b-tree item is less than another. This is required
+// for ordering, inserting, and deleting items from a b-tree. It's important
+// to note that the ctx parameter is used to help with determine which
+// formula to use on an item. Each b-tree should use a different ctx when
+// sharing the same item.
+func (dbi *dbItem) Less(item btree.Item, ctx interface{}) bool {
+ dbi2 := item.(*dbItem)
+ switch ctx := ctx.(type) {
+ case *exctx:
+ // The expires b-tree formula
+ if dbi2.expiresAt().After(dbi.expiresAt()) {
+ return true
+ }
+ if dbi.expiresAt().After(dbi2.expiresAt()) {
+ return false
+ }
+ case *index:
+ if ctx.less != nil {
+ // Using an index
+ if ctx.less(dbi.val, dbi2.val) {
+ return true
+ }
+ if ctx.less(dbi2.val, dbi.val) {
+ return false
+ }
+ }
+ }
+ // Always fall back to the key comparison. This creates absolute uniqueness.
+ if dbi.keyless {
+ return false
+ } else if dbi2.keyless {
+ return true
+ }
+ return dbi.key < dbi2.key
+}
+
+// Rect converts a string to a rectangle.
+// An invalid rectangle will cause a panic.
+func (dbi *dbItem) Rect(ctx interface{}) (min, max []float64) {
+ switch ctx := ctx.(type) {
+ case *index:
+ return ctx.rect(dbi.val)
+ }
+ return nil, nil
+}
+
+// SetOptions represents options that may be included with the Set() command.
+type SetOptions struct {
+ // Expires indicates that the Set() key-value will expire
+ Expires bool
+ // TTL is how much time the key-value will exist in the database
+ // before being evicted. The Expires field must also be set to true.
+ // TTL stands for Time-To-Live.
+ TTL time.Duration
+}
+
+// GetLess returns the less function for an index. This is handy for
+// doing ad-hoc compares inside a transaction.
+// Returns ErrNotFound if the index is not found or there is no less
+// function bound to the index
+func (tx *Tx) GetLess(index string) (func(a, b string) bool, error) {
+ if tx.db == nil {
+ return nil, ErrTxClosed
+ }
+ idx, ok := tx.db.idxs[index]
+ if !ok || idx.less == nil {
+ return nil, ErrNotFound
+ }
+ return idx.less, nil
+}
+
+// GetRect returns the rect function for an index. This is handy for
+// doing ad-hoc searches inside a transaction.
+// Returns ErrNotFound if the index is not found or there is no rect
+// function bound to the index
+func (tx *Tx) GetRect(index string) (func(s string) (min, max []float64),
+ error) {
+ if tx.db == nil {
+ return nil, ErrTxClosed
+ }
+ idx, ok := tx.db.idxs[index]
+ if !ok || idx.rect == nil {
+ return nil, ErrNotFound
+ }
+ return idx.rect, nil
+}
+
+// Set inserts or replaces an item in the database based on the key.
+// The opt params may be used for additional functionality such as forcing
+// the item to be evicted at a specified time. When the return value
+// for err is nil the operation succeeded. When the return value of
+// replaced is true, then the operaton replaced an existing item whose
+// value will be returned through the previousValue variable.
+// The results of this operation will not be available to other
+// transactions until the current transaction has successfully committed.
+//
+// Only a writable transaction can be used with this operation.
+// This operation is not allowed during iterations such as Ascend* & Descend*.
+func (tx *Tx) Set(key, value string, opts *SetOptions) (previousValue string,
+ replaced bool, err error) {
+ if tx.db == nil {
+ return "", false, ErrTxClosed
+ } else if !tx.writable {
+ return "", false, ErrTxNotWritable
+ } else if tx.wc.itercount > 0 {
+ return "", false, ErrTxIterating
+ }
+ item := &dbItem{key: key, val: value}
+ if opts != nil {
+ if opts.Expires {
+ // The caller is requesting that this item expires. Convert the
+ // TTL to an absolute time and bind it to the item.
+ item.opts = &dbItemOpts{ex: true, exat: time.Now().Add(opts.TTL)}
+ }
+ }
+ // Insert the item into the keys tree.
+ prev := tx.db.insertIntoDatabase(item)
+
+ // insert into the rollback map if there has not been a deleteAll.
+ if tx.wc.rbkeys == nil {
+ if prev == nil {
+ // An item with the same key did not previously exist. Let's
+ // create a rollback entry with a nil value. A nil value indicates
+ // that the entry should be deleted on rollback. When the value is
+ // *not* nil, that means the entry should be reverted.
+ tx.wc.rollbackItems[key] = nil
+ } else {
+ // A previous item already exists in the database. Let's create a
+ // rollback entry with the item as the value. We need to check the
+ // map to see if there isn't already an item that matches the
+ // same key.
+ if _, ok := tx.wc.rollbackItems[key]; !ok {
+ tx.wc.rollbackItems[key] = prev
+ }
+ if !prev.expired() {
+ previousValue, replaced = prev.val, true
+ }
+ }
+ }
+ // For commits we simply assign the item to the map. We use this map to
+ // write the entry to disk.
+ if tx.db.persist {
+ tx.wc.commitItems[key] = item
+ }
+ return previousValue, replaced, nil
+}
+
+// Get returns a value for a key. If the item does not exist or if the item
+// has expired then ErrNotFound is returned. If ignoreExpired is true, then
+// the found value will be returned even if it is expired.
+func (tx *Tx) Get(key string, ignoreExpired ...bool) (val string, err error) {
+ if tx.db == nil {
+ return "", ErrTxClosed
+ }
+ var ignore bool
+ if len(ignoreExpired) != 0 {
+ ignore = ignoreExpired[0]
+ }
+ item := tx.db.get(key)
+ if item == nil || (item.expired() && !ignore) {
+ // The item does not exists or has expired. Let's assume that
+ // the caller is only interested in items that have not expired.
+ return "", ErrNotFound
+ }
+ return item.val, nil
+}
+
+// Delete removes an item from the database based on the item's key. If the item
+// does not exist or if the item has expired then ErrNotFound is returned.
+//
+// Only a writable transaction can be used for this operation.
+// This operation is not allowed during iterations such as Ascend* & Descend*.
+func (tx *Tx) Delete(key string) (val string, err error) {
+ if tx.db == nil {
+ return "", ErrTxClosed
+ } else if !tx.writable {
+ return "", ErrTxNotWritable
+ } else if tx.wc.itercount > 0 {
+ return "", ErrTxIterating
+ }
+ item := tx.db.deleteFromDatabase(&dbItem{key: key})
+ if item == nil {
+ return "", ErrNotFound
+ }
+ // create a rollback entry if there has not been a deleteAll call.
+ if tx.wc.rbkeys == nil {
+ if _, ok := tx.wc.rollbackItems[key]; !ok {
+ tx.wc.rollbackItems[key] = item
+ }
+ }
+ if tx.db.persist {
+ tx.wc.commitItems[key] = nil
+ }
+ // Even though the item has been deleted, we still want to check
+ // if it has expired. An expired item should not be returned.
+ if item.expired() {
+ // The item exists in the tree, but has expired. Let's assume that
+ // the caller is only interested in items that have not expired.
+ return "", ErrNotFound
+ }
+ return item.val, nil
+}
+
+// TTL returns the remaining time-to-live for an item.
+// A negative duration will be returned for items that do not have an
+// expiration.
+func (tx *Tx) TTL(key string) (time.Duration, error) {
+ if tx.db == nil {
+ return 0, ErrTxClosed
+ }
+ item := tx.db.get(key)
+ if item == nil {
+ return 0, ErrNotFound
+ } else if item.opts == nil || !item.opts.ex {
+ return -1, nil
+ }
+ dur := item.opts.exat.Sub(time.Now())
+ if dur < 0 {
+ return 0, ErrNotFound
+ }
+ return dur, nil
+}
+
+// scan iterates through a specified index and calls user-defined iterator
+// function for each item encountered.
+// The desc param indicates that the iterator should descend.
+// The gt param indicates that there is a greaterThan limit.
+// The lt param indicates that there is a lessThan limit.
+// The index param tells the scanner to use the specified index tree. An
+// empty string for the index means to scan the keys, not the values.
+// The start and stop params are the greaterThan, lessThan limits. For
+// descending order, these will be lessThan, greaterThan.
+// An error will be returned if the tx is closed or the index is not found.
+func (tx *Tx) scan(desc, gt, lt bool, index, start, stop string,
+ iterator func(key, value string) bool) error {
+ if tx.db == nil {
+ return ErrTxClosed
+ }
+ // wrap a btree specific iterator around the user-defined iterator.
+ iter := func(item btree.Item) bool {
+ dbi := item.(*dbItem)
+ return iterator(dbi.key, dbi.val)
+ }
+ var tr *btree.BTree
+ if index == "" {
+ // empty index means we will use the keys tree.
+ tr = tx.db.keys
+ } else {
+ idx := tx.db.idxs[index]
+ if idx == nil {
+ // index was not found. return error
+ return ErrNotFound
+ }
+ tr = idx.btr
+ if tr == nil {
+ return nil
+ }
+ }
+ // create some limit items
+ var itemA, itemB *dbItem
+ if gt || lt {
+ if index == "" {
+ itemA = &dbItem{key: start}
+ itemB = &dbItem{key: stop}
+ } else {
+ itemA = &dbItem{val: start}
+ itemB = &dbItem{val: stop}
+ if desc {
+ itemA.keyless = true
+ itemB.keyless = true
+ }
+ }
+ }
+ // execute the scan on the underlying tree.
+ if tx.wc != nil {
+ tx.wc.itercount++
+ defer func() {
+ tx.wc.itercount--
+ }()
+ }
+ if desc {
+ if gt {
+ if lt {
+ tr.DescendRange(itemA, itemB, iter)
+ } else {
+ tr.DescendGreaterThan(itemA, iter)
+ }
+ } else if lt {
+ tr.DescendLessOrEqual(itemA, iter)
+ } else {
+ tr.Descend(iter)
+ }
+ } else {
+ if gt {
+ if lt {
+ tr.AscendRange(itemA, itemB, iter)
+ } else {
+ tr.AscendGreaterOrEqual(itemA, iter)
+ }
+ } else if lt {
+ tr.AscendLessThan(itemA, iter)
+ } else {
+ tr.Ascend(iter)
+ }
+ }
+ return nil
+}
+
+// Match returns true if the specified key matches the pattern. This is a very
+// simple pattern matcher where '*' matches on any number characters and '?'
+// matches on any one character.
+func Match(key, pattern string) bool {
+ return match.Match(key, pattern)
+}
+
+// AscendKeys allows for iterating through keys based on the specified pattern.
+func (tx *Tx) AscendKeys(pattern string,
+ iterator func(key, value string) bool) error {
+ if pattern == "" {
+ return nil
+ }
+ if pattern[0] == '*' {
+ if pattern == "*" {
+ return tx.Ascend("", iterator)
+ }
+ return tx.Ascend("", func(key, value string) bool {
+ if match.Match(key, pattern) {
+ if !iterator(key, value) {
+ return false
+ }
+ }
+ return true
+ })
+ }
+ min, max := match.Allowable(pattern)
+ return tx.AscendGreaterOrEqual("", min, func(key, value string) bool {
+ if key > max {
+ return false
+ }
+ if match.Match(key, pattern) {
+ if !iterator(key, value) {
+ return false
+ }
+ }
+ return true
+ })
+}
+
+// DescendKeys allows for iterating through keys based on the specified pattern.
+func (tx *Tx) DescendKeys(pattern string,
+ iterator func(key, value string) bool) error {
+ if pattern == "" {
+ return nil
+ }
+ if pattern[0] == '*' {
+ if pattern == "*" {
+ return tx.Descend("", iterator)
+ }
+ return tx.Descend("", func(key, value string) bool {
+ if match.Match(key, pattern) {
+ if !iterator(key, value) {
+ return false
+ }
+ }
+ return true
+ })
+ }
+ min, max := match.Allowable(pattern)
+ return tx.DescendLessOrEqual("", max, func(key, value string) bool {
+ if key < min {
+ return false
+ }
+ if match.Match(key, pattern) {
+ if !iterator(key, value) {
+ return false
+ }
+ }
+ return true
+ })
+}
+
+// Ascend calls the iterator for every item in the database within the range
+// [first, last], until iterator returns false.
+// When an index is provided, the results will be ordered by the item values
+// as specified by the less() function of the defined index.
+// When an index is not provided, the results will be ordered by the item key.
+// An invalid index will return an error.
+func (tx *Tx) Ascend(index string,
+ iterator func(key, value string) bool) error {
+ return tx.scan(false, false, false, index, "", "", iterator)
+}
+
+// AscendGreaterOrEqual calls the iterator for every item in the database within
+// the range [pivot, last], until iterator returns false.
+// When an index is provided, the results will be ordered by the item values
+// as specified by the less() function of the defined index.
+// When an index is not provided, the results will be ordered by the item key.
+// An invalid index will return an error.
+func (tx *Tx) AscendGreaterOrEqual(index, pivot string,
+ iterator func(key, value string) bool) error {
+ return tx.scan(false, true, false, index, pivot, "", iterator)
+}
+
+// AscendLessThan calls the iterator for every item in the database within the
+// range [first, pivot), until iterator returns false.
+// When an index is provided, the results will be ordered by the item values
+// as specified by the less() function of the defined index.
+// When an index is not provided, the results will be ordered by the item key.
+// An invalid index will return an error.
+func (tx *Tx) AscendLessThan(index, pivot string,
+ iterator func(key, value string) bool) error {
+ return tx.scan(false, false, true, index, pivot, "", iterator)
+}
+
+// AscendRange calls the iterator for every item in the database within
+// the range [greaterOrEqual, lessThan), until iterator returns false.
+// When an index is provided, the results will be ordered by the item values
+// as specified by the less() function of the defined index.
+// When an index is not provided, the results will be ordered by the item key.
+// An invalid index will return an error.
+func (tx *Tx) AscendRange(index, greaterOrEqual, lessThan string,
+ iterator func(key, value string) bool) error {
+ return tx.scan(
+ false, true, true, index, greaterOrEqual, lessThan, iterator,
+ )
+}
+
+// Descend calls the iterator for every item in the database within the range
+// [last, first], until iterator returns false.
+// When an index is provided, the results will be ordered by the item values
+// as specified by the less() function of the defined index.
+// When an index is not provided, the results will be ordered by the item key.
+// An invalid index will return an error.
+func (tx *Tx) Descend(index string,
+ iterator func(key, value string) bool) error {
+ return tx.scan(true, false, false, index, "", "", iterator)
+}
+
+// DescendGreaterThan calls the iterator for every item in the database within
+// the range [last, pivot), until iterator returns false.
+// When an index is provided, the results will be ordered by the item values
+// as specified by the less() function of the defined index.
+// When an index is not provided, the results will be ordered by the item key.
+// An invalid index will return an error.
+func (tx *Tx) DescendGreaterThan(index, pivot string,
+ iterator func(key, value string) bool) error {
+ return tx.scan(true, true, false, index, pivot, "", iterator)
+}
+
+// DescendLessOrEqual calls the iterator for every item in the database within
+// the range [pivot, first], until iterator returns false.
+// When an index is provided, the results will be ordered by the item values
+// as specified by the less() function of the defined index.
+// When an index is not provided, the results will be ordered by the item key.
+// An invalid index will return an error.
+func (tx *Tx) DescendLessOrEqual(index, pivot string,
+ iterator func(key, value string) bool) error {
+ return tx.scan(true, false, true, index, pivot, "", iterator)
+}
+
+// DescendRange calls the iterator for every item in the database within
+// the range [lessOrEqual, greaterThan), until iterator returns false.
+// When an index is provided, the results will be ordered by the item values
+// as specified by the less() function of the defined index.
+// When an index is not provided, the results will be ordered by the item key.
+// An invalid index will return an error.
+func (tx *Tx) DescendRange(index, lessOrEqual, greaterThan string,
+ iterator func(key, value string) bool) error {
+ return tx.scan(
+ true, true, true, index, lessOrEqual, greaterThan, iterator,
+ )
+}
+
+// AscendEqual calls the iterator for every item in the database that equals
+// pivot, until iterator returns false.
+// When an index is provided, the results will be ordered by the item values
+// as specified by the less() function of the defined index.
+// When an index is not provided, the results will be ordered by the item key.
+// An invalid index will return an error.
+func (tx *Tx) AscendEqual(index, pivot string,
+ iterator func(key, value string) bool) error {
+ var err error
+ var less func(a, b string) bool
+ if index != "" {
+ less, err = tx.GetLess(index)
+ if err != nil {
+ return err
+ }
+ }
+ return tx.AscendGreaterOrEqual(index, pivot, func(key, value string) bool {
+ if less == nil {
+ if key != pivot {
+ return false
+ }
+ } else if less(pivot, value) {
+ return false
+ }
+ return iterator(key, value)
+ })
+}
+
+// DescendEqual calls the iterator for every item in the database that equals
+// pivot, until iterator returns false.
+// When an index is provided, the results will be ordered by the item values
+// as specified by the less() function of the defined index.
+// When an index is not provided, the results will be ordered by the item key.
+// An invalid index will return an error.
+func (tx *Tx) DescendEqual(index, pivot string,
+ iterator func(key, value string) bool) error {
+ var err error
+ var less func(a, b string) bool
+ if index != "" {
+ less, err = tx.GetLess(index)
+ if err != nil {
+ return err
+ }
+ }
+ return tx.DescendLessOrEqual(index, pivot, func(key, value string) bool {
+ if less == nil {
+ if key != pivot {
+ return false
+ }
+ } else if less(value, pivot) {
+ return false
+ }
+ return iterator(key, value)
+ })
+}
+
+// rect is used by Intersects and Nearby
+type rect struct {
+ min, max []float64
+}
+
+func (r *rect) Rect(ctx interface{}) (min, max []float64) {
+ return r.min, r.max
+}
+
+// Nearby searches for rectangle items that are nearby a target rect.
+// All items belonging to the specified index will be returned in order of
+// nearest to farthest.
+// The specified index must have been created by AddIndex() and the target
+// is represented by the rect string. This string will be processed by the
+// same bounds function that was passed to the CreateSpatialIndex() function.
+// An invalid index will return an error.
+// The dist param is the distance of the bounding boxes. In the case of
+// simple 2D points, it's the distance of the two 2D points squared.
+func (tx *Tx) Nearby(index, bounds string,
+ iterator func(key, value string, dist float64) bool) error {
+ if tx.db == nil {
+ return ErrTxClosed
+ }
+ if index == "" {
+ // cannot search on keys tree. just return nil.
+ return nil
+ }
+ // // wrap a rtree specific iterator around the user-defined iterator.
+ iter := func(item rtree.Item, dist float64) bool {
+ dbi := item.(*dbItem)
+ return iterator(dbi.key, dbi.val, dist)
+ }
+ idx := tx.db.idxs[index]
+ if idx == nil {
+ // index was not found. return error
+ return ErrNotFound
+ }
+ if idx.rtr == nil {
+ // not an r-tree index. just return nil
+ return nil
+ }
+ // execute the nearby search
+ var min, max []float64
+ if idx.rect != nil {
+ min, max = idx.rect(bounds)
+ }
+ // set the center param to false, which uses the box dist calc.
+ idx.rtr.KNN(&rect{min, max}, false, iter)
+ return nil
+}
+
+// Intersects searches for rectangle items that intersect a target rect.
+// The specified index must have been created by AddIndex() and the target
+// is represented by the rect string. This string will be processed by the
+// same bounds function that was passed to the CreateSpatialIndex() function.
+// An invalid index will return an error.
+func (tx *Tx) Intersects(index, bounds string,
+ iterator func(key, value string) bool) error {
+ if tx.db == nil {
+ return ErrTxClosed
+ }
+ if index == "" {
+ // cannot search on keys tree. just return nil.
+ return nil
+ }
+ // wrap a rtree specific iterator around the user-defined iterator.
+ iter := func(item rtree.Item) bool {
+ dbi := item.(*dbItem)
+ return iterator(dbi.key, dbi.val)
+ }
+ idx := tx.db.idxs[index]
+ if idx == nil {
+ // index was not found. return error
+ return ErrNotFound
+ }
+ if idx.rtr == nil {
+ // not an r-tree index. just return nil
+ return nil
+ }
+ // execute the search
+ var min, max []float64
+ if idx.rect != nil {
+ min, max = idx.rect(bounds)
+ }
+ idx.rtr.Search(&rect{min, max}, iter)
+ return nil
+}
+
+// Len returns the number of items in the database
+func (tx *Tx) Len() (int, error) {
+ if tx.db == nil {
+ return 0, ErrTxClosed
+ }
+ return tx.db.keys.Len(), nil
+}
+
+// IndexOptions provides an index with additional features or
+// alternate functionality.
+type IndexOptions struct {
+ // CaseInsensitiveKeyMatching allow for case-insensitive
+ // matching on keys when setting key/values.
+ CaseInsensitiveKeyMatching bool
+}
+
+// CreateIndex builds a new index and populates it with items.
+// The items are ordered in an b-tree and can be retrieved using the
+// Ascend* and Descend* methods.
+// An error will occur if an index with the same name already exists.
+//
+// When a pattern is provided, the index will be populated with
+// keys that match the specified pattern. This is a very simple pattern
+// match where '*' matches on any number characters and '?' matches on
+// any one character.
+// The less function compares if string 'a' is less than string 'b'.
+// It allows for indexes to create custom ordering. It's possible
+// that the strings may be textual or binary. It's up to the provided
+// less function to handle the content format and comparison.
+// There are some default less function that can be used such as
+// IndexString, IndexBinary, etc.
+func (tx *Tx) CreateIndex(name, pattern string,
+ less ...func(a, b string) bool) error {
+ return tx.createIndex(name, pattern, less, nil, nil)
+}
+
+// CreateIndexOptions is the same as CreateIndex except that it allows
+// for additional options.
+func (tx *Tx) CreateIndexOptions(name, pattern string,
+ opts *IndexOptions,
+ less ...func(a, b string) bool) error {
+ return tx.createIndex(name, pattern, less, nil, opts)
+}
+
+// CreateSpatialIndex builds a new index and populates it with items.
+// The items are organized in an r-tree and can be retrieved using the
+// Intersects method.
+// An error will occur if an index with the same name already exists.
+//
+// The rect function converts a string to a rectangle. The rectangle is
+// represented by two arrays, min and max. Both arrays may have a length
+// between 1 and 20, and both arrays must match in length. A length of 1 is a
+// one dimensional rectangle, and a length of 4 is a four dimension rectangle.
+// There is support for up to 20 dimensions.
+// The values of min must be less than the values of max at the same dimension.
+// Thus min[0] must be less-than-or-equal-to max[0].
+// The IndexRect is a default function that can be used for the rect
+// parameter.
+func (tx *Tx) CreateSpatialIndex(name, pattern string,
+ rect func(item string) (min, max []float64)) error {
+ return tx.createIndex(name, pattern, nil, rect, nil)
+}
+
+// CreateSpatialIndexOptions is the same as CreateSpatialIndex except that
+// it allows for additional options.
+func (tx *Tx) CreateSpatialIndexOptions(name, pattern string,
+ opts *IndexOptions,
+ rect func(item string) (min, max []float64)) error {
+ return tx.createIndex(name, pattern, nil, rect, nil)
+}
+
+// createIndex is called by CreateIndex() and CreateSpatialIndex()
+func (tx *Tx) createIndex(name string, pattern string,
+ lessers []func(a, b string) bool,
+ rect func(item string) (min, max []float64),
+ opts *IndexOptions,
+) error {
+ if tx.db == nil {
+ return ErrTxClosed
+ } else if !tx.writable {
+ return ErrTxNotWritable
+ } else if tx.wc.itercount > 0 {
+ return ErrTxIterating
+ }
+ if name == "" {
+ // cannot create an index without a name.
+ // an empty name index is designated for the main "keys" tree.
+ return ErrIndexExists
+ }
+ // check if an index with that name already exists.
+ if _, ok := tx.db.idxs[name]; ok {
+ // index with name already exists. error.
+ return ErrIndexExists
+ }
+ // genreate a less function
+ var less func(a, b string) bool
+ switch len(lessers) {
+ default:
+ // multiple less functions specified.
+ // create a compound less function.
+ less = func(a, b string) bool {
+ for i := 0; i < len(lessers)-1; i++ {
+ if lessers[i](a, b) {
+ return true
+ }
+ if lessers[i](b, a) {
+ return false
+ }
+ }
+ return lessers[len(lessers)-1](a, b)
+ }
+ case 0:
+ // no less function
+ case 1:
+ less = lessers[0]
+ }
+ var sopts IndexOptions
+ if opts != nil {
+ sopts = *opts
+ }
+ if sopts.CaseInsensitiveKeyMatching {
+ pattern = strings.ToLower(pattern)
+ }
+ // intialize new index
+ idx := &index{
+ name: name,
+ pattern: pattern,
+ less: less,
+ rect: rect,
+ db: tx.db,
+ opts: sopts,
+ }
+ idx.rebuild()
+ // save the index
+ tx.db.idxs[name] = idx
+ if tx.wc.rbkeys == nil {
+ // store the index in the rollback map.
+ if _, ok := tx.wc.rollbackIndexes[name]; !ok {
+ // we use nil to indicate that the index should be removed upon rollback.
+ tx.wc.rollbackIndexes[name] = nil
+ }
+ }
+ return nil
+}
+
+// DropIndex removes an index.
+func (tx *Tx) DropIndex(name string) error {
+ if tx.db == nil {
+ return ErrTxClosed
+ } else if !tx.writable {
+ return ErrTxNotWritable
+ } else if tx.wc.itercount > 0 {
+ return ErrTxIterating
+ }
+ if name == "" {
+ // cannot drop the default "keys" index
+ return ErrInvalidOperation
+ }
+ idx, ok := tx.db.idxs[name]
+ if !ok {
+ return ErrNotFound
+ }
+ // delete from the map.
+ // this is all that is needed to delete an index.
+ delete(tx.db.idxs, name)
+ if tx.wc.rbkeys == nil {
+ // store the index in the rollback map.
+ if _, ok := tx.wc.rollbackIndexes[name]; !ok {
+ // we use a non-nil copy of the index without the data to indicate that the
+ // index should be rebuilt upon rollback.
+ tx.wc.rollbackIndexes[name] = idx.clearCopy()
+ }
+ }
+ return nil
+}
+
+// Indexes returns a list of index names.
+func (tx *Tx) Indexes() ([]string, error) {
+ if tx.db == nil {
+ return nil, ErrTxClosed
+ }
+ names := make([]string, 0, len(tx.db.idxs))
+ for name := range tx.db.idxs {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ return names, nil
+}
+
+// Rect is helper function that returns a string representation
+// of a rect. IndexRect() is the reverse function and can be used
+// to generate a rect from a string.
+func Rect(min, max []float64) string {
+ r := grect.Rect{Min: min, Max: max}
+ return r.String()
+}
+
+// Point is a helper function that converts a series of float64s
+// to a rectangle for a spatial index.
+func Point(coords ...float64) string {
+ return Rect(coords, coords)
+}
+
+// IndexRect is a helper function that converts string to a rect.
+// Rect() is the reverse function and can be used to generate a string
+// from a rect.
+func IndexRect(a string) (min, max []float64) {
+ r := grect.Get(a)
+ return r.Min, r.Max
+}
+
+// IndexString is a helper function that return true if 'a' is less than 'b'.
+// This is a case-insensitive comparison. Use the IndexBinary() for comparing
+// case-sensitive strings.
+func IndexString(a, b string) bool {
+ for i := 0; i < len(a) && i < len(b); i++ {
+ if a[i] >= 'A' && a[i] <= 'Z' {
+ if b[i] >= 'A' && b[i] <= 'Z' {
+ // both are uppercase, do nothing
+ if a[i] < b[i] {
+ return true
+ } else if a[i] > b[i] {
+ return false
+ }
+ } else {
+ // a is uppercase, convert a to lowercase
+ if a[i]+32 < b[i] {
+ return true
+ } else if a[i]+32 > b[i] {
+ return false
+ }
+ }
+ } else if b[i] >= 'A' && b[i] <= 'Z' {
+ // b is uppercase, convert b to lowercase
+ if a[i] < b[i]+32 {
+ return true
+ } else if a[i] > b[i]+32 {
+ return false
+ }
+ } else {
+ // neither are uppercase
+ if a[i] < b[i] {
+ return true
+ } else if a[i] > b[i] {
+ return false
+ }
+ }
+ }
+ return len(a) < len(b)
+}
+
+// IndexBinary is a helper function that returns true if 'a' is less than 'b'.
+// This compares the raw binary of the string.
+func IndexBinary(a, b string) bool {
+ return a < b
+}
+
+// IndexInt is a helper function that returns true if 'a' is less than 'b'.
+func IndexInt(a, b string) bool {
+ ia, _ := strconv.ParseInt(a, 10, 64)
+ ib, _ := strconv.ParseInt(b, 10, 64)
+ return ia < ib
+}
+
+// IndexUint is a helper function that returns true if 'a' is less than 'b'.
+// This compares uint64s that are added to the database using the
+// Uint() conversion function.
+func IndexUint(a, b string) bool {
+ ia, _ := strconv.ParseUint(a, 10, 64)
+ ib, _ := strconv.ParseUint(b, 10, 64)
+ return ia < ib
+}
+
+// IndexFloat is a helper function that returns true if 'a' is less than 'b'.
+// This compares float64s that are added to the database using the
+// Float() conversion function.
+func IndexFloat(a, b string) bool {
+ ia, _ := strconv.ParseFloat(a, 64)
+ ib, _ := strconv.ParseFloat(b, 64)
+ return ia < ib
+}
+
+// IndexJSON provides for the ability to create an index on any JSON field.
+// When the field is a string, the comparison will be case-insensitive.
+// It returns a helper function used by CreateIndex.
+func IndexJSON(path string) func(a, b string) bool {
+ return func(a, b string) bool {
+ return gjson.Get(a, path).Less(gjson.Get(b, path), false)
+ }
+}
+
+// IndexJSONCaseSensitive provides for the ability to create an index on
+// any JSON field.
+// When the field is a string, the comparison will be case-sensitive.
+// It returns a helper function used by CreateIndex.
+func IndexJSONCaseSensitive(path string) func(a, b string) bool {
+ return func(a, b string) bool {
+ return gjson.Get(a, path).Less(gjson.Get(b, path), true)
+ }
+}
+
+// Desc is a helper function that changes the order of an index.
+func Desc(less func(a, b string) bool) func(a, b string) bool {
+ return func(a, b string) bool { return less(b, a) }
+}
diff --git a/vendor/github.com/tidwall/buntdb/logo.png b/vendor/github.com/tidwall/buntdb/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..01c6d75c10b97194fe384e717107fca81d3e4e60
GIT binary patch
literal 95733
zcmbTdWk8i(*ELEgNGqj;ba%IOx3qL^y1S8-kZzFf?rxL@2?^=$PHDd7{XF-3&X4ze
zKMsG`Z1#2Sb*;7LoMVnLR;Z%91PUT9A`}!9ij<_N5)>4SDHIem-76UI4nkpo6ZnPW
zB&Ok{Y-{4=YTy8Y5;C?mgb+zt8<;|rAO^}pS3^PZ3AouA7+FG`hzud7<~AQlPFg!jh|G;Ykf^iEG0EA9K+Mb~Jscp)9`Y(i
z9+pPj#v}s#M0{>M;0D$ZCj%lkYbzT^9=8u9|J;`cy#D((BMH$zA91q$K=PkKX~-!O
ziP$#z$HU0z>gvki%F1BtV9LnM&CSio#KOqJLJvMc@91vhWZ*_`
z<4F4N9Yi6HMh@n7PUf~YM1SvSU})>?^nnD7^goYaZ6_!9KR32<{O?48DPwdquw!Iq
zU}Chk{`*}2eA>}T3G)98%u%+|@)(ahG4NJQkHRVSj7GcYo@
z`THjI--Y_mbBjV8%v~VH;tsafME}e*kNN*H3J8lK2OAf7ZYBc`BYHLnmd
zGl(RipdB!zM1@q`<_~pV>SNB%=r+2&vL}g-Ud6`uZ*8^o6E^pQ2{P;@%>NA=){0Gw
zy@)`-Cmhmx&^N>R+~~O1cGSl)-U08sfUsHAO>1
zv$eHVP*8aLHp!PFfu(E^j0*1gIVL0H>3FGCy;8T`=aGku?1gbkT-?QkXPhJ%yTz2*
zVw3&BOnDlIH6j64sOWPv@1V)HB@1|@?q?Z6Ie${Z4<477d%wE61YuK+Yv)u;REjn0
zO9BF5pXPu67F0}4Ntr-Y%AMHU*%A3{VqzkkR*;vMS5UCMy^UM*Dh&nBdb!R0&+*bi
zqb=Hpq5n?I4GUC;=h=GiXd36qQtMkxOcg~%LEo2=WuIzI(s`Wsr%QMC_V)JoEi5f9
zA&}lJhln{=Nz6-x7{pQB*RMb41_lR%iP*O_vavy3$7i=ti6ZzQ*oFMhu%QFS-ptL-
zWetYEWn6Ce
ze7&U%8G3+T@zj1|@qH_+uB|~O`-5Okd?s~+mlTu0
zFgivXORHi*&lOvamqs=edtquXl}AZ0*o+?;s;F;izP+cv@cVmyP?D#&t7)jDq-|z&
zwB6}r6oX-M=HaIiJV5bK#!qFt`gEjKJK5Pa%DzUn6WCXd!
z;dhlm)(DMi$%E;y_4T6ouRCki3g2^Jjp8;~Yc3!Tr-Vh}UwnzW!g%y$US4NSn5p7M
zXy#&_xmPsI(6^WVvCynkEyopx&a@&;kOmv3)%+LONo03lR?opU;WQin{GZs+KW!xC
z<-H$>A-(@|f`*JtW|_)n&f;;o3pRUGOUq;$wd(%t$1_*h?*tv;$THG#*hBg|w|opw
z_B3NLTd;@FcPd3HdxP`wSFe{4=6hK3~kVZG}e-uLnue9a?C
z>%4!9b}*4||GwsYcOrMi=e}HphMZ7NP7V#9i^=U&KbRfoZ$9j%Tzj~^072q;e@&m5
zo}S)$f80u~T%e(;8Eadc_i`trjsI#DqNp9)(4;~3+K&5h?389xTBmCA2SYhF>H+l|
z#47yPubG^7$D5kCYM{S-_xl>m+4b-6--(Ke(Q7xIf~`+S;{xZ!Z8<~d@rsF<^nN$J
z(3GG(a(E!P3G++)deH8~;S=mmXM}L3+UF*pAyc`ajlK&mD
z&{mb>&d=WPw{gTHYu~>x>a(CHRp&fdRGD-j8I52b~nx&
zmTTrtV`5Zs;d3O7t~oe6@^Jfp>}R|tQ7$Hf>Ap56ZQ+P$y@CN+
zI_c3-h0#O`ldS?{nP2(t?qUvy82wnM?rjS42f2!+L43>EC;g4(2cc0rKhyEJ?1&z%
zLGG^5ZtnT-(o$j2Bv^pb(D`tVl=y*i1-@J>v;p5}r;wPu)
zL{ovTU{^X!l>bIkpC5X2*i)=K?S$~W{WHDQ;5*#ig7d+Q0m^@aF$&^ym!0gpxty`FR=?UAuYnA6+Afcqp5L5-WPr!|-@^3tLa}$XJ0LeFr>f24
zY9Hk3*sG`&F$NwlVkQQJyqwG#co)rVHELB{q|l;@Qlonh_JyglqK$o(ShZ;jXAch#
zLqkIl4fsJ*Q&V+yb&HFO4-XGLJ;I6wsW5NKyapr)6zH(EG&D4{w0OC>n_F5u+}+(h
zJv}`p%C{R3iU%a!4
z{NTOsY@<<{7#{Gn+~u#ol9iP;Td4;R4{wA+!@!f1=l<+sF?)ff6NwCq8+QmE^u7>@=^{4jdn}eQzj`KU0>MdvEsg?f?
zRi2yMTNFGFS&BGrUfxU|=MqrpHBz(`*<4prE)-UhLjtwir)tCy^AkezEORwU=h>}4
zl?Fc%L4<{S0^reX7Mq}(vERR!8?GOM(X4ejn&;=|zX^W~@VJt3p6qJ$zQ2CY?O@#Q
zakcyF{z$hg*pU;XHqXS+ARm=Rxx|8dK~ML(T!ShkDypB~Jhk824_>g)Nna!GFR*-K
z0YGE~lv+g4X}x^;(s);%Bql_Zgo>M%mKF~WPfKf|M74BMY0p({7&4_Om_djWAYg4JKFtg)KwM^XnEayu<-2L$5#}C1h41ja?eih!{
z-ex+m=<4bk^+z79bPBx4fBH81AT_VSx|h>pGKjLlyLAD5~C$o&Au1pvbuzSnw+N~P{&KcO!`h?t8=9{HN;
zW8reb#=n03!sDA53{VK1z(>2OoeOOvy(3M^MDx3A&otlsxT
z!mw9S!Q7KL%ou7b4lb
zShiyPwE5tgoqkexy7hdW`9zM`Qj7C565QSG#m;+PmltbbkLh*#q@RxFy2iwD5DwDE
zO1*e6q}gkhEw<8HFzSvzmTO-WFso^6BbA6eoUfM=Kw6T3{bPE(*sSQFxJDQc5$8aN
zjF1{uDVqA_*?>3wQ9aCff2x?v?6zazd~4WrG*yiMxcz#y(8$M&VC*KZ$zAN+83Rs!
zcOG{bY$-0^C%(t&6jsw!1E&9E6>(w;3OKgEfE`4ypt421<{d2n;p*z@XlPNm>=vIN
zz#cO(GBPqZM>vwKEs)Ol&l?^b92^=7i;DUZ54|KVD*B_h7Yhq30aLf5qvH{PwDD}%
zk*j2re12M=GDZxfCj1U$|HG1R>_u$`<3P09|#6U2gm`8IaBuO@$RZS$*I0kAs8e=PBmq`E0v5D0(&&-YgcoFK7nkET5b&A!C$=NAIm{|`vqfh?I{dOx#V
z2&KSeG$beJk=d?oT@U{ds@LHkx#V&^UIba*t|5U38~>FXdPYXZGUnmoVIqSLVn&}j
zxbt$eqiF(7i{S+KZIBkjGPlDY9ODWA;43&YXx<>2{p8pV8ShR>NdX0)gPA#HzSLXW
z<2nN`YWIC2TJ~o`{G-|fmH~CHovA4mkj=&a>6-kAb=y6h_a^f}
z*VN&48>_pwwKZKJmnNUi<3~pfFC)jypiJe61aS`%n*WVoMj(
zO{|b0n3AuxhF*n9pAKjRUKC89x!G8aOCg6Tf_DElZ2`?=pLwkh}
zs^iuDNheZJ4@q;*NtpEsx{_F*+}23kpy{Q>^RFWQGW7I1u8XnSJBoiQ)XfSJ2?>na
z`A7;YG&D4uknp@a*^!Y@N%<&A>2T>$*@2P4=f8~W
zwD~tTf=Sk@U5zX;zqL}D=TwXLjMZCHMdT>&OA<})TIHmq&Mq!^y>6IZ6g_#^ar`b-
z$mGv-i1^KFu;K>`@2{?*!f`EB>KaUu?^maS`+k%WcYb4m|NY?g>sa45fCK~>g;S^ues9flJ66-5WcF2mV84r3tT3*gecdZmEt+!H#;>5u
z?A~xVy*TN7&Uo5Sv+bB`u*Un(3>A`6Qtp5Zll29Z3@S{-8nAs6_1{Wjd+-nm`N=*p
z@NbEU9u5!OROQu8eQSeBfuYbb$~}fl{izu@6!0z3r0Cg!UyNr}^XyUmb02qTFnl
z`uTN`N5)UwtAaw`4QZ54Kk@l=nbF*d=T3ai|J$?z*d-vcND#s4J2|CR3^2{5*Q?C^ZKDMP-l+vnG#*2KgF9DF;-U^r~=8x5Z8XR|4K!V2$J$N5lq?!s``@vT8#ft!%^od#6GTSnc7
zBgn`<;G)|!93%`_c+|hGJ2iD^8MK6Opb!(zxRQg~G06Xa0($hsdnrw}hvI2olV$c#
zk6PWlVAU)mrPS$je4Qvd@W-L%V6sZ>WczAwLKHh;<0whbXX1n=a3mG1Ur!KHwPNvi
z6av2FT^TC*Dn{%E-M03_#$jx9<|cd^$M5O^5_Pk0RE31Pe^(m`{oXp1qrybRVKKhD
z+`F4nqUq92=XHfcaXtKf1|X(3!`lZw%h^GrKd^MNWaPGbx-nkjCumD~pFVxE(<2b@
z(N=0E;B$+9N=`}PHtPEd+CVwqGys$D@9!PC@Sn4));bR{aFa?K6f1t;FD3KS<8E$m
zg9I8!Fdr{T7Txyq=g)uOz$zg;$%Y^SO+uI$N-yY$Z8ryEtQQ)l6?kxy5}gI84*L$q
zXD1MFCSkDC$bZagAFpI{>fd2nL@4ERB!y_smhiWTTrfmf{2Fwv&~0xq9z=h%SCb^v
z77qF^EVeq-Jz9)47v}G*s;UCL
zJKTrq7ofytf)2e~`t=T&f~(ojd4L&$!CBfU9L)|j7xw9eF1%&Aeel!SK|SuvIUnD*
zTts@y@SvJI>`R>dP7@G*4-rl77u$DN2L`f(67McUF(^^rF?Ibs7Pz%hs&P1Z5v=1*
z@B)&Op!eznf~BSAAenW78nP9Ks!41iJwwOY-Qy_dNt#EK?A~&N>jm6Ri9{5^dmg9X
zb>_bSP18+IM`Mg&2_W*z>k_-2Xt|5+QKOGSdSYV$#%yDiYDhDxB=e
zP_Z&HqWw_Zmn0S{luuWy(3V0!!3hAwTug`ZDNgpt_eO$~w!5yJx+XPk-iKIKqdUZ6
zgP)+WOp;Fdtx$Ye4;jX$ngCi{iZ%4eGZO}zf9$-
zo=gW)@wdxO09&vE%_Nk`@7>~ZBu!OoI+l^dsOJOf1!(UPAb@pH&xDtIyfRB7PcuE@
zcIIW0El^1b6+_SdJbZhSLV=oQ4O-D4|F<6z;u;9v*ID!jtiCFH8!C!~P3LyH3O%af
z(Ch^uQ`N>6*(k9_tBbX;Fbp;}y{2e^!eRRp6A|BxDA`C7Ef43ZCX3RnK`CHXNQQG{
zLwW%Tw$kD}>^2lA%I~oG3Y%!_3+nr@py;N&ElP&)pa&)RS7)!pZ#k?F_ky=%k{H(P
z@{h3(2ICWAZ?0zbwCXLyeA~S5UDVVP<=H)&@orydpZszZFm`33sy_pcDhZadvt6pa;>Fla|&56&dgak}fdg$O8o+J}%)-FqD4s
z`*`2FE2>yfY(`3U!GC(PwI!trQo(h{$Hq7vUM9B29m883?Vhjsm@5wMuhZtnjLAAT
zIX1=!IN;5^G3>jL@Nn29edwswgUZgCrRb4wN9xA`EZOYoWpo^}xIG*D`+;8fM~$as
z(o0eRC)vXS;BWIl{yAW0EK}hootHGx&
z0DyG2aPMazam?3_hsi)68$d@c+`Z#sWG`JcC21s}wh4JPj6dtDy*)7H@Z8TI)xsU6l!^J2$iCHT-sRz%TMhsp(*p^CT27oh*
zc7;rbFd%{GAX!JgOCW20e4LPZjvlDtizNwi&`6`$GCh}PqeB`9>vHYpnum=B
zKxcw>N-<@VRFp%bHY}w$U-TOT{i+@9DqnPt^xKU5AP#nR_UUIt6fKH4ZyN;jzijI&
zl8BkvE`Z$nBzt_N<4KW`ky(A9Q3nLhJ6ta;@imal$J4bMtf*z)JqCT=Oy_poF4t<*
zP)Th8^*92Db^E((Zy7D4|Fy2~4IQ0${b!}jOPuSY8dymMCHbW`i}_pdji
zmc
z(6MRa-ycll|87B06E62xmifRl6Oc#i`I9CAF*QmoXg*dO(IFoZCP_~`$dpRtO{PGe
zH3kV=C>KZ9H*UuPe%c>-QgLQ8WYnY~GI{hfxU8s>3hyTs(k11;{=@cqp+7yI_yQ^}
zURJ<%rfd!<8k&tZCw
z9c>LK4SEgvJl+9T#b|ADL!J1H8iF8~uc_0T>Ya;QL1)3nMnTc19{_lSiS$XV9H9WX
zrmMh<5Z3;+s;}Qj0}DrH^Sx%RWnn6x9Al;PwHcllmxcb)nKvxa@$qZ`dB;xTiX$er
z_&z_u`6D^h^1PJH0ceh30pni2M`L|`orvf8979G%CJlf`PqCystk+a}C%fm-hZ74Z5-D0<6Ql2h%D|Nm
zsg|^ne3JL0`@qRa&C5wlO3BPh$w=>uiGxBkR}hYW6{1p{C>N3`W(*PYVI;3o~a#&fUlbIWx
zVCMgzl|P}8D%tk!2zT1<4Z_W9@kW~^TxP=`fGr+O{7d2Mv|fW^_1uHe>if(us%mUZ
z7CuxACnghIaE=JkdOSYtyyem>V0wcfJRUC
z)uTx)EG#@Q*kuXQ5(@V5%ix!N6bYe4|7Z-Qcxqfk*@O}~U4@uRMvH_aapJQW<4GqhkzP^BI
zAGk9;$#vDe^2NtZP^JplgJ$mT{?d2uRFytgxdF!2UDhA@-g2&ne6Q8$qmc>7UK|6O
ze7V$)-HhB*X0L0c$ZeWT+V0DO-B6E%ZTX+U06{lKep7+WPM|8vcM%`
zTlcuWgj*veC7vNPeh5^ckasDvaJ{m3Rt8zwR#~=oeohaseorhd!#%gK7Z2z3sM(v^
zw0Zow@pfuq%N+Y9H~P>t{DU~ii{M-AiAy?NG;N=Y%}YZldjLe@erDI&tqC(SGN$@u
zT~YIo>v~~Rt)AJnbC^&Z?YmbOO{@T}0T&+z6DdX-LjeMvt#Bs>$N~Ah5+rkN|c;IBDSlGZruF0&%5%b9}yhR)HTV*;szu3m>5m4TIYRsN1`Gk
z*rTg54XiEx3Fxx3?NPjBeZ_*@+>LyhB+2yRMgJT?S>bm+3umi^zl4d7>j;gKlzR49
zsNO7Fw7qwEQ4DHTF5oh7BZ9>u@PsUd(Y@>ZC_YDkrjKVr9Ow`{Zl_s=g_Pmac&*!;
z)3A-sOFh`=)lZ*5KbtQNTXR{yF;{MX)ZomgeT4k0sGzV=LQE_uD5xE5aUe{C1`-gD
zoSbjtrAkcDe<2%whI~O}W@W9duC}+cI|V=2*A>czs72u#)|7aq#Ma)YjLfV%Ll;@U
z(DdMX$wkh?B1(HdqI)r@j~b2%6v=ooOIg7yN?~
zzMnF{ev8I=19$pcvDq{L(D8IhK+wKZVy(ivrIV!Lr(&hj$5u7FTWh8IIOq4vBBF1i
zr6fJ!rMW)b%KE0%tBNzhRub4~Gj%d}T@M@GY;A+DLpxR6y7Da&!KT(-sZEr54wV0N
zw@iptq=0W29Ug|g6QDx0CQ49V9`)Tiz}_05S9$;%aR||?FsW~~CnLi}ZmiWIkuRXfu&7mU
zes_%)`-TTErA^t_K4X0H$Nk$olAyhM1qF-q)hdA@fejbup)Ng;ti-K=hGgntP}
zkT`OXq^$pODKC)wxXoLSN*$=s`o$g<^2U^ad}97hKh^0Yv^}&AfctnQ9nB8mH#oXQ
zj|+a*%BpWvkPvTmElZXn9CUQU(<30g3gk#dYloidOA6->c
zm!gw$J^dHxI{*R#RlxW5oIL)raFJZY;Ng7OW}nz8y82{6lA>%1%UB2!ZKhAYqqx!T={I=lM3i(TIRiXG1<8Yt2L0J
zb>PIAdaB0DSrG
zY(NPYcR>>lbbBEB!@34*zo!@R1)v|)1uRU=w)`rSQQYwdU;wGc36yzOi)dl1Xg?Tw
zJW_u^+6;@5tc4O*Dwpy`bvi@O;>Irz|y3I${LH#gCjD?tvum+w2tAu{AJ
zKK6SsIU2eBB39{AlZ!qV8LwplbGuiWr(`}jQHBVelTQtp#q>ZH2;$bIbw8(W6H4s1
zvQa)w1ce@j?fRO7@y{P3juhkZ?!XyCED|jEyw;NO!Sk9r9-&|k03?0=)fiFFMZg^%01*+x;{^LR-oQ<)|6N%I*hz1dP#
z@d~cX^1?w$ERhZ>7v9?0-ro1!+TS1AQMcJur-+N;vIdD4^o<6@B-HeL)N);=`iJ6tfG>W$^cKL~`nm@GfasSg+Yj4Ff+1O#73*1_L{VhU
z9Ik#HKILKcW5YpYT_Err?_E~xOUz;TJiHJ;#cd#(OL|O$;)9Ten&EXhDNI5En{vw%
zlrP%mviSKzroh>1Rn7;=e(&I50mL5Qrs8?(SKN|QQyKI+9zZ;U{+=RMvO;AE4Oz?K
z#;X%+fx?;}GqXb3fk-}H|)73T+Uh_sHPZN5BgRF^2#
zt5AiM#%EXI7CX{`Ta{9^(LpKBK%B&AIO1N4wAwkcvoh)Y$&+Eo$BEiZ2NC~(0I3&N
zfcXpgbIQl3R$AVL*7Yexsrc!XOY&U6yZqCfW0T8~rk58VwqVL8g|;tNETyta@|IwQ
z61$yiiQZBQmnvL9YQ4qu-K*()J*ZzI!=JlNzW@l@>2~HXHfl-)vp!kd70{TOoTP)*
zELibPpeWo{XDr+D&p1M*yS9t8e1>%Swz9doKoC2SRb+kq9Z6aF9T5>Jt#~d^bug*o
z%HxtXo0aAY&D--yJ%4pq1HD%p@bQCtpZmJ{`hs@}6xj`M?Uq3}Z2(4bT-Ey`9bVii`o8FwRkwE+QCi^V^ybh1l
zRxFw(AsBO;4y7L@&$&>5P4l?(U8;RAiMzvLw<@SvDpqLnu8CcA6DP3I%W0PmIz2xG
z*%Q{UI<(C}jNs=BnsHe=qB4ZHa?e`895j>xn;sD*U$y%7wXtjjlNEPaIa*9F0CcQ3
z4WRYSG@yF@x1xHvgoK5WAQ-M%#&N~Snm^TS_-tXCs+F)4pmMBS?i0O1NcCv?fE+<`
zLCa+&uN#AWH#Yy(bhY72a4>MYRcly{P_KDO?h_`9#U?U#O7h3#$TGoesn2P=JgT?a
z{2Zzy@S;alT%6%kP2j_jTdLJ$UT9PlnsIeuVIh$0tnNU?LV)~CWw$(np*N?mN
zV+@RgU(0AV@1LZR!VQ1bxNXVP(iHgjN+J=nM0JZkOt^nCyReN7+7Y8+_hV;3m@MDQ3AkZCAV1_7rhreXa!hsV4(hYe96
zQ!G-!;BAamoX(n`Icvhhe1f;ELBbjm=GOQn>U)YdFfbq~Bg0zYc?w{Jz~d!GIIiD9
zn>#D{8<4@djw_W@T9k*9X!#CHZBlqf`Y*mOH(1TpfX>2bI~t0BfItVxITN|?bwR)<
z)Xq;yS!lb`k*DAWk|ZF2=|A~yuMGo4+Gk>%78bbe`{Tf$#XkRP=^Mqzx`-7xIJiXL
zMSxy9JpZ7g_s`8WdwW-s*7ip&I8hZUs!^|R+H;8^ZsmyVj4@;&3UwNM4+dK}rjp1@
z>Mc3>4|aQ%S!O<1?Oc9KpC3guD9zeW*GF2|?5SFX^6#+|sAxzlKdXPVfsWLTs_sSr
zw6y9z{$K9$8j3QsY|im9`y*wgirn7v%vTFx#Ztr|;b*O;w}fwohX2}5p1EGUc=3(L
zSz5x!dO^5Vf6ZFu;p~2FbSUFb!Gy&T-N;?T4U^TU{^^+hEzDZ}JeB}4(f}7YVuIH=
zfDp&30k+3&KsIMzA08c{z2^~iAboJ!T+iR>Hr|)XkX;@sz58h^AyiRag!2k`bMjMi
z@wvFTK4SugWwOQjAeMD5n8HMW!6)Z$~F5!0+cNARsW{k(qjYZyNc&b`8qOG01?9-?Xqv{o`pW_G2t6!l)#r
zewS_H2CNP$K?g|`tRMZZ4mjgFuRla
zGGs|D{4K60ksFD8iYh89pnD&XecoQ4_D$!&=aEw$ywMF+Z0bF+@oI@Il^O&UKSvrfoAP~cS-FcIs1k-6ktO_E;}eB3<_BygpZC$VSUzo
zZoEt*lcL%z!g-Pv8eDar@HEDpOiXq_D~Ef-Kxg4F050OR`mK20gn)nmAP+JcTED|=
zpK~JWiE5srT9`Z0@vjR+j&F|&I2fp^OJ6CN#=S%x#Pwxi677>Dqv@gRaOGDwaFL6J
zP@9>jzODj{hFl~An@%sio#Z4&R5xw(*5@@+J#`=dXo@n9
zlc+;`@je`V2X`ZY%JYlriJ8k06R
zG}pMhkB^Uj2%_ob_t#_JxG|7G4?*EPN;J{2aJ!KE=VN|B2(0@b+X;Qm%4@n30r%6@
zZgTcIP#PK=Iivd1K6r9)rH%ZUmGJWN0^vA_8CluV`2JUiyU)k1A)eH*K-0`Ncbqpe
z{IPa;^UmGriKQw#nY(>N#E6}D`?PmofP)7ZXm6X~L@2+LLQk6UNquV8w&_)C*HF*8
z6d`zs;$>@#-sq1qS_8ssckb*P7mIC1Q`?Xsxr^bMm!7nmGfrAeWt8DVy1d?Zm$9s_
zg>q?X#gT^pumzMbR`z?79I;b#L|@Eh*J(BAGj_nIdh$^Jw})a$qCuC0|d+
zykTOqhNMF)-c)^2G8p(#o6}A#usrw$Jf0YWO
zzOOac8>_3pFDbv<229rKZW#K4olkd`;{@+eTgk6vXEHiGZpA{n1u6W1k*2y@m%Tw?
z4Ovoy2gD)J7bVbQ-QC=@+uZEedV=5<0)v7gaM{Ct98|pf)#%q6CtF++NDiq@u(&F?
zXf*fvERZc=iw?8IxN2RX@)rI@$#|w+jZr`94He;p$?n-lk}E;Uy_N^KKFpm-n&OJ3
z*rkm=*=vn@3#`4^pl6{WI~vt}Zchc%ZLHJb0s{{AZ+m8t4(I(>=mxyLPY?ZUJ
zWK+($J4Kmu7T$Wu57Dchfc$8?w
zQoj|_c~s@Qe^wjy`#W9nk`4ERX&+>Rqk0#cbiBOqhs)+u=zu}l4$8iCpA%?>D>E&V
z;M!itYD*CU(FSOvJnu9GcA)Z@eq(y+(_ojUaha{|r#ERBNrniLU@oH|dOk43#%DAz
zb539%n0{L&Gm%1D@!|8-S5JP$MP645eNkiBi`Y_D7c($CEFVGQ5c@gA_&-!<~?_
zq2W;1YWx8kmQJ_rookd>uZoCGY(vXKQ>L$EV6&(;DuiarB6=%wFSmqumoNCQXLw3R
zw@P0KV%o`6cDo{OU|;~)NMmDTH@6!grml66Q&9M`M0`zSY!9uP1D!6AJ-|_gBH-@I
z6_41Ttwi%Z#TaH~zIjG$)|MB4O^%L0J(?UH<9&a0k4V{G%$Dk56Uh``^<=j~La{EBAGfjm*IrgRzVcc%Dr@3#E@^ohwv@4w9jbx}1G&
z8$;ebsXsZ?<^@)8_(r`s3DK+%HngX4ZMuOk>*4WnP`%aV$lvB*xxMwQk@mxf^xsr`
zL^*k^7f9ilLkgEu!&1v+af2qn={59y4cqTp(zGjb_|NsCn=u7L6w4M(eaP6t<55v!
zy+w4Qa!KuUo)ku`OPImv7*gJ0R~o0SZkwB5Cy~s%c%@oH#1}9c*mcN`j*bW~l~CUv
zV=c3>F)CHRYLhXhY7;;=faZ;rBj)0|0d9jl${zrj_V>dRuswNrdin-CZ@hb`JO!Tc
zq9Q76_S!UA;wH@38a~yOxK~R~JfxJA@D}J~*^0$yQB}xgE?5_6{*y%V@Da^rhevLV
zlA0guR1NzxgT(aQaSUnDv8Yw#EL75#I$6V|ixF`wqN5_Z=H_kz4MDG2S2QK}S%b{G
z=VE7!(!yvb;lBH95F0}EPH`%bp67U4*0UFL^!RU3Y(RVM~q+mAWo8?PdM5pfC|~o*FEZ2!S35m6UzX1F|rFtd7vZJC2clIgPbw46?
z7kYDRYuuEY(;vonm|@pv8~v+R8aR{C=u&Ir*&^dLSqg5tPU}W+&uqX_6)P`GhwXY?
z7H4k|N=pFU(Kb8@YS>GTcZiRKo9n_bXrfu2*#s_Eu2@l+X=hLO?C=ScG0YGyRZmTI8
zaX0RAo>hHzm}(Htw@_|{!;uUyW0eg3nKK0W)z|JNs4e^mz+0#;Z16i)6J*_yNEAzF
zTc!K+aDC;NYKRn>aQBt^iIQVphP(>{huVbsr@LjeCb5AXMt+A0V1e^?-2o1ILT-l_
zG;x81{e68d`%`-W-}dG8W-k!|8gtS@=lJaRj5Y-0Nu3?k+l@7zt#yqkHb1n1{q{n0Cr
zfsZu%diTn%UoaY&f^Fw&Ab=M>^+=k=%2F!L4>~`5@GpDlIeZXyvkPM3mKD#C%~rIW
zvWO5b6%v8ILTti!tbX;B8>*qvt72$vquD!^7uH;yUz|LZ`2Ail2?n9KOY00g0$_qa
z4^+#xG*>8o#yS8bHZXsk&UjLI50JhWmW43~!RyBuoJ{m3^>cfTAGRnaxoxxdq;vye<4
zZ{ye2)|Rv?jDf=G%K8;VvljVqvk}Eagpay&?~}gD_D-_T;j?E3F+JCx-K)CgBP|bg
zwCRN356YR)k`#IsHy#HQ%18w&r4}E+DW4|BDzr`L{DOiW)W2u;fQf7?N-?%V$I#d<
zzMeXkmS*E1sUrhfpqrM*>*loCaYqt9I4Nls)Q!PhXmr+^9YQ|WN7axN4VA>|SZY;1
zRMV%f3P(5>@;C$iKda&I+-#jJUvT(JO+0cJQb!P7VgY+f41Xp
znTizVA45jAFj-gWEeURqaAo(jHND@zRvj(AG|Ibo{Sop8K=UjGHfaqU5`P1(NF}rR
zx;-H70s-NgdrZUd^zC|g0DPd|e1rA9ft)1SSjZ<=i}DIswbOnlSr+VfNf~lHD`CZ{
zlwieAGP~*eVuR`i5I0wanfk(@ECi+vr8+PV!Hm5tLbu8@pn-UZuAjzFH
zw{PU&qefCzHF^=*5Ej@CdS1lxz7=IR>O)?PBbP~x?)XxpjZ#B|?K|+GNTiM{mE7d#
zkPytr&X80pFyR6q4OoSI%D|}#9lQsW4l+N1t`ju93VwHXX4=NG!rU1PBuE(C@lRUu
zD|3-6S;0=5xtf=4T5311G*R)>8MlHS*FRxo41+ug&LX)MaZW~b<(NEtrUptXDys^h
zKfli;s=rQ&B|uNY$Irx%iI40@TMsnN8y@a8apbac3%~jDvhGPbw8N0*iKI|4F-84`
zf%pn{T9gdNFFhfpF|J)fZWdd2`k;kJT2z>=n*noGMHhf6kZJ+HN@jFEMqQ@Wh-!37`Ez
zv|(0EK(_P}#z=0TPS@>@Lb+d;q+VQPNV%$L^N`%P6&xXTkoCI#T$cTnJMG8f_S?ZR
z0dHCcUYZvt7S_CYl>75CiNqI|m-xU}3K@eAHUt)kyeSTEF_5z0v*nLxwjBgt>-d%n
z3&wvT%WDh!YWcAgZu!q@lW~Asjv)=Lu%h<)X}#wk08F>6&~^xZVwgDqe|=sf&a!}%
z1=?hf@L(h}PiioOg-#Yz82g+GsxWuLGTP9j;93l_FOwoTFVnLzUb&8ro>q5!7*Wc&
za^}8RqqKxQl#ocVe&SL8T;S^IA&WcY{QUHouS8*U0_@3letP3b_xUvxvQQa-@uCC=
zw}k(wWG!tR{{g}wupGf*5llK>_#4H+_893(kc;N!nMz)4(Xg|T@_D=x9T3HcIsG*j
zCf9z8&!DA8wF;3L91I^DXg+5VrCV*C*KE8jS+2J)2h7Y`S@$GLE>sfE@-gUnB&GZQ
zoUHhQbEu%pJn(;sBzeLR@SGGqyybQ&${hVBcl>6`$-HAcq)D#HY`R!F(TtVJ*H~BC
zN|OV}w5O42R857%KQ@kC1|k>BMevTti7FgX=?du;jBgwc-TNYfk44e3qSGLi5&GO@
z8^1LKT7ag5g9A%mc@#L-@a2mTx;u7)?q^XG;hE^<&%k}&p9e{Z^T)?LJ~EbQk=5()
zGCw^6E`M{9Voczg4}N8=t|l=4wS6?`5T&Vj7O_4fFbI
z-iYA$Fu5^Q8EIaR6P(>zW!MuRdR&f+Nfe3hxka#&!1e`>^datq+wFijUp-zUCEI&D
zRr2m#R7l9HSc3P#0Ryd^%m~u;eaUmE`VyS2Gd^
zi0XhDAN2q%-y<9*I34ljz(3);UQa6M6s|5V(s*4N@*2)Q+2PETy|bvtb#(`1)$63Z
zN(AedPe{vxyE2cI>3U?o1aFXBv5avNQa|M>u7C2U?uF@oe(>vhi*edS#zRCc({g|J
z{kv-h`qq~>cN
zGO;H|eWk8q(h1Fhr4trjd;*gAhKI;JbJz^p@362`S;j?(Uau~ZT_m3&b{0Z?-~>K5
zJ188QT+6&@KL1bdFUNX}M7?~Pk6{+36A|D29F&rin|>|3J9*enEB4Ir;xfpYelB3!xyMka2r-As@hzEL9kDszpD8)=VJVvj3dwiscK8Z#
zJ$w;b^57`%&zW3=wRiof{KQw+
zY2$d6E5KkisEQjJZA$@6DvLxix}ua>YuQp5YvC08`jC&a)73`aj4K}OrKY=*#sSkX6kP6zS9*GS=H~FGGVXZG3fTg=
zK?D8Y;2xBZ!s>W#P*ur*K`ov_;=1q#^#64Es@g^{B1kb`#|KrdxvbW$68cMT@7Y4e
zx3|db)`8HMeN~%w8gsH>n(n9`3C+e<3!GfB;L;3+`9+-wu|2@fa`9@n+*CaiEQ8Xa
zGVv1RbQNWQpdim#(ora_Z)eZwTsQad4NOENOKC0kc7|}w6>;>Y*ZxUJD*E}gzdA}$
zst#?1xz&;rmPn%NBfs7{&L&`C3s{7U@>s)1QF&%0{^be7e*Yp7nU@XR+T?2>8iD7L4om
z=)&_PE`Yw607I~G6IzNkhl30YlB5oN0n4&;GwxH2nt%_`oe8baD}1+e6ElA{{Lgi-
zZjoKxWatp%dxyJV$LU&=zdzf`2pAfU
-b@#W(1(~ygoNy_b67!uxii@&y>B6W8rIkwK#yDZl4&uQki9?agYd!t8aTh_De*%j?BLdaW)=snfc-}J{sEYvC9()r7KcgmsbP9~z~obzZy+p&|{H$vRu^nd{uE|E=GiPSkX~
z3*7(_I1p7tsQg%w18g*k862>w0pJz3ShYMT`>*JV1mv8OFyK1v$*%W
zm^)A0M%Y07^H@8yB`6hdCMxq|F*d#Yfh(cv((evahRi`d^anlwaQ!ABCdS`-C`ic6
zl(%sMGzM`UV+uijAtTx}{K>>wz3Pxg*u&`JsqI6Z6MIEUp&xxEYWaygX7L}l~59F_=5(hDPFj#16D%E(lPJGT6<6h*w3
zG)3cdeplMj%=iW{(gI;)MH1~&0ai3e(?m(XZ2@cVmAfeGA>9@*mlylOBy;odc>pmd
zQXTE^M>#d(#+bG$BB%k?;FiE=*90F1CV6anKN0>a>JJev
z6`Ky=zWuQ)FOFL1s-(Jt(s^U>?%%7^B`e*Vgve{E=X~>2jS~k`Z>u^hFSZq81hHln
zVB{}uqXfTC1W!xA+n10(t6(g&5e9_csS^UWkD%CtcNxV7WDRWpwCwKfiF*cz=MPU-
z46PtN-*Um;Yl?tCbg6mU%drl&UzCT_<&GSdbKo6z8aNTym$>^sJd@Ku?}Vh?1@~U)
zX*Hp5a>CD6jvz-XkhWcH)wghbhxTR8atlZP?Nz@^FFyUR&%#1{U-Xe+liYCla%TUZz`lE&o9FC3o
zSBHPD0FCiemUo|^$+7C
zqYEuY_Zw}=KR!Ng$sh8U1Hlf;)`jmF?WP_0kfP0%;w3%AOpFyNqY4OD=HP`#Z<${4
zXt$7__@y))Gk~1m%WJGx$B<3!NcVwES$n;e`>2&aO0obPk6kncZR;Y%W_px=HZ`P>
z9Ny3VbX)1#%wrLQ{N_e*)x04uXpy0&S){?JX;suGSmOZwuTs8T`4b9G=AWU6+MLk7
zDuU(CEagviBzA;2``|Dd{LJnko*!@im9E+!M5i!b%#Vqc2`cKry9uUNjQn$X=#i>|
zGRMzA-7x21O@0Jg!6q&cS5LNmXoabMbbojT33yQ@><+uxido>yi_n*VPq5s_HSl`8
z-3fUYR4?cBvk&Uk^26tcsJwrRcyD`eP;4X0y}mb&vk1J9V=!yxf{vHRg@uJCfgo5S
zM|W&|-WNn#Q41~tx(G_L+&{Q*{X7yC;_7JerhXJ(5G?xH%waFZMaf!P>S{n?gpBNg
z7%SW;oT6^E9=8hZr(&29nNa4kkCmJW1}|oohb|#I(jipX?!*LHIVqV*Y5F?U+7%Rv
zU4QFZ1Sb7sAYwUA>OxMO_B{z(lgQc
zTRh>KbX(6|-`z!AcPlv^;P&U|i(U=@rFQuQJsWwfWBdZ-gK5__)&%
zsiNVIk+m4;rPMT6rlNV0^u(J-4W@2J`GM$LMM?NapQrtg(@&MDe3tbxdk?!uYaJ}N
zKWq0*F7@>HjEGbAYz~gNCw7oCbwX%g2s3a~v+nwvGG+#`S_tMkju^MUl7!!c8*!Ul
zZT9v0e3@<&lCz8pmsbv0YOqAtv44Gj09MF4-(QYLHfdV5hW>!l0>buOUK1Q)YI^ic
zYV_`-67+#qw6K?5wJ!_+lTZaB`@rF3etic5dwJ^Tcabmy;v%Ep
zKfIfla}4CY3Vpjm9$)J(wd`%n4`+GH-Il0K+za@9vamf-2Ap15ky2h=fU^KvQMwGM
z$sV9=MYR3$x-bn7>X4kMs04LbfU0t`zsTY;nu!knMJ(7~R5kQ#PvX=MjCk6WrkeZEtkP7X1E3fOD-m&8
z6;DAY=8w4JQDYTsN2PM9Zeb`8W;Fm~JS{D}Z-40^wum{*Wl6N@-Vq($j}TK41P^S&
z^}W(5@!QSMIZE%?$FHMD{VXdQh&w9oJ?qYEs}YK4(#$GF8z?N=ceUk8IjErVuc@!E
zk7okGZbe#wB1s8+#iJD7*o(FN~yFRsg`Rs<|
zisd~GYttO+>UaJ5n$7J)I8t{{&xmH2YQ-#`80xxEyD%2;mz;FI-qf;1am$s^sL)z}
z2MIKh*L$iA@|*l#9?H|MYMFVX-@^>?QDJ_ZLn4)}sVZszQrphNY`B4w8fN+I#vx%n@hcNX)x86d(^m$MmItewH@;@|Vm&Gk>V}b12SXu%M$EmZlBr
zXl4`MXY(de1B2{u-;mQ0VGuJzw_zhzCnxKx1V=O(I4YmQnU&fWg9Mx%()UQ-etzdPsIP)_$3NjRIsHuI5`1zP5Uyk+0O+L
zWZ|1}2|oR0z8q{JzzO+-`=Y7dun`H&z}sM!x3dibRfkURfaB8!
znS52fieDpxoZYTmn8@?r+YtGmUIJOMqIN*^4d^3ux~iMF317FYT7j}xp#D+1xQMKi
z$O!}^?WB2@sJ~Z*Czq&}%>rlv&^xPn&wkynR+|{POMb|8{;r=9845gY-q#11tQk3?
zjlk-B&J~$4&U>5vmR98rd{50-)NV))XW(QXh$fK8eVm@4zgPEM3&&+h!OEX7xJ0j(
zMzl3Gq5OiI!gpZQ#-l{K)S}7)cznQ|#ivyCPw2n?{@Eh(Y8XlpOD^v#k8-~@7RQg`
zFqa%mJYCD4dqG=u?9{bCArqlEJ_U5<5w5rj;!;KdFE@ZaFf4CtK&@T0x6@Z*DMei@
zMQbTpYC-*Z@AK|ED>ur-!g(}Znhzhpl|wU_EdSKgF|p;ucC=jW(oZ6+*-)S(VtPT7
zSXc+mB_R$oXYx{czx)XY7yUjP^D|mzkuG)#UI#;thPv`RXPgm%ts(#MNPF=8^|8-s
z;>5%R$ln6K89}J0)orM5+s7RLOE`|eD^>prJ9($v^ML&EOlgntf{Vb<)KH{{6a%gq
zAW|||2;ynLCFmpxhA(2&R=F69G!ij+4|aBTvd*QzaR`V7;?EghrC#)S{&X(aO7I%B
zzfd#76-?v5#i=xx%U)k)909piV(GD~I3#&$JZb^-iQo-PKuZB*n(B9Fq@85(fSiGK
zK1rM3K1+!D%AnDAeN6OSziT_V-C0>QS0VNh%sp)S4m#}rdG&lTY<05+Rx!6p(sQm%
z_E*A1uUFnG0uqwnAPJ-EP+efWV+Qj$sfcsN012``TeMdK}4hwUUqVFB6l6&CSM|6yhqfR
z*?Jx$a&w8>fW_(R6>v_hh$u(&=kZFVz@S`yeDs^bc&s7-NPPSzKcqPFS1t_?XAsxs
zlOW{Kd%0Z`*1}`c`Y~DS6buGJmh|PZo{{OHCe;`l>96Oy^knP~+@pP<}fwCXf{zv)P
z$zXD{&uQz@Zw!GA>hJJWSf@GfOIm-A&MDlvxx+n>nMJ?~8cNFh);lwX0Soi<<54XzWdOY++{bpd0=MI2rcA5tksE!@uYL2GC7qed
z6(g}|9b7NVj{a#3_nCmHN+*b)Sfhctx%~YxkVN`qSVQSbfm8IeX})5)TEz^8Mh%Y^
zIFCPSR@t$PP@7Ub6SX5d7GST_6cyxe5j#alU{6ceEdTP-8D|jk6MF{uCMlwIP{jFz
zlIWBLK70UA%PXhFC=jCGuBVlY6jG@_QJ+PR>BcI*&Ysn*Uy--S+ZPZ8y$%~fY=uJ9
z(YDUu_HhX(vog%FDV`57b@AC@!XP1Q1Y;Z-Wp@1UpM1%RRPkr4n98_FvaK78Blq}VWV3SBfF))h
zzNMZb@gr)>UFl+TN$S#VM?YzgRxS}ET5YwIN^b$@l0rFwe?`O}kd+XpQV?K1YBINf
zcju%$A75TvTUwbhHbeLen2$glgN1=HhV)ba@mky=N_{h_)=&&+KJwPJaq07pf*aw%
ze4ry8EfB8(Y`d<^S(1vS^eoW>{Gro2p5`lNn<>U?f*r9hUtR(K!dc`U%F$w-Pa};C
z+g5iFG^4FRmf+h7QJ4cv2*~POGJ{$SHX`))y4yOpEd@!lO=aR@&r?ZE$5b`
z)?fDO17>4uoRFCH(tk-I)0kKiSpWxWCYV8xv2hc~Bw`sr@1!Mq{^w~qdU(X=?HZ-C
z&x8Q`{h#lRm0D?eT3e-kN+JRMCM;ZNA29Zv^=GM+v3X_G`OYskM$RSw`4&>6*#qyrK>GnMmB0lAV(_SH
zx5?04W?0bK*(`mkjM;L6PYOC;kI%E8$T!o^A&UDEf4)3
zA}$N{*1A87!sFTi9wXB6fR3+2D09wMr*J7VNqb!IOLg%hdb9}d3Y3C+{c8yO8isS3
z+e#?+TZONCNYDA^+rXMq!uaqRkoV^;aM{6;>NgtAj13T{CcE9i6bpR{EU5@XVq3ub-a2}X1dU<(i
zxVsAocs_eB$AWWPaqxlgCaC2dmh-j4#sp8y@AmdQBtUhOT(R1GZO~_Z%FH5UN?~jf
zpR80pk^7`KbLBE_si(n+Rig86UE}IcC(FCD=P%c4tZkPUxJP5FJu@#HRq$2=AU;aS
zWz{vCl??(vfk}KT0kAe81?vgU@c8uRIWWIAIh^q3aMIet37Jm+=Su=m~0^c#m4K+Cg;%gw0n#Yl|VuLS{-U3Z7H7FhEjrOpmas(MAd}_x)Lk-
zmG4ePGsNKC!NHAMr54mq`I~Jc2~e;*#BJan`uXWuJj
z$3ck?cTHgTGpTwsd?>`1yS(9XB&KWrjNl6!b26<={`H01{4PFUTDOb**WF_36hELR
z?11YGMx;bIN5fpp=mGzY!19vf
z!BkIoH>5J;g5pQyu4!Pss4?m^yk~qQEJIWmcHJ8RKIX^;WB&Lmpx8{;LK#Z22zfkw
zSs=3Ws@#%Hc3#fnm*e5~sAo~{o~%jyR+yG#*5`6tb8A6td&PSc?%sVX?ml3Z&>tJq&+h7AcMSEEr?{frHC@m8Wpd35d8Vn=$tHNZrfXJwEbj
zoxO8`ra_N;Xrru&N-z=q)AnipH14nqXAS`cg&1%f9F!~@kn4*0EBl1E&OqU;vZ7DF=Y~$vqVv?!pf}X0Kk-~YO|H**^0wu0?58#7@8q1N-;`r!w;vZ9
zSj_7411A99!u$R_zv)c`SP#LF6?7e7V#m9)IC!g+|M#`s3BCd`(#nDI^<|-WiFB7p
z^X32`=i(DTY=9s?t<%r*zhXwj+|CN%s4hkDHTVnsU~;CPWs7V-ep>gxqQFlQ^+)tT
zEYhG)$#c9--7_4EZDdNVJ=*+tvImpS#k5e4d<%((LKr-dk(?a*R$4F)*wl;1@%0j%
zYmi6yE~7m?P}<1C!7(R**tWH`wX+jCk5ho!eP(I|_oi3x58q7>ZTP$GhWT0SUSEgn
zn5pWCtw=cNFIJ1`(p;})Kn@ZCs+!Bu97_%9EO=we#}D4vj}_vwvx(C+@Db=`nOzQ8
z+Tm<$a&b*?fqe{`sR4@VV0yGPG3!?)C$4l?^Zotqm1lkC1ae#F=|~LPO-0HtHi^E)mXQooRK~?1
zg2K}|gH|6?pOPF1wnEaQCyIeY9-GwjWu%q75x1>Va;5aOMgAcEkmvj`DZIx_Uea=CZdC_Mi~kVs4`Gd&m&pX=p;
zmGOLMB4nds96abWEcs!^CO=}UL|VF}N^##t+_@LbwQf;uXM200*}-hx`xyp#c4Z}-
ze>rvdJz_eB69UF{U?9Yaavr33;J|-$5BdUBYxRD=)V&?IgsCgVfNq9O*m(t-u>mRc
zdwO~#-6zH>q&A$amD$-wm)*Yr#ZjeMiN{RdnC0}NF@NFcCv%je>>uy9{V(|ma)-LK
zrhuYmipp89YTh&O-_e=Lk5oyJJ-oi2%aqMy@i!SfMe2L^Z_?JNVd7$y$arrcbvcnx
zM*@KIOA!(}=XHEK)WG{k7yTyyPvZx!4I4Mp(_z-Z&NcHUW)XW4R2akikFdRb8C2e`
zW@erd9J2=B|9Q8}UN}{6scC)4j&Me9;U~F6-g0gXy&m!Q{dkfk1oznr%$3=>@@#|I
zbkL`&fkK{l3q}FkHb0ywIs);2o5CTU7X-l!y!
z>TNbxHijv~Y1;`G0Llxj{2*ky1(IRL9bR9XEk}6-ia!{WnJso})~=0Y5g#NWgb5@JtVfw8^R6v
zc_=sR&RddYlw@q4W>g_70sZMZ>G(!puv(w*Q(B+}vNAHjy}||rY;e}K))(c5{b;ABcuEi0%IRR!KxMPDBm*S@TEHIEd_#ms67Tzc@
zbJ0OatW~7g6Frpt_gfH~;Q(!Mom@ujT&ivVO(}ACXc!mU+zYUK_)p}C{l|XQTdh>8
zW}Fu`3dyMVdg2y2@YPpv&D#q{C2Fol{T=aC5Ikap$Svak82a1o<t7ZKU!dAO7%~=}bJf;q{a{3;|Iq$cc*M!gOh~0kV8qD_pZJzcW}RnbqpQT|UZa>#
zZ)paLtUp?9@Cp4FA+Es+vAuT!RbUWE4Up3`Vd;=JsYy%2eL;%{CKKgRq}@*^_GTi*
z7nd?DiZSy!zYmS=`J2o*ZEcT$XZ*iJFJJ`=rlA~z`Dmdi)g?=f($I}J+5I99WW+0#
z^`|g&*3gK^u*gxrE^3Hg6iKMtAEes~gP&EgJ(0AkQ8m0hlvzbaV!>n!ortFj^rB$Z
zh7OkI-?Uzlle_K3p!JnD&$uzu8OFV9`9UnxVD}6xn329|?0vYZR5Zle`$`bG-{b%n
zi3a0tD5a3-tV|FKgCX-6*kNUH6b?vXBA=UIS@0TYd{PU;y2@r4)n%`9^@i^^LK@OB
z7%ZxH+Ux3m0VkdR^vTOKEj6zt-KhqKb@06xgvv2f{%4Sl0rV^S
zH5AdF6j`CGjl*_-OJgyl#Pp2~HGcKRA3qw*(4{M9EB^ia^RxpDBcFcz%&xDCtRqpq
zU5Ghs0@@$82fP+^5}L1#i4Dh3_iaO^f2W-fPQ%5%&gF*-0>u_2#!k~eiaSXv3&tilQ6MEa!v!%VNe(uxL$$Vj8Q9
zr^e{&Ukb6^y_7DX4s{_yK|=>6$B~J^@lSY+7=MH0HmTE_EBkr%2u*6UwJiGfj+hVL
z<3o!rQxz+-S9%f9torlTQ?gINo4`WR)jL9Sk33bo
z(a{C*2dP3@SEQ`Uu@Mu~WQYuLo^5Fw?Mc>~PDw~Dl|>Ct^FNcOy$4yKiEChnbO}3q53qtExWI9-FTF;3BWOU@8O8cz
zjkHFIQk@;K+$JUK_emGtjg(!>C7^C~oYU=s7I9-EupIi>#K^U?$7Ue;zl61}JKMO+
z?HS;3AWHNR-ysvO=9e!_VAS>Hmqen%y~>w8fCmiRSVQuA=R|jc?WWcYzZ_paUi~s*
zW4=T#>>;A&;7>K?bQ}jAk9(Q+s6lUDAQbd1upS2b>$F0;7&!=<4S=qpN9nM8=%&;$=JcLx@Z3;(&)J{&iwC?cS4j=d+s!ExK@g0z}=
z6EhQwLlls^GDmAdBtkbRRV$X)SR_`17Ga1C3_*aVTIbw7NCYP#6cxpo+7D`-ac>&^
zD>dqy5v6E_yDirpBqv&ScG?_+ZBgXl!h%BA+>)DDyfy{~;B{d)&r<>FRhTqGJJmMm
zRzRYw%5uHr25kj#q9&x*+>h`di-6mvgv-C4?oUe5eLxkqU+)mwz%JmVpn%Mca|E`+
zy+j&?I#}sg;-M=5?``3OhQi|350c#YedIsKWc2ed2|rY`8(31
zg3OruwA#&yfPmIEh}Q1m?%pQ=gI}gm@yl^N_{d72pj@j!yiY?Aqkuc3M=e*IO0#ro
zCz3`{uXnAR8ydEZj6DX}&>^|Ie+kT0_Tw!xWeQBl5xZ$v=Gx5djnJ65U$Ey8-M50p
z7lGzhbEgsgpMYLK6eI|3UT#
z&sc>h+#jcJk4J3A_*MNYEw1^hKxIy+FO$&LVjI_)FaHRQZ88Q8kj(mN42`JY7}RB$j5s7r)Jgp!LDq+k&eIK*}{
zmo%7K4&7N$J|yEzcX4A((Ni{xW1;G5)*tO~3b`f@a{-OIi9Mmoa-3cpuN+l0g5LBH
zD;t^z7`lQX8dMQuJ&eq+OF9#rDt+3yYOcm8if7<8#!6AKni6Xdy|@lKTEvmzJBM4}o0`
z^exqO4GkgRVI)m9CzVAN^Kn*JRtPoAttff^;%dc1Vx!ovJrRY!nWWYwHm*bCb^zW8
zV|W)4P~*vv8pE1ruTp8(j+Pn%h!fXauU
zdl1b6NO(y7)TdnMLnb4A_vv8&Y4aY{)yM8oyfC
zE~C;vj(s|v^>_}KNeiTv4?V(g+C#V*ROzq7td;bU8~jwUdG#4lmeUEyzOkcqP9h6tVrvMhPGztHiL$l;H=sgo|Ax$ZcRtcEmAMU|X9K2%wh(p_
zh4a9Jo>`fcQIGi!Du(5mnzFWI+Xa=n4o$@sh$u*93)C0QHQd`5O!HVB_<|?UUNA`
z!YdT#cthcAF7-JvNGr1HTHl8=$T)#^65J-?#{-=M1sP8xCgwg*InF}OE7#2K>qRe>
zF|ZbIv4c|u)}|+YIzUZfnB%{FUU)+Uy5cI>_>eZT3}@_C?d#%i|M@vF2LD)r$q86Y
z0-^kq2LZ4{?f&Yd`~OYE|4}(QI6=;MXjZ~CFf~);u-cj#V5lNh{}WzbNegcusz`
z+qR%;dl#6cpZU5?j{QxLiEf}g`X;)&TIQ#{>Gfi=#BZ?^+>FKwCFme0oSE2n7|EF|
zTS#OODFD3`96h)w4XI)V%w`hB@+r+H@9rO0M>p!|5+>^ztTPgcbw=t*GC!V8!e#jZ
z*NdAulPUT>Fee7w9_x-ktB~v3_BDBb
zktehB_;hT`J;6+ME~P=G?kZZWQK1wQ6T1J&xG(c-h9FO8o$M%-og!45&arN+oR9b0
z>u)DV)O!n{jMhQ6NPGpaKj1B*n_usDedQ}O^hB6B5z2#=FK
zXnI=Ee7g*~!xT6mj88V00M0e=XMrJ8Ph&|U%NWzh4Z{y`d*F%JhEZ*P!Qm)T&K=9A
z{f=p@znGqd806WardetFKv7K?wJ>3E^3&LfLtA^EQUpFYNF`|pbaO%d+|;F(Q40~l
z3i6|g)Wx5nxXVp)OR5LQ#^xOAwbfkD!C(uJeoS5UbaWut5jZ5)`p}GiGYPXy_ef?}
zilJNBp|-y&9gPxAEY4D#CEox`XCUil%M`xKBM8hMEYwqZNhYLtT#=BIfT<|00c13!
zFoTTzpAx-Xe|*20yNiZ0opJ+6TGK%-D|k#pk7xPH%oJNKX7ubmC>Tk*0X<_&77x)D
zI2agW0XKw^_v^{&(b3VMkV}D$TL7txZa3TfdcWDR{HjIc5E))6==32^JE?WyD}X|5
zs;K|TaeP4s8V?2%PnIO*dmf%w@YN%5Xy>-WxyH$$+`oTe?9kHTm0EkBGdT5Up?9(V
zt3nIBXo+2UP(0V0!Dn8`vS)EhGxaHMGv}0l1~RtD+P?jrSfr$ek={Kv7w6{?MJDg3
zH-E>(D1m*3U2lBhzre~S&|fw4FTDsC?Ei^@&kz`f-EK_?3QEWjvq^QEE^Y%QmA724
zUcXn5#a5%lhB{?2$RY!rX?FjqdbbE{h7@<5?u$;PrP*`lhaDS7v)Gsy!723ZdaXf0
zV(=c+bacJ!J$3g_PrW+0A7LXs%7o|lSX_)__;oliTLNJWQ=nNrn>3^Wo1m(?T5ilm
z7y{bFUC9D^X
z<#}(RL4!q&A1b(1Y|5fgAyNpiu|s?zWK|07Mfd7v+pwlMMiCaXJ;9dy#t$ql+dH+RTKGV-!v|MqE~O!5z%
zm~JLMfeooZkEv*1!~it}H&~{?-kIDkRYa;msPWx3)(ip%ND7@G&;?RCC${$w`?=kh
zz#WIm`P0R&^V)8xCGZ{1F*Q;GCjP~#w}zrU`aCdrxK=|RYLHV<R8*cMx*8+nlg7n09z1hZ)tePDwo8Pf?<75@OO#BM&NJUBK}YYXU_;~7fhPbY
zJMv6xO|i7H+Z`+%^zOMJb?c)|5*-A6-A}B&bcu!qNO!Y*e7|g;Al@K`SlHSw$B+Q&
zAfy$F8Y697m0nEk<_#H}huh|l>i{umN$(4JC#DN8-62GBpj+~I2HDx57a;i&K0JW=
z1HkHphlfK21wsOoe4k!Nj7;z%)DF2#SMy`1jWs6beWt
z{tt6EPOxfPuC6ZiY=s2B5))WV{^lcgAem7WdPe1vhW_5X8R+`u?T-dK6$=e!zZ$?A
zj1-k-4K91lZ2OZvKL|jm1*rGNo?njF`GskqDW{g&52j%P$)sd0WL<0EBY>=EV?ufB
z<^ti@RQ{>aSrgrWr)dpD+`9sY=tkUHM_<@9vxt9feBB$wManNPZi#OF!Ol%vIjSBb
zRpJeyC0Bw@z#)0H&)&|YT{8n>DQL+R2ju1@`my;zLY3TNE8gzX$c$FS%f@O+B3CE{
z$2z&zx@!KA5t)yVJ{HUb0x5lHYmVRSrs-|rS8sL)$w3qxEUf?Cs^`iWvpMKSpu@jx
zbOp^EcI>RR0EDLMy$gs%0JU3Af%-kS*+utNDJkqxy^=W+xp2X?opq_tETP6{q%cAM
z$7F3SrL6gBh!L=K(iTXpoo}xn+~~$xA(X(ps_VccYA*V0-FHZ9L{ko_1NzcY*!RY)
z^JBj$)k@(W<|9xxUL!IM(13He5kF4JM@wUCKuxd-##*zi*`?N$_laYzX7`_W>)eG?
zrS*62J)ed~vAU&}OlYpUDf0Kozbfuo
z@N9tduQ?1Uh0}V9T2Gc~;C6T{;kyq$GYlU;I%+qaFuNe_#Y5WyxYbppvONvQ1Nyh7
zv%CB1Z{J_7=>r)p{|q?mF=rv!%G|sdd}l<4sYrg(*1Ss+U2Ls;ZO_CKfUmO2(ZRL}
zem|Vd_)?&ngxEqiZV7^&j+a*~I~s}J+a#(Xi_l&+fxHWWC55LJQ3aO1`21&mVj@E}fS!*c-NxM%B@`H?
z?)Ht>_bG`x1^5Jn9Uz=ZiaVVrFO@EvZWdbta{A;#LNB>WJg=Xii2{?10xUlyFWgkj
zS?LX=`Zm^Xo^EaLhQt3$F>2A)fpn?@6E#u3gLiXSbNtKXS}g3g!TeDPAhi5ti0S4d
zNnS;=C5jQq#X1%PbQ1#qJh)Z$fh!eQ!qEM)126@3>c(C=&qSpt{b{6TOEj5y!x>*~
zd0vUNaY>W7Tt;(V8f1Qz=@R@)%}(v}!SKobhF+mSEJ|(egsB$r?rj)T?-~6LHn6n8
zQYT>T>og!U&TUL+Gvg&erT$GXGI=SW&tud^MV4rn`EXv~_9PTIjqwknCbAT)%nA7W
zmxqI$InjSfKO@c3_9XcEB8P+7dou&Gy}01;jp}z6V*;TQNzJ7JRrI(tZPGj_G{?T1{X*3Sf;MV_l0GZ^4A7CBnJRo@s*htJj;1li32NB`BR#x8#q
zczK@u8nhG&IN(w6op%4?ViFMyYI3ajZQlOa)?O62qz$uMQndb9_!swXj8IC?1k{)`
z)=yi%>DEIQUoSTwlSu5|cksli%-`zM?q^7F#Npw)IVg$45n1;MD&pVkK{1)bkPv%&rUgAq6r%z%>rRwJcTc*uf5Rl9
zH+>*7iu}z?O}#oDc^nz?Xdn0?gS_Yv
z$43&91>mT-!6HDvwjrngY^fNAGyZKj@QOqKkw7{A%ku({t>zz6tI>5|pMggBJw@L$
zsF1-!X+o`qJg%J2#A>1V%T1}H2oC>doy?3lb=nO{
zN@IQ1mReIyslJeM$?wXf>X;@0$d*6hoghl2qM{-xDJk}fyZYnu=JH9xAphHU_eGBA
zyq(La7FEEy3?f6L?6jR{J=Y#3=*0@Vf!u~sV654rYE3-HS%)rJEGZfCYVW*e~Qs~`i#1#ZNa6H(Vs6m
zM`UV5iZHNHr5v&qjSG=;)+azO9}-q&zT2t#?oohjV|q2QIRVsJgeyn5h-yLi)h*6G
zvv_IRI~NZ=K1;>0AGdDHK+zq&(@AkPX4R=P2Caut!5E+L14tHoK^7~-GicLWKD+Of
zkwIUDvH6#a>vq?V0H!8}c~A0=zBzp2$L4)(+CzYc0x#Gc3}{6?KW1